Author: nico Date: 2009-11-27 11:07:25 +0100 (Fri, 27 Nov 2009) New Revision: 40000
Added: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCache.java CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/UniqueKeyReadWriteLockSync.java Removed: CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/CacheSettings.java Modified: CMSContainer/trunk/CMSContainer/cmsc/portal/resources/ehcache.xml CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/PortletFragment.java CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/PortletCache.java CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/BlockingCacheManager.java CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCacheManager.java CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SiteModelManager.java Log: FIxes for Ehcache issues which showed up at NAI Modified: CMSContainer/trunk/CMSContainer/cmsc/portal/resources/ehcache.xml =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/portal/resources/ehcache.xml 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/portal/resources/ehcache.xml 2009-11-27 10:07:25 UTC (rev 40000) @@ -29,566 +29,7 @@ xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect"> - <!-- - DiskStore configuration - ======================= - - The diskStore element is optional. To turn off disk store path creation, comment out the diskStore - element below. - - Configure it if you have overflowToDisk or diskPersistent enabled for any cache. - - If it is not configured, and a cache is created which requires a disk store, a warning will be - issued and java.io.tmpdir will automatically be used. - - diskStore has only one attribute - "path". It is the path to the directory where - .data and .index files will be created. - - If the path is one of the following Java System Property it is replaced by its value in the - running VM. For backward compatibility these are not specified without being enclosed in the ${token} - replacement syntax. - - The following properties are translated: - * user.home - User's home directory - * user.dir - User's current working directory - * java.io.tmpdir - Default temp file path - * ehcache.disk.store.dir - A system property you would normally specify on the command line - e.g. java -Dehcache.disk.store.dir=/u01/myapp/diskdir ... - - Subdirectories can be specified below the property e.g. java.io.tmpdir/one - - --> - <diskStore path="java.io.tmpdir"/> - <!-- - CacheManagerEventListener - ========================= - Specifies a CacheManagerEventListenerFactory which is notified when Caches are added - or removed from the CacheManager. - - The attributes of CacheManagerEventListenerFactory are: - * class - a fully qualified factory class name - * properties - comma separated properties having meaning only to the factory. - - Sets the fully qualified class name to be registered as the CacheManager event listener. - - The events include: - * adding a Cache - * removing a Cache - - Callbacks to listener methods are synchronous and unsynchronized. It is the responsibility - of the implementer to safely handle the potential performance and thread safety issues - depending on what their listener is doing. - - If no class is specified, no listener is created. There is no default. - --> - <cacheManagerEventListenerFactory class="" properties=""/> - - - <!-- - CacheManagerPeerProvider - ======================== - (For distributed operation) - - Specifies a CacheManagerPeerProviderFactory which will be used to create a - CacheManagerPeerProvider, which discovers other CacheManagers in the cluster. - - One or more providers can be configured. The first one in the ehcache.xml is the default, which is used - for replication and bootstrapping. - - The attributes of cacheManagerPeerProviderFactory are: - * class - a fully qualified factory class name - * properties - comma separated properties having meaning only to the factory. - - Providers are available for RMI, JGroups and JMS as shown following. - - RMICacheManagerPeerProvider - +++++++++++++++++++++++++++ - - Ehcache comes with a built-in RMI-based distribution system with two means of discovery of - CacheManager peers participating in the cluster: - * automatic, using a multicast group. This one automatically discovers peers and detects - changes such as peers entering and leaving the group - * manual, using manual rmiURL configuration. A hardcoded list of peers is provided at - configuration time. - - Configuring Automatic Discovery: - Automatic discovery is configured as per the following example: - <cacheManagerPeerProviderFactory - class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" - properties="hostName=fully_qualified_hostname_or_ip, - peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, - multicastGroupPort=4446, timeToLive=32"/> - - Valid properties are: - * peerDiscovery (mandatory) - specify "automatic" - * multicastGroupAddress (mandatory) - specify a valid multicast group address - * multicastGroupPort (mandatory) - specify a dedicated port for the multicast heartbeat - traffic - * timeToLive - specify a value between 0 and 255 which determines how far the packets will - propagate. - - By convention, the restrictions are: - 0 - the same host - 1 - the same subnet - 32 - the same site - 64 - the same region - 128 - the same continent - 255 - unrestricted - - * hostName - the hostname or IP of the interface to be used for sending and receiving multicast packets - (relevant to mulithomed hosts only) - - Configuring Manual Discovery: - Manual discovery requires a unique configuration per host. It is contains a list of rmiURLs for the peers, other - than itself. So, if we have server1, server2 and server3 the configuration will be: - - In server1's configuration: - <cacheManagerPeerProviderFactory class= - "net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" - properties="peerDiscovery=manual, - rmiUrls=//server2:40000/sampleCache1|//server3:40000/sampleCache1 - | //server2:40000/sampleCache2|//server3:40000/sampleCache2" - propertySeparator="," /> - - In server2's configuration: - <cacheManagerPeerProviderFactory class= - "net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" - properties="peerDiscovery=manual, - rmiUrls=//server1:40000/sampleCache1|//server3:40000/sampleCache1 - | //server1:40000/sampleCache2|//server3:40000/sampleCache2" - propertySeparator="," /> - - In server3's configuration: - <cacheManagerPeerProviderFactory class= - "net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" - properties="peerDiscovery=manual, - rmiUrls=//server1:40000/sampleCache1|//server2:40000/sampleCache1 - | //server1:40000/sampleCache2|//server2:40000/sampleCache2" - propertySeparator="," /> - - - Valid properties are: - * peerDiscovery (mandatory) - specify "manual" - * rmiUrls (mandatory) - specify a pipe separated list of rmiUrls, in the form - //hostname:port - * hostname (optional) - the hostname is the hostname of the remote CacheManager peer. The port is the listening - port of the RMICacheManagerPeerListener of the remote CacheManager peer. - - JGroupsCacheManagerPeerProvider - +++++++++++++++++++++++++++++++ - <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory" - properties="connect=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_ttl=32; - mcast_send_buf_size=150000;mcast_recv_buf_size=80000): - PING(timeout=2000;num_initial_members=6): - MERGE2(min_interval=5000;max_interval=10000): - FD_SOCK:VERIFY_SUSPECT(timeout=1500): - pbcast.NAKACK(gc_lag=10;retransmit_timeout=3000): - UNICAST(timeout=5000): - pbcast.STABLE(desired_avg_gossip=20000): - FRAG: - pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=false)" - propertySeparator="::" - /> - The only property necessary is the connect String used by jgroups to configure itself. Refer to the Jgroups documentation for explanation - of all the protocols. The example above uses UDP multicast. If the connect property is not specified the default JGroups connection will be - used. - - - JMSCacheManagerPeerProviderFactory - ++++++++++++++++++++++++++++++++++ - <cacheManagerPeerProviderFactory - class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" - properties="..." - propertySeparator="," - /> - - The JMS PeerProviderFactory uses JNDI to maintain message queue independence. Refer to the manual for full configuration - examples using ActiveMQ and Open Message Queue. - - Valid properties are: - * initialContextFactoryName (mandatory) - the name of the factory used to create the message queue initial context. - * providerURL (mandatory) - the JNDI configuration information for the service provider to use. - * topicConnectionFactoryBindingName (mandatory) - the JNDI binding name for the TopicConnectionFactory - * topicBindingName (mandatory) - the JNDI binding name for the topic name - * getQueueBindingName (mandatory only if using jmsCacheLoader) - the JNDI binding name for the queue name - * securityPrincipalName - the JNDI java.naming.security.principal - * securityCredentials - the JNDI java.naming.security.credentials - * urlPkgPrefixes - the JNDI java.naming.factory.url.pkgs - * userName - the user name to use when creating the TopicConnection to the Message Queue - * password - the password to use when creating the TopicConnection to the Message Queue - * acknowledgementMode - the JMS Acknowledgement mode for both publisher and subscriber. The available choices are - AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE and SESSION_TRANSACTED. The default is AUTO_ACKNOWLEDGE. - --> - <cacheManagerPeerProviderFactory - class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" - properties="peerDiscovery=automatic, - multicastGroupAddress=230.0.0.1, - multicastGroupPort=4446, timeToLive=1" - propertySeparator="," - /> - - - <!-- - CacheManagerPeerListener - ======================== - (Enable for distributed operation) - - Specifies a CacheManagerPeerListenerFactory which will be used to create a - CacheManagerPeerListener, which listens for messages from cache replicators participating in the cluster. - - The attributes of cacheManagerPeerListenerFactory are: - class - a fully qualified factory class name - properties - comma separated properties having meaning only to the factory. - - Ehcache comes with a built-in RMI-based distribution system. The listener component is - RMICacheManagerPeerListener which is configured using - RMICacheManagerPeerListenerFactory. It is configured as per the following example: - - <cacheManagerPeerListenerFactory - class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" - properties="hostName=fully_qualified_hostname_or_ip, - port=40001, - remoteObjectPort=40002, - socketTimeoutMillis=120000" - propertySeparator="," /> - - All properties are optional. They are: - * hostName - the hostName of the host the listener is running on. Specify - where the host is multihomed and you want to control the interface over which cluster - messages are received. Defaults to the host name of the default interface if not - specified. - * port - the port the RMI Registry listener listens on. This defaults to a free port if not specified. - * remoteObjectPort - the port number on which the remote objects bound in the registry receive calls. - This defaults to a free port if not specified. - * socketTimeoutMillis - the number of ms client sockets will stay open when sending - messages to the listener. This should be long enough for the slowest message. - If not specified it defaults to 120000ms. - - --> - <cacheManagerPeerListenerFactory - class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/> - - <!-- - TerracottaConfig - ======================== - (Enable for Terracotta clustered operation) - - Note: You need to install and run one or more Terracotta servers to use Terracotta clustering. - See http://www.terracotta.org/web/display/orgsite/Download. - - Specifies a TerracottaConfig which will be used to configure the Terracotta - runtime for this CacheManager. - - Configuration can be specified in two main ways: by reference to a source of - configuration or by use of an embedded Terracotta configuration file. - - To specify a reference to a source (or sources) of configuration, use the url - attribute. The url attribute must contain a comma-separated list of: - * path to Terracotta configuration file (usually named tc-config.xml) - * URL to Terracotta configuration file - * <server host>:<port> of running Terracotta Server instance - - Simplest example for pointing to a Terracotta server on this machine: - <terracottaConfig url="localhost:9510"/> - - Example using a path to Terracotta configuration file: - <terracottaConfig url="/app/config/tc-config.xml"/> - - Example using a URL to a Terracotta configuration file: - <terracottaConfig url="http://internal/ehcache/app/tc-config.xml"/> - - Example using multiple Terracotta server instance URLs (for fault tolerance): - <terracottaConfig url="host1:9510,host2:9510,host3:9510"/> - - To embed a Terracotta configuration file within the ehcache configuration, simply - place a normal Terracotta XML config within the <terracottaConfig> element. - - Example: - <terracottaConfig> - <tc-config> - <servers> - <server host="server1" name="s1"/> - <server host="server2" name="s2"/> - </servers> - <clients> - <logs>app/logs-%i</logs> - </clients> - </tc-config> - </terracottaConfig> - - For more information on the Terracotta configuration, see the Terracotta documentation. - --> - - <!-- - Cache configuration - =================== - - The following attributes are required. - - name: - Sets the name of the cache. This is used to identify the cache. It must be unique. - - maxElementsInMemory: - Sets the maximum number of objects that will be created in memory - - maxElementsOnDisk: - Sets the maximum number of objects that will be maintained in the DiskStore - The default value is zero, meaning unlimited. - - eternal: - Sets whether elements are eternal. If eternal, timeouts are ignored and the - element is never expired. - - overflowToDisk: - Sets whether elements can overflow to disk when the memory store - has reached the maxInMemory limit. - - The following attributes and elements are optional. - - timeToIdleSeconds: - Sets the time to idle for an element before it expires. - i.e. The maximum amount of time between accesses before an element expires - Is only used if the element is not eternal. - Optional attribute. A value of 0 means that an Element can idle for infinity. - The default value is 0. - - timeToLiveSeconds: - Sets the time to live for an element before it expires. - i.e. The maximum time between creation time and when an element expires. - Is only used if the element is not eternal. - Optional attribute. A value of 0 means that and Element can live for infinity. - The default value is 0. - - diskPersistent: - Whether the disk store persists between restarts of the Virtual Machine. - The default value is false. - - diskExpiryThreadIntervalSeconds: - The number of seconds between runs of the disk expiry thread. The default value - is 120 seconds. - - diskSpoolBufferSizeMB: - This is the size to allocate the DiskStore for a spool buffer. Writes are made - to this area and then asynchronously written to disk. The default size is 30MB. - Each spool buffer is used only by its cache. If you get OutOfMemory errors consider - lowering this value. To improve DiskStore performance consider increasing it. Trace level - logging in the DiskStore will show if put back ups are occurring. - - clearOnFlush: - whether the MemoryStore should be cleared when flush() is called on the cache. - By default, this is true i.e. the MemoryStore is cleared. - - memoryStoreEvictionPolicy: - Policy would be enforced upon reaching the maxElementsInMemory limit. Default - policy is Least Recently Used (specified as LRU). Other policies available - - First In First Out (specified as FIFO) and Less Frequently Used - (specified as LFU) - - Cache elements can also contain sub elements which take the same format of a factory class - and properties. Defined sub-elements are: - - * cacheEventListenerFactory - Enables registration of listeners for cache events, such as - put, remove, update, and expire. - - * bootstrapCacheLoaderFactory - Specifies a BootstrapCacheLoader, which is called by a - cache on initialisation to prepopulate itself. - - * cacheExtensionFactory - Specifies a CacheExtension, a generic mechansim to tie a class - which holds a reference to a cache to the cache lifecycle. - - * cacheExceptionHandlerFactory - Specifies a CacheExceptionHandler, which is called when - cache exceptions occur. - - * cacheLoaderFactory - Specifies a CacheLoader, which can be used both asynchronously and - synchronously to load objects into a cache. More than one cacheLoaderFactory element - can be added, in which case the loaders form a chain which are executed in order. If a - loader returns null, the next in chain is called. - - - RMI Cache Replication - +++++++++++++++++++++ - - Each cache that will be distributed needs to set a cache event listener which replicates - messages to the other CacheManager peers. For the built-in RMI implementation this is done - by adding a cacheEventListenerFactory element of type RMICacheReplicatorFactory to each - distributed cache's configuration as per the following example: - - <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" - properties="replicateAsynchronously=true, - replicatePuts=true, - replicatePutsViaCopy=false, - replicateUpdates=true, - replicateUpdatesViaCopy=true, - replicateRemovals=true - asynchronousReplicationIntervalMillis=<number of milliseconds" - propertySeparator="," /> - - The RMICacheReplicatorFactory recognises the following properties: - - * replicatePuts=true|false - whether new elements placed in a cache are - replicated to others. Defaults to true. - - * replicatePutsViaCopy=true|false - whether the new elements are - copied to other caches (true), or whether a remove message is sent. Defaults to true. - - * replicateUpdates=true|false - whether new elements which override an - element already existing with the same key are replicated. Defaults to true. - - * replicateRemovals=true - whether element removals are replicated. Defaults to true. - - * replicateAsynchronously=true | false - whether replications are - asynchronous (true) or synchronous (false). Defaults to true. - - * replicateUpdatesViaCopy=true | false - whether the new elements are - copied to other caches (true), or whether a remove message is sent. Defaults to true. - - * asynchronousReplicationIntervalMillis=<number of milliseconds> - The asynchronous - replicator runs at a set interval of milliseconds. The default is 1000. The minimum - is 10. This property is only applicable if replicateAsynchronously=true - - - JGroups Replication - +++++++++++++++++++ - - For the Jgroups replication this is done with: - <cacheEventListenerFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory" - properties="replicateAsynchronously=true, replicatePuts=true, - replicateUpdates=true, replicateUpdatesViaCopy=false, - replicateRemovals=true,asynchronousReplicationIntervalMillis=1000"/> - This listener supports the same properties as the RMICacheReplicationFactory. - - - JMS Replication - +++++++++++++++ - - For JMS-based replication this is done with: - <cacheEventListenerFactory - class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" - properties="replicateAsynchronously=true, - replicatePuts=true, - replicateUpdates=true, - replicateUpdatesViaCopy=true, - replicateRemovals=true, - asynchronousReplicationIntervalMillis=1000" - propertySeparator=","/> - - This listener supports the same properties as the RMICacheReplicationFactory. - - Cluster Bootstrapping - +++++++++++++++++++++ - - Bootstrapping a cluster may use a different mechanism to replication. e.g you can mix - JMS replication with bootstrap via RMI - just make sure you have the cacheManagerPeerProviderFactory - and cacheManagerPeerListenerFactory configured. - - There are two bootstrapping mechanisms: RMI and JGroups. - - RMI Bootstrap - - The RMIBootstrapCacheLoader bootstraps caches in clusters where RMICacheReplicators are - used. It is configured as per the following example: - - <bootstrapCacheLoaderFactory - class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" - properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000" - propertySeparator="," /> - - The RMIBootstrapCacheLoaderFactory recognises the following optional properties: - - * bootstrapAsynchronously=true|false - whether the bootstrap happens in the background - after the cache has started. If false, bootstrapping must complete before the cache is - made available. The default value is true. - - * maximumChunkSizeBytes=<integer> - Caches can potentially be very large, larger than the - memory limits of the VM. This property allows the bootstraper to fetched elements in - chunks. The default chunk size is 5000000 (5MB). - - JGroups Bootstrap - - Here is an example of bootstrap configuration using JGroups boostrap: - - <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory" - properties="bootstrapAsynchronously=true"/> - - The configuration properties are the same as for RMI above. Note that JGroups bootstrap only supports - asynchronous bootstrap mode. - - - Cache Exception Handling - - By default, most cache operations will propagate a runtime CacheException on failure. An - interceptor, using a dynamic proxy, may be configured so that a CacheExceptionHandler can - be configured to intercept Exceptions. Errors are not intercepted. - - It is configured as per the following example: - - <cacheExceptionHandlerFactory class="com.example.ExampleExceptionHandlerFactory" - properties="logLevel=FINE"/> - - Caches with ExceptionHandling configured are not of type Cache, but are of type Ehcache only, - and are not available using CacheManager.getCache(), but using CacheManager.getEhcache(). - - - Cache Loader - - A default CacheLoader may be set which loads objects into the cache through asynchronous and - synchronous methods on Cache. This is different to the bootstrap cache loader, which is used - only in distributed caching. - - It is configured as per the following example: - - <cacheLoaderFactory class="com.example.ExampleCacheLoaderFactory" - properties="type=int,startCounter=10"/> - - Cache Extension - - CacheExtensions are a general purpose mechanism to allow generic extensions to a Cache. - CacheExtensions are tied into the Cache lifecycle. - - CacheExtensions are created using the CacheExtensionFactory which has a - <code>createCacheCacheExtension()</code> method which takes as a parameter a - Cache and properties. It can thus call back into any public method on Cache, including, of - course, the load methods. - - Extensions are added as per the following example: - - <cacheExtensionFactory class="com.example.FileWatchingCacheRefresherExtensionFactory" - properties="refreshIntervalMillis=18000, loaderTimeout=3000, - flushPeriod=whatever, someOtherProperty=someValue ..."/> - - Terracotta Clustering - - Cache elements can also contain information about whether the cache can be clustered with Terracotta. - The <terracotta> sub-element has the following attributes: - - * clustered=true|false - indicates whether this cache should be clustered with Terracotta. By - default, if the <terracotta> element is included, clustered=true. - * valueMode=serialization|identity - indicates whether this cache should be clustered with - serialized copies of the values or using Terracotta identity mode. By default, values will - be cached in serialization mode which is similar to other replicated Ehcache modes. The identity - mode is only available in certain Terracotta deployment scenarios and will maintain actual object - identity of the keys and values across the cluster. In this case, all users of a value retrieved from - the cache are using the same clustered value and must provide appropriate locking for any changes - made to the value (or objects referred to by the value). - * coherentReads=true|false - indicates whether this cache should have coherent reads with guaranteed - consistency across the cluster. By default, this setting is true. If you set this property to - false, reads are allowed to check the local value without locking, possibly seeing stale values. - This is a performance optimization with weaker concurrency guarantees and should generally be used - with caches that contain read-only data or where the application can tolerate reading stale - data. - - Simplest example to indicate clustering: - <terracotta/> - - To indicate the cache should not be clustered (or remove the <terracotta> element altogether): - <terracotta clustered="false"/> - - To indicate the cache should be clustered using identity mode: - <terracotta clustered="true" valueMode="identity"/> - --> - - <!-- Mandatory Default Cache configuration. These settings will be applied to caches created programmtically using CacheManager.add(String cacheName). @@ -599,12 +40,9 @@ eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" + memoryStoreEvictionPolicy="LRU" overflowToDisk="true" - diskSpoolBufferSizeMB="30" - maxElementsOnDisk="10000000" diskPersistent="false" - diskExpiryThreadIntervalSeconds="120" - memoryStoreEvictionPolicy="LRU" /> <cache name="com.finalist.cmsc.beans.om.PortletDefinition" Modified: CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/PortletFragment.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/PortletFragment.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/PortletFragment.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -9,25 +9,19 @@ */ package com.finalist.pluto.portalImpl.aggregation; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.io.*; +import java.util.*; -import javax.portlet.PortletException; -import javax.portlet.PortletMode; +import javax.portlet.*; import javax.portlet.UnavailableException; -import javax.portlet.WindowState; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; +import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import net.sf.ehcache.CacheException; +import net.sf.ehcache.Element; +import net.sf.ehcache.constructs.blocking.LockTimeoutException; + import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -38,24 +32,15 @@ import org.apache.pluto.om.portlet.PortletDefinition; import org.apache.pluto.om.servlet.ServletDefinition; import org.apache.pluto.om.servlet.ServletDefinitionCtrl; -import org.apache.pluto.om.window.PortletWindow; -import org.apache.pluto.om.window.PortletWindowCtrl; -import org.apache.pluto.om.window.PortletWindowList; -import org.apache.pluto.om.window.PortletWindowListCtrl; +import org.apache.pluto.om.window.*; -import com.finalist.cmsc.beans.om.NodeParameter; -import com.finalist.cmsc.beans.om.PortletParameter; -import com.finalist.cmsc.beans.om.View; +import com.finalist.cmsc.beans.om.*; import com.finalist.cmsc.portalImpl.PortalConstants; import com.finalist.cmsc.portalImpl.headerresource.HeaderResource; import com.finalist.cmsc.util.HttpUtil; import com.finalist.cmsc.util.ServerUtil; -import com.finalist.pluto.portalImpl.aggregation.portletcache.CacheSettings; import com.finalist.pluto.portalImpl.aggregation.portletcache.PortletCache; -import com.finalist.pluto.portalImpl.core.PortalControlParameter; -import com.finalist.pluto.portalImpl.core.PortalEnvironment; -import com.finalist.pluto.portalImpl.core.PortalURL; -import com.finalist.pluto.portalImpl.core.PortletContainerFactory; +import com.finalist.pluto.portalImpl.core.*; import com.finalist.pluto.portalImpl.om.common.impl.PreferenceSetImpl; import com.finalist.pluto.portalImpl.om.entity.impl.PortletEntityImpl; import com.finalist.pluto.portalImpl.om.servlet.impl.WebApplicationDefinitionImpl; @@ -211,146 +196,151 @@ cleanRequest(request); } - public void service(HttpServletRequest request, HttpServletResponse response) { log.debug("PortletFragment service enters"); storedWriter = new StringWriter(); HttpServletRequest wrappedRequest = ServletObjectAccess.getServletRequest(request, portletWindow); - CacheSettings settings = PortletCache.getCacheSettings(this, wrappedRequest); + int cacheTime = getExpirationCache(); + boolean isPortletCacheable = (cacheTime > 0); + + if (ServerUtil.isLive() && isPortletCacheable) { + Serializable key = PortletCache.getCacheKey(this, wrappedRequest); + PortletCache portletCache = PortletCache.getPortletCache(); + try { + //if null will lock here + Element element = portletCache.get(key); + if (element == null) { + // Value not cached - fetch it + servicePortlet(request, response, wrappedRequest); + String value = storedWriter.toString(); + element = new Element(key, value, false, cacheTime, cacheTime); + portletCache.put(element); + } + else { + storedWriter.write((String) element.getValue()); + } + } catch (LockTimeoutException e) { + //do not release the lock, because you never acquired it + String message = "Timeout while waiting on another thread " + + "to fetch object for cache entry \"" + key + "\"."; + throw new LockTimeoutException(message, e); + } catch (final Throwable throwable) { + // Could not fetch - Ditch the entry from the cache and rethrow + + //release the lock you acquired + portletCache.put(new Element(key, null)); + throw new CacheException("Could not fetch object for cache entry with key \"" + key + "\".", throwable); + } + } + else { + servicePortlet(request, response, wrappedRequest); + } + log.debug("PortletFragment service exits"); + } + + public void servicePortlet(HttpServletRequest request, HttpServletResponse response, + HttpServletRequest wrappedRequest) throws Error { + setupRequest(request); + + // load the Portlet + // If there is an error loading, then we will save the error message and + // attempt to render it inside the Portlet, so the Portal has a chance of + // still looking okay + String errorMsg = null; try { - if(settings != null) { - settings.setCachedVersion(PortletCache.getCachedVersion(settings)); - } - - // if we have a cached version, write this one, otherwise render - // cached version is also null when the portlet is not cacheable - if(settings != null && settings.getCachedVersion() != null) { - if(settings != null) { - ReentrantReadWriteLock lock = settings.getCacheLock(); - lock.writeLock().unlock(); - } - storedWriter.write(settings.getCachedVersion()); - } - else { - setupRequest(request); - - // load the Portlet - // If there is an error loading, then we will save the error message and - // attempt to render it inside the Portlet, so the Portal has a chance of - // still looking okay - String errorMsg = null; - try { - log.debug("|| portletLoad:'" + portletWindow.getId() + "'"); - - PortletContainerFactory.getPortletContainer().portletLoad(portletWindow, wrappedRequest, response); - - PortletDefinition def = portletWindow.getPortletEntity().getPortletDefinition(); - if (def == null) { - throw new PortletException("PortletDefinition not found for window " + portletWindow.getId()); - } - - // store the context path in the webapp. - PortletApplicationDefinition app = def.getPortletApplicationDefinition(); - WebApplicationDefinitionImpl wa = (WebApplicationDefinitionImpl) app.getWebApplicationDefinition(); - wa.setContextRoot(request.getContextPath()); - } - catch (PortletContainerException e) { - log.error("PortletContainerException-Error in Portlet", e); - errorMsg = getErrorMsg(e); - } - catch (Throwable t) { - // If we catch any throwable, we want to try to continue - // so that the rest of the portal renders correctly - log.error("Error in Portlet", t); - if (t instanceof VirtualMachineError) { - // if the Throwable is a VirtualMachineError then - // it is very unlikely (!) that the portal is going - // to render correctly. - throw (Error) t; - } - else { - errorMsg = getErrorMsg(t); - } - } - - if (errorMsg != null) { - storedWriter.write(errorMsg); - return; - } - - PortalEnvironment env = (PortalEnvironment) request.getAttribute(PortalEnvironment.REQUEST_PORTALENV); - PortalURL thisURL = env.getRequestedPortalURL(); - - log.debug("|| thisURL='" + thisURL + "'"); - - PortalControlParameter thisControl = new PortalControlParameter(thisURL); - if (thisControl.isOnePortletWindowMaximized()) { - WindowState currentState = thisControl.getState(portletWindow); - if (!WindowState.MAXIMIZED.equals(currentState)) { - return; - } - } - ServletDefinition servletDefinition = getServletDefinition(); - - if (servletDefinition != null && !servletDefinition.isUnavailable()) { - PrintWriter writer2 = new PrintWriter(storedWriter); - - // create a wrapped response which the Portlet will be rendered to - ServletResponseImpl wrappedResponse = (ServletResponseImpl) ServletObjectAccess.getStoredServletResponse( - response, writer2); - - try { - // render the Portlet to the wrapped response, to be output - // later. - PortletContainerFactory.getPortletContainer().renderPortlet(portletWindow, wrappedRequest, wrappedResponse); - } - catch (UnavailableException e) { - writer2.println("the portlet is currently unavailable!"); - - ServletDefinitionCtrl servletDefinitionCtrl = (ServletDefinitionCtrl) ControllerObjectAccess - .get(servletDefinition); - if (e.isPermanent()) { - servletDefinitionCtrl.setAvailable(Long.MAX_VALUE); - } - else { - int unavailableSeconds = e.getUnavailableSeconds(); - if (unavailableSeconds <= 0) { - unavailableSeconds = 60; // arbitrary default - } - servletDefinitionCtrl.setAvailable(System.currentTimeMillis() + unavailableSeconds * 1000); - } - } - catch (Exception e) { - writer2.println(getErrorMsg(e)); - } - - } - else { - log.error("Error no servletDefinition!!!"); - } + log.debug("|| portletLoad:'" + portletWindow.getId() + "'"); - if(settings != null) { - PortletCache.cacheRenderedPortlet(settings, storedWriter.getBuffer().toString()); - } - } + PortletContainerFactory.getPortletContainer().portletLoad(portletWindow, wrappedRequest, response); + + PortletDefinition def = portletWindow.getPortletEntity().getPortletDefinition(); + if (def == null) { + throw new PortletException("PortletDefinition not found for window " + portletWindow.getId()); + } + + // store the context path in the webapp. + PortletApplicationDefinition app = def.getPortletApplicationDefinition(); + WebApplicationDefinitionImpl wa = (WebApplicationDefinitionImpl) app.getWebApplicationDefinition(); + wa.setContextRoot(request.getContextPath()); } - finally { - if(settings != null) { - ReentrantReadWriteLock lock = settings.getCacheLock(); - if(lock.isWriteLockedByCurrentThread()) { - lock.writeLock().unlock(); - } - lock.readLock().unlock(); - } + catch (PortletContainerException e) { + log.error("PortletContainerException-Error in Portlet", e); + errorMsg = getErrorMsg(e); } + catch (Throwable t) { + // If we catch any throwable, we want to try to continue + // so that the rest of the portal renders correctly + log.error("Error in Portlet", t); + if (t instanceof VirtualMachineError) { + // if the Throwable is a VirtualMachineError then + // it is very unlikely (!) that the portal is going + // to render correctly. + throw (Error) t; + } + else { + errorMsg = getErrorMsg(t); + } + } + if (errorMsg != null) { + storedWriter.write(errorMsg); + return; + } + PortalEnvironment env = (PortalEnvironment) request.getAttribute(PortalEnvironment.REQUEST_PORTALENV); + PortalURL thisURL = env.getRequestedPortalURL(); + + log.debug("|| thisURL='" + thisURL + "'"); + + PortalControlParameter thisControl = new PortalControlParameter(thisURL); + if (thisControl.isOnePortletWindowMaximized()) { + WindowState currentState = thisControl.getState(portletWindow); + if (!WindowState.MAXIMIZED.equals(currentState)) { + return; + } + } + ServletDefinition servletDefinition = getServletDefinition(); + + if (servletDefinition != null && !servletDefinition.isUnavailable()) { + PrintWriter writer2 = new PrintWriter(storedWriter); + + // create a wrapped response which the Portlet will be rendered to + ServletResponseImpl wrappedResponse = (ServletResponseImpl) ServletObjectAccess.getStoredServletResponse( + response, writer2); + + try { + // render the Portlet to the wrapped response, to be output + // later. + PortletContainerFactory.getPortletContainer().renderPortlet(portletWindow, wrappedRequest, wrappedResponse); + } + catch (UnavailableException e) { + writer2.println("the portlet is currently unavailable!"); + + ServletDefinitionCtrl servletDefinitionCtrl = (ServletDefinitionCtrl) ControllerObjectAccess + .get(servletDefinition); + if (e.isPermanent()) { + servletDefinitionCtrl.setAvailable(Long.MAX_VALUE); + } + else { + int unavailableSeconds = e.getUnavailableSeconds(); + if (unavailableSeconds <= 0) { + unavailableSeconds = 60; // arbitrary default + } + servletDefinitionCtrl.setAvailable(System.currentTimeMillis() + unavailableSeconds * 1000); + } + } + catch (Exception e) { + writer2.println(getErrorMsg(e)); + } + + } + else { + log.error("Error no servletDefinition!!!"); + } cleanRequest(request); - log.debug("PortletFragment service exits"); } @@ -482,4 +472,5 @@ public Fragment getParent() { return parent; } + } Deleted: CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/CacheSettings.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/CacheSettings.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/CacheSettings.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -1,37 +0,0 @@ -package com.finalist.pluto.portalImpl.aggregation.portletcache; - -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class CacheSettings { - private int cacheTime = 0; - private ReentrantReadWriteLock cacheLock = null; - private String cachedVersion = null; - private String cacheKey = null; - - - public CacheSettings(int cacheTime, ReentrantReadWriteLock cacheLock, String cacheKey) { - this.cacheTime = cacheTime; - this.cacheLock = cacheLock; - this.cacheKey = cacheKey; - } - - public int getCacheTime() { - return cacheTime; - } - - public ReentrantReadWriteLock getCacheLock() { - return cacheLock; - } - - public String getCachedVersion() { - return cachedVersion; - } - - public String getCacheKey() { - return cacheKey; - } - - public void setCachedVersion(String cachedVersion) { - this.cachedVersion = cachedVersion; - } -} Modified: CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/PortletCache.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/PortletCache.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/portal/src/java/com/finalist/pluto/portalImpl/aggregation/portletcache/PortletCache.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -1,67 +1,34 @@ package com.finalist.pluto.portalImpl.aggregation.portletcache; -import java.util.HashMap; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - import javax.servlet.http.HttpServletRequest; -import net.sf.ehcache.Cache; import net.sf.ehcache.CacheException; import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Element; +import net.sf.ehcache.concurrent.CacheLockProvider; +import net.sf.ehcache.concurrent.Sync; +import net.sf.ehcache.constructs.blocking.BlockingCache; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.finalist.cmsc.util.ServerUtil; +import com.finalist.cmsc.services.sitemanagement.UniqueKeyReadWriteLockSync; import com.finalist.pluto.portalImpl.aggregation.PortletFragment; import com.finalist.pluto.portalImpl.aggregation.ScreenFragment; -public class PortletCache { +public class PortletCache extends BlockingCache { - private static final Log log = LogFactory.getLog(PortletCache.class); + private static PortletCache cache = new PortletCache(); - private static HashMap<String, ReentrantReadWriteLock> cacheLocks = new HashMap<String, ReentrantReadWriteLock>(); - - private static ReentrantLock cacheLockLock = new ReentrantLock(); + private final CacheLockProvider cacheLockProvider; + + public PortletCache() throws CacheException { + super(CacheManager.create().getCache("PortletCache")); + this.cacheLockProvider = new UniqueKeyReadWriteLockSync(); + } + + @Override + protected Sync getLockForKey(Object key) { + return cacheLockProvider.getSyncForKey(key); + } - private static Cache cache; - /** - * initialize the cache - */ - static { - try { - CacheManager manager = CacheManager.create(); - cache = manager.getCache("PortletCache"); - } catch (CacheException e) { - log.error("Unable to create portlet cache!",e); - } - } - - /** - * Get a cache lock, needed for concurrency - * - * @param cacheKey - * @return - */ - public static ReentrantReadWriteLock getCacheLock(String cacheKey) { - cacheLockLock.lock(); - try { - ReentrantReadWriteLock lock = cacheLocks.get(cacheKey); - if (lock == null) { - lock = new ReentrantReadWriteLock(); - cacheLocks.put(cacheKey, lock); - } - return lock; - } - finally { - cacheLockLock.unlock(); - } - } - - /** * The cache key is: page number, portlet position and parameters * * @param request @@ -82,87 +49,7 @@ return key.toString(); } - /** - * Get the cache version of a portlet for certain settings - * - * @param settings - * @return - */ - public static String getCachedVersion(CacheSettings settings) { - int cacheTime = settings.getCacheTime(); - String cacheKey = settings.getCacheKey(); - Cache cache = PortletCache.getCache(); - - String value = null; - try { - Element element = cache.get(cacheKey); - if(element != null) { - value = (String)element.getValue(); - } - } catch (IllegalStateException e) { - log.error("Unable to read from portlet cache", e); - } catch (CacheException e) { - log.error("Unable to read from portlet cache", e); - } - - if(log.isDebugEnabled()) { - log.debug("Serving out from cache("+cacheTime+") "+cacheKey+": "+((value == null)?null:(value.length()+" bytes"))); - } - return value; - } - - /** - * Get the cache - * @param cacheTime - * @return - */ - public static synchronized Cache getCache() { + public static PortletCache getPortletCache() { return cache; } - - /** - * Store a rendered version of a portlet in the cache - * - * @param settings - * @param value - */ - public static void cacheRenderedPortlet(CacheSettings settings, String value) { - int cacheTime = settings.getCacheTime(); - String cacheKey = settings.getCacheKey(); - - if(log.isDebugEnabled()) { - log.debug("Storing in cache("+cacheTime+") "+cacheKey+": "+((value == null)?null:(value.length()+" bytes"))); - } - Element element = new Element(cacheKey, value, false, cacheTime, cacheTime); - PortletCache.getCache().put(element); - } - - - /** - * Create the CacheSettings object for a certain portlet request - * - * @param fragment - * @param request - * @param wrappedRequest - * @return - */ - public static CacheSettings getCacheSettings(PortletFragment fragment, HttpServletRequest wrappedRequest) { - - if (ServerUtil.isLive()) { - - int cacheTime = fragment.getExpirationCache(); - boolean isPortletCacheable = (cacheTime > 0); - - if (isPortletCacheable) { - String cacheKey = PortletCache.getCacheKey(fragment, wrappedRequest); - ReentrantReadWriteLock cacheLock = PortletCache.getCacheLock(cacheKey); - CacheSettings settings = new CacheSettings(cacheTime, cacheLock, cacheKey); - cacheLock.writeLock().lock(); - cacheLock.readLock().lock(); - return settings; - } - } - - return null; - } } Modified: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/BlockingCacheManager.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/BlockingCacheManager.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/BlockingCacheManager.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -25,14 +25,14 @@ /** * A map of BlockingCaches, keyed by cache name */ - protected final Map caches; + protected final Map<String, BlockingCache> caches; /** * Empty Constructor */ public BlockingCacheManager() { - caches = new HashMap(); + caches = new HashMap<String, BlockingCache>(); } /** @@ -41,7 +41,7 @@ */ public BlockingCacheManager(CacheManager mgr) { manager = mgr; - caches = new HashMap(); + caches = new HashMap<String, BlockingCache>(); } /** @@ -73,12 +73,12 @@ * Drops the contents of all caches. */ public void clearAll() throws CacheException { - final List cacheList = getCaches(); + final List<BlockingCache> cacheList = getCaches(); if (LOG.isDebugEnabled()) { LOG.debug("Removing all blocking caches"); } for (int i = 0; i < cacheList.size(); i++) { - final BlockingCache cache = (BlockingCache) cacheList.get(i); + final BlockingCache cache = cacheList.get(i); cache.removeAll(); } } @@ -116,8 +116,8 @@ * Builds the set of caches. * Returns a copy so that the monitor can be released. */ - private synchronized List getCaches() { - final ArrayList blockingCaches = new ArrayList(); + private synchronized List<BlockingCache> getCaches() { + final ArrayList<BlockingCache> blockingCaches = new ArrayList<BlockingCache>(); blockingCaches.addAll(this.caches.values()); return blockingCaches; } Added: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCache.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCache.java (rev 0) +++ CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCache.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -0,0 +1,23 @@ +package com.finalist.cmsc.services.sitemanagement; + +import net.sf.ehcache.CacheException; +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.concurrent.*; +import net.sf.ehcache.constructs.blocking.CacheEntryFactory; + + +public class SelfPopulatingCache extends net.sf.ehcache.constructs.blocking.SelfPopulatingCache { + + private final CacheLockProvider cacheLockProvider; + + public SelfPopulatingCache(Ehcache cache, CacheEntryFactory factory) throws CacheException { + super(cache, factory); + this.cacheLockProvider = new UniqueKeyReadWriteLockSync(); + } + + @Override + protected Sync getLockForKey(Object key) { + return cacheLockProvider.getSyncForKey(key); + } + +} Modified: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCacheManager.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCacheManager.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SelfPopulatingCacheManager.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -1,14 +1,13 @@ package com.finalist.cmsc.services.sitemanagement; import java.util.ArrayList; + import java.util.List; import net.sf.ehcache.CacheException; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; -import net.sf.ehcache.constructs.blocking.CacheEntryFactory; -import net.sf.ehcache.constructs.blocking.SelfPopulatingCache; -import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory; +import net.sf.ehcache.constructs.blocking.*; public abstract class SelfPopulatingCacheManager extends BlockingCacheManager { @@ -47,7 +46,7 @@ * Refreshes all caches. */ public void refreshAll() throws Exception { - final List caches = getCaches(); + final List<BlockingCache> caches = getCaches(); for (int i = 0; i < caches.size(); i++) { final SelfPopulatingCache cache = (SelfPopulatingCache) caches.get(i); cache.refresh(); @@ -111,8 +110,8 @@ /** * Builds the set of caches. Returns a copy so that the monitor can be released. */ - protected synchronized List getCaches() { - final ArrayList caches = new ArrayList(); + protected synchronized List<BlockingCache> getCaches() { + final ArrayList<BlockingCache> caches = new ArrayList<BlockingCache>(); caches.addAll(this.caches.values()); return caches; } Modified: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SiteModelManager.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SiteModelManager.java 2009-11-27 09:21:29 UTC (rev 39999) +++ CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/SiteModelManager.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -10,13 +10,13 @@ package com.finalist.cmsc.services.sitemanagement; import java.util.ArrayList; + import java.util.Collections; import java.util.List; import java.util.Set; import net.sf.ehcache.CacheException; import net.sf.ehcache.Element; -import net.sf.ehcache.constructs.blocking.SelfPopulatingCache; import org.mmbase.util.logging.Logger; import org.mmbase.util.logging.Logging; Added: CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/UniqueKeyReadWriteLockSync.java =================================================================== --- CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/UniqueKeyReadWriteLockSync.java (rev 0) +++ CMSContainer/trunk/CMSContainer/cmsc/sitemanagement/src/java/com/finalist/cmsc/services/sitemanagement/UniqueKeyReadWriteLockSync.java 2009-11-27 10:07:25 UTC (rev 40000) @@ -0,0 +1,25 @@ +package com.finalist.cmsc.services.sitemanagement; + +import java.util.HashMap; +import java.util.Map; + +import net.sf.ehcache.concurrent.*; + +public class UniqueKeyReadWriteLockSync implements CacheLockProvider { + + private final Map<Object, Sync> locks = new HashMap<Object, Sync>(); + + public Sync getSyncForKey(Object key) { + return checkLockExistsForKey(key); + } + + private synchronized Sync checkLockExistsForKey(Object key) { + Sync lock = locks.get(key); + if (lock == null) { + lock = new ReadWriteLockSync(); + locks.put(key, lock); + } + return lock; + } + +} _______________________________________________ Cvs mailing list Cvs@lists.mmbase.org http://lists.mmbase.org/mailman/listinfo/cvs