diff --git a/build.gradle b/build.gradle index f70b755a316..f77c116a0ff 100644 --- a/build.gradle +++ b/build.gradle @@ -217,6 +217,7 @@ project('spring-context') { exclude group: 'org.slf4j', module: 'slf4j-api' } compile("joda-time:joda-time:1.6", optional) + compile("javax.cache:cache-api:0.5", optional) compile("net.sf.ehcache:ehcache-core:2.0.0", optional) compile("org.slf4j:slf4j-api:1.6.1", optional) compile("org.codehaus.jsr166-mirror:jsr166:1.7.0", provided) diff --git a/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCache.java b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCache.java new file mode 100644 index 00000000000..0e66d5ebe48 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCache.java @@ -0,0 +1,131 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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.springframework.cache.jcache; + +import java.io.Serializable; + +import javax.cache.Status; + +import org.springframework.cache.Cache; +import org.springframework.cache.support.SimpleValueWrapper; +import org.springframework.util.Assert; + +/** + * {@link org.springframework.cache.Cache} implementation on top of a + * {@link javax.cache.Cache} instance. + * + * @author Juergen Hoeller + * @since 3.2 + */ +public class JCacheCache implements Cache { + + private static final Object NULL_HOLDER = new NullHolder(); + + @SuppressWarnings("rawtypes") + private final javax.cache.Cache cache; + + private final boolean allowNullValues; + + + /** + * Create an {@link org.springframework.cache.jcache.JCacheCache} instance. + * @param jcache backing JCache Cache instance + */ + public JCacheCache(javax.cache.Cache jcache) { + this(jcache, true); + } + + /** + * Create an {@link org.springframework.cache.jcache.JCacheCache} instance. + * @param jcache backing JCache Cache instance + * @param allowNullValues whether to accept and convert null values for this cache + */ + public JCacheCache(javax.cache.Cache jcache, boolean allowNullValues) { + Assert.notNull(jcache, "Cache must not be null"); + Status status = jcache.getStatus(); + Assert.isTrue(Status.STARTED.equals(status), + "A 'started' cache is required - current cache is " + status.toString()); + this.cache = jcache; + this.allowNullValues = allowNullValues; + } + + + public String getName() { + return this.cache.getName(); + } + + public javax.cache.Cache getNativeCache() { + return this.cache; + } + + public boolean isAllowNullValues() { + return this.allowNullValues; + } + + @SuppressWarnings("unchecked") + public ValueWrapper get(Object key) { + Object value = this.cache.get(key); + return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); + } + + @SuppressWarnings("unchecked") + public void put(Object key, Object value) { + this.cache.put(key, toStoreValue(value)); + } + + @SuppressWarnings("unchecked") + public void evict(Object key) { + this.cache.remove(key); + } + + public void clear() { + this.cache.removeAll(); + } + + + /** + * Convert the given value from the internal store to a user value + * returned from the get method (adapting null). + * @param storeValue the store value + * @return the value to return to the user + */ + protected Object fromStoreValue(Object storeValue) { + if (this.allowNullValues && storeValue == NULL_HOLDER) { + return null; + } + return storeValue; + } + + /** + * Convert the given user value, as passed into the put method, + * to a value in the internal store (adapting null). + * @param userValue the given user value + * @return the value to store + */ + protected Object toStoreValue(Object userValue) { + if (this.allowNullValues && userValue == null) { + return NULL_HOLDER; + } + return userValue; + } + + + @SuppressWarnings("serial") + private static class NullHolder implements Serializable { + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java new file mode 100644 index 00000000000..fe7212c145d --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java @@ -0,0 +1,104 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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.springframework.cache.jcache; + +import java.util.Collection; +import java.util.LinkedHashSet; + +import javax.cache.Status; + +import org.springframework.cache.Cache; +import org.springframework.cache.support.AbstractCacheManager; +import org.springframework.util.Assert; + +/** + * {@link org.springframework.cache.CacheManager} implementation + * backed by a JCache {@link javax.cache.CacheManager}. + * + * @author Juergen Hoeller + * @since 3.2 + */ +public class JCacheCacheManager extends AbstractCacheManager { + + private javax.cache.CacheManager cacheManager; + + private boolean allowNullValues = true; + + + /** + * Set the backing JCache {@link javax.cache.CacheManager}. + */ + public void setCacheManager(javax.cache.CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + /** + * Return the backing JCache {@link javax.cache.CacheManager}. + */ + public javax.cache.CacheManager getCacheManager() { + return this.cacheManager; + } + + /** + * Specify whether to accept and convert null values for all caches + * in this cache manager. + *

Default is "true", despite JSR-107 itself not supporting null values. + * An internal holder object will be used to store user-level null values. + */ + public void setAllowNullValues(boolean allowNullValues) { + this.allowNullValues = allowNullValues; + } + + /** + * Return whether this cache manager accepts and converts null values + * for all of its caches. + */ + public boolean isAllowNullValues() { + return this.allowNullValues; + } + + + @Override + protected Collection loadCaches() { + Assert.notNull(this.cacheManager, "A backing CacheManager is required"); + Status status = this.cacheManager.getStatus(); + Assert.isTrue(Status.STARTED.equals(status), + "A 'started' JCache CacheManager is required - current cache is " + status.toString()); + + Collection caches = new LinkedHashSet(); + for (javax.cache.Cache jcache : this.cacheManager.getCaches()) { + caches.add(new JCacheCache(jcache, this.allowNullValues)); + } + return caches; + } + + @Override + public Cache getCache(String name) { + Cache cache = super.getCache(name); + if (cache == null) { + // check the JCache cache again + // (in case the cache was added at runtime) + javax.cache.Cache jcache = this.cacheManager.getCache(name); + if (jcache != null) { + cache = new JCacheCache(jcache, this.allowNullValues); + addCache(cache); + } + } + return cache; + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/jcache/JCacheManagerFactoryBean.java b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheManagerFactoryBean.java new file mode 100644 index 00000000000..af1e1428bb6 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/jcache/JCacheManagerFactoryBean.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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.springframework.cache.jcache; + +import javax.cache.CacheManager; +import javax.cache.Caching; + +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * {@link FactoryBean} for a JCache {@link javax.cache.CacheManager}, + * obtaining a pre-defined CacheManager by name through the standard + * JCache {@link javax.cache.Caching} class. + * + * @author Juergen Hoeller + * @since 3.2 + * @see javax.cache.Caching#getCacheManager() + * @see javax.cache.Caching#getCacheManager(String) + */ +public class JCacheManagerFactoryBean implements FactoryBean, BeanClassLoaderAware, InitializingBean { + + private String cacheManagerName = Caching.DEFAULT_CACHE_MANAGER_NAME; + + private ClassLoader beanClassLoader; + + private CacheManager cacheManager; + + + /** + * Specify the name of the desired CacheManager. + * Default is JCache's default. + * @see Caching#DEFAULT_CACHE_MANAGER_NAME + */ + public void setCacheManagerName(String cacheManagerName) { + this.cacheManagerName = cacheManagerName; + } + + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } + + public void afterPropertiesSet() { + this.cacheManager = (this.beanClassLoader != null ? + Caching.getCacheManager(this.beanClassLoader, this.cacheManagerName) : + Caching.getCacheManager(this.cacheManagerName)); + } + + + public CacheManager getObject() { + return this.cacheManager; + } + + public Class getObjectType() { + return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class); + } + + public boolean isSingleton() { + return true; + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/jcache/package-info.java b/spring-context/src/main/java/org/springframework/cache/jcache/package-info.java new file mode 100644 index 00000000000..59393e85a1c --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/jcache/package-info.java @@ -0,0 +1,7 @@ +/** + * Implementation package for JSR-107 (javax.cache aka "JCache") based caches. + * Provides a {@link org.springframework.cache.CacheManager CacheManager} + * and {@link org.springframework.cache.Cache Cache} implementation for + * use in a Spring context, using a JSR-107 compliant cache provider. + */ +package org.springframework.cache.jcache;