001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.core.config.cache;
019:
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.sape.carbon.core.config.Configuration;
025: import org.sape.carbon.core.config.ConfigurationNotFoundException;
026: import org.sape.carbon.core.config.ConfigurationService;
027: import org.sape.carbon.core.config.format.ConfigurationFormatException;
028: import org.sape.carbon.core.config.node.ConfigurationDocument;
029: import org.sape.carbon.core.config.node.Node;
030: import org.sape.carbon.core.config.node.NodeIOException;
031: import org.sape.carbon.core.config.node.NodeNotFoundException;
032: import org.sape.carbon.core.config.node.event.NodeEventListener;
033: import org.sape.carbon.core.exception.ExceptionUtility;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037:
038: /**
039: * <p>
040: * This implementation uses a synchronized hash map to store all configurations.
041: * This is a bottleneck and may be a point of contention when many threads
042: * required configurations simultaniously.
043: * </p>
044: * <p>
045: * Configuration objects are cached lazily, i.e. they are cached after they
046: * are requested the first time. Once a configuration object is cached, the
047: * cache will listen for Node events on the configuration. If a node event
048: * occurs, the entire cache is cleared. This is done to prevent inconsistencies
049: * between this cache and caches internal to the configuration objects
050: * themselves in the case of nested configurations.
051: * </p>
052: *
053: * Copyright 2002 Sapient
054: * @since carbon 1.1
055: * @author Douglas Voet, Oct 24, 2002
056: * @version $Revision: 1.11 $($Author: dvoet $ / $Date: 2003/05/05 21:21:16 $)
057: */
058: public class SynchronizedConfigurationCache implements
059: ConfigurationCache, NodeEventListener {
060:
061: /** Provides a handle to Apache-commons logger */
062: private Log log = LogFactory.getLog(this .getClass());
063:
064: /** Holds a synchronized cache of value. */
065: private Map cache = Collections.synchronizedMap(new HashMap());
066:
067: /** Holds the config service being cached. */
068: private ConfigurationService configService;
069:
070: /**
071: * Constructs a new SynchronizedConfigurationCache.
072: *
073: * @param configService the service to cache
074: */
075: public SynchronizedConfigurationCache(
076: ConfigurationService configService) {
077: this .configService = configService;
078: }
079:
080: /**
081: * Gets back a configuration with the given name. This will
082: * either return the configuration from the cache or load it
083: * if needed.
084: *
085: * @param name the name of the configuration to load
086: * @return configuration at the given name
087: * @throws ConfigurationNotFoundException indicates there is
088: * no configuration at the given name
089: */
090: public Configuration getConfiguration(String name) {
091: Configuration config = (Configuration) this .cache.get(name);
092: if (config == null) {
093: config = loadConfiguration(name);
094: }
095: return config;
096: }
097:
098: /**
099: * Updates the configuration cache with the new instance of the node.
100: *
101: * @param changedNode the node that has been changed
102: */
103: public void nodeChanged(Node changedNode) {
104: try {
105: this .cache.clear();
106: this .cache.put(changedNode.getAbsoluteName(),
107: ((ConfigurationDocument) changedNode)
108: .readConfiguration());
109:
110: } catch (NodeIOException nioe) {
111: if (log.isWarnEnabled()) {
112: log.warn("Could not read new configuration: "
113: + ExceptionUtility
114: .printStackTracesToString(nioe));
115: }
116: } catch (ConfigurationFormatException cfe) {
117: if (log.isWarnEnabled()) {
118: log.warn("Could not read new configuration: "
119: + ExceptionUtility
120: .printStackTracesToString(cfe));
121: }
122: }
123: }
124:
125: /**
126: * Removes the node from the cache.
127: *
128: * @param removedNodeName name of the node to remove
129: */
130: public void nodeRemoved(String removedNodeName) {
131: this .cache.clear();
132: }
133:
134: /**
135: * Synchronized method to load configurations. Ensures that any
136: * configuration is loaded only once.
137: *
138: * @param name name of the requested configuration
139: * @return Configuration at the given name
140: * @throws ConfigurationNotFoundException indicates there is
141: * no configuration at the given name
142: */
143: private synchronized Configuration loadConfiguration(String name) {
144: // this is a legal double check because this.cache is synchronized,
145: // at no point is unsynchronized data accessed
146: // this double check exists to ensure that any configuration is only
147: // loaded into the cache once
148: Configuration config = (Configuration) this .cache.get(name);
149: if (config == null) {
150: try {
151: config = this .configService
152: .fetchWritableConfiguration(name);
153: this .cache.put(name, config);
154:
155: if (log.isTraceEnabled()) {
156: log.trace("Marking configuration [" + name
157: + "] as read only");
158: }
159: config.setConfigurationReadOnly();
160: this .configService.addNodeListener(name, this );
161: } catch (NodeNotFoundException nnfe) {
162: // Catch checked configuration exception and rethrow as runtime
163: throw new ConfigurationNotFoundException(this
164: .getClass(), "The configuration [" + name
165: + "] was not found", nnfe);
166: }
167: }
168: return config;
169: }
170:
171: }
|