From c26a23bf90dd6b1885ee8cc21ac83bab33cad5ad Mon Sep 17 00:00:00 2001 From: Alex Snaps Date: Thu, 14 Jul 2011 15:14:35 +0200 Subject: [PATCH] EHC-864 Moved all Hibernate dependent classes from ehcache-core to hibernate-ehcache module and fixed to use new API --- .../internal/RegionFactoryInitiator.java | 17 +- .../org/hibernate/cfg/SettingsFactory.java | 15 +- hibernate-ehcache/hibernate-ehcache.gradle | 7 +- .../ehcache/AbstractEhcacheRegionFactory.java | 223 ++++++ .../EhCacheMessageLogger.java | 2 +- .../cache/ehcache/EhCacheRegionFactory.java | 140 ++++ .../cache/ehcache/HibernateUtil.java | 120 +++ .../SingletonEhCacheRegionFactory.java | 123 ++++ .../management/impl/BaseEmitterBean.java | 182 +++++ .../ehcache/management/impl/BeanUtils.java | 145 ++++ .../management/impl/CacheRegionStats.java | 303 ++++++++ .../management/impl/CacheRegionUtils.java | 126 ++++ .../management/impl/CollectionStats.java | 273 +++++++ .../management/impl/EhcacheHibernate.java | 597 +++++++++++++++ .../EhcacheHibernateMBeanRegistration.java | 61 ++ ...EhcacheHibernateMBeanRegistrationImpl.java | 177 +++++ .../impl/EhcacheHibernateMbeanNames.java | 89 +++ .../management/impl/EhcacheStatsImpl.java | 689 ++++++++++++++++++ .../ehcache/management/impl/EntityStats.java | 288 ++++++++ .../management/impl/HibernateStatsImpl.java | 330 +++++++++ .../management/impl/NullHibernateStats.java | 300 ++++++++ .../impl/ProviderMBeanRegistrationHelper.java | 186 +++++ .../ehcache/management/impl/QueryStats.java | 322 ++++++++ ...HibernateNonstopCacheExceptionHandler.java | 92 +++ .../nonstop/NonstopAccessStrategyFactory.java | 80 ++ ...opAwareCollectionRegionAccessStrategy.java | 227 ++++++ ...onstopAwareEntityRegionAccessStrategy.java | 290 ++++++++ .../regions/EhcacheCollectionRegion.java | 71 ++ .../ehcache/regions/EhcacheDataRegion.java | 223 ++++++ .../ehcache/regions/EhcacheEntityRegion.java | 70 ++ .../regions/EhcacheGeneralDataRegion.java | 163 +++++ .../regions/EhcacheQueryResultsRegion.java} | 28 +- .../regions/EhcacheTimestampsRegion.java} | 28 +- .../EhcacheTransactionalDataRegion.java | 286 ++++++++ .../AbstractEhcacheAccessStrategy.java | 140 ++++ ...bstractReadWriteEhcacheAccessStrategy.java | 428 +++++++++++ .../EhcacheAccessStrategyFactory.java | 61 ++ .../EhcacheAccessStrategyFactoryImpl.java | 110 +++ ...EhcacheCollectionRegionAccessStrategy.java | 99 +++ ...riteEhcacheEntityRegionAccessStrategy.java | 131 ++++ ...EhcacheCollectionRegionAccessStrategy.java | 93 +++ ...OnlyEhcacheEntityRegionAccessStrategy.java | 131 ++++ ...EhcacheCollectionRegionAccessStrategy.java | 54 ++ ...riteEhcacheEntityRegionAccessStrategy.java | 128 ++++ ...EhcacheCollectionRegionAccessStrategy.java | 127 ++++ ...onalEhcacheEntityRegionAccessStrategy.java | 167 +++++ .../AbstractEhCacheRegionFactory.java | 117 --- .../ehcache/strategy/ItemValueExtractor.java | 29 + .../test/cache/EhcacheStatsImplTest.java | 36 + .../test/cache/HibernateCacheTest.java | 258 +++++++ .../ehcache/EhCacheRegionFactoryImpl.java | 30 +- .../test/cache/ehcache/EhCacheRegionTest.java | 16 +- .../test/cache/ehcache/EhCacheTest.java | 38 +- .../hibernate/test/cache/ehcache/Item.java | 2 +- .../org/hibernate/test/domain/Account.java | 47 ++ .../java/org/hibernate/test/domain/Event.java | 83 +++ .../hibernate/test/domain/EventManager.java | 249 +++++++ .../test/domain/HolidayCalendar.java | 78 ++ .../java/org/hibernate/test/domain/Item.java | 46 ++ .../org/hibernate/test/domain/Person.java | 120 +++ .../hibernate/test/domain/PhoneNumber.java | 87 +++ .../org/hibernate/test/domain/UuidItem.java | 46 ++ .../hibernate/test/domain/VersionedItem.java | 28 + .../hibernate-config/domain/Account.hbm.xml | 19 + .../hibernate-config/domain/Event.hbm.xml | 26 + .../domain/HolidayCalendar.hbm.xml | 21 + .../hibernate-config/domain/Item.hbm.xml | 28 + .../hibernate-config/domain/Person.hbm.xml | 40 + .../domain/PhoneNumber.hbm.xml | 20 + .../resources/hibernate-config/ehcache.xml | 9 + .../hibernate-config/hibernate.cfg.xml | 48 ++ 71 files changed, 9279 insertions(+), 184 deletions(-) create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/AbstractEhcacheRegionFactory.java rename hibernate-ehcache/src/main/java/org/hibernate/cache/{internal => ehcache}/EhCacheMessageLogger.java (98%) create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheRegionFactory.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/HibernateUtil.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/SingletonEhCacheRegionFactory.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BaseEmitterBean.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BeanUtils.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionStats.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionUtils.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CollectionStats.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernate.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistration.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistrationImpl.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMbeanNames.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheStatsImpl.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EntityStats.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/HibernateStatsImpl.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/NullHibernateStats.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/ProviderMBeanRegistrationHelper.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/QueryStats.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/HibernateNonstopCacheExceptionHandler.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAccessStrategyFactory.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareCollectionRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareEntityRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheCollectionRegion.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheDataRegion.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheEntityRegion.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheGeneralDataRegion.java rename hibernate-ehcache/src/main/java/org/hibernate/cache/{internal/EhCacheRegionFactory.java => ehcache/regions/EhcacheQueryResultsRegion.java} (56%) rename hibernate-ehcache/src/main/java/org/hibernate/cache/{internal/SingletonEhCacheRegionFactory.java => ehcache/regions/EhcacheTimestampsRegion.java} (54%) create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTransactionalDataRegion.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractEhcacheAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractReadWriteEhcacheAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactory.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactoryImpl.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheCollectionRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheCollectionRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheEntityRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheCollectionRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheEntityRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheCollectionRegionAccessStrategy.java create mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheEntityRegionAccessStrategy.java delete mode 100644 hibernate-ehcache/src/main/java/org/hibernate/cache/internal/AbstractEhCacheRegionFactory.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/strategy/ItemValueExtractor.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/cache/EhcacheStatsImplTest.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/Account.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/Event.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/EventManager.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/Item.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/Person.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/UuidItem.java create mode 100644 hibernate-ehcache/src/test/java/org/hibernate/test/domain/VersionedItem.java create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/Account.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/Event.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/Item.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/Person.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/ehcache.xml create mode 100644 hibernate-ehcache/src/test/resources/hibernate-config/hibernate.cfg.xml diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java index f14dec3b3e..4089bd846f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java @@ -66,7 +66,8 @@ public class RegionFactoryInitiator implements BasicServiceInitiator) impl; } else { - customImplClass = registry.getService( ClassLoaderService.class ).classForName( impl.toString() ); + customImplClass = registry.getService( ClassLoaderService.class ) + .classForName( mapLegacyNames( impl.toString() ) ); } try { @@ -78,4 +79,18 @@ public class RegionFactoryInitiator implements BasicServiceInitiator + * If not set, ehcache.xml will be looked for in the root of the classpath. + *

+ * If set to say ehcache-1.xml, ehcache-1.xml will be looked for in the root of the classpath. + */ + public static final String NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME = "net.sf.ehcache.configurationResourceName"; + + private static final Logger LOG = LoggerFactory.getLogger( AbstractEhcacheRegionFactory.class ); + + protected ClassLoaderService classLoaderService; + + /** + * MBean registration helper class instance for Ehcache Hibernate MBeans. + */ + protected final ProviderMBeanRegistrationHelper mbeanRegistrationHelper = new ProviderMBeanRegistrationHelper(); + + /** + * Ehcache CacheManager that supplied Ehcache instances for this Hibernate RegionFactory. + */ + protected volatile CacheManager manager; + + /** + * Settings object for the Hibernate persistence unit. + */ + protected Settings settings; + + /** + * {@link EhcacheAccessStrategyFactory} for creating various access strategies + */ + protected final EhcacheAccessStrategyFactory accessStrategyFactory = + new NonstopAccessStrategyFactory( new EhcacheAccessStrategyFactoryImpl() ); + + /** + * Whether to optimize for minimals puts or minimal gets. + *

+ * Indicates whether when operating in non-strict read/write or read-only mode + * Hibernate should optimize the access patterns for minimal puts or minimal gets. + * In Ehcache we default to minimal puts since this should have minimal to no + * affect on unclustered users, and has great benefit for clustered users. + *

+ * This setting can be overridden by setting the "hibernate.cache.use_minimal_puts" + * property in the Hibernate configuration. + * + * @return true, optimize for minimal puts + */ + public boolean isMinimalPutsEnabledByDefault() { + return true; + } + + /** + * {@inheritDoc} + */ + public long nextTimestamp() { + return net.sf.ehcache.util.Timestamper.next(); + } + + /** + * {@inheritDoc} + */ + public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) + throws CacheException { + return new EhcacheEntityRegion( accessStrategyFactory, getCache( regionName ), settings, metadata, properties ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) + throws CacheException { + return new EhcacheCollectionRegion( + accessStrategyFactory, + getCache( regionName ), + settings, + metadata, + properties + ); + } + + /** + * {@inheritDoc} + */ + public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException { + return new EhcacheQueryResultsRegion( accessStrategyFactory, getCache( regionName ), properties ); + } + + /** + * {@inheritDoc} + */ + public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException { + return new EhcacheTimestampsRegion( accessStrategyFactory, getCache( regionName ), properties ); + } + + private Ehcache getCache(String name) throws CacheException { + try { + Ehcache cache = manager.getEhcache( name ); + if ( cache == null ) { + LOG.warn( "Couldn't find a specific ehcache configuration for cache named [" + name + "]; using defaults." ); + manager.addCache( name ); + cache = manager.getEhcache( name ); + LOG.debug( "started EHCache region: " + name ); + } + HibernateUtil.validateEhcache( cache ); + return cache; + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + + } + + /** + * Load a resource from the classpath. + */ + protected URL loadResource(String configurationResourceName) { + URL url = null; + if ( classLoaderService != null ) { + url = classLoaderService.locateResource( configurationResourceName ); + } + if ( url == null ) { + ClassLoader standardClassloader = ClassLoaderUtil.getStandardClassLoader(); + if ( standardClassloader != null ) { + url = standardClassloader.getResource( configurationResourceName ); + } + if ( url == null ) { + url = AbstractEhcacheRegionFactory.class.getResource( configurationResourceName ); + } + } + + LOG.debug( + "Creating EhCacheRegionFactory from a specified resource: {}. Resolved to URL: {}", + configurationResourceName, + url + ); + if ( url == null ) { + + LOG.warn( + "A configurationResourceName was set to {} but the resource could not be loaded from the classpath." + + "Ehcache will configure itself using defaults.", configurationResourceName + ); + } + return url; + } + + /** + * Default access-type used when the configured using JPA 2.0 config. JPA 2.0 allows @Cacheable(true) to be attached to an + * entity without any access type or usage qualification. + *

+ * We are conservative here in specifying {@link AccessType#READ_WRITE} so as to follow the mantra of "do no harm". + *

+ * This is a Hibernate 3.5 method. + */ + public AccessType getDefaultAccessType() { + return AccessType.READ_WRITE; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheMessageLogger.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheMessageLogger.java similarity index 98% rename from hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheMessageLogger.java rename to hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheMessageLogger.java index 3ffe579987..4f366ff5f4 100644 --- a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheMessageLogger.java +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheMessageLogger.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.cache.internal; +package org.hibernate.cache.ehcache; import static org.jboss.logging.Logger.Level.WARN; diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheRegionFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheRegionFactory.java new file mode 100644 index 0000000000..37853e79f1 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/EhCacheRegionFactory.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache; + +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.config.Configuration; +import net.sf.ehcache.config.ConfigurationFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cfg.Settings; +import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.service.spi.InjectService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; + +/** + * A non-singleton EhCacheRegionFactory implementation. + * + * @author Chris Dennis + * @author Greg Luck + * @author Emmanuel Bernard + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class EhCacheRegionFactory extends AbstractEhcacheRegionFactory { + + private static final Logger LOG = LoggerFactory.getLogger( EhCacheRegionFactory.class ); + + private ClassLoaderService classLoaderService; + + public EhCacheRegionFactory() { + } + + /** + * Creates a non-singleton EhCacheRegionFactory + */ + public EhCacheRegionFactory(Properties prop) { + super(); + } + + /** + * {@inheritDoc} + */ + public void start(Settings settings, Properties properties) throws CacheException { + this.settings = settings; + if ( manager != null ) { + LOG.warn( + "Attempt to restart an already started EhCacheRegionFactory. Use sessionFactory.close() " + + " between repeated calls to buildSessionFactory. Using previously created EhCacheRegionFactory." + + " If this behaviour is required, consider using SingletonEhCacheRegionFactory." + ); + return; + } + + try { + String configurationResourceName = null; + if ( properties != null ) { + configurationResourceName = (String) properties.get( NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME ); + } + if ( configurationResourceName == null || configurationResourceName.length() == 0 ) { + Configuration configuration = ConfigurationFactory.parseConfiguration(); + manager = new CacheManager( configuration ); + } + else { + URL url; + try { + url = new URL( configurationResourceName ); + } + catch ( MalformedURLException e ) { + url = loadResource( configurationResourceName ); + } + Configuration configuration = HibernateUtil.loadAndCorrectConfiguration( url ); + manager = new CacheManager( configuration ); + } + mbeanRegistrationHelper.registerMBean( manager, properties ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e.getMessage().startsWith( + "Cannot parseConfiguration CacheManager. Attempt to create a new instance of " + + "CacheManager using the diskStorePath" + ) ) { + throw new CacheException( + "Attempt to restart an already started EhCacheRegionFactory. " + + "Use sessionFactory.close() between repeated calls to buildSessionFactory. " + + "Consider using SingletonEhCacheRegionFactory. Error from ehcache was: " + e.getMessage() + ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public void stop() { + try { + if ( manager != null ) { + mbeanRegistrationHelper.unregisterMBean(); + manager.shutdown(); + manager = null; + } + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + @InjectService + public void setClassLoaderService(ClassLoaderService classLoaderService) { + this.classLoaderService = classLoaderService; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/HibernateUtil.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/HibernateUtil.java new file mode 100644 index 0000000000..e706aa6e51 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/HibernateUtil.java @@ -0,0 +1,120 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache; + +import java.net.URL; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.config.CacheConfiguration; +import net.sf.ehcache.config.Configuration; +import net.sf.ehcache.config.ConfigurationFactory; +import net.sf.ehcache.config.NonstopConfiguration; +import net.sf.ehcache.config.TerracottaConfiguration; +import net.sf.ehcache.config.TerracottaConfiguration.ValueMode; +import net.sf.ehcache.config.TimeoutBehaviorConfiguration.TimeoutBehaviorType; + +import org.hibernate.cache.CacheException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Chris Dennis + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +final class HibernateUtil { + + private static final Logger LOG = LoggerFactory.getLogger( HibernateUtil.class ); + + private HibernateUtil() { + } + + /** + * Create a cache manager configuration from the supplied url, correcting it for Hibernate compatibility. + *

+ * Currently correcting for Hibernate compatibility means simply switching any identity based value modes to serialization. + */ + static Configuration loadAndCorrectConfiguration(URL url) { + Configuration config = ConfigurationFactory.parseConfiguration( url ); + if ( config.getDefaultCacheConfiguration().isTerracottaClustered() ) { + if ( ValueMode.IDENTITY + .equals( config.getDefaultCacheConfiguration().getTerracottaConfiguration().getValueMode() ) ) { + LOG.warn( + "The default cache value mode for this Ehcache configuration is \"identity\". This is incompatible with clustered " + + "Hibernate caching - the value mode has therefore been switched to \"serialization\"" + ); + config.getDefaultCacheConfiguration() + .getTerracottaConfiguration() + .setValueMode( ValueMode.SERIALIZATION.name() ); + } + setupHibernateTimeoutBehavior( + config.getDefaultCacheConfiguration() + .getTerracottaConfiguration() + .getNonstopConfiguration() + ); + } + + for ( CacheConfiguration cacheConfig : config.getCacheConfigurations().values() ) { + if ( cacheConfig.isTerracottaClustered() ) { + if ( ValueMode.IDENTITY.equals( cacheConfig.getTerracottaConfiguration().getValueMode() ) ) { + LOG.warn( + "The value mode for the {0} cache is \"identity\". This is incompatible with clustered Hibernate caching - " + + "the value mode has therefore been switched to \"serialization\"", + cacheConfig.getName() + ); + cacheConfig.getTerracottaConfiguration().setValueMode( ValueMode.SERIALIZATION.name() ); + } + setupHibernateTimeoutBehavior( cacheConfig.getTerracottaConfiguration().getNonstopConfiguration() ); + } + } + return config; + } + + private static void setupHibernateTimeoutBehavior(NonstopConfiguration nonstopConfig) { + nonstopConfig.getTimeoutBehavior().setType( TimeoutBehaviorType.EXCEPTION.getTypeName() ); + } + + /** + * Validates that the supplied Ehcache instance is valid for use as a Hibernate cache. + */ + static void validateEhcache(Ehcache cache) throws CacheException { + CacheConfiguration cacheConfig = cache.getCacheConfiguration(); + + if ( cacheConfig.isTerracottaClustered() ) { + TerracottaConfiguration tcConfig = cacheConfig.getTerracottaConfiguration(); + switch ( tcConfig.getValueMode() ) { + case IDENTITY: + throw new CacheException( + "The clustered Hibernate cache " + cache.getName() + " is using IDENTITY value mode.\n" + + "Identity value mode cannot be used with Hibernate cache regions." + ); + case SERIALIZATION: + default: + // this is the recommended valueMode + break; + } + } + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/SingletonEhCacheRegionFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/SingletonEhCacheRegionFactory.java new file mode 100644 index 0000000000..53076fddcf --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/SingletonEhCacheRegionFactory.java @@ -0,0 +1,123 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache; + +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.config.Configuration; + +import org.hibernate.cache.CacheException; +import org.hibernate.cfg.Settings; +import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.service.spi.InjectService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A singleton EhCacheRegionFactory implementation. + * + * @author Chris Dennis + * @author Greg Luck + * @author Emmanuel Bernard + * @author Alex Snaps + */ +public class SingletonEhCacheRegionFactory extends AbstractEhcacheRegionFactory { + + private static final Logger LOG = LoggerFactory.getLogger( SingletonEhCacheRegionFactory.class ); + + private static final AtomicInteger REFERENCE_COUNT = new AtomicInteger(); + + /** + * Returns a representation of the singleton EhCacheRegionFactory + */ + public SingletonEhCacheRegionFactory(Properties prop) { + super(); + } + + /** + * {@inheritDoc} + */ + public void start(Settings settings, Properties properties) throws CacheException { + try { + String configurationResourceName = null; + if ( properties != null ) { + configurationResourceName = (String) properties.get( NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME ); + } + if ( configurationResourceName == null || configurationResourceName.length() == 0 ) { + manager = CacheManager.create(); + REFERENCE_COUNT.incrementAndGet(); + } + else { + URL url; + try { + url = new URL( configurationResourceName ); + } + catch ( MalformedURLException e ) { + if ( !configurationResourceName.startsWith( "/" ) ) { + configurationResourceName = "/" + configurationResourceName; + LOG.debug( + "prepending / to {}. It should be placed in the root of the classpath rather than in a package.", + configurationResourceName + ); + } + url = loadResource( configurationResourceName ); + } + Configuration configuration = HibernateUtil.loadAndCorrectConfiguration( url ); + manager = CacheManager.create( configuration ); + REFERENCE_COUNT.incrementAndGet(); + } + mbeanRegistrationHelper.registerMBean( manager, properties ); + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public void stop() { + try { + if ( manager != null ) { + if ( REFERENCE_COUNT.decrementAndGet() == 0 ) { + manager.shutdown(); + } + manager = null; + } + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + @InjectService + public void setClassLoaderService(ClassLoaderService classLoaderService) { + this.classLoaderService = classLoaderService; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BaseEmitterBean.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BaseEmitterBean.java new file mode 100644 index 0000000000..2c6a6d27f5 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BaseEmitterBean.java @@ -0,0 +1,182 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +package org.hibernate.cache.ehcache.management.impl; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.StandardMBean; + +/** + * @author gkeim + */ +public abstract class BaseEmitterBean extends StandardMBean implements NotificationEmitter { + /** + * emitter + */ + protected final Emitter emitter = new Emitter(); + + /** + * sequenceNumber + */ + protected final AtomicLong sequenceNumber = new AtomicLong(); + + + private final List notificationListeners = new CopyOnWriteArrayList(); + + /** + * BaseEmitterBean + * + * @param + * @param mbeanInterface + * + * @throws javax.management.NotCompliantMBeanException + */ + protected BaseEmitterBean(Class mbeanInterface) throws NotCompliantMBeanException { + super( mbeanInterface ); + } + + /** + * sendNotification + * + * @param eventType + */ + public void sendNotification(String eventType) { + sendNotification( eventType, null, null ); + } + + /** + * sendNotification + * + * @param eventType + * @param data + */ + public void sendNotification(String eventType, Object data) { + sendNotification( eventType, data, null ); + } + + /** + * sendNotification + * + * @param eventType + * @param data + * @param msg + */ + public void sendNotification(String eventType, Object data, String msg) { + Notification notif = new Notification( + eventType, + this, + sequenceNumber.incrementAndGet(), + System.currentTimeMillis(), + msg + ); + if ( data != null ) { + notif.setUserData( data ); + } + emitter.sendNotification( notif ); + } + + /** + * Dispose of this SampledCacheManager and clean up held resources + */ + public final void dispose() { + doDispose(); + removeAllNotificationListeners(); + } + + /** + * Dispose callback of subclasses + */ + protected abstract void doDispose(); + + /** + * @author gkeim + */ + private class Emitter extends NotificationBroadcasterSupport { + /** + * @see javax.management.NotificationBroadcasterSupport#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return BaseEmitterBean.this.getNotificationInfo(); + } + } + + /** + * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, + * javax.management.NotificationFilter, java.lang.Object) + */ + public void addNotificationListener(NotificationListener notif, NotificationFilter filter, Object callBack) { + emitter.addNotificationListener( notif, filter, callBack ); + notificationListeners.add( notif ); + } + + /** + * remove all added notification listeners + */ + private void removeAllNotificationListeners() { + for ( NotificationListener listener : notificationListeners ) { + try { + emitter.removeNotificationListener( listener ); + } + catch ( ListenerNotFoundException e ) { + // ignore + } + } + notificationListeners.clear(); + } + + /** + * @see javax.management.NotificationBroadcaster#getNotificationInfo() + */ + public abstract MBeanNotificationInfo[] getNotificationInfo(); + + + /** + * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) + */ + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + emitter.removeNotificationListener( listener ); + notificationListeners.remove( listener ); + } + + /** + * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, + * javax.management.NotificationFilter, java.lang.Object) + */ + public void removeNotificationListener(NotificationListener notif, NotificationFilter filter, Object callBack) + throws ListenerNotFoundException { + emitter.removeNotificationListener( notif, filter, callBack ); + notificationListeners.remove( notif ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BeanUtils.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BeanUtils.java new file mode 100644 index 0000000000..1352c0fbfc --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/BeanUtils.java @@ -0,0 +1,145 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +package org.hibernate.cache.ehcache.management.impl; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Reflective utilities for dealing with backward-incompatible change to statistics types in Hibernate 3.5. + * + * @author gkeim + */ +public class BeanUtils { + /** + * Return the named getter method on the bean or null if not found. + * + * @param bean + * @param propertyName + * + * @return the named getter method + */ + private static Method getMethod(Object bean, String propertyName) { + StringBuilder sb = new StringBuilder( "get" ).append( Character.toUpperCase( propertyName.charAt( 0 ) ) ); + if ( propertyName.length() > 1 ) { + sb.append( propertyName.substring( 1 ) ); + } + String getterName = sb.toString(); + for ( Method m : bean.getClass().getMethods() ) { + if ( getterName.equals( m.getName() ) && m.getParameterTypes().length == 0 ) { + return m; + } + } + return null; + } + + /** + * Return the named field on the bean or null if not found. + * + * @param bean + * @param propertyName + * + * @return the named field + */ + private static Field getField(Object bean, String propertyName) { + for ( Field f : bean.getClass().getDeclaredFields() ) { + if ( propertyName.equals( f.getName() ) ) { + return f; + } + } + return null; + } + + private static void validateArgs(Object bean, String propertyName) { + if ( bean == null ) { + throw new IllegalArgumentException( "bean is null" ); + } + if ( propertyName == null ) { + throw new IllegalArgumentException( "propertyName is null" ); + } + if ( propertyName.trim().length() == 0 ) { + throw new IllegalArgumentException( "propertyName is empty" ); + } + } + + /** + * Retrieve a named bean property value. + * + * @param bean bean + * @param propertyName + * + * @return the property value + */ + public static Object getBeanProperty(Object bean, String propertyName) { + validateArgs( bean, propertyName ); + + // try getters first + Method getter = getMethod( bean, propertyName ); + if ( getter != null ) { + try { + return getter.invoke( bean ); + } + catch ( Exception e ) { + /**/ + } + } + + // then try fields + Field field = getField( bean, propertyName ); + if ( field != null ) { + try { + field.setAccessible( true ); + return field.get( bean ); + } + catch ( Exception e ) { + /**/ + } + } + + return null; + } + + /** + * Retrieve a Long bean property value. + * + * @param bean bean + * @param propertyName + * + * @return long value + * + * @throws NoSuchFieldException + */ + public static long getLongBeanProperty(final Object bean, final String propertyName) throws NoSuchFieldException { + validateArgs( bean, propertyName ); + Object o = getBeanProperty( bean, propertyName ); + if ( o == null ) { + throw new NoSuchFieldException( propertyName ); + } + else if ( !( o instanceof Number ) ) { + throw new IllegalArgumentException( propertyName + " not an Number" ); + } + return ( (Number) o ).longValue(); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionStats.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionStats.java new file mode 100644 index 0000000000..677f217d1f --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionStats.java @@ -0,0 +1,303 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.hibernate.stat.SecondLevelCacheStatistics; + +/** + * @author gkeim + */ +public class CacheRegionStats implements Serializable { + private static final String COMPOSITE_TYPE_NAME = "CacheRegionStats"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Statistics per Cache-region"; + private static final String[] ITEM_NAMES = new String[] { + "region", "shortName", "hitCount", + "missCount", "putCount", "hitRatio", "elementCountInMemory", "elementCountOnDisk", "elementCountTotal", + }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { + "region", "shortName", "hitCount", + "missCount", "putCount", "hitRatio", "elementCountInMemory", "elementCountOnDisk", "elementCountTotal", + }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { + SimpleType.STRING, + SimpleType.STRING, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.DOUBLE, SimpleType.LONG, + SimpleType.LONG, SimpleType.LONG, + }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "Statistics by Cache-region"; + private static final String TABULAR_TYPE_DESCRIPTION = "All Cache Region Statistics"; + private static final String[] INDEX_NAMES = new String[] { "region", }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType( + COMPOSITE_TYPE_NAME, COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, + ITEM_DESCRIPTIONS, ITEM_TYPES + ); + TABULAR_TYPE = new TabularType( TABULAR_TYPE_NAME, TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * region name + */ + protected final String region; + + /** + * region short name + */ + protected final String shortName; + + /** + * hit count + */ + protected long hitCount; + + /** + * miss count + */ + protected long missCount; + + /** + * put count + */ + protected long putCount; + + /** + * hit ratio + */ + protected double hitRatio; + + /** + * in-memory element count + */ + protected long elementCountInMemory; + + /** + * on-disk element count + */ + protected long elementCountOnDisk; + + /** + * total element count + */ + protected long elementCountTotal; + + /** + * @param region + */ + public CacheRegionStats(String region) { + this.region = region; + this.shortName = CacheRegionUtils.determineShortName( region ); + } + + /** + * @param region + * @param src + */ + public CacheRegionStats(String region, SecondLevelCacheStatistics src) { + this( region ); + + try { + this.hitCount = BeanUtils.getLongBeanProperty( src, "hitCount" ); + this.missCount = BeanUtils.getLongBeanProperty( src, "missCount" ); + this.putCount = BeanUtils.getLongBeanProperty( src, "putCount" ); + this.hitRatio = determineHitRatio(); + this.elementCountInMemory = BeanUtils.getLongBeanProperty( src, "elementCountInMemory" ); + this.elementCountOnDisk = BeanUtils.getLongBeanProperty( src, "elementCountOnDisk" ); + this.elementCountTotal = BeanUtils.getLongBeanProperty( src, "elementCountOnDisk" ); + } + catch ( Exception e ) { + e.printStackTrace(); + throw new RuntimeException( "Exception retrieving statistics", e ); + } + } + + /** + * @param cData + */ + public CacheRegionStats(final CompositeData cData) { + int i = 0; + region = (String) cData.get( ITEM_NAMES[i++] ); + shortName = (String) cData.get( ITEM_NAMES[i++] ); + hitCount = (Long) cData.get( ITEM_NAMES[i++] ); + missCount = (Long) cData.get( ITEM_NAMES[i++] ); + putCount = (Long) cData.get( ITEM_NAMES[i++] ); + hitRatio = (Double) cData.get( ITEM_NAMES[i++] ); + elementCountInMemory = (Long) cData.get( ITEM_NAMES[i++] ); + elementCountOnDisk = (Long) cData.get( ITEM_NAMES[i++] ); + elementCountTotal = (Long) cData.get( ITEM_NAMES[i++] ); + } + + private static int safeParseInt(String s) { + try { + return Integer.parseInt( s ); + } + catch ( Exception e ) { + return -1; + } + } + + /** + * @return hit ratio + */ + protected double determineHitRatio() { + double result = 0; + long readCount = getHitCount() + getMissCount(); + if ( readCount > 0 ) { + result = getHitCount() / ( (double) readCount ); + } + return result; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "region=" + getRegion() + "shortName=" + getShortName() + ", hitCount=" + getHitCount() + ", missCount=" + + getMissCount() + ", putCount" + getPutCount() + ", hitRatio" + getHitRatio() + ", elementCountInMemory=" + + getElementCountInMemory() + ", elementCountOnDisk=" + getElementCountOnDisk() + ", elementCountTotal=" + + getElementCountTotal(); + } + + /** + * @return region name + */ + public String getRegion() { + return region; + } + + /** + * @return short name + */ + public String getShortName() { + return shortName; + } + + /** + * @return hit count + */ + public long getHitCount() { + return hitCount; + } + + /** + * @return miss count + */ + public long getMissCount() { + return missCount; + } + + /** + * @return put count + */ + public long getPutCount() { + return putCount; + } + + /** + * @return hit ratio + */ + public double getHitRatio() { + return hitRatio; + } + + /** + * @return in-memory element count + */ + public long getElementCountInMemory() { + return elementCountInMemory; + } + + /** + * @return on-disk element count + */ + public long getElementCountOnDisk() { + return elementCountOnDisk; + } + + /** + * @return total element count + */ + public long getElementCountTotal() { + return elementCountTotal; + } + + /** + * @return composite data + */ + public CompositeData toCompositeData() { + try { + return new CompositeDataSupport( + COMPOSITE_TYPE, ITEM_NAMES, new Object[] { + getRegion(), getShortName(), + getHitCount(), getMissCount(), getPutCount(), getHitRatio(), getElementCountInMemory(), + getElementCountOnDisk(), getElementCountTotal(), + } + ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * @return tabular data + */ + public static TabularData newTabularDataInstance() { + return new TabularDataSupport( TABULAR_TYPE ); + } + + /** + * @param tabularData + * + * @return array of region statistics + */ + public static CacheRegionStats[] fromTabularData(final TabularData tabularData) { + final List countList = new ArrayList( tabularData.size() ); + for ( final Iterator pos = tabularData.values().iterator(); pos.hasNext(); ) { + countList.add( new CacheRegionStats( (CompositeData) pos.next() ) ); + } + return countList.toArray( new CacheRegionStats[countList.size()] ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionUtils.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionUtils.java new file mode 100644 index 0000000000..023876fa68 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CacheRegionUtils.java @@ -0,0 +1,126 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.awt.*; + +/** + * CacheRegionUtils + * + * @author gkeim + */ +public abstract class CacheRegionUtils { + /** + * HIT_COLOR + */ + public static final Color HIT_COLOR = Color.green; + + /** + * MISS_COLOR + */ + public static final Color MISS_COLOR = Color.red; + + /** + * PUT_COLOR + */ + public static final Color PUT_COLOR = Color.blue; + + /** + * HIT_FILL_COLOR + */ + public final static Color HIT_FILL_COLOR = CacheRegionUtils.HIT_COLOR.brighter().brighter().brighter(); + + /** + * MISS_FILL_COLOR + */ + public final static Color MISS_FILL_COLOR = CacheRegionUtils.MISS_COLOR.brighter().brighter().brighter(); + + /** + * PUT_FILL_COLOR + */ + public final static Color PUT_FILL_COLOR = CacheRegionUtils.PUT_COLOR.brighter().brighter().brighter(); + + /** + * HIT_DRAW_COLOR + */ + public final static Color HIT_DRAW_COLOR = CacheRegionUtils.HIT_COLOR.darker(); + + /** + * MISS_DRAW_COLOR + */ + public final static Color MISS_DRAW_COLOR = CacheRegionUtils.MISS_COLOR.darker(); + + /** + * PUT_DRAW_COLOR + */ + public final static Color PUT_DRAW_COLOR = CacheRegionUtils.PUT_COLOR.darker(); + + + /** + * determineShortName + * + * @param fullName + */ + public static String determineShortName(String fullName) { + String result = fullName; + + if ( fullName != null ) { + String[] comps = fullName.split( "\\." ); + if ( comps.length == 1 ) { + return fullName; + } + boolean truncate = true; + for ( int i = 0; i < comps.length; i++ ) { + String comp = comps[i]; + char c = comp.charAt( 0 ); + if ( truncate && Character.isUpperCase( c ) ) { + truncate = false; + } + if ( truncate ) { + comps[i] = Character.toString( c ); + } + } + result = join( comps, '.' ); + } + + return result; + } + + /** + * join + * + * @param elements + * @param c + */ + private static String join(String[] elements, char c) { + if ( elements == null ) { + return null; + } + StringBuilder sb = new StringBuilder(); + for ( String s : elements ) { + sb.append( s ).append( c ); + } + return sb.length() > 0 ? sb.substring( 0, sb.length() - 1 ) : ""; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CollectionStats.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CollectionStats.java new file mode 100644 index 0000000000..a6eebe5280 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/CollectionStats.java @@ -0,0 +1,273 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.hibernate.stat.CollectionStatistics; + +/** + * CollectionStats + * + * @author gkeim + */ +public class CollectionStats implements Serializable { + private static final String COMPOSITE_TYPE_NAME = "CollectionsStats"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Statistics per Collections"; + private static final String[] ITEM_NAMES = new String[] { + "roleName", "shortName", "loadCount", + "fetchCount", "updateCount", "removeCount", "recreateCount", + }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { + "roleName", "shortName", "loadCount", + "fetchCount", "updateCount", "removeCount", "recreateCount", + }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { + SimpleType.STRING, + SimpleType.STRING, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, + }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "Statistics by Collection"; + private static final String TABULAR_TYPE_DESCRIPTION = "All Collection Statistics"; + private static final String[] INDEX_NAMES = new String[] { "roleName", }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType( + COMPOSITE_TYPE_NAME, COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, + ITEM_DESCRIPTIONS, ITEM_TYPES + ); + TABULAR_TYPE = new TabularType( TABULAR_TYPE_NAME, TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * roleName + */ + protected final String roleName; + + /** + * shortName + */ + protected final String shortName; + + /** + * loadCount + */ + protected long loadCount; + + /** + * fetchCount + */ + protected long fetchCount; + + /** + * updateCount + */ + protected long updateCount; + + /** + * removeCount + */ + protected long removeCount; + + /** + * recreateCount + */ + protected long recreateCount; + + + /** + * @param roleName + */ + public CollectionStats(String roleName) { + this.roleName = roleName; + this.shortName = CacheRegionUtils.determineShortName( roleName ); + } + + /** + * @param name + * @param src + */ + public CollectionStats(String name, CollectionStatistics src) { + this( name ); + + try { + this.loadCount = BeanUtils.getLongBeanProperty( src, "loadCount" ); + this.fetchCount = BeanUtils.getLongBeanProperty( src, "fetchCount" ); + this.updateCount = BeanUtils.getLongBeanProperty( src, "updateCount" ); + this.removeCount = BeanUtils.getLongBeanProperty( src, "removeCount" ); + this.recreateCount = BeanUtils.getLongBeanProperty( src, "recreateCount" ); + } + catch ( Exception e ) { + e.printStackTrace(); + throw new RuntimeException( "Exception retrieving statistics", e ); + } + } + + /** + * @param cData + */ + public CollectionStats(final CompositeData cData) { + int i = 0; + roleName = (String) cData.get( ITEM_NAMES[i++] ); + shortName = (String) cData.get( ITEM_NAMES[i++] ); + loadCount = (Long) cData.get( ITEM_NAMES[i++] ); + fetchCount = (Long) cData.get( ITEM_NAMES[i++] ); + updateCount = (Long) cData.get( ITEM_NAMES[i++] ); + removeCount = (Long) cData.get( ITEM_NAMES[i++] ); + recreateCount = (Long) cData.get( ITEM_NAMES[i++] ); + } + + private static int safeParseInt(String s) { + try { + return Integer.parseInt( s ); + } + catch ( Exception e ) { + return -1; + } + } + + /** + * @param stats + */ + public void add(CollectionStats stats) { + loadCount += stats.getLoadCount(); + fetchCount += stats.getFetchCount(); + updateCount += stats.getUpdateCount(); + removeCount += stats.getRemoveCount(); + recreateCount += stats.getRecreateCount(); + } + + /** + * toString + */ + @Override + public String toString() { + return "roleName=" + roleName + "shortName=" + shortName + ", loadCount=" + loadCount + ", fetchCount=" + + fetchCount + ", updateCount=" + updateCount + ", removeCount=" + removeCount + ", recreateCount" + + recreateCount; + } + + /** + * getRoleName + */ + public String getRoleName() { + return roleName; + } + + /** + * getShortName + */ + public String getShortName() { + return shortName; + } + + /** + * getLoadCount + */ + public long getLoadCount() { + return loadCount; + } + + /** + * getFetchCount + */ + public long getFetchCount() { + return fetchCount; + } + + /** + * getUpdateCount + */ + public long getUpdateCount() { + return updateCount; + } + + /** + * getRemoveCount + */ + public long getRemoveCount() { + return removeCount; + } + + /** + * getRecreateCount + */ + public long getRecreateCount() { + return recreateCount; + } + + /** + * toCompositeData + */ + public CompositeData toCompositeData() { + try { + return new CompositeDataSupport( + COMPOSITE_TYPE, ITEM_NAMES, new Object[] { + roleName, shortName, loadCount, + fetchCount, updateCount, removeCount, recreateCount, + } + ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * newTabularDataInstance + */ + public static TabularData newTabularDataInstance() { + return new TabularDataSupport( TABULAR_TYPE ); + } + + /** + * fromTabularData + */ + public static CollectionStats[] fromTabularData(final TabularData tabularData) { + final List countList = new ArrayList( tabularData.size() ); + for ( final Iterator pos = tabularData.values().iterator(); pos.hasNext(); ) { + countList.add( new CollectionStats( (CompositeData) pos.next() ) ); + } + return countList.toArray( new CollectionStats[countList.size()] ); + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernate.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernate.java new file mode 100644 index 0000000000..d63a0c8407 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernate.java @@ -0,0 +1,597 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.openmbean.TabularData; + +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.hibernate.management.api.EhcacheHibernateMBean; +import net.sf.ehcache.hibernate.management.api.EhcacheStats; +import net.sf.ehcache.hibernate.management.api.HibernateStats; + +import org.hibernate.SessionFactory; + +/** + * Implementation of the {@link EhcacheHibernateMBean} + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public class EhcacheHibernate extends BaseEmitterBean implements EhcacheHibernateMBean { + private static final MBeanNotificationInfo NOTIFICATION_INFO; + + private final AtomicBoolean statsEnabled = new AtomicBoolean( true ); + private EhcacheStats ehcacheStats; + private volatile HibernateStats hibernateStats = NullHibernateStats.INSTANCE; + + static { + final String[] notifTypes = new String[] { }; + final String name = Notification.class.getName(); + final String description = "Ehcache Hibernate Statistics Event"; + NOTIFICATION_INFO = new MBeanNotificationInfo( notifTypes, name, description ); + } + + /** + * Constructor accepting the backing {@link CacheManager} + * + * @param manager the backing {@link CacheManager} + * + * @throws NotCompliantMBeanException + */ + public EhcacheHibernate(CacheManager manager) throws NotCompliantMBeanException { + super( EhcacheHibernateMBean.class ); + ehcacheStats = new EhcacheStatsImpl( manager ); + } + + /** + * Enable hibernate statistics with the input session factory + */ + public void enableHibernateStatistics(SessionFactory sessionFactory) { + try { + hibernateStats = new HibernateStatsImpl( sessionFactory ); + } + catch ( Exception e ) { + throw new RuntimeException( e ); + } + } + + /** + * {@inheritDoc} + */ + public boolean isHibernateStatisticsSupported() { + return hibernateStats instanceof HibernateStatsImpl; + } + + /** + * {@inheritDoc} + */ + public void setStatisticsEnabled(boolean flag) { + if ( flag ) { + ehcacheStats.enableStats(); + hibernateStats.enableStats(); + } + else { + ehcacheStats.disableStats(); + hibernateStats.disableStats(); + } + statsEnabled.set( flag ); + sendNotification( HibernateStats.CACHE_STATISTICS_ENABLED, flag ); + } + + /** + * {@inheritDoc} + */ + public boolean isStatisticsEnabled() { + return statsEnabled.get(); + } + + /** + * {@inheritDoc} + */ + public void clearStats() { + ehcacheStats.clearStats(); + hibernateStats.clearStats(); + } + + /** + * {@inheritDoc} + */ + public void disableStats() { + setStatisticsEnabled( false ); + } + + /** + * {@inheritDoc} + */ + public void enableStats() { + setStatisticsEnabled( true ); + } + + /** + * {@inheritDoc} + */ + public void flushRegionCache(String region) { + ehcacheStats.flushRegionCache( region ); + sendNotification( HibernateStats.CACHE_REGION_FLUSHED, region ); + } + + /** + * {@inheritDoc} + */ + public void flushRegionCaches() { + ehcacheStats.flushRegionCaches(); + sendNotification( HibernateStats.CACHE_FLUSHED ); + } + + /** + * {@inheritDoc} + */ + public String generateActiveConfigDeclaration() { + return ehcacheStats.generateActiveConfigDeclaration(); + } + + /** + * {@inheritDoc} + */ + public String generateActiveConfigDeclaration(String region) { + return ehcacheStats.generateActiveConfigDeclaration( region ); + } + + /** + * {@inheritDoc} + */ + public long getCacheHitCount() { + return ehcacheStats.getCacheHitCount(); + } + + /** + * {@inheritDoc} + */ + public double getCacheHitRate() { + return ehcacheStats.getCacheHitRate(); + } + + /** + * {@inheritDoc} + */ + public long getCacheHitSample() { + return ehcacheStats.getCacheHitSample(); + } + + /** + * {@inheritDoc} + */ + public long getCacheMissCount() { + return ehcacheStats.getCacheMissCount(); + } + + /** + * {@inheritDoc} + */ + public double getCacheMissRate() { + return ehcacheStats.getCacheMissRate(); + } + + /** + * {@inheritDoc} + */ + public long getCacheMissSample() { + return ehcacheStats.getCacheMissSample(); + } + + /** + * {@inheritDoc} + */ + public long getCachePutCount() { + return ehcacheStats.getCachePutCount(); + } + + /** + * {@inheritDoc} + */ + public double getCachePutRate() { + return ehcacheStats.getCachePutRate(); + } + + /** + * {@inheritDoc} + */ + public long getCachePutSample() { + return ehcacheStats.getCachePutSample(); + } + + /** + * {@inheritDoc} + */ + public TabularData getCacheRegionStats() { + return hibernateStats.getCacheRegionStats(); + } + + /** + * {@inheritDoc} + */ + public long getCloseStatementCount() { + return hibernateStats.getCloseStatementCount(); + } + + /** + * {@inheritDoc} + */ + public TabularData getCollectionStats() { + return hibernateStats.getCollectionStats(); + } + + /** + * {@inheritDoc} + */ + public long getConnectCount() { + return hibernateStats.getConnectCount(); + } + + /** + * {@inheritDoc} + */ + public TabularData getEntityStats() { + return hibernateStats.getEntityStats(); + } + + /** + * {@inheritDoc} + */ + public long getFlushCount() { + return hibernateStats.getFlushCount(); + } + + /** + * {@inheritDoc} + */ + public long getOptimisticFailureCount() { + return hibernateStats.getOptimisticFailureCount(); + } + + /** + * {@inheritDoc} + */ + public String getOriginalConfigDeclaration() { + return ehcacheStats.getOriginalConfigDeclaration(); + } + + /** + * {@inheritDoc} + */ + public String getOriginalConfigDeclaration(String region) { + return ehcacheStats.getOriginalConfigDeclaration( region ); + } + + /** + * {@inheritDoc} + */ + public long getPrepareStatementCount() { + return hibernateStats.getPrepareStatementCount(); + } + + /** + * {@inheritDoc} + */ + public long getQueryExecutionCount() { + return hibernateStats.getQueryExecutionCount(); + } + + /** + * {@inheritDoc} + */ + public double getQueryExecutionRate() { + return hibernateStats.getQueryExecutionRate(); + } + + /** + * {@inheritDoc} + */ + public long getQueryExecutionSample() { + return hibernateStats.getQueryExecutionSample(); + } + + /** + * {@inheritDoc} + */ + public TabularData getQueryStats() { + return hibernateStats.getQueryStats(); + } + + /** + * {@inheritDoc} + */ + public Map> getRegionCacheAttributes() { + return ehcacheStats.getRegionCacheAttributes(); + } + + /** + * {@inheritDoc} + */ + public Map getRegionCacheAttributes(String regionName) { + return ehcacheStats.getRegionCacheAttributes( regionName ); + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheMaxTTISeconds(String region) { + return ehcacheStats.getRegionCacheMaxTTISeconds( region ); + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheMaxTTLSeconds(String region) { + return ehcacheStats.getRegionCacheMaxTTLSeconds( region ); + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheOrphanEvictionPeriod(String region) { + return ehcacheStats.getRegionCacheOrphanEvictionPeriod( region ); + } + + /** + * {@inheritDoc} + */ + public Map getRegionCacheSamples() { + return ehcacheStats.getRegionCacheSamples(); + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheTargetMaxInMemoryCount(String region) { + return ehcacheStats.getRegionCacheTargetMaxInMemoryCount( region ); + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheTargetMaxTotalCount(String region) { + return ehcacheStats.getRegionCacheTargetMaxTotalCount( region ); + } + + /** + * {@inheritDoc} + */ + public long getSessionCloseCount() { + return hibernateStats.getSessionCloseCount(); + } + + /** + * {@inheritDoc} + */ + public long getSessionOpenCount() { + return hibernateStats.getSessionOpenCount(); + } + + /** + * {@inheritDoc} + */ + public long getSuccessfulTransactionCount() { + return hibernateStats.getSuccessfulTransactionCount(); + } + + /** + * {@inheritDoc} + */ + public String[] getTerracottaHibernateCacheRegionNames() { + return ehcacheStats.getTerracottaHibernateCacheRegionNames(); + } + + /** + * {@inheritDoc} + */ + public long getTransactionCount() { + return hibernateStats.getTransactionCount(); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheEnabled(String region) { + return ehcacheStats.isRegionCacheEnabled( region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCachesEnabled(boolean enabled) { + ehcacheStats.setRegionCachesEnabled( enabled ); + sendNotification( HibernateStats.CACHE_ENABLED, enabled ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheEnabled(String region, boolean enabled) { + ehcacheStats.setRegionCacheEnabled( region, enabled ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheLoggingEnabled(String region) { + return ehcacheStats.isRegionCacheLoggingEnabled( region ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheOrphanEvictionEnabled(String region) { + return ehcacheStats.isRegionCacheOrphanEvictionEnabled( region ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCachesEnabled() { + return ehcacheStats.isRegionCachesEnabled(); + } + + /** + * {@inheritDoc} + */ + public boolean isTerracottaHibernateCache(String region) { + return ehcacheStats.isTerracottaHibernateCache( region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheLoggingEnabled(String region, boolean loggingEnabled) { + ehcacheStats.setRegionCacheLoggingEnabled( region, loggingEnabled ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheMaxTTISeconds(String region, int maxTTISeconds) { + ehcacheStats.setRegionCacheMaxTTISeconds( region, maxTTISeconds ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheMaxTTLSeconds(String region, int maxTTLSeconds) { + ehcacheStats.setRegionCacheMaxTTLSeconds( region, maxTTLSeconds ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheTargetMaxInMemoryCount(String region, int targetMaxInMemoryCount) { + ehcacheStats.setRegionCacheTargetMaxInMemoryCount( region, targetMaxInMemoryCount ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheTargetMaxTotalCount(String region, int targetMaxTotalCount) { + ehcacheStats.setRegionCacheTargetMaxTotalCount( region, targetMaxTotalCount ); + sendNotification( HibernateStats.CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsInMemory(java.lang.String) + */ + public int getNumberOfElementsInMemory(String region) { + return ehcacheStats.getNumberOfElementsInMemory( region ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsInMemory(java.lang.String) + */ + public int getNumberOfElementsOffHeap(String region) { + return ehcacheStats.getNumberOfElementsOffHeap( region ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsOnDisk(java.lang.String) + */ + public int getNumberOfElementsOnDisk(String region) { + return ehcacheStats.getNumberOfElementsOnDisk( region ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMaxGetTimeMillis() + */ + public long getMaxGetTimeMillis() { + return ehcacheStats.getMaxGetTimeMillis(); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMaxGetTimeMillis(java.lang.String) + */ + public long getMaxGetTimeMillis(String cacheName) { + return ehcacheStats.getMaxGetTimeMillis( cacheName ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMinGetTimeMillis() + */ + public long getMinGetTimeMillis() { + return ehcacheStats.getMinGetTimeMillis(); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMinGetTimeMillis(java.lang.String) + */ + public long getMinGetTimeMillis(String cacheName) { + return ehcacheStats.getMinGetTimeMillis( cacheName ); + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getAverageGetTimeMillis(java.lang.String) + */ + public float getAverageGetTimeMillis(String region) { + return ehcacheStats.getAverageGetTimeMillis( region ); + } + + /** + * {@inheritDoc} + */ + @Override + protected void doDispose() { + // no-op + } + + /** + * @see BaseEmitterBean#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] { NOTIFICATION_INFO }; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistration.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistration.java new file mode 100644 index 0000000000..2fdeba973d --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistration.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.util.Properties; + +import net.sf.ehcache.CacheManager; + +import org.hibernate.SessionFactory; + +/** + * Interface for helping registering mbeans for ehcache backed hibernate second-level cache + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public interface EhcacheHibernateMBeanRegistration { + + /** + * Registers MBean for the input manager and session factory properties. + *

+ * MBeans will be registered based on the input session factory name. If the input name is null or blank, the name of the cache-manager + * is used + * + * @param manager + * @param properties + * + * @throws Exception + */ + public void registerMBeanForCacheManager(CacheManager manager, Properties properties) throws Exception; + + /** + * Enable hibernate statistics in the mbean. + * + * @param sessionFactory + */ + public void enableHibernateStatisticsSupport(SessionFactory sessionFactory); + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistrationImpl.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistrationImpl.java new file mode 100644 index 0000000000..cc6190d399 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMBeanRegistrationImpl.java @@ -0,0 +1,177 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.lang.management.ManagementFactory; +import java.util.Properties; +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import net.sf.ehcache.CacheException; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Status; +import net.sf.ehcache.event.CacheManagerEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Environment; + +/** + * Implementation of {@link EhcacheHibernateMBeanRegistration}. + * Also implements {@link net.sf.ehcache.event.CacheManagerEventListener}. Deregisters mbeans when the associated cachemanager is shutdown. + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public class EhcacheHibernateMBeanRegistrationImpl + implements EhcacheHibernateMBeanRegistration, CacheManagerEventListener { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheHibernateMBeanRegistrationImpl.class.getName() ); + + private static final int MAX_MBEAN_REGISTRATION_RETRIES = 50; + private String cacheManagerClusterUUID; + private String registeredCacheManagerName; + private Status status = Status.STATUS_UNINITIALISED; + private volatile EhcacheHibernate ehcacheHibernate; + private volatile ObjectName cacheManagerObjectName; + + /** + * {@inheritDoc} + */ + public synchronized void registerMBeanForCacheManager(final CacheManager manager, final Properties properties) + throws Exception { + String sessionFactoryName = properties.getProperty( Environment.SESSION_FACTORY_NAME ); + String name = null; + if ( sessionFactoryName == null ) { + name = manager.getName(); + } + else { + name = "".equals( sessionFactoryName.trim() ) ? manager.getName() : sessionFactoryName; + } + registerBean( name, manager ); + } + + private void registerBean(String name, CacheManager manager) throws Exception { + ehcacheHibernate = new EhcacheHibernate( manager ); + int tries = 0; + boolean success = false; + Exception exception = null; + cacheManagerClusterUUID = manager.getClusterUUID(); + do { + this.registeredCacheManagerName = name; + if ( tries != 0 ) { + registeredCacheManagerName += "_" + tries; + } + try { + // register the CacheManager MBean + MBeanServer mBeanServer = getMBeanServer(); + cacheManagerObjectName = EhcacheHibernateMbeanNames.getCacheManagerObjectName( + cacheManagerClusterUUID, + registeredCacheManagerName + ); + mBeanServer.registerMBean( ehcacheHibernate, cacheManagerObjectName ); + success = true; + break; + } + catch ( InstanceAlreadyExistsException e ) { + success = false; + exception = e; + } + tries++; + } while ( tries < MAX_MBEAN_REGISTRATION_RETRIES ); + if ( !success ) { + throw new Exception( + "Cannot register mbean for CacheManager with name" + manager.getName() + " after " + + MAX_MBEAN_REGISTRATION_RETRIES + " retries. Last tried name=" + registeredCacheManagerName, + exception + ); + } + status = status.STATUS_ALIVE; + } + + private MBeanServer getMBeanServer() { + return ManagementFactory.getPlatformMBeanServer(); + } + + /** + * {@inheritDoc} + */ + public void enableHibernateStatisticsSupport(SessionFactory sessionFactory) { + ehcacheHibernate.enableHibernateStatistics( sessionFactory ); + } + + /** + * {@inheritDoc} + */ + public synchronized void dispose() throws CacheException { + if ( status == Status.STATUS_SHUTDOWN ) { + return; + } + + try { + getMBeanServer().unregisterMBean( cacheManagerObjectName ); + } + catch ( Exception e ) { + LOG.warn( + "Error unregistering object instance " + cacheManagerObjectName + " . Error was " + e.getMessage(), + e + ); + } + ehcacheHibernate = null; + cacheManagerObjectName = null; + status = Status.STATUS_SHUTDOWN; + } + + /** + * {@inheritDoc} + */ + public synchronized Status getStatus() { + return status; + } + + /** + * No-op in this case + */ + public void init() throws CacheException { + // no-op + } + + /** + * No-op in this case + */ + public void notifyCacheAdded(String cacheName) { + // no-op + } + + /** + * No-op in this case + */ + public void notifyCacheRemoved(String cacheName) { + // no-op + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMbeanNames.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMbeanNames.java new file mode 100644 index 0000000000..46fd1c2176 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheHibernateMbeanNames.java @@ -0,0 +1,89 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * Utility class used for getting {@link javax.management.ObjectName}'s for ehcache hibernate MBeans + *

+ *

+ * + * @author Abhishek Sanoujam + * @since 1.7 + */ +public abstract class EhcacheHibernateMbeanNames { + + /** + * Group id for all sampled mbeans registered + */ + public static final String GROUP_ID = "net.sf.ehcache.hibernate"; + + /** + * Type for the ehcache backed hibernate second level cache statistics mbean + */ + public static final String EHCACHE_HIBERNATE_TYPE = "EhcacheHibernateStats"; + + /** + * Filter out invalid ObjectName characters from s. + * + * @param s + * + * @return A valid JMX ObjectName attribute value. + */ + public static String mbeanSafe(String s) { + return s == null ? "" : s.replaceAll( ":|=|\n", "." ); + } + + /** + * Returns an ObjectName for the passed name + * + * @param name + * + * @return An {@link javax.management.ObjectName} using the input name of cache manager + * + * @throws javax.management.MalformedObjectNameException + */ + public static ObjectName getCacheManagerObjectName(String cacheManagerClusterUUID, String name) + throws MalformedObjectNameException { + ObjectName objectName = new ObjectName( + GROUP_ID + ":type=" + EHCACHE_HIBERNATE_TYPE + ",name=" + mbeanSafe( name ) + + getBeanNameSuffix( cacheManagerClusterUUID ) + ); + return objectName; + } + + private static String getBeanNameSuffix(String cacheManagerClusterUUID) { + String suffix = ""; + if ( !isBlank( cacheManagerClusterUUID ) ) { + suffix = ",node=" + cacheManagerClusterUUID; + } + return suffix; + } + + private static boolean isBlank(String param) { + return param == null || "".equals( param.trim() ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheStatsImpl.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheStatsImpl.java new file mode 100644 index 0000000000..190c9245a2 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EhcacheStatsImpl.java @@ -0,0 +1,689 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.hibernate.management.api.EhcacheStats; +import net.sf.ehcache.management.sampled.SampledCacheManager; + +/** + * Implementation of {@link EhcacheStats} + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public class EhcacheStatsImpl extends BaseEmitterBean implements EhcacheStats { + private static final long MILLIS_PER_SECOND = 1000; + private static final MBeanNotificationInfo NOTIFICATION_INFO; + + private final SampledCacheManager sampledCacheManager; + private final CacheManager cacheManager; + private long statsSince = System.currentTimeMillis(); + + static { + final String[] notifTypes = new String[] { + CACHE_ENABLED, CACHE_REGION_CHANGED, CACHE_FLUSHED, CACHE_REGION_FLUSHED, + CACHE_STATISTICS_ENABLED, CACHE_STATISTICS_RESET, + }; + final String name = Notification.class.getName(); + final String description = "Ehcache Hibernate Statistics Event"; + NOTIFICATION_INFO = new MBeanNotificationInfo( notifTypes, name, description ); + } + + /** + * Constructor accepting the backing {@link CacheManager} + * + * @throws javax.management.NotCompliantMBeanException + */ + public EhcacheStatsImpl(CacheManager manager) throws NotCompliantMBeanException { + super( EhcacheStats.class ); + this.sampledCacheManager = new SampledCacheManager( manager ); + this.cacheManager = manager; + } + + /** + * {@inheritDoc} + */ + public boolean isStatisticsEnabled() { + return false; + } + + /** + * {@inheritDoc} + */ + public void clearStats() { + sampledCacheManager.clearStatistics(); + statsSince = System.currentTimeMillis(); + } + + /** + * {@inheritDoc} + */ + public void disableStats() { + setStatisticsEnabled( false ); + } + + /** + * {@inheritDoc} + */ + public void enableStats() { + setStatisticsEnabled( true ); + } + + /** + * {@inheritDoc} + */ + public void flushRegionCache(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.flush(); + } + } + + /** + * {@inheritDoc} + */ + public void flushRegionCaches() { + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = this.cacheManager.getCache( name ); + if ( cache != null ) { + cache.flush(); + } + } + + } + + /** + * {@inheritDoc} + */ + public String generateActiveConfigDeclaration() { + return this.cacheManager.getActiveConfigurationText(); + } + + /** + * {@inheritDoc} + */ + public String generateActiveConfigDeclaration(String region) { + return this.cacheManager.getActiveConfigurationText( region ); + } + + /** + * {@inheritDoc} + */ + public long getCacheHitCount() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getLiveCacheStatistics().getCacheHitCount(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public double getCacheHitRate() { + long now = System.currentTimeMillis(); + double deltaSecs = (double) ( now - statsSince ) / MILLIS_PER_SECOND; + return getCacheHitCount() / deltaSecs; + } + + /** + * {@inheritDoc} + */ + public long getCacheHitSample() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getSampledCacheStatistics().getCacheHitMostRecentSample(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public long getCacheMissCount() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getLiveCacheStatistics().getCacheMissCount(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public double getCacheMissRate() { + long now = System.currentTimeMillis(); + double deltaSecs = (double) ( now - statsSince ) / MILLIS_PER_SECOND; + return getCacheMissCount() / deltaSecs; + } + + /** + * {@inheritDoc} + */ + public long getCacheMissSample() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getSampledCacheStatistics().getCacheMissMostRecentSample(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public long getCachePutCount() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getLiveCacheStatistics().getPutCount(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public double getCachePutRate() { + long now = System.currentTimeMillis(); + double deltaSecs = (double) ( now - statsSince ) / MILLIS_PER_SECOND; + return getCachePutCount() / deltaSecs; + } + + /** + * {@inheritDoc} + */ + public long getCachePutSample() { + long count = 0; + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + count += cache.getSampledCacheStatistics().getCacheElementPutMostRecentSample(); + } + } + return count; + } + + /** + * {@inheritDoc} + */ + public String getOriginalConfigDeclaration() { + return this.cacheManager.getOriginalConfigurationText(); + } + + /** + * {@inheritDoc} + */ + public String getOriginalConfigDeclaration(String region) { + return this.cacheManager.getOriginalConfigurationText( region ); + } + + /** + * {@inheritDoc} + */ + public Map> getRegionCacheAttributes() { + Map> result = new HashMap>(); + for ( String regionName : this.cacheManager.getCacheNames() ) { + result.put( regionName, getRegionCacheAttributes( regionName ) ); + } + return result; + } + + /** + * {@inheritDoc} + */ + public Map getRegionCacheAttributes(String regionName) { + Map result = new HashMap(); + result.put( "Enabled", isRegionCacheEnabled( regionName ) ); + result.put( "LoggingEnabled", isRegionCacheLoggingEnabled( regionName ) ); + result.put( "MaxTTISeconds", getRegionCacheMaxTTISeconds( regionName ) ); + result.put( "MaxTTLSeconds", getRegionCacheMaxTTLSeconds( regionName ) ); + result.put( "TargetMaxInMemoryCount", getRegionCacheTargetMaxInMemoryCount( regionName ) ); + result.put( "TargetMaxTotalCount", getRegionCacheTargetMaxTotalCount( regionName ) ); + result.put( "OrphanEvictionEnabled", isRegionCacheOrphanEvictionEnabled( regionName ) ); + result.put( "OrphanEvictionPeriod", getRegionCacheOrphanEvictionPeriod( regionName ) ); + return result; + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheMaxTTISeconds(String region) { + Cache cache = cacheManager.getCache( region ); + if ( cache != null ) { + return (int) cache.getCacheConfiguration().getTimeToIdleSeconds(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheMaxTTLSeconds(String region) { + Cache cache = cacheManager.getCache( region ); + if ( cache != null ) { + return (int) cache.getCacheConfiguration().getTimeToLiveSeconds(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheOrphanEvictionPeriod(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null && cache.isTerracottaClustered() ) { + return cache.getCacheConfiguration().getTerracottaConfiguration().getOrphanEvictionPeriod(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public Map getRegionCacheSamples() { + Map rv = new HashMap(); + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + rv.put( + name, new int[] { + (int) cache.getSampledCacheStatistics().getCacheHitMostRecentSample(), + (int) ( cache.getSampledCacheStatistics().getCacheMissNotFoundMostRecentSample() + + cache.getSampledCacheStatistics().getCacheMissExpiredMostRecentSample() ), + (int) cache.getSampledCacheStatistics().getCacheElementPutMostRecentSample(), + } + ); + } + } + return rv; + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheTargetMaxInMemoryCount(String region) { + Cache cache = cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getCacheConfiguration().getMaxElementsInMemory(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public int getRegionCacheTargetMaxTotalCount(String region) { + Cache cache = cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getCacheConfiguration().getMaxElementsOnDisk(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public String[] getTerracottaHibernateCacheRegionNames() { + ArrayList rv = new ArrayList(); + for ( String name : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( name ); + if ( cache != null ) { + if ( cache.getCacheConfiguration().isTerracottaClustered() ) { + rv.add( name ); + } + } + } + return rv.toArray( new String[] { } ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheEnabled(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return !cache.isDisabled(); + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheEnabled(String region, boolean enabled) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.setDisabled( !enabled ); + } + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCachesEnabled() { + for ( String name : this.cacheManager.getCacheNames() ) { + Cache cache = this.cacheManager.getCache( name ); + if ( cache != null ) { + if ( cache.isDisabled() ) { + return false; + } + } + } + return true; + } + + /** + * @see EhcacheStats#setRegionCachesEnabled(boolean) + */ + public void setRegionCachesEnabled(final boolean flag) { + for ( String name : this.cacheManager.getCacheNames() ) { + Cache cache = this.cacheManager.getCache( name ); + if ( cache != null ) { + cache.setDisabled( !flag ); + } + } + sendNotification( CACHE_ENABLED, flag ); + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheLoggingEnabled(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getCacheConfiguration().getLogging(); + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public boolean isRegionCacheOrphanEvictionEnabled(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null && cache.isTerracottaClustered() ) { + return cache.getCacheConfiguration().getTerracottaConfiguration().getOrphanEviction(); + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public boolean isTerracottaHibernateCache(String region) { + Cache cache = cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getCacheConfiguration().isTerracottaClustered(); + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheLoggingEnabled(String region, boolean loggingEnabled) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.getCacheConfiguration().setLogging( loggingEnabled ); + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheMaxTTISeconds(String region, int maxTTISeconds) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.getCacheConfiguration().setTimeToIdleSeconds( maxTTISeconds ); + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheMaxTTLSeconds(String region, int maxTTLSeconds) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.getCacheConfiguration().setTimeToLiveSeconds( maxTTLSeconds ); + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheTargetMaxInMemoryCount(String region, int targetMaxInMemoryCount) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.getCacheConfiguration().setMaxElementsInMemory( targetMaxInMemoryCount ); + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + } + + /** + * {@inheritDoc} + */ + public void setRegionCacheTargetMaxTotalCount(String region, int targetMaxTotalCount) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + cache.getCacheConfiguration().setMaxElementsOnDisk( targetMaxTotalCount ); + sendNotification( CACHE_REGION_CHANGED, getRegionCacheAttributes( region ), region ); + } + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsInMemory(java.lang.String) + */ + public int getNumberOfElementsInMemory(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return (int) cache.getMemoryStoreSize(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsOffHeap(java.lang.String) + */ + public int getNumberOfElementsOffHeap(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return (int) cache.getOffHeapStoreSize(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getNumberOfElementsOnDisk(java.lang.String) + */ + public int getNumberOfElementsOnDisk(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getDiskStoreSize(); + } + else { + return -1; + } + } + + /** + * {@inheritDoc} + */ + public void setStatisticsEnabled(boolean flag) { + for ( String cacheName : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( cacheName ); + if ( cache != null ) { + cache.setStatisticsEnabled( flag ); + } + } + if ( flag ) { + clearStats(); + } + sendNotification( CACHE_STATISTICS_ENABLED, flag ); + } + + /** + * {@inheritDoc} + */ + public long getMaxGetTimeMillis() { + long rv = 0; + for ( String cacheName : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( cacheName ); + if ( cache != null ) { + rv = Math.max( rv, cache.getLiveCacheStatistics().getMaxGetTimeMillis() ); + } + } + return rv; + } + + /** + * {@inheritDoc} + */ + public long getMinGetTimeMillis() { + long rv = 0; + for ( String cacheName : cacheManager.getCacheNames() ) { + Cache cache = cacheManager.getCache( cacheName ); + if ( cache != null ) { + rv = Math.max( rv, cache.getLiveCacheStatistics().getMinGetTimeMillis() ); + } + } + return rv; + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMaxGetTimeMillis(java.lang.String) + */ + public long getMaxGetTimeMillis(String cacheName) { + Cache cache = cacheManager.getCache( cacheName ); + if ( cache != null ) { + return cache.getLiveCacheStatistics().getMaxGetTimeMillis(); + } + else { + return 0; + } + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getMinGetTimeMillis(java.lang.String) + */ + public long getMinGetTimeMillis(String cacheName) { + Cache cache = cacheManager.getCache( cacheName ); + if ( cache != null ) { + return cache.getLiveCacheStatistics().getMinGetTimeMillis(); + } + else { + return 0; + } + } + + /** + * {@inheritDoc} + * + * @see EhcacheStats#getAverageGetTimeMillis(java.lang.String) + */ + public float getAverageGetTimeMillis(String region) { + Cache cache = this.cacheManager.getCache( region ); + if ( cache != null ) { + return cache.getLiveCacheStatistics().getAverageGetTimeMillis(); + } + else { + return -1f; + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void doDispose() { + // no-op + } + + /** + * @see BaseEmitterBean#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] { NOTIFICATION_INFO }; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EntityStats.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EntityStats.java new file mode 100644 index 0000000000..6ce06f1cee --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/EntityStats.java @@ -0,0 +1,288 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.hibernate.stat.EntityStatistics; + +/** + * When we only support Java 6, all of this OpenMBean scaffolding can be removed in favor or MXBeans. + * + * @author gkeim + */ +public class EntityStats implements Serializable { + private static final String COMPOSITE_TYPE_NAME = "EntityStats"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Statistics per Entity"; + private static final String[] ITEM_NAMES = new String[] { + "name", "shortName", "loadCount", + "updateCount", "insertCount", "deleteCount", "fetchCount", "optimisticFailureCount", + }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { + "name", "shortName", "loadCount", + "updateCount", "insertCount", "deleteCount", "fetchCount", "optimisticFailureCount", + }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { + SimpleType.STRING, + SimpleType.STRING, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, + SimpleType.LONG, + }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "Statistics by Entity"; + private static final String TABULAR_TYPE_DESCRIPTION = "All Entity Statistics"; + private static final String[] INDEX_NAMES = new String[] { "name", }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType( + COMPOSITE_TYPE_NAME, COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, + ITEM_DESCRIPTIONS, ITEM_TYPES + ); + TABULAR_TYPE = new TabularType( TABULAR_TYPE_NAME, TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * name + */ + protected final String name; + + /** + * shortName + */ + protected final String shortName; + + /** + * loadCount + */ + protected long loadCount; + + /** + * updateCount + */ + protected long updateCount; + + /** + * insertCount + */ + protected long insertCount; + + /** + * deleteCount + */ + protected long deleteCount; + + /** + * fetchCount + */ + protected long fetchCount; + + /** + * optimisticFailureCount + */ + protected long optimisticFailureCount; + + /** + * @param name + */ + public EntityStats(String name) { + this.name = name; + this.shortName = CacheRegionUtils.determineShortName( name ); + } + + /** + * @param name + * @param src + */ + public EntityStats(String name, EntityStatistics src) { + this( name ); + + try { + this.loadCount = BeanUtils.getLongBeanProperty( src, "loadCount" ); + this.updateCount = BeanUtils.getLongBeanProperty( src, "updateCount" ); + this.insertCount = BeanUtils.getLongBeanProperty( src, "insertCount" ); + this.deleteCount = BeanUtils.getLongBeanProperty( src, "deleteCount" ); + this.fetchCount = BeanUtils.getLongBeanProperty( src, "fetchCount" ); + this.optimisticFailureCount = BeanUtils.getLongBeanProperty( src, "optimisticFailureCount" ); + } + catch ( Exception e ) { + e.printStackTrace(); + throw new RuntimeException( "Exception retrieving statistics", e ); + } + } + + /** + * @param cData + */ + public EntityStats(final CompositeData cData) { + int i = 0; + name = (String) cData.get( ITEM_NAMES[i++] ); + shortName = (String) cData.get( ITEM_NAMES[i++] ); + loadCount = (Long) cData.get( ITEM_NAMES[i++] ); + updateCount = (Long) cData.get( ITEM_NAMES[i++] ); + insertCount = (Long) cData.get( ITEM_NAMES[i++] ); + deleteCount = (Long) cData.get( ITEM_NAMES[i++] ); + fetchCount = (Long) cData.get( ITEM_NAMES[i++] ); + optimisticFailureCount = (Long) cData.get( ITEM_NAMES[i++] ); + } + + private static int safeParseInt(String s) { + try { + return Integer.parseInt( s ); + } + catch ( Exception e ) { + return -1; + } + } + + /** + * @param stats + */ + public void add(EntityStats stats) { + loadCount += stats.getLoadCount(); + updateCount += stats.getUpdateCount(); + insertCount += stats.getInsertCount(); + deleteCount += stats.getDeleteCount(); + fetchCount += stats.getFetchCount(); + optimisticFailureCount += stats.getOptimisticFailureCount(); + } + + /** + * toString + */ + @Override + public String toString() { + return "name=" + name + ", shortName=" + shortName + ",loadCount=" + loadCount + ", updateCount=" + updateCount + + ", insertCount=" + insertCount + ", deleteCount=" + deleteCount + ", fetchCount=" + fetchCount + + ", optimisticFailureCount" + optimisticFailureCount; + } + + /** + * getName + */ + public String getName() { + return name; + } + + /** + * getShortName + */ + public String getShortName() { + return shortName; + } + + /** + * getLoadCount + */ + public long getLoadCount() { + return loadCount; + } + + /** + * getUpdateCount + */ + public long getUpdateCount() { + return updateCount; + } + + /** + * getInsertCount + */ + public long getInsertCount() { + return insertCount; + } + + /** + * getDeleteCount + */ + public long getDeleteCount() { + return deleteCount; + } + + /** + * getFetchCount + */ + public long getFetchCount() { + return fetchCount; + } + + /** + * getOptimisticFailureCount + */ + public long getOptimisticFailureCount() { + return optimisticFailureCount; + } + + /** + * toCompositeData + */ + public CompositeData toCompositeData() { + try { + return new CompositeDataSupport( + COMPOSITE_TYPE, ITEM_NAMES, new Object[] { + name, shortName, loadCount, + updateCount, insertCount, deleteCount, fetchCount, optimisticFailureCount, + } + ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * newTabularDataInstance + */ + public static TabularData newTabularDataInstance() { + return new TabularDataSupport( TABULAR_TYPE ); + } + + /** + * fromTabularData + */ + public static EntityStats[] fromTabularData(final TabularData tabularData) { + final List countList = new ArrayList( tabularData.size() ); + for ( final Iterator pos = tabularData.values().iterator(); pos.hasNext(); ) { + countList.add( new EntityStats( (CompositeData) pos.next() ) ); + } + return countList.toArray( new EntityStats[countList.size()] ); + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/HibernateStatsImpl.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/HibernateStatsImpl.java new file mode 100644 index 0000000000..f75bfa3ebe --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/HibernateStatsImpl.java @@ -0,0 +1,330 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.util.ArrayList; +import java.util.List; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import net.sf.ehcache.hibernate.management.api.HibernateStats; + +import org.hibernate.SessionFactory; +import org.hibernate.stat.Statistics; + +/** + * Implementation of {@link HibernateStats} + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public class HibernateStatsImpl extends BaseEmitterBean implements HibernateStats { + private static final double MILLIS_PER_SECOND = 1000; + private static final MBeanNotificationInfo NOTIFICATION_INFO; + + private final SessionFactory sessionFactory; + + static { + final String[] notifTypes = new String[] { }; + final String name = Notification.class.getName(); + final String description = "Hibernate Statistics Event"; + NOTIFICATION_INFO = new MBeanNotificationInfo( notifTypes, name, description ); + } + + /** + * Constructor accepting the backing {@link SessionFactory} + * + * @param sessionFactory + * + * @throws javax.management.NotCompliantMBeanException + */ + public HibernateStatsImpl(SessionFactory sessionFactory) throws NotCompliantMBeanException { + super( HibernateStats.class ); + this.sessionFactory = sessionFactory; + } + + /** + * @return statistics + */ + private Statistics getStatistics() { + return sessionFactory.getStatistics(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#clearStats() + */ + public void clearStats() { + getStatistics().clear(); + sendNotification( CACHE_STATISTICS_RESET ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#disableStats() + */ + public void disableStats() { + setStatisticsEnabled( false ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#enableStats() + */ + public void enableStats() { + setStatisticsEnabled( true ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getCloseStatementCount() + */ + public long getCloseStatementCount() { + return getStatistics().getCloseStatementCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getConnectCount() + */ + public long getConnectCount() { + return getStatistics().getConnectCount(); + } + + /** + * Not supported right now + */ + public long getDBSQLExecutionSample() { + throw new UnsupportedOperationException( "Use getQueryExecutionCount() instead" ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getFlushCount() + */ + public long getFlushCount() { + return getStatistics().getFlushCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getOptimisticFailureCount() + */ + public long getOptimisticFailureCount() { + return getStatistics().getOptimisticFailureCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getPrepareStatementCount() + */ + public long getPrepareStatementCount() { + return getStatistics().getPrepareStatementCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionCount() + */ + public long getQueryExecutionCount() { + return getStatistics().getQueryExecutionCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionRate() + */ + public double getQueryExecutionRate() { + long startTime = getStatistics().getStartTime(); + long now = System.currentTimeMillis(); + double deltaSecs = ( now - startTime ) / MILLIS_PER_SECOND; + return getQueryExecutionCount() / deltaSecs; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionSample() + */ + public long getQueryExecutionSample() { + throw new UnsupportedOperationException( "TODO: need to impl. rates for query execution" ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSessionCloseCount() + */ + public long getSessionCloseCount() { + return getStatistics().getSessionCloseCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSessionOpenCount() + */ + public long getSessionOpenCount() { + return getStatistics().getSessionOpenCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSuccessfulTransactionCount() + */ + public long getSuccessfulTransactionCount() { + return getStatistics().getSuccessfulTransactionCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getTransactionCount() + */ + public long getTransactionCount() { + return getStatistics().getTransactionCount(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#isStatisticsEnabled() + */ + public boolean isStatisticsEnabled() { + return getStatistics().isStatisticsEnabled(); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#setStatisticsEnabled(boolean) + */ + public void setStatisticsEnabled(boolean flag) { + getStatistics().setStatisticsEnabled( flag ); + sendNotification( CACHE_STATISTICS_ENABLED, flag ); + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getEntityStats() + */ + public TabularData getEntityStats() { + List result = new ArrayList(); + Statistics statistics = getStatistics(); + for ( String entity : statistics.getEntityNames() ) { + EntityStats entityStats = new EntityStats( entity, statistics.getEntityStatistics( entity ) ); + result.add( entityStats.toCompositeData() ); + } + TabularData td = EntityStats.newTabularDataInstance(); + td.putAll( result.toArray( new CompositeData[result.size()] ) ); + return td; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getCollectionStats() + */ + public TabularData getCollectionStats() { + List result = new ArrayList(); + Statistics statistics = getStatistics(); + for ( String roleName : statistics.getCollectionRoleNames() ) { + CollectionStats collectionStats = new CollectionStats( + roleName, + statistics.getCollectionStatistics( roleName ) + ); + result.add( collectionStats.toCompositeData() ); + } + TabularData td = CollectionStats.newTabularDataInstance(); + td.putAll( result.toArray( new CompositeData[result.size()] ) ); + return td; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryStats() + */ + public TabularData getQueryStats() { + List result = new ArrayList(); + Statistics statistics = getStatistics(); + for ( String query : statistics.getQueries() ) { + QueryStats queryStats = new QueryStats( query, statistics.getQueryStatistics( query ) ); + result.add( queryStats.toCompositeData() ); + } + TabularData td = QueryStats.newTabularDataInstance(); + td.putAll( result.toArray( new CompositeData[result.size()] ) ); + return td; + } + + /** + * {@inheritDoc} + */ + public TabularData getCacheRegionStats() { + List list = new ArrayList(); + Statistics statistics = getStatistics(); + for ( String region : statistics.getSecondLevelCacheRegionNames() ) { + CacheRegionStats l2CacheStats = new CacheRegionStats( + region, + statistics.getSecondLevelCacheStatistics( region ) + ); + list.add( l2CacheStats.toCompositeData() ); + } + TabularData td = CacheRegionStats.newTabularDataInstance(); + td.putAll( list.toArray( new CompositeData[list.size()] ) ); + return td; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doDispose() { + // no-op + } + + /** + * @see BaseEmitterBean#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] { NOTIFICATION_INFO }; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/NullHibernateStats.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/NullHibernateStats.java new file mode 100644 index 0000000000..b7712a71c1 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/NullHibernateStats.java @@ -0,0 +1,300 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + + +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.openmbean.TabularData; + +import net.sf.ehcache.hibernate.management.api.HibernateStats; + +/** + * Implementation of {@link HibernateStats} that does nothing + *

+ *

+ * + * @author Abhishek Sanoujam + */ +public final class NullHibernateStats implements HibernateStats { + + /** + * Singleton instance. + */ + public static final HibernateStats INSTANCE = new NullHibernateStats(); + + /** + * private constructor. No need to create instances of this. Use singleton instance + */ + private NullHibernateStats() { + super(); + // TODO Auto-generated constructor stub + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#clearStats() + */ + public void clearStats() { + // no-op + + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#disableStats() + */ + public void disableStats() { + // no-op + + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#enableStats() + */ + public void enableStats() { + // no-op + + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getCloseStatementCount() + */ + public long getCloseStatementCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getCollectionStats() + */ + public TabularData getCollectionStats() { + // no-op + return null; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getConnectCount() + */ + public long getConnectCount() { + // no-op + return 0; + } + + /** + * Not supported right now + */ + public long getDBSQLExecutionSample() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getEntityStats() + */ + public TabularData getEntityStats() { + // no-op + return null; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getFlushCount() + */ + public long getFlushCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getOptimisticFailureCount() + */ + public long getOptimisticFailureCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getPrepareStatementCount() + */ + public long getPrepareStatementCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionCount() + */ + public long getQueryExecutionCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionRate() + */ + public double getQueryExecutionRate() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryExecutionSample() + */ + public long getQueryExecutionSample() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getQueryStats() + */ + public TabularData getQueryStats() { + // no-op + return null; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSessionCloseCount() + */ + public long getSessionCloseCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSessionOpenCount() + */ + public long getSessionOpenCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getSuccessfulTransactionCount() + */ + public long getSuccessfulTransactionCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#getTransactionCount() + */ + public long getTransactionCount() { + // no-op + return 0; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#isStatisticsEnabled() + */ + public boolean isStatisticsEnabled() { + // no-op + return false; + } + + /** + * {@inheritDoc} + * + * @see HibernateStats#setStatisticsEnabled(boolean) + */ + public void setStatisticsEnabled(boolean flag) { + // no-op + } + + /** + * @see HibernateStats#getCacheRegionStats() + */ + public TabularData getCacheRegionStats() { + return null; + } + + /** + * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + /**/ + } + + /** + * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) + throws IllegalArgumentException { + /**/ + } + + /** + * @see javax.management.NotificationBroadcaster#getNotificationInfo() + */ + public MBeanNotificationInfo[] getNotificationInfo() { + return null; + } + + /** + * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) + */ + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + /**/ + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/ProviderMBeanRegistrationHelper.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/ProviderMBeanRegistrationHelper.java new file mode 100644 index 0000000000..d560bd9b54 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/ProviderMBeanRegistrationHelper.java @@ -0,0 +1,186 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.sf.ehcache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.SessionFactory; +import org.hibernate.cache.CacheException; +import org.hibernate.cfg.Environment; +import org.hibernate.internal.SessionFactoryRegistry; + +/** + * Helper class for registering mbeans for ehcache backed hibernate second level cache + *

+ *

+ * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class ProviderMBeanRegistrationHelper { + private static final Logger LOG = LoggerFactory.getLogger( ProviderMBeanRegistrationHelper.class ); + + private static final int MILLIS_PER_SECOND = 1000; + private static final int SLEEP_MILLIS = 500; + + private volatile EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration; + + /** + * Registers mbean for the input cache manager and the session factory name + * + * @param manager the backing cachemanager + * @param properties session factory config properties + */ + public void registerMBean(final CacheManager manager, final Properties properties) { + if ( Boolean.getBoolean( "tc.active" ) ) { + ehcacheHibernateMBeanRegistration = new EhcacheHibernateMBeanRegistrationImpl(); + manager.getTimer().scheduleAtFixedRate( + new RegisterMBeansTask( ehcacheHibernateMBeanRegistration, manager, properties ), SLEEP_MILLIS, + SLEEP_MILLIS + ); + } + } + + /** + * Unregisters previously registered mbean. + */ + public void unregisterMBean() { + if ( ehcacheHibernateMBeanRegistration != null ) { + ehcacheHibernateMBeanRegistration.dispose(); + ehcacheHibernateMBeanRegistration = null; + } + } + + /** + * Task for running mbean registration that can be scheduled in a timer + */ + private static class RegisterMBeansTask extends TimerTask { + private static final int NUM_SECONDS = 30; + private long startTime; + private final AtomicBoolean mbeanRegistered = new AtomicBoolean( false ); + private final EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration; + private final CacheManager manager; + private final Properties properties; + + public RegisterMBeansTask(EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration, + CacheManager manager, Properties properties) { + this.ehcacheHibernateMBeanRegistration = ehcacheHibernateMBeanRegistration; + this.manager = manager; + this.properties = properties; + } + + @Override + public void run() { + LOG.debug( "Running mbean initializer task for ehcache hibernate..." ); + startTime = System.currentTimeMillis(); + if ( mbeanRegistered.compareAndSet( false, true ) ) { + try { + ehcacheHibernateMBeanRegistration.registerMBeanForCacheManager( manager, properties ); + LOG.debug( "Successfully registered bean" ); + } + catch ( Exception e ) { + throw new CacheException( e ); + } + } + SessionFactory sessionFactory = locateSessionFactory(); + if ( sessionFactory == null ) { + LOG.debug( + "SessionFactory is probably still being initialized..." + + " waiting for it to complete before enabling hibernate statistics monitoring via JMX" + ); + if ( System.currentTimeMillis() > startTime + ( NUM_SECONDS * MILLIS_PER_SECOND ) ) { + LOG.info( "Hibernate statistics monitoring through JMX is DISABLED." ); + LOG.info( + "Failed to look up SessionFactory after " + NUM_SECONDS + " seconds using session-factory properties '" + + properties + "'" + ); + this.cancel(); + } + return; + } + else { + ehcacheHibernateMBeanRegistration.enableHibernateStatisticsSupport( sessionFactory ); + LOG.info( "Hibernate statistics monitoring through JMX is ENABLED. " ); + this.cancel(); + } + } + + private SessionFactory locateSessionFactory() { + String jndiName = properties.getProperty( Environment.SESSION_FACTORY_NAME ); + if ( jndiName != null ) { + return SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( jndiName ); + } + try { + Class factoryType = SessionFactoryRegistry.class; + Field instancesField = getField( factoryType, "sessionFactoryMap" ); + if ( instancesField == null ) { + throw new RuntimeException( "Expected 'sessionFactoryMap' field on " + SessionFactoryRegistry.class.getName() ); + } + instancesField.setAccessible( true ); + Map map = (Map) instancesField.get( null ); + if ( map == null ) { + return null; + } + Iterator values = map.values().iterator(); + while ( values.hasNext() ) { + SessionFactory sessionFactory = (SessionFactory) values.next(); + Class sessionFactoryType = sessionFactory.getClass(); + Field propertiesField = getField( sessionFactoryType, "properties" ); + if ( propertiesField != null ) { + propertiesField.setAccessible( true ); + Properties props = (Properties) propertiesField.get( sessionFactory ); + if ( props != null && props.equals( properties ) ) { + return sessionFactory; + } + } + } + } + catch ( RuntimeException re ) { + LOG.error( "Error locating Hibernate Session Factory", re ); + } + catch ( IllegalAccessException iae ) { + LOG.error( "Error locating Hibernate Session Factory", iae ); + } + return null; + } + } + + private static Field getField(Class c, String fieldName) { + for ( Field field : c.getDeclaredFields() ) { + if ( field.getName().equals( fieldName ) ) { + return field; + } + } + throw new NoSuchFieldError( "Type '" + c + "' has no field '" + fieldName + "'" ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/QueryStats.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/QueryStats.java new file mode 100644 index 0000000000..594b3f022f --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/management/impl/QueryStats.java @@ -0,0 +1,322 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.management.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.hibernate.stat.QueryStatistics; + + +/** + * @author gkeim + * @author Alex Snaps + */ +public class QueryStats implements Serializable { + private static final String COMPOSITE_TYPE_NAME = "QueryStats"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Statistics per Query"; + private static final String[] ITEM_NAMES = new String[] { + "query", "cacheHitCount", + "cacheMissCount", + "cachePutCount", + "executionCount", + "executionRowCount", + "executionAvgTime", + "executionMaxTime", + "executionMinTime", + }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { + "query", "cacheHitCount", + "cacheMissCount", + "cachePutCount", + "executionCount", + "executionRowCount", + "executionAvgTime", + "executionMaxTime", + "executionMinTime", + }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { + SimpleType.STRING, SimpleType.LONG, + SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, + SimpleType.LONG, + }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "Statistics by Query"; + private static final String TABULAR_TYPE_DESCRIPTION = "All Query Statistics"; + private static final String[] INDEX_NAMES = new String[] { "query", }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType( + COMPOSITE_TYPE_NAME, COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, + ITEM_DESCRIPTIONS, ITEM_TYPES + ); + TABULAR_TYPE = new TabularType( TABULAR_TYPE_NAME, TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * query + */ + protected final String query; + + /** + * cacheHitCount + */ + protected long cacheHitCount; + + /** + * cacheMissCount + */ + protected long cacheMissCount; + + /** + * cachePutCount + */ + protected long cachePutCount; + + /** + * executionCount + */ + protected long executionCount; + + /** + * executionRowCount + */ + protected long executionRowCount; + + /** + * executionAvgTime + */ + protected long executionAvgTime; + + /** + * executionMaxTime + */ + protected long executionMaxTime; + + /** + * executionMinTime + */ + protected long executionMinTime; + + /** + * @param name + */ + public QueryStats(String name) { + this.query = name; + } + + /** + * @param name + * @param src + */ + public QueryStats(String name, QueryStatistics src) { + this( name ); + + try { + this.cacheHitCount = BeanUtils.getLongBeanProperty( src, "cacheHitCount" ); + this.cacheMissCount = BeanUtils.getLongBeanProperty( src, "cacheMissCount" ); + this.cachePutCount = BeanUtils.getLongBeanProperty( src, "cachePutCount" ); + this.executionCount = BeanUtils.getLongBeanProperty( src, "executionCount" ); + this.executionRowCount = BeanUtils.getLongBeanProperty( src, "executionRowCount" ); + this.executionAvgTime = BeanUtils.getLongBeanProperty( src, "executionAvgTime" ); + this.executionMaxTime = BeanUtils.getLongBeanProperty( src, "executionMaxTime" ); + this.executionMinTime = + BeanUtils.getLongBeanProperty( src, "executionMinTime" ); + } + catch ( Exception e ) { + e.printStackTrace(); + throw new RuntimeException( "Exception retrieving statistics", e ); + } + } + + /** + * @param cData + */ + public QueryStats(final CompositeData cData) { + int i = 0; + query = (String) cData.get( ITEM_NAMES[i++] ); + cacheHitCount = (Long) cData.get( ITEM_NAMES[i++] ); + cacheMissCount = (Long) cData.get( ITEM_NAMES[i++] ); + cachePutCount = (Long) cData.get( ITEM_NAMES[i++] ); + executionCount = (Long) cData.get( ITEM_NAMES[i++] ); + executionRowCount = (Long) cData.get( ITEM_NAMES[i++] ); + executionAvgTime = (Long) cData.get( ITEM_NAMES[i++] ); + executionMaxTime = (Long) cData.get( ITEM_NAMES[i++] ); + executionMinTime = (Long) cData.get( ITEM_NAMES[i++] ); + } + + private static int safeParseInt(String s) { + try { + return Integer.parseInt( s ); + } + catch ( Exception e ) { + return -1; + } + } + + /** + * @param stats + */ + public void add(QueryStats stats) { + cacheHitCount += stats.getCacheHitCount(); + cacheMissCount += stats.getCacheMissCount(); + cachePutCount += stats.getCachePutCount(); + executionCount += stats.getExecutionCount(); + executionRowCount += stats.getExecutionRowCount(); + executionAvgTime += stats.getExecutionAvgTime(); + executionMaxTime += stats.getExecutionMaxTime(); + executionMinTime += stats.getExecutionMinTime(); + } + + /** + * toString + */ + @Override + public String toString() { + return "query=" + query + ", cacheHitCount=" + cacheHitCount + ", cacheMissCount=" + cacheMissCount + + ", cachePutCount=" + cachePutCount + ", executionCount=" + executionCount + ", executionRowCount=" + + executionRowCount + ", executionAvgTime=" + executionAvgTime + ", executionMaxTime=" + executionMaxTime + + ", executionMinTime=" + executionMinTime; + } + + /** + * getQuery + */ + public String getQuery() { + return query; + } + + /** + * getCacheHitCount + */ + public long getCacheHitCount() { + return cacheHitCount; + } + + /** + * getCacheMissCount + */ + public long getCacheMissCount() { + return cacheMissCount; + } + + /** + * getCachePutCount + */ + public long getCachePutCount() { + return cachePutCount; + } + + /** + * getExecutionCount + */ + public long getExecutionCount() { + return executionCount; + } + + /** + * getExecutionRowCount + */ + public long getExecutionRowCount() { + return executionRowCount; + } + + /** + * getExecutionAvgTime + */ + public long getExecutionAvgTime() { + return executionAvgTime; + } + + /** + * getExecutionMaxTime + */ + public long getExecutionMaxTime() { + return executionMaxTime; + } + + /** + * getExecutionMinTime + */ + public long getExecutionMinTime() { + return executionMinTime; + } + + /** + * toCompositeData + */ + public CompositeData toCompositeData() { + try { + return new CompositeDataSupport( + COMPOSITE_TYPE, ITEM_NAMES, new Object[] { + query, cacheHitCount, cacheMissCount, + cachePutCount, + executionCount, + executionRowCount, + executionAvgTime, + executionMaxTime, + executionMinTime, + } + ); + } + catch ( OpenDataException e ) { + throw new RuntimeException( e ); + } + } + + /** + * newTabularDataInstance + */ + public static TabularData newTabularDataInstance() { + return new TabularDataSupport( TABULAR_TYPE ); + } + + /** + * fromTabularData + */ + public static QueryStats[] fromTabularData(final TabularData tabularData) { + final List countList = new ArrayList( tabularData.size() ); + for ( final Iterator pos = tabularData.values().iterator(); pos.hasNext(); ) { + countList.add( new QueryStats( (CompositeData) pos.next() ) ); + } + return countList.toArray( new QueryStats[countList.size()] ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/HibernateNonstopCacheExceptionHandler.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/HibernateNonstopCacheExceptionHandler.java new file mode 100644 index 0000000000..2f4f762a3e --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/HibernateNonstopCacheExceptionHandler.java @@ -0,0 +1,92 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.nonstop; + +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class that takes care of {@link net.sf.ehcache.constructs.nonstop.NonStopCacheException} that happens in hibernate module + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public final class HibernateNonstopCacheExceptionHandler { + /** + * Property name which set as "true" will throw exceptions on timeout with hibernate + */ + public static final String HIBERNATE_THROW_EXCEPTION_ON_TIMEOUT_PROPERTY = "ehcache.hibernate.propagateNonStopCacheException"; + + /** + * Property name for logging the stack trace of the nonstop cache exception too. False by default + */ + public static final String HIBERNATE_LOG_EXCEPTION_STACK_TRACE_PROPERTY = "ehcache.hibernate.logNonStopCacheExceptionStackTrace"; + + private static final Logger LOGGER = LoggerFactory.getLogger( HibernateNonstopCacheExceptionHandler.class ); + + private static final HibernateNonstopCacheExceptionHandler INSTANCE = new HibernateNonstopCacheExceptionHandler(); + + /** + * private constructor + */ + private HibernateNonstopCacheExceptionHandler() { + // private + } + + /** + * Returns the singleton instance + * + * @return the singleton instance + */ + public static HibernateNonstopCacheExceptionHandler getInstance() { + return INSTANCE; + } + + /** + * Handle {@link net.sf.ehcache.constructs.nonstop.NonStopCacheException}. + * If {@link HibernateNonstopCacheExceptionHandler#HIBERNATE_THROW_EXCEPTION_ON_TIMEOUT_PROPERTY} system property is set to true, + * rethrows the {@link net.sf.ehcache.constructs.nonstop.NonStopCacheException}, otherwise logs the exception. While logging, if + * {@link HibernateNonstopCacheExceptionHandler#HIBERNATE_LOG_EXCEPTION_STACK_TRACE_PROPERTY} is set to true, logs the exception stack + * trace too, otherwise logs the exception message only + * + * @param nonStopCacheException + */ + public void handleNonstopCacheException(NonStopCacheException nonStopCacheException) { + if ( Boolean.getBoolean( HIBERNATE_THROW_EXCEPTION_ON_TIMEOUT_PROPERTY ) ) { + throw nonStopCacheException; + } + else { + if ( Boolean.getBoolean( HIBERNATE_LOG_EXCEPTION_STACK_TRACE_PROPERTY ) ) { + LOGGER.debug( + "Ignoring NonstopCacheException - " + nonStopCacheException.getMessage(), + nonStopCacheException + ); + } + else { + LOGGER.debug( "Ignoring NonstopCacheException - " + nonStopCacheException.getMessage() ); + } + } + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAccessStrategyFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAccessStrategyFactory.java new file mode 100644 index 0000000000..d662c8cd3e --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAccessStrategyFactory.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.nonstop; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; + +/** + * Implementation of {@link org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory} that takes care of Nonstop cache exceptions using + * {@link HibernateNonstopCacheExceptionHandler} + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class NonstopAccessStrategyFactory implements EhcacheAccessStrategyFactory { + + private static final Logger LOG = LoggerFactory.getLogger( NonstopAccessStrategyFactory.class ); + private final EhcacheAccessStrategyFactory actualFactory; + + /** + * Constructor accepting the actual factory + * + * @param actualFactory + */ + public NonstopAccessStrategyFactory(EhcacheAccessStrategyFactory actualFactory) { + this.actualFactory = actualFactory; + } + + /** + * {@inheritDoc} + */ + public EntityRegionAccessStrategy createEntityRegionAccessStrategy(EhcacheEntityRegion entityRegion, AccessType accessType) { + return new NonstopAwareEntityRegionAccessStrategy( + actualFactory.createEntityRegionAccessStrategy( entityRegion, accessType ), + HibernateNonstopCacheExceptionHandler.getInstance() + ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(EhcacheCollectionRegion collectionRegion, + AccessType accessType) { + return new NonstopAwareCollectionRegionAccessStrategy( + actualFactory.createCollectionRegionAccessStrategy( + collectionRegion, + accessType + ), HibernateNonstopCacheExceptionHandler.getInstance() + ); + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareCollectionRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareCollectionRegionAccessStrategy.java new file mode 100644 index 0000000000..a5c534b981 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareCollectionRegionAccessStrategy.java @@ -0,0 +1,227 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.nonstop; + +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; + +/** + * Implementation of {@link CollectionRegionAccessStrategy} that handles {@link NonStopCacheException} using + * {@link HibernateNonstopCacheExceptionHandler} + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class NonstopAwareCollectionRegionAccessStrategy implements CollectionRegionAccessStrategy { + + private final CollectionRegionAccessStrategy actualStrategy; + private final HibernateNonstopCacheExceptionHandler hibernateNonstopExceptionHandler; + + /** + * Constructor accepting the actual {@link CollectionRegionAccessStrategy} and the {@link HibernateNonstopCacheExceptionHandler} + * + * @param actualStrategy + * @param hibernateNonstopExceptionHandler + */ + public NonstopAwareCollectionRegionAccessStrategy(CollectionRegionAccessStrategy actualStrategy, + HibernateNonstopCacheExceptionHandler hibernateNonstopExceptionHandler) { + this.actualStrategy = actualStrategy; + this.hibernateNonstopExceptionHandler = hibernateNonstopExceptionHandler; + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#getRegion() + */ + public CollectionRegion getRegion() { + return actualStrategy.getRegion(); + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evict(java.lang.Object) + */ + public void evict(Object key) throws CacheException { + try { + actualStrategy.evict( key ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evictAll() + */ + public void evictAll() throws CacheException { + try { + actualStrategy.evictAll(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#get(java.lang.Object, long) + */ + public Object get(Object key, long txTimestamp) throws CacheException { + try { + return actualStrategy.get( key, txTimestamp ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockItem(java.lang.Object, java.lang.Object) + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + try { + return actualStrategy.lockItem( key, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockRegion() + */ + public SoftLock lockRegion() throws CacheException { + try { + return actualStrategy.lockRegion(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, + * boolean) + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + try { + return actualStrategy.putFromLoad( key, value, txTimestamp, version, minimalPutOverride ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object) + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException { + try { + return actualStrategy.putFromLoad( key, value, txTimestamp, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#remove(java.lang.Object) + */ + public void remove(Object key) throws CacheException { + try { + actualStrategy.remove( key ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#removeAll() + */ + public void removeAll() throws CacheException { + try { + actualStrategy.removeAll(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockItem(java.lang.Object, org.hibernate.cache.spi.access.SoftLock) + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + try { + actualStrategy.unlockItem( key, lock ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockRegion(org.hibernate.cache.spi.access.SoftLock) + */ + public void unlockRegion(SoftLock lock) throws CacheException { + try { + actualStrategy.unlockRegion( lock ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareEntityRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareEntityRegionAccessStrategy.java new file mode 100644 index 0000000000..b3d527b593 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/nonstop/NonstopAwareEntityRegionAccessStrategy.java @@ -0,0 +1,290 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.nonstop; + +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; + +/** + * Implementation of {@link EntityRegionAccessStrategy} that handles {@link net.sf.ehcache.constructs.nonstop.NonStopCacheException} using + * {@link HibernateNonstopCacheExceptionHandler} + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class NonstopAwareEntityRegionAccessStrategy implements EntityRegionAccessStrategy { + + private final EntityRegionAccessStrategy actualStrategy; + private final HibernateNonstopCacheExceptionHandler hibernateNonstopExceptionHandler; + + /** + * Constructor accepting the actual {@link EntityRegionAccessStrategy} and the {@link HibernateNonstopCacheExceptionHandler} + * + * @param actualStrategy + * @param hibernateNonstopExceptionHandler + */ + public NonstopAwareEntityRegionAccessStrategy(EntityRegionAccessStrategy actualStrategy, + HibernateNonstopCacheExceptionHandler hibernateNonstopExceptionHandler) { + this.actualStrategy = actualStrategy; + this.hibernateNonstopExceptionHandler = hibernateNonstopExceptionHandler; + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#getRegion() + */ + public EntityRegion getRegion() { + return actualStrategy.getRegion(); + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#afterInsert(java.lang.Object, java.lang.Object, java.lang.Object) + */ + public boolean afterInsert(Object key, Object value, Object version) throws CacheException { + try { + return actualStrategy.afterInsert( key, value, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#afterUpdate(java.lang.Object, java.lang.Object, java.lang.Object, + * java.lang.Object, org.hibernate.cache.spi.access.SoftLock) + */ + public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) + throws CacheException { + try { + return actualStrategy.afterUpdate( key, value, currentVersion, previousVersion, lock ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evict(java.lang.Object) + */ + public void evict(Object key) throws CacheException { + try { + actualStrategy.evict( key ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evictAll() + */ + public void evictAll() throws CacheException { + try { + actualStrategy.evictAll(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#get(java.lang.Object, long) + */ + public Object get(Object key, long txTimestamp) throws CacheException { + try { + return actualStrategy.get( key, txTimestamp ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#insert(java.lang.Object, java.lang.Object, java.lang.Object) + */ + public boolean insert(Object key, Object value, Object version) throws CacheException { + try { + return actualStrategy.insert( key, value, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockItem(java.lang.Object, java.lang.Object) + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + try { + return actualStrategy.lockItem( key, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockRegion() + */ + public SoftLock lockRegion() throws CacheException { + try { + return actualStrategy.lockRegion(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return null; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, + * boolean) + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + try { + return actualStrategy.putFromLoad( key, value, txTimestamp, version, minimalPutOverride ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object) + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException { + try { + return actualStrategy.putFromLoad( key, value, txTimestamp, version ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#remove(java.lang.Object) + */ + public void remove(Object key) throws CacheException { + try { + actualStrategy.remove( key ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#removeAll() + */ + public void removeAll() throws CacheException { + try { + actualStrategy.removeAll(); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockItem(java.lang.Object, org.hibernate.cache.spi.access.SoftLock) + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + try { + actualStrategy.unlockItem( key, lock ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockRegion(org.hibernate.cache.spi.access.SoftLock) + */ + public void unlockRegion(SoftLock lock) throws CacheException { + try { + actualStrategy.unlockRegion( lock ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + } + } + + /** + * {@inheritDoc} + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#update(java.lang.Object, java.lang.Object, java.lang.Object, + * java.lang.Object) + */ + public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) + throws CacheException { + try { + return actualStrategy.update( key, value, currentVersion, previousVersion ); + } + catch ( NonStopCacheException nonStopCacheException ) { + hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException ); + return false; + } + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheCollectionRegion.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheCollectionRegion.java new file mode 100644 index 0000000000..da658e74c3 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheCollectionRegion.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +package org.hibernate.cache.ehcache.regions; + +import java.util.Properties; + +import net.sf.ehcache.Ehcache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cfg.Settings; + +/** + * A collection region specific wrapper around an Ehcache instance. + *

+ * This implementation returns Ehcache specific access strategy instances for all the non-transactional access types. Transactional access + * is not supported. + * + * @author Chris Dennis + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class EhcacheCollectionRegion extends EhcacheTransactionalDataRegion implements CollectionRegion { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheCollectionRegion.class ); + + /** + * Constructs an EhcacheCollectionRegion around the given underlying cache. + * + * @param accessStrategyFactory + */ + public EhcacheCollectionRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache underlyingCache, Settings settings, + CacheDataDescription metadata, Properties properties) { + super( accessStrategyFactory, underlyingCache, settings, metadata, properties ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { + return accessStrategyFactory.createCollectionRegionAccessStrategy( this, accessType ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheDataRegion.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheDataRegion.java new file mode 100644 index 0000000000..8a55b3d111 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheDataRegion.java @@ -0,0 +1,223 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +package org.hibernate.cache.ehcache.regions; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; +import net.sf.ehcache.util.Timestamper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.nonstop.HibernateNonstopCacheExceptionHandler; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.Region; + +/** + * An Ehcache specific data region implementation. + *

+ * This class is the ultimate superclass for all Ehcache Hibernate cache regions. + * + * @author Chris Dennis + * @author Greg Luck + * @author Emmanuel Bernard + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public abstract class EhcacheDataRegion implements Region { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheDataRegion.class ); + + private static final String CACHE_LOCK_TIMEOUT_PROPERTY = "net.sf.ehcache.hibernate.cache_lock_timeout"; + private static final int DEFAULT_CACHE_LOCK_TIMEOUT = 60000; + + /** + * Ehcache instance backing this Hibernate data region. + */ + protected final Ehcache cache; + + /** + * The {@link EhcacheAccessStrategyFactory} used for creating various access strategies + */ + protected final EhcacheAccessStrategyFactory accessStrategyFactory; + + private final int cacheLockTimeout; + + + /** + * Create a Hibernate data region backed by the given Ehcache instance. + */ + EhcacheDataRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache cache, Properties properties) { + this.accessStrategyFactory = accessStrategyFactory; + this.cache = cache; + String timeout = properties.getProperty( + CACHE_LOCK_TIMEOUT_PROPERTY, + Integer.toString( DEFAULT_CACHE_LOCK_TIMEOUT ) + ); + this.cacheLockTimeout = Timestamper.ONE_MS * Integer.decode( timeout ); + } + + /** + * {@inheritDoc} + */ + public String getName() { + return cache.getName(); + } + + /** + * {@inheritDoc} + */ + public void destroy() throws CacheException { + try { + cache.getCacheManager().removeCache( cache.getName() ); + } + catch ( IllegalStateException e ) { + //When Spring and Hibernate are both involved this will happen in normal shutdown operation. + //Do not throw an exception, simply log this one. + LOG.debug( "This can happen if multiple frameworks both try to shutdown ehcache", e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public long getSizeInMemory() { + try { + return cache.calculateInMemorySize(); + } + catch ( Throwable t ) { + if ( t instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) t ); + } + return -1; + } + } + + /** + * {@inheritDoc} + */ + public long getElementCountInMemory() { + try { + return cache.getMemoryStoreSize(); + } + catch ( net.sf.ehcache.CacheException ce ) { + if ( ce instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) ce ); + return -1; + } + else { + throw new CacheException( ce ); + } + } + } + + /** + * {@inheritDoc} + */ + public long getElementCountOnDisk() { + try { + return cache.getDiskStoreSize(); + } + catch ( net.sf.ehcache.CacheException ce ) { + if ( ce instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) ce ); + return -1; + } + else { + throw new CacheException( ce ); + } + } + } + + /** + * {@inheritDoc} + */ + public Map toMap() { + try { + Map result = new HashMap(); + for ( Object key : cache.getKeys() ) { + result.put( key, cache.get( key ).getObjectValue() ); + } + return result; + } + catch ( Exception e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + return Collections.emptyMap(); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public long nextTimestamp() { + return Timestamper.next(); + } + + /** + * {@inheritDoc} + */ + public int getTimeout() { + return cacheLockTimeout; + } + + /** + * Return the Ehcache instance backing this Hibernate data region. + */ + public Ehcache getEhcache() { + return cache; + } + + /** + * Returns true if this region contains data for the given key. + *

+ * This is a Hibernate 3.5 method. + */ + public boolean contains(Object key) { + return cache.isKeyInCache( key ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheEntityRegion.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheEntityRegion.java new file mode 100644 index 0000000000..14b055b03e --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheEntityRegion.java @@ -0,0 +1,70 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.regions; + +import java.util.Properties; + +import net.sf.ehcache.Ehcache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cfg.Settings; + +/** + * An entity region specific wrapper around an Ehcache instance. + *

+ * This implementation returns Ehcache specific access strategy instances for all the non-transactional access types. Transactional access + * is not supported. + * + * @author Chris Dennis + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class EhcacheEntityRegion extends EhcacheTransactionalDataRegion implements EntityRegion { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheEntityRegion.class ); + + /** + * Constructs an EhcacheEntityRegion around the given underlying cache. + * + * @param accessStrategyFactory + */ + public EhcacheEntityRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache underlyingCache, Settings settings, + CacheDataDescription metadata, Properties properties) { + super( accessStrategyFactory, underlyingCache, settings, metadata, properties ); + } + + /** + * {@inheritDoc} + */ + public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { + return accessStrategyFactory.createEntityRegionAccessStrategy( this, accessType ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheGeneralDataRegion.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheGeneralDataRegion.java new file mode 100644 index 0000000000..a6a2b21767 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheGeneralDataRegion.java @@ -0,0 +1,163 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.regions; + +import java.util.Properties; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.Element; +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.nonstop.HibernateNonstopCacheExceptionHandler; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.GeneralDataRegion; + +/** + * An Ehcache specific GeneralDataRegion. + *

+ * GeneralDataRegion instances are used for both the timestamps and query caches. + * + * @author Chris Dennis + * @author Greg Luck + * @author Emmanuel Bernard + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +abstract class EhcacheGeneralDataRegion extends EhcacheDataRegion implements GeneralDataRegion { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheGeneralDataRegion.class ); + + /** + * Creates an EhcacheGeneralDataRegion using the given Ehcache instance as a backing. + */ + public EhcacheGeneralDataRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache cache, Properties properties) { + super( accessStrategyFactory, cache, properties ); + } + + /** + * {@inheritDoc} + */ + public Object get(Object key) throws CacheException { + try { + LOG.debug( "key: {}", key ); + if ( key == null ) { + return null; + } + else { + Element element = cache.get( key ); + if ( element == null ) { + LOG.debug( "Element for key {} is null", key ); + return null; + } + else { + return element.getObjectValue(); + } + } + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + return null; + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public void put(Object key, Object value) throws CacheException { + LOG.debug( "key: {} value: {}", key, value ); + try { + Element element = new Element( key, value ); + cache.put( element ); + } + catch ( IllegalArgumentException e ) { + throw new CacheException( e ); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public void evict(Object key) throws CacheException { + try { + cache.remove( key ); + } + catch ( ClassCastException e ) { + throw new CacheException( e ); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * {@inheritDoc} + */ + public void evictAll() throws CacheException { + try { + cache.removeAll(); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheRegionFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheQueryResultsRegion.java similarity index 56% rename from hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheRegionFactory.java rename to hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheQueryResultsRegion.java index c3607e80b8..409857dac8 100644 --- a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/EhCacheRegionFactory.java +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheQueryResultsRegion.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. @@ -21,20 +21,30 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.cache.internal; +package org.hibernate.cache.ehcache.regions; import java.util.Properties; +import net.sf.ehcache.Ehcache; + +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.QueryResultsRegion; + /** - * Thin wrapper class around the within Ehcache-core packaged EhCacheRegionFactory. - * It directly delegates to the wrapped instance, enabling users to upgrade Ehcache-core versions - * by simply dropping in the new jar. + * A query results region specific wrapper around an Ehcache instance. * + * @author Chris Dennis * @author Alex Snaps */ -public final class EhCacheRegionFactory extends AbstractEhCacheRegionFactory { +public class EhcacheQueryResultsRegion extends EhcacheGeneralDataRegion implements QueryResultsRegion { - public EhCacheRegionFactory(Properties properties) { - super(new net.sf.ehcache.hibernate.EhCacheRegionFactory(properties)); + /** + * Constructs an EhcacheQueryResultsRegion around the given underlying cache. + * + * @param accessStrategyFactory + */ + public EhcacheQueryResultsRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache underlyingCache, Properties properties) { + super( accessStrategyFactory, underlyingCache, properties ); } -} + +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/SingletonEhCacheRegionFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTimestampsRegion.java similarity index 54% rename from hibernate-ehcache/src/main/java/org/hibernate/cache/internal/SingletonEhCacheRegionFactory.java rename to hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTimestampsRegion.java index bec857559f..8dc0fe4a44 100644 --- a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/SingletonEhCacheRegionFactory.java +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTimestampsRegion.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. @@ -21,20 +21,30 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.cache.internal; +package org.hibernate.cache.ehcache.regions; + +import net.sf.ehcache.Ehcache; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.TimestampsRegion; import java.util.Properties; /** - * Thin wrapper class around the within Ehcache-core packaged SingletonEhCacheRegionFactory. - * It directly delegates to the wrapped instance, enabling user to upgrade the Ehcache-core version - * by simply dropping in a new jar. + * A timestamps region specific wrapper around an Ehcache instance. * + * @author Chris Dennis + * @author Abhishek Sanoujam * @author Alex Snaps */ -public final class SingletonEhCacheRegionFactory extends AbstractEhCacheRegionFactory { +public class EhcacheTimestampsRegion extends EhcacheGeneralDataRegion implements TimestampsRegion { - public SingletonEhCacheRegionFactory(Properties properties) { - super(new net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory(properties)); + /** + * Constructs an EhcacheTimestampsRegion around the given underlying cache. + * + * @param accessStrategyFactory + */ + public EhcacheTimestampsRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache underlyingCache, Properties properties) { + super(accessStrategyFactory, underlyingCache, properties); } -} +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTransactionalDataRegion.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTransactionalDataRegion.java new file mode 100644 index 0000000000..27db91ae5c --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/regions/EhcacheTransactionalDataRegion.java @@ -0,0 +1,286 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.regions; + +import java.util.Properties; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.Element; +import net.sf.ehcache.concurrent.CacheLockProvider; +import net.sf.ehcache.concurrent.LockType; +import net.sf.ehcache.concurrent.StripedReadWriteLockSync; +import net.sf.ehcache.constructs.nonstop.NonStopCacheException; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.nonstop.HibernateNonstopCacheExceptionHandler; +import org.hibernate.cache.ehcache.strategy.EhcacheAccessStrategyFactory; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.TransactionalDataRegion; +import org.hibernate.cfg.Settings; + +/** + * An Ehcache specific TransactionalDataRegion. + *

+ * This is the common superclass entity and collection regions. + * + * @author Chris Dennis + * @author Greg Luck + * @author Emmanuel Bernard + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class EhcacheTransactionalDataRegion extends EhcacheDataRegion implements TransactionalDataRegion { + + private static final int LOCAL_LOCK_PROVIDER_CONCURRENCY = 128; + + /** + * Hibernate settings associated with the persistence unit. + */ + protected final Settings settings; + + /** + * Metadata associated with the objects stored in the region. + */ + protected final CacheDataDescription metadata; + + private final CacheLockProvider lockProvider; + + /** + * Construct an transactional Hibernate cache region around the given Ehcache instance. + */ + EhcacheTransactionalDataRegion(EhcacheAccessStrategyFactory accessStrategyFactory, Ehcache cache, Settings settings, + CacheDataDescription metadata, Properties properties) { + super( accessStrategyFactory, cache, properties ); + this.settings = settings; + this.metadata = metadata; + + Object context = cache.getInternalContext(); + if ( context instanceof CacheLockProvider ) { + this.lockProvider = (CacheLockProvider) context; + } + else { + this.lockProvider = new StripedReadWriteLockSync( LOCAL_LOCK_PROVIDER_CONCURRENCY ); + } + } + + /** + * Return the hibernate settings + * + * @return settings + */ + public Settings getSettings() { + return settings; + } + + /** + * {@inheritDoc} + */ + public boolean isTransactionAware() { + return false; + } + + /** + * {@inheritDoc} + */ + public CacheDataDescription getCacheDataDescription() { + return metadata; + } + + /** + * Get the value mapped to this key, or null if no value is mapped to this key. + */ + public final Object get(Object key) { + try { + Element element = cache.get( key ); + if ( element == null ) { + return null; + } + else { + return element.getObjectValue(); + } + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + return null; + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Map the given value to the given key, replacing any existing mapping for this key + */ + public final void put(Object key, Object value) throws CacheException { + try { + Element element = new Element( key, value ); + cache.put( element ); + } + catch ( IllegalArgumentException e ) { + throw new CacheException( e ); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Remove the mapping for this key (if any exists). + */ + public final void remove(Object key) throws CacheException { + try { + cache.remove( key ); + } + catch ( ClassCastException e ) { + throw new CacheException( e ); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Remove all mapping from this cache region. + */ + public final void clear() throws CacheException { + try { + cache.removeAll(); + } + catch ( IllegalStateException e ) { + throw new CacheException( e ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Attempts to write lock the mapping for the given key. + */ + public final void writeLock(Object key) { + try { + lockProvider.getSyncForKey( key ).lock( LockType.WRITE ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Attempts to write unlock the mapping for the given key. + */ + public final void writeUnlock(Object key) { + try { + lockProvider.getSyncForKey( key ).unlock( LockType.WRITE ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Attempts to read lock the mapping for the given key. + */ + public final void readLock(Object key) { + try { + lockProvider.getSyncForKey( key ).lock( LockType.WRITE ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Attempts to read unlock the mapping for the given key. + */ + public final void readUnlock(Object key) { + try { + lockProvider.getSyncForKey( key ).unlock( LockType.WRITE ); + } + catch ( net.sf.ehcache.CacheException e ) { + if ( e instanceof NonStopCacheException ) { + HibernateNonstopCacheExceptionHandler.getInstance() + .handleNonstopCacheException( (NonStopCacheException) e ); + } + else { + throw new CacheException( e ); + } + } + } + + /** + * Returns true if the locks used by the locking methods of this region are the independent of the cache. + *

+ * Independent locks are not locked by the cache when the cache is accessed directly. This means that for an independent lock + * lock holds taken through a region method will not block direct access to the cache via other means. + */ + public final boolean locksAreIndependentOfCache() { + return lockProvider instanceof StripedReadWriteLockSync; + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractEhcacheAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractEhcacheAccessStrategy.java new file mode 100644 index 0000000000..5ab4ac3bf3 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractEhcacheAccessStrategy.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheTransactionalDataRegion; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Ultimate superclass for all Ehcache specific Hibernate AccessStrategy implementations. + * + * @param type of the enclosed region + * + * @author Chris Dennis + * @author Alex Snaps + */ +abstract class AbstractEhcacheAccessStrategy { + + /** + * The wrapped Hibernate cache region. + */ + protected final T region; + /** + * The settings for this persistence unit. + */ + protected final Settings settings; + + /** + * Create an access strategy wrapping the given region. + */ + AbstractEhcacheAccessStrategy(T region, Settings settings) { + this.region = region; + this.settings = settings; + } + + /** + * This method is a placeholder for method signatures supplied by interfaces pulled in further down the class + * hierarchy. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object) + */ + public final boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException { + return putFromLoad( key, value, txTimestamp, version, settings.isMinimalPutsEnabled() ); + } + + /** + * This method is a placeholder for method signatures supplied by interfaces pulled in further down the class + * hierarchy. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean) + */ + public abstract boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException; + + /** + * Region locks are not supported. + * + * @return null + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockRegion() + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#lockRegion() + */ + public final SoftLock lockRegion() { + return null; + } + + /** + * Region locks are not supported - perform a cache clear as a precaution. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockRegion(org.hibernate.cache.spi.access.SoftLock) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#unlockRegion(org.hibernate.cache.spi.access.SoftLock) + */ + public final void unlockRegion(SoftLock lock) throws CacheException { + region.clear(); + } + + /** + * A no-op since this is an asynchronous cache access strategy. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#remove(java.lang.Object) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#remove(java.lang.Object) + */ + public void remove(Object key) throws CacheException { + } + + /** + * Called to evict data from the entire region + * + * @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region} + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#removeAll() + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#removeAll() + */ + public final void removeAll() throws CacheException { + region.clear(); + } + + /** + * Remove the given mapping without regard to transactional safety + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evict(java.lang.Object) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#evict(java.lang.Object) + */ + public final void evict(Object key) throws CacheException { + region.remove( key ); + } + + /** + * Remove all mappings without regard to transactional safety + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#evictAll() + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#evictAll() + */ + public final void evictAll() throws CacheException { + region.clear(); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractReadWriteEhcacheAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractReadWriteEhcacheAccessStrategy.java new file mode 100644 index 0000000000..c82bbfb6ba --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/AbstractReadWriteEhcacheAccessStrategy.java @@ -0,0 +1,428 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheTransactionalDataRegion; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Superclass for all Ehcache specific read/write AccessStrategy implementations. + * + * @param the type of the enclosed cache region + * + * @author Chris Dennis + * @author Alex Snaps + */ +abstract class AbstractReadWriteEhcacheAccessStrategy + extends AbstractEhcacheAccessStrategy { + + private static final Logger LOG = LoggerFactory.getLogger( AbstractReadWriteEhcacheAccessStrategy.class ); + + private final UUID uuid = UUID.randomUUID(); + private final AtomicLong nextLockId = new AtomicLong(); + + private final Comparator versionComparator; + + /** + * Creates a read/write cache access strategy around the given cache region. + */ + public AbstractReadWriteEhcacheAccessStrategy(T region, Settings settings) { + super( region, settings ); + this.versionComparator = region.getCacheDataDescription().getVersionComparator(); + } + + /** + * Returns null if the item is not readable. Locked items are not readable, nor are items created + * after the start of this transaction. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#get(java.lang.Object, long) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#get(java.lang.Object, long) + */ + public final Object get(Object key, long txTimestamp) throws CacheException { + readLockIfNeeded( key ); + try { + Lockable item = (Lockable) region.get( key ); + + boolean readable = item != null && item.isReadable( txTimestamp ); + if ( readable ) { + return item.getValue(); + } + else { + return null; + } + } + finally { + readUnlockIfNeeded( key ); + } + } + + /** + * Returns false and fails to put the value if there is an existing un-writeable item mapped to this + * key. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean) + */ + @Override + public final boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + region.writeLock( key ); + try { + Lockable item = (Lockable) region.get( key ); + boolean writeable = item == null || item.isWriteable( txTimestamp, version, versionComparator ); + if ( writeable ) { + region.put( key, new Item( value, version, region.nextTimestamp() ) ); + return true; + } + else { + return false; + } + } + finally { + region.writeUnlock( key ); + } + } + + /** + * Soft-lock a cache item. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#lockItem(java.lang.Object, java.lang.Object) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#lockItem(java.lang.Object, java.lang.Object) + */ + public final SoftLock lockItem(Object key, Object version) throws CacheException { + region.writeLock( key ); + try { + Lockable item = (Lockable) region.get( key ); + long timeout = region.nextTimestamp() + region.getTimeout(); + final Lock lock = ( item == null ) ? new Lock( timeout, uuid, nextLockId(), version ) : item.lock( + timeout, + uuid, + nextLockId() + ); + region.put( key, lock ); + return lock; + } + finally { + region.writeUnlock( key ); + } + } + + /** + * Soft-unlock a cache item. + * + * @see org.hibernate.cache.spi.access.EntityRegionAccessStrategy#unlockItem(java.lang.Object, org.hibernate.cache.spi.access.SoftLock) + * @see org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#unlockItem(java.lang.Object, org.hibernate.cache.spi.access.SoftLock) + */ + public final void unlockItem(Object key, SoftLock lock) throws CacheException { + region.writeLock( key ); + try { + Lockable item = (Lockable) region.get( key ); + + if ( ( item != null ) && item.isUnlockable( lock ) ) { + decrementLock( key, (Lock) item ); + } + else { + handleLockExpiry( key, item ); + } + } + finally { + region.writeUnlock( key ); + } + } + + private long nextLockId() { + return nextLockId.getAndIncrement(); + } + + /** + * Unlock and re-put the given key, lock combination. + */ + protected void decrementLock(Object key, Lock lock) { + lock.unlock( region.nextTimestamp() ); + region.put( key, lock ); + } + + /** + * Handle the timeout of a previous lock mapped to this key + */ + protected void handleLockExpiry(Object key, Lockable lock) { + LOG.warn( + "Cache " + region.getName() + " Key " + key + " Lockable : " + lock + "\n" + + "A soft-locked cache entry was expired by the underlying Ehcache. " + + "If this happens regularly you should consider increasing the cache timeouts and/or capacity limits" + ); + long ts = region.nextTimestamp() + region.getTimeout(); + // create new lock that times out immediately + Lock newLock = new Lock( ts, uuid, nextLockId.getAndIncrement(), null ); + newLock.unlock( ts ); + region.put( key, newLock ); + } + + /** + * Read lock the entry for the given key if internal cache locks will not provide correct exclusion. + */ + private void readLockIfNeeded(Object key) { + if ( region.locksAreIndependentOfCache() ) { + region.readLock( key ); + } + } + + /** + * Read unlock the entry for the given key if internal cache locks will not provide correct exclusion. + */ + private void readUnlockIfNeeded(Object key) { + if ( region.locksAreIndependentOfCache() ) { + region.readUnlock( key ); + } + } + + /** + * Interface type implemented by all wrapper objects in the cache. + */ + protected static interface Lockable { + + /** + * Returns true if the enclosed value can be read by a transaction started at the given time. + */ + public boolean isReadable(long txTimestamp); + + /** + * Returns true if the enclosed value can be replaced with one of the given version by a + * transaction started at the given time. + */ + public boolean isWriteable(long txTimestamp, Object version, Comparator versionComparator); + + /** + * Returns the enclosed value. + */ + public Object getValue(); + + /** + * Returns true if the given lock can be unlocked using the given SoftLock instance as a handle. + */ + public boolean isUnlockable(SoftLock lock); + + /** + * Locks this entry, stamping it with the UUID and lockId given, with the lock timeout occuring at the specified + * time. The returned Lock object can be used to unlock the entry in the future. + */ + public Lock lock(long timeout, UUID uuid, long lockId); + } + + /** + * Wrapper type representing unlocked items. + */ + protected final static class Item implements Serializable, Lockable { + + private static final long serialVersionUID = 1L; + private final Object value; + private final Object version; + private final long timestamp; + + /** + * Creates an unlocked item wrapping the given value with a version and creation timestamp. + */ + Item(Object value, Object version, long timestamp) { + this.value = value; + this.version = version; + this.timestamp = timestamp; + } + + /** + * {@inheritDoc} + */ + public boolean isReadable(long txTimestamp) { + return txTimestamp > timestamp; + } + + /** + * {@inheritDoc} + */ + public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) { + return version != null && versionComparator.compare( version, newVersion ) < 0; + } + + /** + * {@inheritDoc} + */ + public Object getValue() { + return value; + } + + /** + * {@inheritDoc} + */ + public boolean isUnlockable(SoftLock lock) { + return false; + } + + /** + * {@inheritDoc} + */ + public Lock lock(long timeout, UUID uuid, long lockId) { + return new Lock( timeout, uuid, lockId, version ); + } + } + + /** + * Wrapper type representing locked items. + */ + protected final static class Lock implements Serializable, Lockable, SoftLock { + + private static final long serialVersionUID = 2L; + + private final UUID sourceUuid; + private final long lockId; + private final Object version; + + private long timeout; + private boolean concurrent; + private int multiplicity = 1; + private long unlockTimestamp; + + /** + * Creates a locked item with the given identifiers and object version. + */ + Lock(long timeout, UUID sourceUuid, long lockId, Object version) { + this.timeout = timeout; + this.lockId = lockId; + this.version = version; + this.sourceUuid = sourceUuid; + } + + /** + * {@inheritDoc} + */ + public boolean isReadable(long txTimestamp) { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) { + if ( txTimestamp > timeout ) { + // if timedout then allow write + return true; + } + if ( multiplicity > 0 ) { + // if still locked then disallow write + return false; + } + return version == null ? txTimestamp > unlockTimestamp : versionComparator.compare( + version, + newVersion + ) < 0; + } + + /** + * {@inheritDoc} + */ + public Object getValue() { + return null; + } + + /** + * {@inheritDoc} + */ + public boolean isUnlockable(SoftLock lock) { + return equals( lock ); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + if ( o == this ) { + return true; + } + else if ( o instanceof Lock ) { + return ( lockId == ( (Lock) o ).lockId ) && sourceUuid.equals( ( (Lock) o ).sourceUuid ); + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int hash = ( sourceUuid != null ? sourceUuid.hashCode() : 0 ); + int temp = (int) lockId; + for ( int i = 1; i < Long.SIZE / Integer.SIZE; i++ ) { + temp ^= ( lockId >>> ( i * Integer.SIZE ) ); + } + return hash + temp; + } + + /** + * Returns true if this Lock has been concurrently locked by more than one transaction. + */ + public boolean wasLockedConcurrently() { + return concurrent; + } + + /** + * {@inheritDoc} + */ + public Lock lock(long timeout, UUID uuid, long lockId) { + concurrent = true; + multiplicity++; + this.timeout = timeout; + return this; + } + + /** + * Unlocks this Lock, and timestamps the unlock event. + */ + public void unlock(long timestamp) { + if ( --multiplicity == 0 ) { + unlockTimestamp = timestamp; + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder( "Lock Source-UUID:" + sourceUuid + " Lock-ID:" + lockId ); + return sb.toString(); + } + } +} + diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactory.java new file mode 100644 index 0000000000..2e44ee5e38 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactory.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; + +/** + * Factory to create {@link EntityRegionAccessStrategy} + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public interface EhcacheAccessStrategyFactory { + + /** + * Create {@link EntityRegionAccessStrategy} for the input {@link EhcacheEntityRegion} and {@link AccessType} + * + * @param entityRegion + * @param accessType + * + * @return the created {@link EntityRegionAccessStrategy} + */ + public EntityRegionAccessStrategy createEntityRegionAccessStrategy(EhcacheEntityRegion entityRegion, AccessType accessType); + + /** + * Create {@link CollectionRegionAccessStrategy} for the input {@link org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion} and {@link AccessType} + * + * @param collectionRegion + * @param accessType + * + * @return the created {@link CollectionRegionAccessStrategy} + */ + public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(EhcacheCollectionRegion collectionRegion, + AccessType accessType); + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactoryImpl.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactoryImpl.java new file mode 100644 index 0000000000..382af7ea3a --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/EhcacheAccessStrategyFactoryImpl.java @@ -0,0 +1,110 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; + +/** + * Class implementing {@link EhcacheAccessStrategyFactory} + * + * @author Abhishek Sanoujam + * @author Alex Snaps + */ +public class EhcacheAccessStrategyFactoryImpl implements EhcacheAccessStrategyFactory { + + private static final Logger LOG = LoggerFactory.getLogger( EhcacheAccessStrategyFactoryImpl.class ); + + /** + * {@inheritDoc} + */ + public EntityRegionAccessStrategy createEntityRegionAccessStrategy(EhcacheEntityRegion entityRegion, AccessType accessType) { + if ( AccessType.READ_ONLY.equals( accessType ) ) { + if ( entityRegion.getCacheDataDescription().isMutable() ) { + LOG.warn( "read-only cache configured for mutable entity [" + entityRegion.getName() + "]" ); + } + return new ReadOnlyEhcacheEntityRegionAccessStrategy( entityRegion, entityRegion.getSettings() ); + } + else if ( AccessType.READ_WRITE.equals( accessType ) ) { + return new ReadWriteEhcacheEntityRegionAccessStrategy( entityRegion, entityRegion.getSettings() ); + } + else if ( AccessType.NONSTRICT_READ_WRITE.equals( accessType ) ) { + return new NonStrictReadWriteEhcacheEntityRegionAccessStrategy( entityRegion, entityRegion.getSettings() ); + } + else if ( AccessType.TRANSACTIONAL.equals( accessType ) ) { + return new TransactionalEhcacheEntityRegionAccessStrategy( + entityRegion, + entityRegion.getEhcache(), + entityRegion.getSettings() + ); + } + else { + throw new IllegalArgumentException( "unrecognized access strategy type [" + accessType + "]" ); + } + } + + /** + * {@inheritDoc} + */ + public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(EhcacheCollectionRegion collectionRegion, + AccessType accessType) { + if ( AccessType.READ_ONLY.equals( accessType ) ) { + if ( collectionRegion.getCacheDataDescription().isMutable() ) { + LOG.warn( "read-only cache configured for mutable entity [" + collectionRegion.getName() + "]" ); + } + return new ReadOnlyEhcacheCollectionRegionAccessStrategy( + collectionRegion, + collectionRegion.getSettings() + ); + } + else if ( AccessType.READ_WRITE.equals( accessType ) ) { + return new ReadWriteEhcacheCollectionRegionAccessStrategy( + collectionRegion, + collectionRegion.getSettings() + ); + } + else if ( AccessType.NONSTRICT_READ_WRITE.equals( accessType ) ) { + return new NonStrictReadWriteEhcacheCollectionRegionAccessStrategy( + collectionRegion, + collectionRegion.getSettings() + ); + } + else if ( AccessType.TRANSACTIONAL.equals( accessType ) ) { + return new TransactionalEhcacheCollectionRegionAccessStrategy( + collectionRegion, collectionRegion.getEhcache(), collectionRegion + .getSettings() + ); + } + else { + throw new IllegalArgumentException( "unrecognized access strategy type [" + accessType + "]" ); + } + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheCollectionRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheCollectionRegionAccessStrategy.java new file mode 100644 index 0000000000..a607f1105a --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheCollectionRegionAccessStrategy.java @@ -0,0 +1,99 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Ehcache specific non-strict read/write collection region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class NonStrictReadWriteEhcacheCollectionRegionAccessStrategy + extends AbstractEhcacheAccessStrategy + implements CollectionRegionAccessStrategy { + + /** + * Create a non-strict read/write access strategy accessing the given collection region. + */ + public NonStrictReadWriteEhcacheCollectionRegionAccessStrategy(EhcacheCollectionRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + return region.get( key ); + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + if ( minimalPutOverride && region.contains( key ) ) { + return false; + } + else { + region.put( key, value ); + return true; + } + } + + /** + * Since this is a non-strict read/write strategy item locking is not used. + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + return null; + } + + /** + * Since this is a non-strict read/write strategy item locking is not used. + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + region.remove( key ); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Object key) throws CacheException { + region.remove( key ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java new file mode 100644 index 0000000000..13dfba7a5c --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java @@ -0,0 +1,131 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Ehcache specific non-strict read/write entity region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class NonStrictReadWriteEhcacheEntityRegionAccessStrategy + extends AbstractEhcacheAccessStrategy + implements EntityRegionAccessStrategy { + + /** + * Create a non-strict read/write access strategy accessing the given collection region. + */ + public NonStrictReadWriteEhcacheEntityRegionAccessStrategy(EhcacheEntityRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public EntityRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + return region.get( key ); + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + if ( minimalPutOverride && region.contains( key ) ) { + return false; + } + else { + region.put( key, value ); + return true; + } + } + + /** + * Since this is a non-strict read/write strategy item locking is not used. + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + return null; + } + + /** + * Since this is a non-strict read/write strategy item locking is not used. + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + region.remove( key ); + } + + /** + * Returns false since this is an asynchronous cache access strategy. + */ + public boolean insert(Object key, Object value, Object version) throws CacheException { + return false; + } + + /** + * Returns false since this is a non-strict read/write cache access strategy + */ + public boolean afterInsert(Object key, Object value, Object version) throws CacheException { + return false; + } + + /** + * Removes the entry since this is a non-strict read/write cache strategy. + */ + public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) + throws CacheException { + remove( key ); + return false; + } + + /** + * {@inheritDoc} + */ + public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) + throws CacheException { + unlockItem( key, lock ); + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Object key) throws CacheException { + region.remove( key ); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheCollectionRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheCollectionRegionAccessStrategy.java new file mode 100644 index 0000000000..fb17a10e77 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheCollectionRegionAccessStrategy.java @@ -0,0 +1,93 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Ehcache specific read-only collection region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class ReadOnlyEhcacheCollectionRegionAccessStrategy + extends AbstractEhcacheAccessStrategy + implements CollectionRegionAccessStrategy { + + /** + * Create a read-only access strategy accessing the given collection region. + */ + public ReadOnlyEhcacheCollectionRegionAccessStrategy(EhcacheCollectionRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + return region.get( key ); + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + if ( minimalPutOverride && region.contains( key ) ) { + return false; + } + else { + region.put( key, value ); + return true; + } + } + + /** + * Throws UnsupportedOperationException since this cache is read-only + * + * @throws UnsupportedOperationException always + */ + public SoftLock lockItem(Object key, Object version) throws UnsupportedOperationException { + throw new UnsupportedOperationException( "Can't write to a readonly object" ); + } + + /** + * A no-op since this cache is read-only + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + //throw new UnsupportedOperationException("Can't write to a readonly object"); + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheEntityRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheEntityRegionAccessStrategy.java new file mode 100644 index 0000000000..c0d7916835 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadOnlyEhcacheEntityRegionAccessStrategy.java @@ -0,0 +1,131 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * @author Alex Snaps + */ + +/** + * Ehcache specific read-only entity region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class ReadOnlyEhcacheEntityRegionAccessStrategy extends AbstractEhcacheAccessStrategy + implements EntityRegionAccessStrategy { + + /** + * Create a read-only access strategy accessing the given entity region. + */ + public ReadOnlyEhcacheEntityRegionAccessStrategy(EhcacheEntityRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public EntityRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + return region.get( key ); + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) + throws CacheException { + if ( minimalPutOverride && region.contains( key ) ) { + return false; + } + else { + region.put( key, value ); + return true; + } + } + + /** + * Throws UnsupportedOperationException since this cache is read-only + * + * @throws UnsupportedOperationException always + */ + public SoftLock lockItem(Object key, Object version) throws UnsupportedOperationException { + throw new UnsupportedOperationException( "Can't write to a readonly object" ); + } + + /** + * A no-op since this cache is read-only + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + //throw new UnsupportedOperationException("Can't write to a readonly object"); + } + + /** + * This cache is asynchronous hence a no-op + */ + public boolean insert(Object key, Object value, Object version) throws CacheException { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean afterInsert(Object key, Object value, Object version) throws CacheException { + region.put( key, value ); + return true; + } + + /** + * Throws UnsupportedOperationException since this cache is read-only + * + * @throws UnsupportedOperationException always + */ + public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( "Can't write to a readonly object" ); + } + + /** + * Throws UnsupportedOperationException since this cache is read-only + * + * @throws UnsupportedOperationException always + */ + public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( "Can't write to a readonly object" ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheCollectionRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheCollectionRegionAccessStrategy.java new file mode 100644 index 0000000000..2f3261aa86 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheCollectionRegionAccessStrategy.java @@ -0,0 +1,54 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cfg.Settings; + +/** + * Ehcache specific read/write collection region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class ReadWriteEhcacheCollectionRegionAccessStrategy + extends AbstractReadWriteEhcacheAccessStrategy + implements CollectionRegionAccessStrategy { + + /** + * Create a read/write access strategy accessing the given collection region. + */ + public ReadWriteEhcacheCollectionRegionAccessStrategy(EhcacheCollectionRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public CollectionRegion getRegion() { + return region; + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheEntityRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheEntityRegionAccessStrategy.java new file mode 100644 index 0000000000..11a6d35c4f --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/ReadWriteEhcacheEntityRegionAccessStrategy.java @@ -0,0 +1,128 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * Ehcache specific read/write entity region access strategy + * + * @author Chris Dennis + * @author Alex Snaps + */ +public class ReadWriteEhcacheEntityRegionAccessStrategy + extends AbstractReadWriteEhcacheAccessStrategy + implements EntityRegionAccessStrategy { + + /** + * Create a read/write access strategy accessing the given entity region. + */ + public ReadWriteEhcacheEntityRegionAccessStrategy(EhcacheEntityRegion region, Settings settings) { + super( region, settings ); + } + + /** + * {@inheritDoc} + */ + public EntityRegion getRegion() { + return region; + } + + /** + * A no-op since this is an asynchronous cache access strategy. + */ + public boolean insert(Object key, Object value, Object version) throws CacheException { + return false; + } + + /** + * {@inheritDoc} + *

+ * Inserts will only succeed if there is no existing value mapped to this key. + */ + public boolean afterInsert(Object key, Object value, Object version) throws CacheException { + region.writeLock( key ); + try { + Lockable item = (Lockable) region.get( key ); + if ( item == null ) { + region.put( key, new Item( value, version, region.nextTimestamp() ) ); + return true; + } + else { + return false; + } + } + finally { + region.writeUnlock( key ); + } + } + + /** + * A no-op since this is an asynchronous cache access strategy. + */ + public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) + throws CacheException { + return false; + } + + /** + * {@inheritDoc} + *

+ * Updates will only succeed if this entry was locked by this transaction and exclusively this transaction for the + * duration of this transaction. It is important to also note that updates will fail if the soft-lock expired during + * the course of this transaction. + */ + public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) + throws CacheException { + //what should we do with previousVersion here? + region.writeLock( key ); + try { + Lockable item = (Lockable) region.get( key ); + + if ( item != null && item.isUnlockable( lock ) ) { + Lock lockItem = (Lock) item; + if ( lockItem.wasLockedConcurrently() ) { + decrementLock( key, lockItem ); + return false; + } + else { + region.put( key, new Item( value, currentVersion, region.nextTimestamp() ) ); + return true; + } + } + else { + handleLockExpiry( key, item ); + return false; + } + } + finally { + region.writeUnlock( key ); + } + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheCollectionRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheCollectionRegionAccessStrategy.java new file mode 100644 index 0000000000..c9b8abddd1 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheCollectionRegionAccessStrategy.java @@ -0,0 +1,127 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.Element; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheCollectionRegion; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * JTA CollectionRegionAccessStrategy. + * + * @author Chris Dennis + * @author Ludovic Orban + * @author Alex Snaps + */ +public class TransactionalEhcacheCollectionRegionAccessStrategy + extends AbstractEhcacheAccessStrategy + implements CollectionRegionAccessStrategy { + + private final Ehcache ehcache; + + /** + * Construct a new collection region access strategy. + * + * @param region the Hibernate region. + * @param ehcache the cache. + * @param settings the Hibernate settings. + */ + public TransactionalEhcacheCollectionRegionAccessStrategy(EhcacheCollectionRegion region, Ehcache ehcache, Settings settings) { + super( region, settings ); + this.ehcache = ehcache; + } + + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + try { + Element element = ehcache.get( key ); + return element == null ? null : element.getObjectValue(); + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public CollectionRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + return null; + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, + Object version, boolean minimalPutOverride) throws CacheException { + try { + if ( minimalPutOverride && ehcache.get( key ) != null ) { + return false; + } + //OptimisticCache? versioning? + ehcache.put( new Element( key, value ) ); + return true; + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Object key) throws CacheException { + try { + ehcache.remove( key ); + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + // no-op + } + +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheEntityRegionAccessStrategy.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheEntityRegionAccessStrategy.java new file mode 100644 index 0000000000..6b521a6fd6 --- /dev/null +++ b/hibernate-ehcache/src/main/java/org/hibernate/cache/ehcache/strategy/TransactionalEhcacheEntityRegionAccessStrategy.java @@ -0,0 +1,167 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.ehcache.strategy; + +import net.sf.ehcache.Ehcache; +import net.sf.ehcache.Element; + +import org.hibernate.cache.CacheException; +import org.hibernate.cache.ehcache.regions.EhcacheEntityRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Settings; + +/** + * JTA EntityRegionAccessStrategy. + * + * @author Chris Dennis + * @author Ludovic Orban + * @author Alex Snaps + */ +public class TransactionalEhcacheEntityRegionAccessStrategy extends AbstractEhcacheAccessStrategy + implements EntityRegionAccessStrategy { + + private final Ehcache ehcache; + + /** + * Construct a new entity region access strategy. + * + * @param region the Hibernate region. + * @param ehcache the cache. + * @param settings the Hibernate settings. + */ + public TransactionalEhcacheEntityRegionAccessStrategy(EhcacheEntityRegion region, Ehcache ehcache, Settings settings) { + super( region, settings ); + this.ehcache = ehcache; + } + + /** + * {@inheritDoc} + */ + public boolean afterInsert(Object key, Object value, Object version) { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) { + return false; + } + + /** + * {@inheritDoc} + */ + public Object get(Object key, long txTimestamp) throws CacheException { + try { + Element element = ehcache.get( key ); + return element == null ? null : element.getObjectValue(); + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public EntityRegion getRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + public boolean insert(Object key, Object value, Object version) + throws CacheException { + //OptimisticCache? versioning? + try { + ehcache.put( new Element( key, value ) ); + return true; + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public SoftLock lockItem(Object key, Object version) throws CacheException { + return null; + } + + /** + * {@inheritDoc} + */ + public boolean putFromLoad(Object key, Object value, long txTimestamp, + Object version, boolean minimalPutOverride) throws CacheException { + try { + if ( minimalPutOverride && ehcache.get( key ) != null ) { + return false; + } + //OptimisticCache? versioning? + ehcache.put( new Element( key, value ) ); + return true; + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Object key) throws CacheException { + try { + ehcache.remove( key ); + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } + + /** + * {@inheritDoc} + */ + public void unlockItem(Object key, SoftLock lock) throws CacheException { + // no-op + } + + /** + * {@inheritDoc} + */ + public boolean update(Object key, Object value, Object currentVersion, + Object previousVersion) throws CacheException { + try { + ehcache.put( new Element( key, value ) ); + return true; + } + catch ( net.sf.ehcache.CacheException e ) { + throw new CacheException( e ); + } + } +} diff --git a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/AbstractEhCacheRegionFactory.java b/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/AbstractEhCacheRegionFactory.java deleted file mode 100644 index 742ca16d68..0000000000 --- a/hibernate-ehcache/src/main/java/org/hibernate/cache/internal/AbstractEhCacheRegionFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.cache.internal; - -import java.util.Properties; - -import org.hibernate.cache.CacheException; -import org.hibernate.cache.spi.CacheDataDescription; -import org.hibernate.cache.spi.CollectionRegion; -import org.hibernate.cache.spi.EntityRegion; -import org.hibernate.cache.spi.QueryResultsRegion; -import org.hibernate.cache.spi.RegionFactory; -import org.hibernate.cache.spi.TimestampsRegion; -import org.hibernate.cache.spi.access.AccessType; -import org.hibernate.cfg.Settings; - -/** - * Abstract class that will delegate all calls to org.hibernate.cache.spi.RegionFactory to the instance it wraps. - * This abstracts the Singleton CacheManager construct of Ehcache - * - * @author Alex Snaps - */ -class AbstractEhCacheRegionFactory implements RegionFactory { - - private final RegionFactory underlyingRegionFactory; - - /** - * {@inheritDoc} - */ - protected AbstractEhCacheRegionFactory(RegionFactory regionFactory) { - underlyingRegionFactory = regionFactory; - } - - /** - * {@inheritDoc} - */ - public final void start(final Settings settings, final Properties properties) throws CacheException { - underlyingRegionFactory.start(settings, properties); - } - - /** - * {@inheritDoc} - */ - public final void stop() { - underlyingRegionFactory.stop(); - } - - /** - * {@inheritDoc} - */ - public final boolean isMinimalPutsEnabledByDefault() { - return underlyingRegionFactory.isMinimalPutsEnabledByDefault(); - } - - /** - * {@inheritDoc} - */ - public final AccessType getDefaultAccessType() { - return underlyingRegionFactory.getDefaultAccessType(); - } - - /** - * {@inheritDoc} - */ - public final long nextTimestamp() { - return underlyingRegionFactory.nextTimestamp(); - } - - /** - * {@inheritDoc} - */ - public final EntityRegion buildEntityRegion(final String regionName, final Properties properties, final CacheDataDescription metadata) throws CacheException { - return underlyingRegionFactory.buildEntityRegion(regionName, properties, metadata); - } - - /** - * {@inheritDoc} - */ - public final CollectionRegion buildCollectionRegion(final String regionName, final Properties properties, final CacheDataDescription metadata) throws CacheException { - return underlyingRegionFactory.buildCollectionRegion(regionName, properties, metadata); - } - - /** - * {@inheritDoc} - */ - public final QueryResultsRegion buildQueryResultsRegion(final String regionName, final Properties properties) throws CacheException { - return underlyingRegionFactory.buildQueryResultsRegion(regionName, properties); - } - - /** - * {@inheritDoc} - */ - public final TimestampsRegion buildTimestampsRegion(final String regionName, final Properties properties) throws CacheException { - return underlyingRegionFactory.buildTimestampsRegion(regionName, properties); - } -} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/strategy/ItemValueExtractor.java b/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/strategy/ItemValueExtractor.java new file mode 100644 index 0000000000..4442d12029 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/strategy/ItemValueExtractor.java @@ -0,0 +1,29 @@ +package org.hibernate.cache.ehcache.strategy; + +import org.hibernate.cache.ehcache.regions.EhcacheTransactionalDataRegion; +import org.hibernate.cache.ehcache.regions.EhcacheTransactionalDataRegion; +import org.hibernate.cache.ehcache.strategy.AbstractReadWriteEhcacheAccessStrategy; +import org.hibernate.cfg.Settings; + + +/** + * @author Alex Snaps + */ +public class ItemValueExtractor extends AbstractReadWriteEhcacheAccessStrategy { + + + /** + * Creates a read/write cache access strategy around the given cache region. + */ + public ItemValueExtractor(EhcacheTransactionalDataRegion region, Settings settings) { + super(region, settings); + } + + + public static T getValue(final Object entry) { + if(!(entry instanceof Item)) { + throw new IllegalArgumentException("Entry needs to be of type " + Item.class.getName()); + } + return (T)((Item)entry).getValue(); + } +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/EhcacheStatsImplTest.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/EhcacheStatsImplTest.java new file mode 100644 index 0000000000..4563433cd5 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/EhcacheStatsImplTest.java @@ -0,0 +1,36 @@ +package org.hibernate.test.cache; + +import net.sf.ehcache.CacheManager; + +import org.hibernate.cache.ehcache.management.impl.EhcacheStatsImpl; +import org.hibernate.cache.ehcache.management.impl.EhcacheStatsImpl; + +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author Alex Snaps + */ +public class EhcacheStatsImplTest { + + private static EhcacheStatsImpl stats; + + @BeforeClass + public static void createCache() throws Exception { + CacheManager manager = CacheManager.getInstance(); + stats = new EhcacheStatsImpl( manager ); + } + + @Test + public void testIsRegionCacheOrphanEvictionEnabled() { + assertThat( stats.isRegionCacheOrphanEvictionEnabled( "sampleCache1" ), is( false ) ); + } + + @Test + public void testGetRegionCacheOrphanEvictionPeriod() { + assertThat( stats.getRegionCacheOrphanEvictionPeriod( "sampleCache1" ), is( -1 ) ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java new file mode 100644 index 0000000000..7c0a7117e1 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java @@ -0,0 +1,258 @@ +package org.hibernate.test.cache; + +import org.hamcrest.CoreMatchers; + +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cache.ehcache.strategy.ItemValueExtractor; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.Configuration; +import org.hibernate.stat.QueryStatistics; +import org.hibernate.stat.SecondLevelCacheStatistics; +import org.hibernate.stat.Statistics; +import org.hibernate.test.domain.Event; +import org.hibernate.test.domain.EventManager; +import org.hibernate.test.domain.Item; +import org.hibernate.test.domain.Person; +import org.hibernate.test.domain.PhoneNumber; +import org.hibernate.test.domain.VersionedItem; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * @author Chris Dennis + */ +public class HibernateCacheTest { + + private static SessionFactory sessionFactory; + private static Configuration config; + private static final String REGION_PREFIX = "hibernate.test."; + + public synchronized static SessionFactory getSessionFactory() { + if ( sessionFactory == null ) { + try { + sessionFactory = config.buildSessionFactory(); + } + catch ( HibernateException ex ) { + System.err.println( "Initial SessionFactory creation failed." + ex ); + throw new ExceptionInInitializerError( ex ); + } + } + return sessionFactory; + } + + @BeforeClass + public static void setUp() { + System.setProperty( "derby.system.home", "target/derby" ); + config = new Configuration().configure( "/hibernate-config/hibernate.cfg.xml" ); + config.setProperty( "hibernate.hbm2ddl.auto", "create" ); + getSessionFactory().getStatistics().setStatisticsEnabled( true ); + } + + @AfterClass + public static void tearDown() { + getSessionFactory().close(); + } + + @Test + public void testQueryCacheInvalidation() throws Exception { + Session s = getSessionFactory().openSession(); + Transaction t = s.beginTransaction(); + Item i = new Item(); + i.setName( "widget" ); + i.setDescription( "A really top-quality, full-featured widget." ); + s.persist( i ); + t.commit(); + s.close(); + + SecondLevelCacheStatistics slcs = s.getSessionFactory() + .getStatistics() + .getSecondLevelCacheStatistics( REGION_PREFIX + Item.class.getName() ); + + assertThat( slcs.getPutCount(), equalTo( 1L ) ); + assertThat( slcs.getElementCountInMemory(), equalTo( 1L ) ); + assertThat( slcs.getEntries().size(), equalTo( 1 ) ); + + s = getSessionFactory().openSession(); + t = s.beginTransaction(); + i = (Item) s.get( Item.class, i.getId() ); + + assertThat( slcs.getHitCount(), equalTo( 1L ) ); + assertThat( slcs.getMissCount(), equalTo( 0L ) ); + + i.setDescription( "A bog standard item" ); + + t.commit(); + s.close(); + + assertThat( slcs.getPutCount(), equalTo( 2L ) ); + + Object entry = slcs.getEntries().get( i.getId() ); + Map map; + if ( entry instanceof Map ) { + map = (Map) entry; + } + else { + map = ItemValueExtractor.getValue( entry ); + } + assertThat( (String) map.get( "description" ), equalTo( "A bog standard item" ) ); + assertThat( (String) map.get( "name" ), equalTo( "widget" ) ); + + // cleanup + s = getSessionFactory().openSession(); + t = s.beginTransaction(); + s.delete( i ); + t.commit(); + s.close(); + } + + @Test + public void testEmptySecondLevelCacheEntry() throws Exception { + getSessionFactory().evictEntity( Item.class.getName() ); + Statistics stats = getSessionFactory().getStatistics(); + stats.clear(); + SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( REGION_PREFIX + Item.class.getName() ); + Map cacheEntries = statistics.getEntries(); + assertThat( cacheEntries.size(), equalTo( 0 ) ); + } + + @Test + public void testStaleWritesLeaveCacheConsistent() { + Session s = getSessionFactory().openSession(); + Transaction txn = s.beginTransaction(); + VersionedItem item = new VersionedItem(); + item.setName( "steve" ); + item.setDescription( "steve's item" ); + s.save( item ); + txn.commit(); + s.close(); + + Long initialVersion = item.getVersion(); + + // manually revert the version property + item.setVersion( item.getVersion() - 1 ); + + try { + s = getSessionFactory().openSession(); + txn = s.beginTransaction(); + s.update( item ); + txn.commit(); + s.close(); + fail( "expected stale write to fail" ); + } + catch ( Throwable expected ) { + // expected behavior here + if ( txn != null ) { + try { + txn.rollback(); + } + catch ( Throwable ignore ) { + } + } + } + finally { + if ( s != null && s.isOpen() ) { + try { + s.close(); + } + catch ( Throwable ignore ) { + } + } + } + + // check the version value in the cache... + SecondLevelCacheStatistics slcs = getSessionFactory().getStatistics() + .getSecondLevelCacheStatistics( REGION_PREFIX + VersionedItem.class.getName() ); + assertThat( slcs, CoreMatchers.notNullValue() ); + final Map entries = slcs.getEntries(); + Object entry = entries.get( item.getId() ); + Long cachedVersionValue; + if ( entry instanceof SoftLock ) { + //FIXME don't know what to test here + //cachedVersionValue = new Long( ( (ReadWriteCache.Lock) entry).getUnlockTimestamp() ); + } + else { + cachedVersionValue = (Long) ( (Map) entry ).get( "_version" ); + assertThat( initialVersion, equalTo( cachedVersionValue ) ); + } + + + // cleanup + s = getSessionFactory().openSession(); + txn = s.beginTransaction(); + item = (VersionedItem) s.load( VersionedItem.class, item.getId() ); + s.delete( item ); + txn.commit(); + s.close(); + + } + + @Test + public void testGeneralUsage() { + EventManager mgr = new EventManager( getSessionFactory() ); + Statistics stats = getSessionFactory().getStatistics(); + + // create 3 persons Steve, Orion, Tim + Person stevePerson = new Person(); + stevePerson.setFirstname( "Steve" ); + stevePerson.setLastname( "Harris" ); + Long steveId = mgr.createAndStorePerson( stevePerson ); + mgr.addEmailToPerson( steveId, "steve@tc.com" ); + mgr.addEmailToPerson( steveId, "sharrif@tc.com" ); + mgr.addTalismanToPerson( steveId, "rabbit foot" ); + mgr.addTalismanToPerson( steveId, "john de conqueroo" ); + + PhoneNumber p1 = new PhoneNumber(); + p1.setNumberType( "Office" ); + p1.setPhone( 111111 ); + mgr.addPhoneNumberToPerson( steveId, p1 ); + + PhoneNumber p2 = new PhoneNumber(); + p2.setNumberType( "Home" ); + p2.setPhone( 222222 ); + mgr.addPhoneNumberToPerson( steveId, p2 ); + + Person orionPerson = new Person(); + orionPerson.setFirstname( "Orion" ); + orionPerson.setLastname( "Letizi" ); + Long orionId = mgr.createAndStorePerson( orionPerson ); + mgr.addEmailToPerson( orionId, "orion@tc.com" ); + mgr.addTalismanToPerson( orionId, "voodoo doll" ); + + Long timId = mgr.createAndStorePerson( "Tim", "Teck" ); + mgr.addEmailToPerson( timId, "teck@tc.com" ); + mgr.addTalismanToPerson( timId, "magic decoder ring" ); + + Long engMeetingId = mgr.createAndStoreEvent( "Eng Meeting", stevePerson, new Date() ); + mgr.addPersonToEvent( steveId, engMeetingId ); + mgr.addPersonToEvent( orionId, engMeetingId ); + mgr.addPersonToEvent( timId, engMeetingId ); + + Long docMeetingId = mgr.createAndStoreEvent( "Doc Meeting", orionPerson, new Date() ); + mgr.addPersonToEvent( steveId, docMeetingId ); + mgr.addPersonToEvent( orionId, docMeetingId ); + + for ( Event event : (List) mgr.listEvents() ) { + mgr.listEmailsOfEvent( event.getId() ); + } + + getSessionFactory().close(); + + QueryStatistics queryStats = stats.getQueryStatistics( "from Event" ); + assertThat( "Cache Miss Count", queryStats.getCacheMissCount(), equalTo( 1L ) ); + assertThat( "Cache Hit Count", queryStats.getCacheHitCount(), equalTo( 0L ) ); + assertThat( "Cache Put Count", queryStats.getCachePutCount(), equalTo( 1L ) ); + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionFactoryImpl.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionFactoryImpl.java index 81806f627f..e8a6ff44ac 100644 --- a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionFactoryImpl.java +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionFactoryImpl.java @@ -1,6 +1,6 @@ package org.hibernate.test.cache.ehcache; -import org.hibernate.cache.internal.EhCacheRegionFactory; +import org.hibernate.cache.ehcache.EhCacheRegionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; @@ -14,25 +14,31 @@ public class EhCacheRegionFactoryImpl extends EhCacheTest { @Override protected void configCache(final Configuration cfg) { - cfg.setProperty(Environment.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName()); + cfg.setProperty( Environment.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName() ); cfg.setProperty( Environment.CACHE_PROVIDER_CONFIG, "ehcache.xml" ); } @Override protected Map getMapFromCacheEntry(final Object entry) { final Map map; - if ("net.sf.ehcache.hibernate.strategy.AbstractReadWriteEhcacheAccessStrategy$Item".equals(entry.getClass().getName())) { + if ( "org.hibernate.cache.ehcache.strategy.AbstractReadWriteEhcacheAccessStrategy$Item".equals( + entry.getClass() + .getName() + ) ) { try { - Field field = entry.getClass().getDeclaredField("value"); - field.setAccessible(true); - map = (Map)field.get(entry); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); + Field field = entry.getClass().getDeclaredField( "value" ); + field.setAccessible( true ); + map = (Map) field.get( entry ); } - } else { - map = (Map)entry; + catch ( NoSuchFieldException e ) { + throw new RuntimeException( e ); + } + catch ( IllegalAccessException e ) { + throw new RuntimeException( e ); + } + } + else { + map = (Map) entry; } return map; } diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionTest.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionTest.java index 62588a4ed6..b20efd8b27 100644 --- a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionTest.java +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheRegionTest.java @@ -1,12 +1,12 @@ package org.hibernate.test.cache.ehcache; +import org.hibernate.cache.ehcache.EhCacheRegionFactory; +import org.hibernate.cache.ehcache.strategy.ItemValueExtractor; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import java.util.Map; -import net.sf.ehcache.hibernate.EhCacheRegionFactory; - /** * @author Alex Snaps */ @@ -20,12 +20,14 @@ public class EhCacheRegionTest extends EhCacheTest { @Override protected Map getMapFromCacheEntry(final Object entry) { final Map map; -// if ( entry instanceof Item ) { -// map = (Map) ( (Item) entry ).getValue(); -// } -// else { + if ( entry.getClass() + .getName() + .equals( "org.hibernate.cache.ehcache.strategy.AbstractReadWriteEhcacheAccessStrategy$Item" ) ) { + map = ItemValueExtractor.getValue( entry ); + } + else { map = (Map) entry; -// } + } return map; } } diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java index c0190c8b0c..86da43f344 100644 --- a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java @@ -24,6 +24,7 @@ package org.hibernate.test.cache.ehcache; import java.util.Map; + import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; @@ -67,7 +68,7 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); cfg.setProperty( Environment.USE_STRUCTURED_CACHE, "true" ); - configCache(cfg); + configCache( cfg ); cfg.setProperty( Environment.TRANSACTION_STRATEGY, JdbcTransactionFactory.class.getName() ); } @@ -78,9 +79,9 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { Session s = openSession(); Transaction t = s.beginTransaction(); Item i = new Item(); - i.setName("widget"); - i.setDescription("A really top-quality, full-featured widget."); - s.persist(i); + i.setName( "widget" ); + i.setDescription( "A really top-quality, full-featured widget." ); + s.persist( i ); t.commit(); s.close(); @@ -98,7 +99,7 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { assertEquals( slcs.getHitCount(), 1 ); assertEquals( slcs.getMissCount(), 0 ); - i.setDescription("A bog standard item"); + i.setDescription( "A bog standard item" ); t.commit(); s.close(); @@ -107,9 +108,9 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { Object entry = slcs.getEntries().get( i.getId() ); Map map; - map = getMapFromCacheEntry(entry); - assertTrue( map.get("description").equals("A bog standard item") ); - assertTrue( map.get("name").equals("widget") ); + map = getMapFromCacheEntry( entry ); + assertTrue( map.get( "description" ).equals( "A bog standard item" ) ); + assertTrue( map.get( "name" ).equals( "widget" ) ); // cleanup s = openSession(); @@ -127,11 +128,11 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { Statistics stats = sessionFactory().getStatistics(); stats.clear(); SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( Item.class.getName() ); - Map cacheEntries = statistics.getEntries(); + Map cacheEntries = statistics.getEntries(); assertEquals( 0, cacheEntries.size() ); } - @SuppressWarnings( {"UnnecessaryBoxing", "UnnecessaryUnboxing", "UnusedAssignment"}) + @SuppressWarnings( { "UnnecessaryBoxing", "UnnecessaryUnboxing", "UnusedAssignment" }) @Test public void testStaleWritesLeaveCacheConsistent() { Session s = openSession(); @@ -156,13 +157,13 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { s.close(); fail( "expected stale write to fail" ); } - catch( Throwable expected ) { + catch ( Throwable expected ) { // expected behavior here if ( txn != null ) { try { txn.rollback(); } - catch( Throwable ignore ) { + catch ( Throwable ignore ) { } } } @@ -171,7 +172,7 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { try { s.close(); } - catch( Throwable ignore ) { + catch ( Throwable ignore ) { } } } @@ -186,10 +187,13 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { // //FIXME don't know what to test here // cachedVersionValue = Long.valueOf( ((ReadWriteCache.Lock) entry).getUnlockTimestamp() ); // } else - if(entry.getClass().getName().equals("net.sf.ehcache.hibernate.strategy.AbstractReadWriteEhcacheAccessStrategy$Lock")) { + if ( entry.getClass() + .getName() + .equals( "org.hibernate.cache.ehcache.strategy.AbstractReadWriteEhcacheAccessStrategy$Lock" ) ) { //FIXME don't know what to test here - } else { - cachedVersionValue = ( Long ) getMapFromCacheEntry(entry).get( "_version" ); + } + else { + cachedVersionValue = (Long) getMapFromCacheEntry( entry ).get( "_version" ); assertEquals( initialVersion.longValue(), cachedVersionValue.longValue() ); } @@ -197,7 +201,7 @@ public abstract class EhCacheTest extends BaseCoreFunctionalTestCase { // cleanup s = openSession(); txn = s.beginTransaction(); - item = ( VersionedItem ) s.load( VersionedItem.class, item.getId() ); + item = (VersionedItem) s.load( VersionedItem.class, item.getId() ); s.delete( item ); txn.commit(); s.close(); diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/Item.java b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/Item.java index 65155646f2..8331e7528a 100755 --- a/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/Item.java +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/Item.java @@ -31,7 +31,7 @@ public class Item { private Long id; private String name; private String description; - + public String getDescription() { return description; } diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Account.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Account.java new file mode 100644 index 0000000000..b47900c0a9 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Account.java @@ -0,0 +1,47 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hibernate.test.domain; + +public class Account { + + private Long id; + private Person person; + + public Account() { + // + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String toString() { + return super.toString(); + } +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Event.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Event.java new file mode 100644 index 0000000000..3be93f424d --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Event.java @@ -0,0 +1,83 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +public class Event { + private Long id; + + private String title; + private Date date; + private Set participants = new HashSet(); + private Person organizer; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setOrganizer(Person organizer) { + this.organizer = organizer; + } + + public Person getOrganizer() { + return organizer; + } + + public Set getParticipants() { + return participants; + } + + public void setParticipants(Set participants) { + this.participants = participants; + } + + public void addParticipant(Person person) { + participants.add(person); + person.getEvents().add(this); + } + + public void removeParticipant(Person person) { + participants.remove(person); + person.getEvents().remove(this); + } + + public String toString() { + return getTitle() + ": " + getDate(); + } +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/EventManager.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/EventManager.java new file mode 100644 index 0000000000..d387dfbb07 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/EventManager.java @@ -0,0 +1,249 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +public class EventManager { + + private final SessionFactory sessionFactory; + + public EventManager(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + + public List listEmailsOfEvent(Long eventId) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + List emailList = new ArrayList(); + Event event = (Event)session.load(Event.class, eventId); + for (Iterator it = event.getParticipants().iterator(); it.hasNext(); ) { + Person person = (Person)it.next(); + emailList.addAll(person.getEmailAddresses()); + } + + session.getTransaction().commit(); + return emailList; + } + + public Long createAndStoreEvent(String title, Person organizer, Date theDate) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Event theEvent = new Event(); + theEvent.setTitle(title); + theEvent.setDate(theDate); + theEvent.setOrganizer(organizer); + + Long eventId = (Long)session.save(theEvent); + + session.getTransaction().commit(); + return eventId; + } + + public Long createAndStorePerson(String firstName, String lastName) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Person person = new Person(); + person.setFirstname(firstName); + person.setLastname(lastName); + + Long personId = (Long)session.save(person); + + session.getTransaction().commit(); + return personId; + } + + public Long createAndStorePerson(Person person) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Long personId = (Long)session.save(person); + + session.getTransaction().commit(); + return personId; + } + + public List listEvents() { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List result = session.createQuery("from Event").setCacheable(true).list(); + + session.getTransaction().commit(); + + return result; + } + + /** + * Call setEntity() on a cacheable query - see FORGE-265 + */ + public List listEventsOfOrganizer(Person organizer) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Query query = session.createQuery("from Event ev where ev.organizer = :organizer"); + + query.setCacheable(true); + query.setEntity("organizer", organizer); + List result = query.list(); + + session.getTransaction().commit(); + + return result; + } + + /** + * Use a Criteria query - see FORGE-247 + */ + public List listEventsWithCriteria() { + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List result = session.createCriteria(Event.class) + .setCacheable(true) + .list(); + + session.getTransaction().commit(); + + return result; + } + + public void addPersonToEvent(Long personId, Long eventId) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + Event anEvent = (Event)session.load(Event.class, eventId); + + aPerson.getEvents().add(anEvent); + + session.getTransaction().commit(); + } + + public Long addPersonToAccount(Long personId, Account account) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + account.setPerson(aPerson); + + Long accountId = (Long)session.save(account); + + session.getTransaction().commit(); + return accountId; + } + + public Account getAccount(Long accountId) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Account account = (Account)session.load(Account.class, accountId); + + session.getTransaction().commit(); + return account; + } + + public void addEmailToPerson(Long personId, String emailAddress) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + + // The getEmailAddresses() might trigger a lazy load of the collection + aPerson.getEmailAddresses().add(emailAddress); + + session.getTransaction().commit(); + } + + public void addPhoneNumberToPerson(Long personId, PhoneNumber pN) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + pN.setPersonId(personId.longValue()); + aPerson.getPhoneNumbers().add(pN); + + session.getTransaction().commit(); + } + + public void addTalismanToPerson(Long personId, String talisman) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + aPerson.addTalisman(talisman); + + session.getTransaction().commit(); + } + + public Long createHolidayCalendar() { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + // delete all existing calendars + List calendars = session.createQuery("from HolidayCalendar").setCacheable(true).list(); + for (ListIterator li = calendars.listIterator(); li.hasNext(); ) { + session.delete(li.next()); + } + + HolidayCalendar calendar = new HolidayCalendar(); + calendar.init(); + + Long calendarId = (Long)session.save(calendar); + + session.getTransaction().commit(); + return calendarId; + } + + public HolidayCalendar getHolidayCalendar() { + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List calendars = session.createQuery("from HolidayCalendar").setCacheable(true).list(); + + session.getTransaction().commit(); + + return calendars.isEmpty() ? null : (HolidayCalendar)calendars.get(0); + } +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java new file mode 100644 index 0000000000..fabc90afc5 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java @@ -0,0 +1,78 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class HolidayCalendar { + + private static final Logger LOG = LoggerFactory.getLogger(HolidayCalendar.class); + + private Long id; + // Date -> String + private Map holidays = new HashMap(); + + public HolidayCalendar init() { + DateFormat df = new SimpleDateFormat("yyyy.MM.dd"); + try { + holidays.clear(); + holidays.put(df.parse("2009.01.01"), "New Year's Day"); + holidays.put(df.parse("2009.02.14"), "Valentine's Day"); + holidays.put(df.parse("2009.11.11"), "Armistice Day"); + } catch (ParseException e) { + LOG.info("Error parsing date string"); + throw new RuntimeException(e); + } + return this; + } + + public Map getHolidays() { + return holidays; + } + + protected void setHolidays(Map holidays) { + this.holidays = holidays; + } + + public void addHoliday(Date d, String name) { + holidays.put(d, name); + } + + public String getHoliday(Date d) { + return (String)holidays.get(d); + } + + public boolean isHoliday(Date d) { + return holidays.containsKey(d); + } + + protected Long getId() { + return id; + } + + protected void setId(Long id) { + this.id = id; + } +} + diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Item.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Item.java new file mode 100644 index 0000000000..b5d5935a8a --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Item.java @@ -0,0 +1,46 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +public class Item { + private Long id; + private String name; + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Person.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Person.java new file mode 100644 index 0000000000..2b14e92297 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/Person.java @@ -0,0 +1,120 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hibernate.test.domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Person { + + private Long id; + private int age; + private String firstname; + private String lastname; + private List events = new ArrayList(); // list semantics, e.g., indexed + private Set emailAddresses = new HashSet(); + private Set phoneNumbers = new HashSet(); + private List talismans = new ArrayList(); // a Bag of good-luck charms. + + public Person() { + // + } + + public List getEvents() { + return events; + } + + protected void setEvents(List events) { + this.events = events; + } + + public void addToEvent(Event event) { + this.getEvents().add(event); + event.getParticipants().add(this); + } + + public void removeFromEvent(Event event) { + this.getEvents().remove(event); + event.getParticipants().remove(this); + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Set getEmailAddresses() { + return emailAddresses; + } + + public void setEmailAddresses(Set emailAddresses) { + this.emailAddresses = emailAddresses; + } + + public Set getPhoneNumbers() { + return phoneNumbers; + } + + public void setPhoneNumbers(Set phoneNumbers) { + this.phoneNumbers = phoneNumbers; + } + + public void addTalisman(String name) { + talismans.add(name); + } + + public List getTalismans() { + return talismans; + } + + public void setTalismans(List talismans) { + this.talismans = talismans; + } + + public String toString() { + return getFirstname() + " " + getLastname(); + } +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java new file mode 100644 index 0000000000..5509ed2f7f --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java @@ -0,0 +1,87 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hibernate.test.domain; + +import java.io.Serializable; + +/** + * PhoneNumber + */ +public class PhoneNumber implements Serializable { + private long personId = 0; + private String numberType = "home"; + private long phone = 0; + + public long getPersonId() { + return personId; + } + + public void setPersonId(long personId) { + this.personId = personId; + } + + public String getNumberType() { + return numberType; + } + + public void setNumberType(String numberType) { + this.numberType = numberType; + } + + public long getPhone() { + return phone; + } + + public void setPhone(long phone) { + this.phone = phone; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((numberType == null) ? 0 : numberType.hashCode()); + result = prime * result + (int)(personId ^ (personId >>> 32)); + result = prime * result + (int)(phone ^ (phone >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final PhoneNumber other = (PhoneNumber)obj; + if (numberType == null) { + if (other.numberType != null) + return false; + } else if (!numberType.equals(other.numberType)) + return false; + if (personId != other.personId) + return false; + if (phone != other.phone) + return false; + return true; + } + + public String toString() { + return numberType + ":" + phone; + } + +} diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/UuidItem.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/UuidItem.java new file mode 100644 index 0000000000..0a861e2984 --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/UuidItem.java @@ -0,0 +1,46 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +public class UuidItem { + private String id; + private String name; + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/hibernate-ehcache/src/test/java/org/hibernate/test/domain/VersionedItem.java b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/VersionedItem.java new file mode 100644 index 0000000000..e1626dbeef --- /dev/null +++ b/hibernate-ehcache/src/test/java/org/hibernate/test/domain/VersionedItem.java @@ -0,0 +1,28 @@ +/** + * Copyright 2003-2010 Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.test.domain; + +public class VersionedItem extends Item { + private Long version; + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +} diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/Account.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Account.hbm.xml new file mode 100644 index 0000000000..6209323e27 --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Account.hbm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/Event.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Event.hbm.xml new file mode 100644 index 0000000000..da3db818fd --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Event.hbm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml new file mode 100644 index 0000000000..47617b44cf --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/Item.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Item.hbm.xml new file mode 100644 index 0000000000..6591e4c4e7 --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Item.hbm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/Person.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Person.hbm.xml new file mode 100644 index 0000000000..af867e8ac2 --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/Person.hbm.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml b/hibernate-ehcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml new file mode 100644 index 0000000000..6177bbe287 --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/ehcache.xml b/hibernate-ehcache/src/test/resources/hibernate-config/ehcache.xml new file mode 100644 index 0000000000..d10f41e700 --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/ehcache.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/hibernate-ehcache/src/test/resources/hibernate-config/hibernate.cfg.xml b/hibernate-ehcache/src/test/resources/hibernate-config/hibernate.cfg.xml new file mode 100644 index 0000000000..5136ffff3a --- /dev/null +++ b/hibernate-ehcache/src/test/resources/hibernate-config/hibernate.cfg.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + + + 1 + + + + org.hibernate.dialect.H2Dialect + + + + thread + + true + true + true + org.hibernate.cache.EhCacheRegionFactory + /hibernate-config/ehcache.xml + + true + + + + + + + + + + + +