You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/02/10 14:32:57 UTC

cayenne git commit: CAY-2223 JCacheQueryCache - a query cache provider to plug in JCache implementers

Repository: cayenne
Updated Branches:
  refs/heads/master 00551aa28 -> 4365a8f94


CAY-2223 JCacheQueryCache - a query cache provider to plug in JCache implementers


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/4365a8f9
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/4365a8f9
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/4365a8f9

Branch: refs/heads/master
Commit: 4365a8f948ab1d74bd547153b203bc1d5b9dadbb
Parents: 00551aa
Author: Nikita Timofeev <st...@gmail.com>
Authored: Fri Feb 10 17:32:46 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Fri Feb 10 17:32:46 2017 +0300

----------------------------------------------------------------------
 assembly/pom.xml                                |   6 +
 .../resources/assemblies/assembly-generic.xml   |   1 +
 .../main/resources/assemblies/assembly-mac.xml  |   1 +
 .../resources/assemblies/assembly-windows.xml   |   1 +
 cayenne-jcache/pom.xml                          | 147 ++++++++++++++++
 .../jcache/JCacheConfigurationFactory.java      |  35 ++++
 .../apache/cayenne/jcache/JCacheConstants.java  |  33 ++++
 .../JCacheDefaultConfigurationFactory.java      |  56 ++++++
 .../cayenne/jcache/JCacheEntryLoader.java       |  54 ++++++
 .../cayenne/jcache/JCacheManagerProvider.java   |  76 ++++++++
 .../org/apache/cayenne/jcache/JCacheModule.java |  49 ++++++
 .../cayenne/jcache/JCacheModuleProvider.java    |  47 +++++
 .../apache/cayenne/jcache/JCacheQueryCache.java | 176 +++++++++++++++++++
 .../org.apache.cayenne.di.spi.ModuleProvider    |   1 +
 .../cayenne/jcache/CayenneJCacheModuleIT.java   |  85 +++++++++
 .../jcache/CayenneJCacheModuleProviderTest.java |  31 ++++
 .../jcache/unit/CacheServerRuntimeProvider.java |  51 ++++++
 .../apache/cayenne/jcache/unit/JCacheCase.java  |  59 +++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   1 +
 pom.xml                                         |   1 +
 20 files changed, 911 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 74d643f..4bfbaa9 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -97,6 +97,12 @@
          </dependency>
 
 		<dependency>
+			<groupId>org.apache.cayenne</groupId>
+			<artifactId>cayenne-jcache</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
 			<groupId>org.apache.cayenne.modeler</groupId>
 			<artifactId>cayenne-modeler</artifactId>
 			<version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/assembly/src/main/resources/assemblies/assembly-generic.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-generic.xml b/assembly/src/main/resources/assemblies/assembly-generic.xml
