Skip to content

Commit

Permalink
ComponentConfig.java refactor second pass - thread-safety for the sta…
Browse files Browse the repository at this point in the history
…tic collections. Also fixed some flawed logic in the getComponentConfig factory method that caused redundant parsing of the XML file.

git-svn-id: https://svn.apache.org/repos/asf/ofbiz/trunk@1534012 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
adrian-crum committed Oct 20, 2013
1 parent 6d6ccf3 commit b888457
Showing 1 changed file with 98 additions and 69 deletions.
167 changes: 98 additions & 69 deletions framework/base/src/org/ofbiz/base/component/ComponentConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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;
Expand All @@ -34,6 +35,7 @@
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;
Expand All @@ -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() {
Expand All @@ -73,13 +79,7 @@ public static List<ClasspathInfo> getAllClasspathInfos(String componentName) {
}

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() {
Expand Down Expand Up @@ -189,45 +189,46 @@ public static List<WebappInfo> getAppBarWebInfos(String serverName) {
}

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 {
Expand All @@ -237,34 +238,32 @@ public static ComponentConfig getComponentConfig(String globalName) throws Compo

public static ComponentConfig getComponentConfig(String globalName, String rootLocation) throws ComponentException {
ComponentConfig componentConfig = null;
if (UtilValidate.isNotEmpty(globalName)) {
componentConfig = componentConfigs.get(globalName);
}
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 (globalName != null && !globalName.isEmpty()) {
componentConfig = componentConfigCache.fromGlobalName(globalName);
if (componentConfig != null) {
return componentConfig;
}
}
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);
}
Expand All @@ -281,40 +280,38 @@ public static KeystoreInfo getKeystoreInfo(String componentName, String keystore
}
}
}

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);
}
return cc.getRootLocation();
}

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);
}
return cc.getStream(resourceLoaderName, location);
}

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);
}
return cc.getURL(resourceLoaderName, location);
}

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())) {
Expand All @@ -326,7 +323,7 @@ public static WebappInfo getWebAppInfo(String serverName, String contextRoot) {
}

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);
}
Expand Down Expand Up @@ -571,6 +568,38 @@ public ClasspathInfo(ComponentConfig componentConfig, Element element) {
}
}

// 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;
Expand Down

0 comments on commit b888457

Please sign in to comment.