Author: adrianc Date: Sun Oct 20 23:57:31 2013 New Revision: 1534012 URL: http://svn.apache.org/r1534012 Log: ComponentConfig.java refactor second pass - thread-safety for the static collections. Also fixed some flawed logic in the getComponentConfig factory method that caused redundant parsing of the XML file.
Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/component/ComponentConfig.java Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/component/ComponentConfig.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/component/ComponentConfig.java?rev=1534012&r1=1534011&r2=1534012&view=diff ============================================================================== --- ofbiz/trunk/framework/base/src/org/ofbiz/base/component/ComponentConfig.java (original) +++ ofbiz/trunk/framework/base/src/org/ofbiz/base/component/ComponentConfig.java Sun Oct 20 23:57:31 2013 @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -34,6 +35,7 @@ import java.util.TreeMap; import org.ofbiz.base.container.ContainerConfig; import org.ofbiz.base.container.ContainerException; import org.ofbiz.base.location.FlexibleLocation; +import org.ofbiz.base.util.Assert; import org.ofbiz.base.util.Debug; import org.ofbiz.base.util.KeyStoreUtil; import org.ofbiz.base.util.UtilURL; @@ -50,12 +52,16 @@ public class ComponentConfig { public static final String module = ComponentConfig.class.getName(); public static final String OFBIZ_COMPONENT_XML_FILENAME = "ofbiz-component.xml"; - // this is not a UtilCache because reloading may cause problems - private static final Map<String, ComponentConfig> componentConfigs = new LinkedHashMap<String, ComponentConfig>(); + /* Note: These Maps are not UtilCache instances because there is no strategy or implementation for reloading components. + * Also, we are using LinkedHashMap to maintain insertion order - which client code depends on. This means + * we will need to use synchronization code because there is no concurrent implementation of LinkedHashMap. + */ + private static final ComponentConfigCache componentConfigCache = new ComponentConfigCache(); private static final Map<String, List<WebappInfo>> serverWebApps = new LinkedHashMap<String, List<WebappInfo>>(); public static Boolean componentExists(String componentName) { - return componentConfigs.get(componentName) != null; + Assert.notEmpty("componentName", componentName); + return componentConfigCache.fromGlobalName(componentName) != null; } public static List<ClasspathInfo> getAllClasspathInfos() { @@ -73,13 +79,7 @@ public class ComponentConfig { } public static Collection<ComponentConfig> getAllComponents() { - Collection<ComponentConfig> values = componentConfigs.values(); - if (values != null) { - return values; - } else { - Debug.logWarning("No components were found, something is probably missing or incorrect in the component-load setup.", module); - return Collections.emptyList(); - } + return componentConfigCache.values(); } public static List<ContainerConfig.Container> getAllContainers() { @@ -189,45 +189,46 @@ public class ComponentConfig { } public static List<WebappInfo> getAppBarWebInfos(String serverName, Comparator<? super String> comp, String menuName) { - List<WebappInfo> webInfos = serverWebApps.get(serverName + menuName); + String serverWebAppsKey = serverName + menuName; + List<WebappInfo> webInfos = null; + synchronized (serverWebApps) { + webInfos = serverWebApps.get(serverWebAppsKey); + } if (webInfos == null) { - synchronized (ComponentConfig.class) { - if (webInfos == null) { - Map<String, WebappInfo> tm = null; - - // use a TreeMap to sort the components alpha by title - if (comp != null) { - tm = new TreeMap<String, WebappInfo>(comp); - } else { - tm = new TreeMap<String, WebappInfo>(); - } - - for (ComponentConfig cc : getAllComponents()) { - for (WebappInfo wInfo : cc.getWebappInfos()) { - String key = UtilValidate.isNotEmpty(wInfo.position) ? wInfo.position : wInfo.title; - if (serverName.equals(wInfo.server) && wInfo.appBarDisplay) { - if (UtilValidate.isNotEmpty(menuName)) { - if (menuName.equals(wInfo.menuName)) { - tm.put(key, wInfo); - } - } else { - tm.put(key, wInfo); - } + Map<String, WebappInfo> tm = null; + // use a TreeMap to sort the components alpha by title + if (comp != null) { + tm = new TreeMap<String, WebappInfo>(comp); + } else { + tm = new TreeMap<String, WebappInfo>(); + } + for (ComponentConfig cc : getAllComponents()) { + for (WebappInfo wInfo : cc.getWebappInfos()) { + String key = UtilValidate.isNotEmpty(wInfo.position) ? wInfo.position : wInfo.title; + if (serverName.equals(wInfo.server) && wInfo.appBarDisplay) { + if (UtilValidate.isNotEmpty(menuName)) { + if (menuName.equals(wInfo.menuName)) { + tm.put(key, wInfo); } + } else { + tm.put(key, wInfo); } } - List<WebappInfo> webInfoList = new ArrayList<WebappInfo>(); - webInfoList.addAll(tm.values()); - serverWebApps.put(serverName + menuName, webInfoList); - return webInfoList; } } + webInfos = new ArrayList<WebappInfo>(tm.size()); + webInfos.addAll(tm.values()); + webInfos = Collections.unmodifiableList(webInfos); + synchronized (serverWebApps) { + // We are only preventing concurrent modification, we are not guaranteeing a singleton. + serverWebApps.put(serverWebAppsKey, webInfos); + } } return webInfos; } public static List<WebappInfo> getAppBarWebInfos(String serverName, String menuName) { - return ComponentConfig.getAppBarWebInfos(serverName, null, menuName); + return getAppBarWebInfos(serverName, null, menuName); } public static ComponentConfig getComponentConfig(String globalName) throws ComponentException { @@ -237,34 +238,32 @@ public class ComponentConfig { public static ComponentConfig getComponentConfig(String globalName, String rootLocation) throws ComponentException { ComponentConfig componentConfig = null; - if (UtilValidate.isNotEmpty(globalName)) { - componentConfig = componentConfigs.get(globalName); + if (globalName != null && !globalName.isEmpty()) { + componentConfig = componentConfigCache.fromGlobalName(globalName); + if (componentConfig != null) { + return componentConfig; + } } - if (componentConfig == null) { - if (rootLocation != null) { - synchronized (ComponentConfig.class) { - if (UtilValidate.isNotEmpty(globalName)) { - componentConfig = componentConfigs.get(globalName); - } - if (componentConfig == null) { - componentConfig = new ComponentConfig(globalName, rootLocation); - if (componentConfigs.containsKey(componentConfig.getGlobalName())) { - Debug.logWarning("WARNING: Loading ofbiz-component using a global name that already exists, will over-write: " + componentConfig.getGlobalName(), module); - } - if (componentConfig.enabled()) { - componentConfigs.put(componentConfig.getGlobalName(), componentConfig); - } - } - } - } else { - throw new ComponentException("No component found named : " + globalName); + if (rootLocation != null && !rootLocation.isEmpty()) { + componentConfig = componentConfigCache.fromRootLocation(rootLocation); + if (componentConfig != null) { + return componentConfig; + } + } + if (rootLocation != null) { + componentConfig = new ComponentConfig(globalName, rootLocation); + if (componentConfig.enabled()) { + componentConfigCache.put(componentConfig); } + return componentConfig; + } else { + // Do we really need to do this? + throw new ComponentException("No component found named : " + globalName); } - return componentConfig; } public static String getFullLocation(String componentName, String resourceLoaderName, String location) throws ComponentException { - ComponentConfig cc = ComponentConfig.getComponentConfig(componentName); + ComponentConfig cc = getComponentConfig(componentName, null); if (cc == null) { throw new ComponentException("Could not find component with name: " + componentName); } @@ -281,12 +280,11 @@ public class ComponentConfig { } } } - return null; } public static String getRootLocation(String componentName) throws ComponentException { - ComponentConfig cc = ComponentConfig.getComponentConfig(componentName); + ComponentConfig cc = getComponentConfig(componentName); if (cc == null) { throw new ComponentException("Could not find component with name: " + componentName); } @@ -294,7 +292,7 @@ public class ComponentConfig { } public static InputStream getStream(String componentName, String resourceLoaderName, String location) throws ComponentException { - ComponentConfig cc = ComponentConfig.getComponentConfig(componentName); + ComponentConfig cc = getComponentConfig(componentName); if (cc == null) { throw new ComponentException("Could not find component with name: " + componentName); } @@ -302,7 +300,7 @@ public class ComponentConfig { } public static URL getURL(String componentName, String resourceLoaderName, String location) throws ComponentException { - ComponentConfig cc = ComponentConfig.getComponentConfig(componentName); + ComponentConfig cc = getComponentConfig(componentName); if (cc == null) { throw new ComponentException("Could not find component with name: " + componentName); } @@ -310,11 +308,10 @@ public class ComponentConfig { } public static WebappInfo getWebAppInfo(String serverName, String contextRoot) { - ComponentConfig.WebappInfo info = null; if (serverName == null || contextRoot == null) { - return info; + return null; } - + ComponentConfig.WebappInfo info = null; for (ComponentConfig cc : getAllComponents()) { for (WebappInfo wInfo : cc.getWebappInfos()) { if (serverName.equals(wInfo.server) && contextRoot.equals(wInfo.getContextRoot())) { @@ -326,7 +323,7 @@ public class ComponentConfig { } public static boolean isFileResourceLoader(String componentName, String resourceLoaderName) throws ComponentException { - ComponentConfig cc = ComponentConfig.getComponentConfig(componentName); + ComponentConfig cc = getComponentConfig(componentName); if (cc == null) { throw new ComponentException("Could not find component with name: " + componentName); } @@ -571,6 +568,38 @@ public class ComponentConfig { } } + // ComponentConfig instances need to be looked up by their global name and root location, + // so this class encapsulates the Maps and synchronization code required to do that. + private static final class ComponentConfigCache { + // Key is the global name. + private final Map<String, ComponentConfig> componentConfigs = new LinkedHashMap<String, ComponentConfig>(); + // Root location mapped to global name. + private final Map<String, String> componentLocations = new HashMap<String, String>(); + + private synchronized ComponentConfig fromGlobalName(String globalName) { + return componentConfigs.get(globalName); + } + + private synchronized ComponentConfig fromRootLocation(String rootLocation) { + String globalName = componentLocations.get(rootLocation); + if (globalName == null) { + return null; + } + return componentConfigs.get(globalName); + } + + private synchronized ComponentConfig put(ComponentConfig config) { + String globalName = config.getGlobalName(); + String fileLocation = config.getRootLocation(); + componentLocations.put(fileLocation, globalName); + return componentConfigs.put(globalName, config); + } + + private synchronized Collection<ComponentConfig> values() { + return Collections.unmodifiableList(new ArrayList<ComponentConfig>(componentConfigs.values())); + } + } + public static class EntityResourceInfo extends ResourceInfo { public String type; public String readerName;