index e297a8a..679a2d2 100644
--- a/assembly/src/main/resources/assemblies/assembly-generic.xml
+++ b/assembly/src/main/resources/assemblies/assembly-generic.xml
@@ -85,6 +85,7 @@
 				<include>org.apache.cayenne:cayenne-ant</include>
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
+				<include>org.apache.cayenne:cayenne-jcache</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/assembly/src/main/resources/assemblies/assembly-mac.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-mac.xml b/assembly/src/main/resources/assemblies/assembly-mac.xml
index 8d94bb6..d65e9ba 100644
--- a/assembly/src/main/resources/assemblies/assembly-mac.xml
+++ b/assembly/src/main/resources/assemblies/assembly-mac.xml
@@ -85,6 +85,7 @@
 				<include>org.apache.cayenne:cayenne-ant</include>
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
+				<include>org.apache.cayenne:cayenne-jcache</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/assembly/src/main/resources/assemblies/assembly-windows.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-windows.xml b/assembly/src/main/resources/assemblies/assembly-windows.xml
index 2dd3781..660ba58 100644
--- a/assembly/src/main/resources/assemblies/assembly-windows.xml
+++ b/assembly/src/main/resources/assemblies/assembly-windows.xml
@@ -85,6 +85,7 @@
 				<include>org.apache.cayenne:cayenne-ant</include>
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
+				<include>org.apache.cayenne:cayenne-jcache</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-jcache/pom.xml b/cayenne-jcache/pom.xml
new file mode 100644
index 0000000..320d4e0
--- /dev/null
+++ b/cayenne-jcache/pom.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~   Licensed to the Apache Software Foundation (ASF) under one
+  ~  or more contributor license agreements.  See the NOTICE file
+  ~  distributed with this work for additional information
+  ~  regarding copyright ownership.  The ASF licenses this file
+  ~  to you 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>cayenne-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M5-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cayenne-jcache</artifactId>
+    <name>cayenne-jcache: Cayenne JCache Integration</name>
+    <packaging>jar</packaging>
+
+    <properties>
+        <ehcache-version>3.2.0</ehcache-version>
+        <jcache-version>1.0.0</jcache-version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.ehcache</groupId>
+                <artifactId>ehcache</artifactId>
+                <version>${ehcache-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.cache</groupId>
+                <artifactId>cache-api</artifactId>
+                <version>${jcache-version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+        <!-- Compile dependencies -->
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.cache</groupId>
+            <artifactId>cache-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cayenne.build-tools</groupId>
+            <artifactId>cayenne-test-utilities</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- This ensures LICENSE and NOTICE inclusion in all jars -->
+            <plugin>
+                <artifactId>maven-remote-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>process</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+            <plugin>
+				<artifactId>maven-checkstyle-plugin</artifactId>
+				<!--<configuration>
+				    <suppressionsLocation>${project.basedir}/cayenne-checkstyle-suppression.xml</suppressionsLocation>
+                </configuration> -->
+			</plugin>
+			<plugin>
+				<artifactId>maven-pmd-plugin</artifactId>
+			</plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConfigurationFactory.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConfigurationFactory.java
new file mode 100644
index 0000000..e13c33a
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConfigurationFactory.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.util.List;
+import javax.cache.configuration.Configuration;
+
+/**
+ * JCache configuration factory
+ *
+ * @see JCacheDefaultConfigurationFactory default implementation
+ * @since 4.0
+ */
+public interface JCacheConfigurationFactory {
+
+    Configuration<String, List> create(String cacheGroup);
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConstants.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConstants.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConstants.java
new file mode 100644
index 0000000..63a2ca6
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheConstants.java
@@ -0,0 +1,33 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+/**
+ * @since 4.0
+ */
+public interface JCacheConstants {
+
+    /**
+     * Default JCache cache name. This will be the cache used for queries with no explicit cache groups.
+     */
+    String DEFAULT_CACHE_NAME = "cayenne.default.cache";
+
+    String JCACHE_PROVIDER_CONFIG = "cayenne.jcache.provider_config";
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheDefaultConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheDefaultConfigurationFactory.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheDefaultConfigurationFactory.java
new file mode 100644
index 0000000..fa1d611
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheDefaultConfigurationFactory.java
@@ -0,0 +1,56 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.util.List;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+
+/**
+ * <p>
+ *     Default JCache configuration factory.
+ * </p>
+ * <p>
+ *     Parameters:
+ *     <ul>
+ *         <li>store-by-reference</li>
+ *         <li>expire in 10 minutes</li>
+ *     </ul>
+ * </p>
+ *
+ * @since 4.0
+ */
+public class JCacheDefaultConfigurationFactory implements JCacheConfigurationFactory {
+
+    private final Configuration<String, List> configuration = new MutableConfiguration<String, List>()
+            .setTypes(String.class, List.class)
+            .setStoreByValue(false)
+            .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.TEN_MINUTES));
+
+    /**
+     * @param cacheGroup is unused by default configuration factory
+     * @return cache configuration
+     */
+    public Configuration<String, List> create(String cacheGroup) {
+        return configuration;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheEntryLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheEntryLoader.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheEntryLoader.java
new file mode 100644
index 0000000..1dbf3c1
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheEntryLoader.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.util.List;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.cache.QueryCacheEntryFactory;
+
+/**
+ * @since 4.0
+ */
+public class JCacheEntryLoader implements EntryProcessor<String, List, List> {
+
+    private QueryCacheEntryFactory entryFactory;
+
+    public JCacheEntryLoader(QueryCacheEntryFactory entryFactory) {
+        this.entryFactory = entryFactory;
+    }
+
+    @Override
+    public List process(MutableEntry<String, List> entry, Object... arguments) throws EntryProcessorException {
+        if (!entry.exists()) {
+            List result = (List)entryFactory.createObject();
+            // sanity checking value
+            if (result == null) {
+                throw new CayenneRuntimeException("Null object created: " + entry.getKey());
+            }
+            entry.setValue(result);
+        }
+
+        return entry.getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheManagerProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheManagerProvider.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheManagerProvider.java
new file mode 100644
index 0000000..cb69a7b
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheManagerProvider.java
@@ -0,0 +1,76 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import javax.cache.CacheException;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.spi.CachingProvider;
+
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+
+/**
+ * @since 4.0
+ */
+public class JCacheManagerProvider implements Provider<CacheManager> {
+
+    @Inject
+    private RuntimeProperties properties;
+
+    @Override
+    public CacheManager get() throws DIRuntimeException {
+        CachingProvider provider;
+        try {
+            provider = Caching.getCachingProvider();
+        } catch (CacheException e) {
+            throw new RuntimeException("'cayenne-jcache' doesn't bundle any JCache providers. " +
+                    "You must place a JCache 1.0 provider on classpath explicitly.", e);
+        }
+
+        CacheManager manager;
+        URI jcacheConfig = getConfig();
+
+        if(jcacheConfig == null) {
+            manager = provider.getCacheManager();
+        } else {
+            manager = provider.getCacheManager(jcacheConfig, null);
+        }
+
+        return manager;
+    }
+
+    private URI getConfig() {
+        String config = properties.get(JCacheConstants.JCACHE_PROVIDER_CONFIG);
+        if(config == null) {
+            return null;
+        } else {
+            try {
+                return new URI(config);
+            } catch (URISyntaxException ex) {
+                throw new RuntimeException("Wrong value for JCache provider config property", ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModule.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModule.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModule.java
new file mode 100644
index 0000000..081f56b
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModule.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import javax.cache.CacheManager;
+
+import org.apache.cayenne.cache.QueryCache;
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * <p>
+ *     JCache Module
+ * </p>
+ *
+ * @since 4.0
+ */
+public class JCacheModule implements Module {
+
+    public static void contributeJCacheProviderConfig(Binder binder, String providerConfigURI) {
+        ServerModule.contributeProperties(binder).put(JCacheConstants.JCACHE_PROVIDER_CONFIG, providerConfigURI);
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        binder.bind(CacheManager.class).toProvider(JCacheManagerProvider.class);
+        binder.bind(JCacheConfigurationFactory.class).to(JCacheDefaultConfigurationFactory.class);
+        binder.bind(QueryCache.class).to(JCacheQueryCache.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModuleProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModuleProvider.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModuleProvider.java
new file mode 100644
index 0000000..34f9cca
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheModuleProvider.java
@@ -0,0 +1,47 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.ModuleProvider;
+
+/**
+ * @since 4.0
+ */
+public class JCacheModuleProvider implements ModuleProvider {
+
+    @Override
+    public Module module() {
+        return new JCacheModule();
+    }
+
+    @Override
+    public Class<? extends Module> moduleType() {
+        return JCacheModule.class;
+    }
+
+    @Override
+    public Collection<Class<? extends Module>> overrides() {
+        return Collections.emptyList();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheQueryCache.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheQueryCache.java b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheQueryCache.java
new file mode 100644
index 0000000..cc8c91f
--- /dev/null
+++ b/cayenne-jcache/src/main/java/org/apache/cayenne/jcache/JCacheQueryCache.java
@@ -0,0 +1,176 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheManager;
+
+import org.apache.cayenne.cache.QueryCache;
+import org.apache.cayenne.cache.QueryCacheEntryFactory;
+import org.apache.cayenne.di.BeforeScopeEnd;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @since 4.0
+ */
+public class JCacheQueryCache implements QueryCache {
+
+    private static final Log LOGGER = LogFactory.getLog(JCacheQueryCache.class);
+
+    @Inject
+    private CacheManager cacheManager;
+
+    @Inject
+    private JCacheConfigurationFactory configurationFactory;
+
+    private ConcurrentMap<String, Object> seenCacheNames = new ConcurrentHashMap<>();
+
+    @Override
+    public List get(QueryMetadata metadata) {
+        String key = Objects.requireNonNull(metadata.getCacheKey());
+        Cache<String, List> cache = createIfAbsent(metadata);
+
+        return cache.get(key);
+    }
+
+    @Override
+    public List get(QueryMetadata metadata, QueryCacheEntryFactory factory) {
+        String key = Objects.requireNonNull(metadata.getCacheKey());
+        Cache<String, List> cache = createIfAbsent(metadata);
+
+        List<?> result = cache.get(key);
+        return result != null
+                ? result
+                : cache.invoke(key, new JCacheEntryLoader(factory));
+    }
+
+    @Override
+    public void put(QueryMetadata metadata, List results) {
+        String key = Objects.requireNonNull(metadata.getCacheKey());
+        Cache<String, List> cache = createIfAbsent(metadata);
+
+        cache.put(key, results);
+    }
+
+    @Override
+    public void remove(String key) {
+        if (key != null) {
+            for (String cache : cacheManager.getCacheNames()) {
+                getCache(cache).remove(key);
+            }
+        }
+    }
+
+    @Override
+    public void removeGroup(String groupKey) {
+        Cache<String, List> cache = getCache(groupKey);
+        if (cache != null) {
+            cache.clear();
+        }
+    }
+
+    @Override
+    public void clear() {
+        for(String name : seenCacheNames.keySet()) {
+            getCache(name).clear();
+        }
+    }
+
+    /**
+     * Returns -1 to indicate that we can't calculate the size. JCache and EhCache can potentially have a complex topology
+     * that can not be meaningfully described by a single int. Use other means (like provider-specific JMX) to monitor cache.
+     *
+     * @deprecated since 4.0
+     * @return -1
+     */
+    @Override
+    @Deprecated
+    public int size() {
+        return -1;
+    }
+
+    protected Cache<String, List> createIfAbsent(QueryMetadata metadata) {
+        return createIfAbsent(cacheName(metadata));
+    }
+
+    protected Cache<String, List> createIfAbsent(String cacheName) {
+
+        Cache<String, List> cache = getCache(cacheName);
+        if (cache == null) {
+
+            try {
+                cache = cacheManager.createCache(cacheName, configurationFactory.create(cacheName));
+            } catch (CacheException e) {
+                // someone else just created this cache?
+                cache = getCache(cacheName);
+                if (cache == null) {
+                    // giving up... the error was about something else...
+                    throw e;
+                }
+            }
+
+            seenCacheNames.put(cacheName, 1);
+        }
+
+        return cache;
+    }
+
+    protected Cache<String, List> getCache(String name) {
+        return cacheManager.getCache(name, String.class, List.class);
+    }
+
+    protected String cacheName(QueryMetadata metadata) {
+
+        String[] cacheGroups = metadata.getCacheGroups();
+
+        if (cacheGroups != null && cacheGroups.length > 0) {
+
+            if (cacheGroups.length > 1) {
+                if (LOGGER.isWarnEnabled()) {
+                    List<String> ignored = Arrays.asList(cacheGroups).subList(1, cacheGroups.length);
+                    LOGGER.warn("multiple cache groups per key '" + metadata.getCacheKey() + "', using the first one: "
+                            + cacheGroups[0] + ". Ignoring others: " + ignored);
+                }
+            }
+
+            return cacheGroups[0];
+        }
+
+        // no explicit cache groups
+        return JCacheConstants.DEFAULT_CACHE_NAME;
+    }
+
+    /**
+     * Shuts down CacheManager
+     */
+    @BeforeScopeEnd
+    public void shutdown() {
+        cacheManager.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider b/cayenne-jcache/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
new file mode 100644
index 0000000..2fff139
--- /dev/null
+++ b/cayenne-jcache/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
@@ -0,0 +1 @@
+org.apache.cayenne.jcache.JCacheModuleProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleIT.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleIT.java b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleIT.java
new file mode 100644
index 0000000..156625a
--- /dev/null
+++ b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleIT.java
@@ -0,0 +1,85 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.cache.NestedQueryCache;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.jcache.unit.JCacheCase;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class CayenneJCacheModuleIT extends JCacheCase {
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Inject
+    ObjectContext context;
+
+    @Inject
+    ServerRuntime runtime;
+
+    private TableHelper tArtist;
+
+    @Before
+    public void setUpTableHelper() throws Exception {
+        tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
+        tArtist.deleteAll();
+    }
+
+
+    @Test
+    public void testCachedQueries() throws Exception {
+        // make sure that we have JCacheQueryCache
+        assertEquals(JCacheQueryCache.class, ((NestedQueryCache)runtime.getDataDomain().getQueryCache()).getDelegate().getClass());
+
+        ObjectSelect<Artist> g1 = ObjectSelect.query(Artist.class).localCache("g1");
+        ObjectSelect<Artist> g2 = ObjectSelect.query(Artist.class).localCache("g2");
+
+        tArtist.insert(1, "artist1");
+        tArtist.insert(2, "artist2");
+        assertEquals(2, g1.select(context).size());
+
+        // we are still cached, must not see the new changes
+        tArtist.insert(3, "artist3");
+        tArtist.insert(4, "artist4");
+        assertEquals(2, g1.select(context).size());
+
+        // different cache group - must see the changes
+        assertEquals(4, g2.select(context).size());
+
+        // refresh the cache, so that "g1" could see the changes
+        runtime.getDataDomain().getQueryCache().removeGroup("g1");
+        assertEquals(4, g1.select(context).size());
+        assertEquals(4, g2.select(context).size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleProviderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleProviderTest.java b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleProviderTest.java
new file mode 100644
index 0000000..10b9388
--- /dev/null
+++ b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/CayenneJCacheModuleProviderTest.java
@@ -0,0 +1,31 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache;
+
+import org.apache.cayenne.unit.util.ModuleProviderChecker;
+import org.junit.Test;
+
+public class CayenneJCacheModuleProviderTest {
+
+    @Test
+    public void testAutoLoadable() {
+        ModuleProviderChecker.testProviderPresent(JCacheModuleProvider.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
new file mode 100644
index 0000000..4595a28
--- /dev/null
+++ b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache.unit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.jcache.JCacheModule;
+import org.apache.cayenne.unit.UnitDbAdapter;
+import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
+import org.apache.cayenne.unit.di.server.ServerCaseProperties;
+import org.apache.cayenne.unit.di.server.ServerRuntimeProvider;
+
+public class CacheServerRuntimeProvider extends ServerRuntimeProvider {
+
+    public CacheServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
+                                      @Inject ServerCaseProperties properties,
+                                      @Inject Provider<DbAdapter> dbAdapterProvider,
+                                      @Inject UnitDbAdapter unitDbAdapter) {
+        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+    }
+
+    @Override
+    protected Collection<? extends Module> getExtraModules() {
+        Collection<Module> modules = new ArrayList<>();
+        modules.addAll(super.getExtraModules());
+        modules.add(new JCacheModule());
+        return modules;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/JCacheCase.java
----------------------------------------------------------------------
diff --git a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/JCacheCase.java b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/JCacheCase.java
new file mode 100644
index 0000000..d87ff26
--- /dev/null
+++ b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/JCacheCase.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.jcache.unit;
+
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.DefaultScope;
+import org.apache.cayenne.unit.di.DICase;
+import org.apache.cayenne.unit.di.server.SchemaBuilder;
+import org.apache.cayenne.unit.di.server.ServerCaseModule;
+import org.junit.BeforeClass;
+
+/**
+ * @since 4.0
+ */
+public class JCacheCase extends DICase {
+
+    private static Injector injector;
+
+    @BeforeClass
+    static public void setupInjector() {
+        final DefaultScope testScope = new DefaultScope();
+        injector = DIBootstrap.createInjector(
+                new ServerCaseModule(testScope),
+                new Module() {
+                    @Override
+                    public void configure(Binder binder) {
+                        binder.bind(ServerRuntime.class).toProvider(CacheServerRuntimeProvider.class).in(testScope);
+                    }
+                }
+        );
+        injector.getInstance(SchemaBuilder.class).rebuildSchema();
+    }
+
+    @Override
+    protected Injector getUnitTestInjector() {
+        return injector;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 4d59c51..b94f929 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -28,6 +28,7 @@ CAY-2177 Sync auto generated state of PK between model and DB
 CAY-2187 Support for the scalar and aggregate SQL functions in ObjectSelect API
 CAY-2197 Update sqlite version and enable in-memory default config
 CAY-2212 cdbimport cleanup and configuration schema refactoring
+CAY-2223 JCacheQueryCache - a query cache provider to plug in JCache implementers
 
 Bug Fixes:
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/4365a8f9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0c492f4..ec57fe9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@
 		<module>cayenne-crypto</module>
 		<module>cayenne-joda</module>
 		<module>cayenne-dbcp2</module>
+		<module>cayenne-jcache</module>
 		<module>itests</module>
 		<module>modeler</module>
 		<module>maven-plugins</module>