CAY-2259 QueryCache: support for referencing type-safe caches + move cayenne-cache-invalidation module out of lifecycle module
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/d71bfe1b Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/d71bfe1b Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/d71bfe1b Branch: refs/heads/master Commit: d71bfe1b2ca47dfed034a5f51058429c338bf835 Parents: 67225b3 Author: Nikita Timofeev <stari...@gmail.com> Authored: Mon Apr 3 15:49:18 2017 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Mon Apr 3 15:49:18 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-cache-invalidation/pom.xml | 92 +++++++++++ .../cayenne/lifecycle/cache/CacheGroup.java | 60 +++++++ .../lifecycle/cache/CacheGroupDescriptor.java | 60 +++++++ .../cayenne/lifecycle/cache/CacheGroups.java | 56 +++++++ .../lifecycle/cache/CacheGroupsHandler.java | 79 +++++++++ .../cache/CacheInvalidationFilter.java | 159 +++++++++++++++++++ .../cache/CacheInvalidationModule.java | 46 ++++++ .../cache/CacheInvalidationModuleBuilder.java | 84 ++++++++++ .../cache/CacheInvalidationModuleProvider.java | 50 ++++++ .../lifecycle/cache/InvalidationFunction.java | 36 +++++ .../lifecycle/cache/InvalidationHandler.java | 35 ++++ .../org.apache.cayenne.di.spi.ModuleProvider | 20 +++ .../lifecycle/cache/CacheGroupsHandlerTest.java | 68 ++++++++ .../CacheInvalidationCacheGroupsHandlerIT.java | 153 ++++++++++++++++++ .../cache/CacheInvalidationCustomHandlerIT.java | 98 ++++++++++++ ...enneCacheInvalidationModuleProviderTest.java | 31 ++++ .../org/apache/cayenne/lifecycle/db/E1.java | 11 ++ .../org/apache/cayenne/lifecycle/db/E2.java | 21 +++ .../apache/cayenne/lifecycle/db/auto/_E1.java | 18 +++ .../apache/cayenne/lifecycle/db/auto/_E2.java | 18 +++ .../lifecycle/unit/CacheInvalidationCase.java | 75 +++++++++ .../src/test/resources/cayenne-lifecycle.xml | 17 ++ .../src/test/resources/lifecycle-map.map.xml | 17 ++ .../cayenne/lifecycle/cache/CacheGroups.java | 46 ------ .../lifecycle/cache/CacheGroupsHandler.java | 59 ------- .../cache/CacheInvalidationFilter.java | 154 ------------------ .../cache/CacheInvalidationModuleBuilder.java | 96 ----------- .../lifecycle/cache/InvalidationFunction.java | 36 ----- .../lifecycle/cache/InvalidationHandler.java | 35 ---- .../lifecycle/cache/CacheInvalidationIT.java | 76 --------- .../lifecycle/unit/CacheInvalidationCase.java | 83 ---------- docs/doc/src/main/resources/RELEASE-NOTES.txt | 4 +- docs/doc/src/main/resources/UPGRADE.txt | 7 + pom.xml | 3 +- 38 files changed, 1324 insertions(+), 588 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/assembly/pom.xml ---------------------------------------------------------------------- diff --git a/assembly/pom.xml b/assembly/pom.xml index b35ee1a..24a13f8 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -103,6 +103,12 @@ </dependency> <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-cache-invalidation</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/d71bfe1b/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 e688faa..7c19a8c 100644 --- a/assembly/src/main/resources/assemblies/assembly-generic.xml +++ b/assembly/src/main/resources/assemblies/assembly-generic.xml @@ -90,6 +90,7 @@ <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> <include>org.apache.cayenne:cayenne-jcache</include> + <include>org.apache.cayenne:cayenne-cache-invalidation</include> <include>org.apache.cayenne:cayenne-jgroups</include> <include>org.apache.cayenne:cayenne-jms</include> <include>org.apache.cayenne:cayenne-xmpp</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/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 45efd5d..85400b4 100644 --- a/assembly/src/main/resources/assemblies/assembly-mac.xml +++ b/assembly/src/main/resources/assemblies/assembly-mac.xml @@ -90,6 +90,7 @@ <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> <include>org.apache.cayenne:cayenne-jcache</include> + <include>org.apache.cayenne:cayenne-cache-invalidation</include> <include>org.apache.cayenne:cayenne-jgroups</include> <include>org.apache.cayenne:cayenne-jms</include> <include>org.apache.cayenne:cayenne-xmpp</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/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 25f1099..0e229d1 100644 --- a/assembly/src/main/resources/assemblies/assembly-windows.xml +++ b/assembly/src/main/resources/assemblies/assembly-windows.xml @@ -90,6 +90,7 @@ <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> <include>org.apache.cayenne:cayenne-jcache</include> + <include>org.apache.cayenne:cayenne-cache-invalidation</include> <include>org.apache.cayenne:cayenne-jgroups</include> <include>org.apache.cayenne:cayenne-jms</include> <include>org.apache.cayenne:cayenne-xmpp</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/pom.xml b/cayenne-cache-invalidation/pom.xml new file mode 100644 index 0000000..a59a576 --- /dev/null +++ b/cayenne-cache-invalidation/pom.xml @@ -0,0 +1,92 @@ +<?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.M6-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>cayenne-cache-invalidation</artifactId> + <name>cayenne-cache-invalidation: Cayenne Cache Invalidation</name> + <packaging>jar</packaging> + + <dependencies> + <!-- Compile dependencies --> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-server</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</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> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + <!-- 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> + </plugins> + </build> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroup.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroup.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroup.java new file mode 100644 index 0000000..be8bda5 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroup.java @@ -0,0 +1,60 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for defining cache group in case different cache types are targeted. + * + * @see CacheGroups + * @see CacheGroupsHandler + * + * @since 4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface CacheGroup { + + /** + * @return cache group name + */ + String value(); + + + /** + * Defines key type of the cache. + * Could be used for managing external caches that is strictly typed (e.g. JCache). + */ + Class<?> keyType() default Void.class; + + /** + * Defines value type of the cache. + * Could be used for managing external caches that is strictly typed (e.g. JCache). + */ + Class<?> valueType() default Void.class; +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupDescriptor.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupDescriptor.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupDescriptor.java new file mode 100644 index 0000000..bc38393 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupDescriptor.java @@ -0,0 +1,60 @@ +/***************************************************************** + * 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.lifecycle.cache; + +/** + * Immutable object describing cache group. + * Used as a result in {@link InvalidationFunction}. + * + * @see CacheGroupDescriptor#CacheGroupDescriptor(String) + * @see CacheGroupDescriptor#CacheGroupDescriptor(String, Class, Class) + * + * @since 4.0 + */ +public class CacheGroupDescriptor { + + private final String cacheGroupName; + + private final Class<?> keyType; + + private final Class<?> valueType; + + public CacheGroupDescriptor(String cacheGroupName) { + this(cacheGroupName, Void.class, Void.class); + } + + public CacheGroupDescriptor(String cacheGroupName, Class<?> keyType, Class<?> valueType) { + this.cacheGroupName = cacheGroupName; + this.keyType = keyType; + this.valueType = valueType; + } + + public String getCacheGroupName() { + return cacheGroupName; + } + + public Class<?> getKeyType() { + return keyType; + } + + public Class<?> getValueType() { + return valueType; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java new file mode 100644 index 0000000..1e5449b6 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.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.lifecycle.cache; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p> + * A built-in annotation that provides declarative cache management for persistent + * objects. + * </p> + * + * @see InvalidationHandler + * @see CacheGroup + * + * @since 3.1 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface CacheGroups { + + /** + * Defines one or more cache group names associated with the tagged entity. + */ + String[] value() default {}; + + /** + * Defines one or more typed cache groups associated with the tagged entity. + * + * @since 4.0 + */ + CacheGroup[] groups() default {}; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java new file mode 100644 index 0000000..63979fd --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java @@ -0,0 +1,79 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.cayenne.Persistent; + +import static java.util.Arrays.asList; + +/** + * @since 4.0 + */ +public class CacheGroupsHandler implements InvalidationHandler { + + /** + * Return invalidation function that returns values + * of {@link CacheGroups} and {@link CacheGroup} annotations for the given type. + */ + @Override + public InvalidationFunction canHandle(Class<? extends Persistent> type) { + + CacheGroup multipleCacheGroups = type.getAnnotation(CacheGroup.class); + CacheGroups cacheGroups = type.getAnnotation(CacheGroups.class); + if (cacheGroups == null && multipleCacheGroups == null) { + return null; + } + + final Collection<CacheGroupDescriptor> groupsList = new ArrayList<>(); + extractCacheGroups(cacheGroups, groupsList); + extractCacheGroups(multipleCacheGroups, groupsList); + + return new InvalidationFunction() { + @Override + public Collection<CacheGroupDescriptor> apply(Persistent persistent) { + return groupsList; + } + }; + } + + private void extractCacheGroups(CacheGroup cacheGroup, Collection<CacheGroupDescriptor> groupsList) { + if(cacheGroup == null) { + return; + } + groupsList.add(new CacheGroupDescriptor(cacheGroup.value(), cacheGroup.keyType(), cacheGroup.valueType())); + } + + private void extractCacheGroups(CacheGroups cacheGroups, Collection<CacheGroupDescriptor> groupsList) { + if(cacheGroups == null) { + return; + } + + for(String name : cacheGroups.value()) { + groupsList.add(new CacheGroupDescriptor(name)); + } + + for(CacheGroup group : cacheGroups.groups()) { + extractCacheGroups(group, groupsList); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java new file mode 100644 index 0000000..8f1e3bd --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java @@ -0,0 +1,159 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.cayenne.DataChannel; +import org.apache.cayenne.DataChannelFilter; +import org.apache.cayenne.DataChannelFilterChain; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.Persistent; +import org.apache.cayenne.QueryResponse; +import org.apache.cayenne.annotation.PrePersist; +import org.apache.cayenne.annotation.PreRemove; +import org.apache.cayenne.annotation.PreUpdate; +import org.apache.cayenne.cache.QueryCache; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.di.Provider; +import org.apache.cayenne.graph.GraphDiff; +import org.apache.cayenne.query.Query; + +/** + * <p> + * A {@link DataChannelFilter} that invalidates cache groups. + * Use custom rules for invalidation provided via DI. + * </p> + * <p> + * Default rule is based on entities' {@link CacheGroups} annotation. + * </p> + * <p> + * To add default filter: <pre> + * ServerRuntime.builder("cayenne-project.xml") + * .addModule(CacheInvalidationModuleBuilder.builder().build()); + * </pre> + * </p> + * + * @see CacheInvalidationModuleBuilder + * @see InvalidationHandler + * + * @since 3.1 + * @since 4.0 enhanced to support custom handlers. + */ +public class CacheInvalidationFilter implements DataChannelFilter { + + @Inject + private Provider<QueryCache> cacheProvider; + + @Inject + private List<InvalidationHandler> handlers; + + private final Map<Class<? extends Persistent>, InvalidationFunction> mappedHandlers; + + private final InvalidationFunction skipHandler; + + private final ThreadLocal<Set<CacheGroupDescriptor>> groups; + + public CacheInvalidationFilter() { + mappedHandlers = new ConcurrentHashMap<>(); + skipHandler = new InvalidationFunction() { + @Override + public Collection<CacheGroupDescriptor> apply(Persistent p) { + return Collections.emptyList(); + } + }; + groups = new ThreadLocal<>(); + } + + public void init(DataChannel channel) { + // noop + } + + public QueryResponse onQuery(ObjectContext originatingContext, Query query, DataChannelFilterChain filterChain) { + return filterChain.onQuery(originatingContext, query); + } + + public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, + int syncType, DataChannelFilterChain filterChain) { + try { + GraphDiff result = filterChain.onSync(originatingContext, changes, syncType); + // no exceptions, flush... + Collection<CacheGroupDescriptor> groupSet = groups.get(); + if (groupSet != null && !groupSet.isEmpty()) { + QueryCache cache = cacheProvider.get(); + for (CacheGroupDescriptor group : groupSet) { + if(group.getKeyType() != Void.class) { + cache.removeGroup(group.getCacheGroupName(), group.getKeyType(), group.getValueType()); + } else { + cache.removeGroup(group.getCacheGroupName()); + } + } + } + return result; + } finally { + groups.set(null); + } + } + + /** + * A callback method that records cache group to flush at the end of the commit. + */ + @PrePersist + @PreRemove + @PreUpdate + protected void preCommit(Object object) { + // TODO: for some reason we can't use Persistent as the argument type... (is it fixed in Cayenne 4.0.M4?) + Persistent p = (Persistent) object; + + InvalidationFunction invalidationFunction = mappedHandlers.get(p.getClass()); + if(invalidationFunction == null) { + invalidationFunction = skipHandler; + for (InvalidationHandler handler : handlers) { + InvalidationFunction function = handler.canHandle(p.getClass()); + if (function != null) { + invalidationFunction = function; + break; + } + } + mappedHandlers.put(p.getClass(), invalidationFunction); + } + + Collection<CacheGroupDescriptor> objectGroups = invalidationFunction.apply(p); + if (!objectGroups.isEmpty()) { + getOrCreateTxGroups().addAll(objectGroups); + } + } + + protected Set<CacheGroupDescriptor> getOrCreateTxGroups() { + Set<CacheGroupDescriptor> txGroups = groups.get(); + if (txGroups == null) { + txGroups = new HashSet<>(); + groups.set(txGroups); + } + + return txGroups; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModule.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModule.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModule.java new file mode 100644 index 0000000..8cb86ad --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModule.java @@ -0,0 +1,46 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.ListBuilder; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.tx.TransactionFilter; + +/** + * This module is autoloaded, all extensions should be done via {@link CacheInvalidationModuleBuilder}. + * @since 4.0 + */ +public class CacheInvalidationModule implements Module { + + static ListBuilder<InvalidationHandler> contributeInvalidationHandler(Binder binder) { + return binder.bindList(InvalidationHandler.class); + } + + @Override + public void configure(Binder binder) { + contributeInvalidationHandler(binder); + + // want the filter to be INSIDE transaction + ServerModule.contributeDomainFilters(binder) + .insertBefore(CacheInvalidationFilter.class, TransactionFilter.class); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java new file mode 100644 index 0000000..56049f1 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java @@ -0,0 +1,84 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.Collection; +import java.util.HashSet; + +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.ListBuilder; +import org.apache.cayenne.di.Module; + +/** + * @since 4.0 + */ +public class CacheInvalidationModuleBuilder { + + private Collection<Class<? extends InvalidationHandler>> handlerTypes; + + private Collection<InvalidationHandler> handlerInstances; + + private boolean noCacheGroupsHandler; + + public static CacheInvalidationModuleBuilder builder() { + return new CacheInvalidationModuleBuilder(); + } + + CacheInvalidationModuleBuilder() { + this.handlerTypes = new HashSet<>(); + this.handlerInstances = new HashSet<>(); + } + + /** + * Disable {@link CacheGroupsHandler} based on {@link CacheGroups} annotation. + */ + public CacheInvalidationModuleBuilder noCacheGroupsHandler() { + noCacheGroupsHandler = true; + return this; + } + + public CacheInvalidationModuleBuilder invalidationHandler(Class<? extends InvalidationHandler> handlerType) { + handlerTypes.add(handlerType); + return this; + } + + public CacheInvalidationModuleBuilder invalidationHandler(InvalidationHandler handlerInstance) { + handlerInstances.add(handlerInstance); + return this; + } + + public Module build() { + return new Module() { + @Override + public void configure(Binder binder) { + ListBuilder<InvalidationHandler> handlers = CacheInvalidationModule.contributeInvalidationHandler(binder); + + if(!noCacheGroupsHandler) { + handlers.add(CacheGroupsHandler.class); + } + handlers.addAll(handlerInstances); + + for(Class<? extends InvalidationHandler> handlerType : handlerTypes) { + handlers.add(handlerType); + } + } + }; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleProvider.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleProvider.java new file mode 100644 index 0000000..3696686 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleProvider.java @@ -0,0 +1,50 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.di.spi.ModuleProvider; + +/** + * @since 4.0 + */ +public class CacheInvalidationModuleProvider implements ModuleProvider { + + @Override + public Module module() { + return new CacheInvalidationModule(); + } + + @Override + public Class<? extends Module> moduleType() { + return CacheInvalidationModule.class; + } + + @SuppressWarnings("unchecked") + @Override + public Collection<Class<? extends Module>> overrides() { + Collection modules = Collections.singletonList(ServerModule.class); + return modules; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java new file mode 100644 index 0000000..6f6d6d5 --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java @@ -0,0 +1,36 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.Collection; + +import org.apache.cayenne.Persistent; + +/** + * @since 4.0 + */ +public interface InvalidationFunction { + + /** + * @return collection of cache groups to invalidate for given object + */ + Collection<CacheGroupDescriptor> apply(Persistent persistent); + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java new file mode 100644 index 0000000..a667ead --- /dev/null +++ b/cayenne-cache-invalidation/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.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.lifecycle.cache; + +import org.apache.cayenne.Persistent; + +/** + * A pluggable handler to invalidate cache groups on changes in certain objects. + * @since 4.0 + */ +public interface InvalidationHandler { + + /** + * @return invalidation function or null if there is nothing to invalidate + */ + InvalidationFunction canHandle(Class<? extends Persistent> type); + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider b/cayenne-cache-invalidation/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider new file mode 100644 index 0000000..4fc646c --- /dev/null +++ b/cayenne-cache-invalidation/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider @@ -0,0 +1,20 @@ +################################################################## +# 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. +################################################################## + +org.apache.cayenne.lifecycle.cache.CacheInvalidationModuleProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandlerTest.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandlerTest.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandlerTest.java new file mode 100644 index 0000000..1afa4f5 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandlerTest.java @@ -0,0 +1,68 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.cayenne.lifecycle.db.E1; +import org.apache.cayenne.lifecycle.db.E2; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @since 4.0 + */ +public class CacheGroupsHandlerTest { + + @Test + public void canHandleE1() throws Exception { + CacheGroupsHandler handler = new CacheGroupsHandler(); + InvalidationFunction function = handler.canHandle(E1.class); + Collection<CacheGroupDescriptor> result = function.apply(null); + + assertEquals(2, result.size()); + + String[] names = {"g1", "g2"}; + Collection<String> extractedNames = new ArrayList<>(); + for(CacheGroupDescriptor descriptor : result) { + extractedNames.add(descriptor.getCacheGroupName()); + } + assertArrayEquals(names, extractedNames.toArray()); + } + + @Test + public void canHandleE2() throws Exception { + CacheGroupsHandler handler = new CacheGroupsHandler(); + InvalidationFunction function = handler.canHandle(E2.class); + Collection<CacheGroupDescriptor> result = function.apply(null); + + assertEquals(6, result.size()); + + String[] names = {"g1", "g2", "g3", "g4", "g5", "g6"}; + Collection<String> extractedNames = new ArrayList<>(); + for(CacheGroupDescriptor descriptor : result) { + extractedNames.add(descriptor.getCacheGroupName()); + } + assertArrayEquals(names, extractedNames.toArray()); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCacheGroupsHandlerIT.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCacheGroupsHandlerIT.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCacheGroupsHandlerIT.java new file mode 100644 index 0000000..2b090ea --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCacheGroupsHandlerIT.java @@ -0,0 +1,153 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.cache.MapQueryCache; +import org.apache.cayenne.cache.QueryCache; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.lifecycle.db.E1; +import org.apache.cayenne.lifecycle.db.E2; +import org.apache.cayenne.lifecycle.unit.CacheInvalidationCase; +import org.apache.cayenne.query.ObjectSelect; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @since 4.0 + */ +public class CacheInvalidationCacheGroupsHandlerIT extends CacheInvalidationCase { + + private AtomicInteger removeGroupUntypedCounter; + private AtomicInteger removeGroupTypedCounter; + + @Before + public void resetCounters() { + removeGroupUntypedCounter = new AtomicInteger(0); + removeGroupTypedCounter = new AtomicInteger(0); + } + + @Override + protected Module buildInvalidationModule() { + return CacheInvalidationModuleBuilder.builder().build(); + } + + @Override + protected Module buildCustomModule() { + // Proxy query cache that will count methods calls + final QueryCache cache = new MapQueryCache() { + @Override + public void removeGroup(String groupKey) { + removeGroupUntypedCounter.incrementAndGet(); + super.removeGroup(groupKey); + } + + @Override + public void removeGroup(String groupKey, Class<?> keyType, Class<?> valueType) { + removeGroupTypedCounter.incrementAndGet(); + super.removeGroup(groupKey, keyType, valueType); + } + }; + + return new Module() { + @Override + public void configure(Binder binder) { + binder.bind(QueryCache.class).toInstance(cache); + } + }; + } + + @Test + public void invalidateE1() throws Exception { + ObjectContext context = runtime.newContext(); + + ObjectSelect<E1> g0 = ObjectSelect.query(E1.class).localCache(); + ObjectSelect<E1> g1 = ObjectSelect.query(E1.class).localCache("g1"); + ObjectSelect<E1> g2 = ObjectSelect.query(E1.class).localCache("g2"); + + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + + e1.insert(1).insert(2); + + // inserted via SQL... query results are still cached... + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + + + context.newObject(E1.class); + context.commitChanges(); + + assertEquals(2, removeGroupUntypedCounter.get()); + assertEquals(0, removeGroupTypedCounter.get()); + // inserted via Cayenne... "g1" and "g2" should get auto refreshed... + assertEquals(0, g0.selectCount(context)); + assertEquals(3, g1.selectCount(context)); + assertEquals(3, g2.selectCount(context)); + } + + @Test + public void invalidateE2() throws Exception { + ObjectContext context = runtime.newContext(); + + ObjectSelect<E2> g0 = ObjectSelect.query(E2.class).localCache(); + ObjectSelect<E2> g1 = ObjectSelect.query(E2.class).localCache("g1"); + ObjectSelect<E2> g2 = ObjectSelect.query(E2.class).localCache("g2"); + ObjectSelect<E2> g3 = ObjectSelect.query(E2.class).localCache("g3"); + ObjectSelect<E2> g5 = ObjectSelect.query(E2.class).localCache("g5"); + + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + assertEquals(0, g3.selectCount(context)); + assertEquals(0, g5.selectCount(context)); + + e2.insert(1).insert(2); + + // inserted via SQL... query results are still cached... + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + assertEquals(0, g3.selectCount(context)); + assertEquals(0, g5.selectCount(context)); + + + context.newObject(E2.class); + context.commitChanges(); + + // Typed remove will actually call untyped version, thus 4 + 2 + assertEquals(4 + 2, removeGroupUntypedCounter.get()); + assertEquals(2, removeGroupTypedCounter.get()); + + // inserted via Cayenne... "g1" and "g2" should get auto refreshed... + assertEquals(0, g0.selectCount(context)); + assertEquals(3, g1.selectCount(context)); + assertEquals(3, g2.selectCount(context)); + assertEquals(3, g3.selectCount(context)); + assertEquals(3, g5.selectCount(context)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCustomHandlerIT.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCustomHandlerIT.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCustomHandlerIT.java new file mode 100644 index 0000000..56ae915 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationCustomHandlerIT.java @@ -0,0 +1,98 @@ +/***************************************************************** + * 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.lifecycle.cache; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.Persistent; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.lifecycle.db.E1; +import org.apache.cayenne.lifecycle.unit.CacheInvalidationCase; +import org.apache.cayenne.query.ObjectSelect; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @since 4.0 + */ +public class CacheInvalidationCustomHandlerIT extends CacheInvalidationCase { + + @Override + protected Module buildInvalidationModule() { + return CacheInvalidationModuleBuilder.builder() + .noCacheGroupsHandler() + .invalidationHandler(G1InvalidationHandler.class) + .build(); + } + + @Test + public void testInvalidate() throws Exception { + ObjectContext context = runtime.newContext(); + + // no explicit cache group must still work - it lands inside default cache called 'cayenne.default.cache' + ObjectSelect<E1> g0 = ObjectSelect.query(E1.class).localCache(); + ObjectSelect<E1> g1 = ObjectSelect.query(E1.class).localCache("g1"); + ObjectSelect<E1> g2 = ObjectSelect.query(E1.class).localCache("g2"); + + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + + e1.insert(1).insert(2); + + // inserted via SQL... query results are still cached... + assertEquals(0, g0.selectCount(context)); + assertEquals(0, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + + + E1 e1 = context.newObject(E1.class); + context.commitChanges(); + + // inserted via Cayenne... "g1" should get auto refreshed... + assertEquals(0, g0.selectCount(context)); + assertEquals(3, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + + context.deleteObject(e1); + context.commitChanges(); + + // deleted via Cayenne... "g1" should get auto refreshed + assertEquals(0, g0.selectCount(context)); + assertEquals(2, g1.selectCount(context)); + assertEquals(0, g2.selectCount(context)); + } + + public static class G1InvalidationHandler implements InvalidationHandler { + @Override + public InvalidationFunction canHandle(Class<? extends Persistent> type) { + return new InvalidationFunction() { + @Override + public Collection<CacheGroupDescriptor> apply(Persistent persistent) { + return Collections.singleton(new CacheGroupDescriptor("g1")); + } + }; + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CayenneCacheInvalidationModuleProviderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CayenneCacheInvalidationModuleProviderTest.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CayenneCacheInvalidationModuleProviderTest.java new file mode 100644 index 0000000..4928fd1 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/cache/CayenneCacheInvalidationModuleProviderTest.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.lifecycle.cache; + +import org.apache.cayenne.unit.util.ModuleProviderChecker; +import org.junit.Test; + +public class CayenneCacheInvalidationModuleProviderTest { + + @Test + public void testAutoLoadable() { + ModuleProviderChecker.testProviderPresent(CacheInvalidationModuleProvider.class); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E1.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E1.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E1.java new file mode 100644 index 0000000..a6ae37d --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E1.java @@ -0,0 +1,11 @@ +package org.apache.cayenne.lifecycle.db; + +import org.apache.cayenne.lifecycle.cache.CacheGroups; +import org.apache.cayenne.lifecycle.db.auto._E1; + +@CacheGroups({"g1", "g2"}) +public class E1 extends _E1 { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E2.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E2.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E2.java new file mode 100644 index 0000000..44a8f4f --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/E2.java @@ -0,0 +1,21 @@ +package org.apache.cayenne.lifecycle.db; + +import org.apache.cayenne.lifecycle.cache.CacheGroup; +import org.apache.cayenne.lifecycle.cache.CacheGroups; +import org.apache.cayenne.lifecycle.db.auto._E2; + + +@CacheGroups( + value = {"g1", "g2"}, + groups = { + @CacheGroup("g3"), + @CacheGroup(value = "g4", keyType = String.class, valueType = Object.class), + @CacheGroup(value = "g5", keyType = Integer.class, valueType = Object.class), + } +) +@CacheGroup("g6") +public class E2 extends _E2 { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E1.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E1.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E1.java new file mode 100644 index 0000000..ce307f3 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E1.java @@ -0,0 +1,18 @@ +package org.apache.cayenne.lifecycle.db.auto; + +import org.apache.cayenne.CayenneDataObject; + +/** + * Class _E1 was generated by Cayenne. + * It is probably a good idea to avoid changing this class manually, + * since it may be overwritten next time code is regenerated. + * If you need to make any customizations, please use subclass. + */ +public abstract class _E1 extends CayenneDataObject { + + private static final long serialVersionUID = 1L; + + public static final String ID_PK_COLUMN = "ID"; + + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E2.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E2.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E2.java new file mode 100644 index 0000000..e333492 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/db/auto/_E2.java @@ -0,0 +1,18 @@ +package org.apache.cayenne.lifecycle.db.auto; + +import org.apache.cayenne.CayenneDataObject; + +/** + * Class _E2 was generated by Cayenne. + * It is probably a good idea to avoid changing this class manually, + * since it may be overwritten next time code is regenerated. + * If you need to make any customizations, please use subclass. + */ +public abstract class _E2 extends CayenneDataObject { + + private static final long serialVersionUID = 1L; + + public static final String ID_PK_COLUMN = "ID"; + + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/unit/CacheInvalidationCase.java ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/unit/CacheInvalidationCase.java b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/unit/CacheInvalidationCase.java new file mode 100644 index 0000000..bfe6272 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/java/org/apache/cayenne/lifecycle/unit/CacheInvalidationCase.java @@ -0,0 +1,75 @@ +/***************************************************************** + * 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.lifecycle.unit; + +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.cayenne.configuration.server.ServerRuntimeBuilder; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.test.jdbc.DBHelper; +import org.apache.cayenne.test.jdbc.TableHelper; +import org.junit.After; +import org.junit.Before; + +public abstract class CacheInvalidationCase { + + protected ServerRuntime runtime; + + protected TableHelper e1; + + protected TableHelper e2; + + @Before + public void startCayenne() throws Exception { + this.runtime = configureCayenne().build(); + + DBHelper dbHelper = new DBHelper(runtime.getDataSource()); + + this.e1 = new TableHelper(dbHelper, "E1").setColumns("ID"); + this.e1.deleteAll(); + + this.e2 = new TableHelper(dbHelper, "E2").setColumns("ID"); + this.e2.deleteAll(); + } + + protected abstract Module buildInvalidationModule(); + + protected Module buildCustomModule() { + return new Module() { + @Override + public void configure(Binder binder) { + } + }; + } + + protected ServerRuntimeBuilder configureCayenne() { + return ServerRuntime.builder() + .addModule(buildInvalidationModule()) + .addModule(buildCustomModule()) + .addConfig("cayenne-lifecycle.xml"); + } + + @After + public void shutdownCayenne() { + if (runtime != null) { + runtime.shutdown(); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/resources/cayenne-lifecycle.xml ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/resources/cayenne-lifecycle.xml b/cayenne-cache-invalidation/src/test/resources/cayenne-lifecycle.xml new file mode 100644 index 0000000..5b9a83e --- /dev/null +++ b/cayenne-cache-invalidation/src/test/resources/cayenne-lifecycle.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<domain project-version="9"> + <map name="lifecycle-map"/> + + <node name="lifecycle-db" + factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory" + schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy" + > + <map-ref name="lifecycle-map"/> + <data-source> + <driver value="org.hsqldb.jdbcDriver"/> + <url value="jdbc:hsqldb:mem:lifecycle"/> + <connectionPool min="1" max="1"/> + <login userName="sa"/> + </data-source> + </node> +</domain> http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-cache-invalidation/src/test/resources/lifecycle-map.map.xml ---------------------------------------------------------------------- diff --git a/cayenne-cache-invalidation/src/test/resources/lifecycle-map.map.xml b/cayenne-cache-invalidation/src/test/resources/lifecycle-map.map.xml new file mode 100644 index 0000000..80d44d6 --- /dev/null +++ b/cayenne-cache-invalidation/src/test/resources/lifecycle-map.map.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<data-map xmlns="http://cayenne.apache.org/schema/9/modelMap" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://cayenne.apache.org/schema/9/modelMap http://cayenne.apache.org/schema/9/modelMap.xsd" + project-version="9"> + <property name="defaultPackage" value="org.apache.cayenne.lifecycle.db"/> + <db-entity name="E1"> + <db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/> + </db-entity> + <db-entity name="E2"> + <db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/> + </db-entity> + <obj-entity name="E1" className="org.apache.cayenne.lifecycle.db.E1" dbEntityName="E1"> + </obj-entity> + <obj-entity name="E2" className="org.apache.cayenne.lifecycle.db.E2" dbEntityName="E2"> + </obj-entity> +</data-map> http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java deleted file mode 100644 index 2020575..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroups.java +++ /dev/null @@ -1,46 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A built-in annotation that provides declarative cache management for persistent - * objects. - * - * @since 3.1 - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -public @interface CacheGroups { - - /** - * Defines one or more cache group names associated with the tagged entity. If - * omitted, it is assumed that cache group names are determined dynamically based on - * the object type or state in whatever listener is to process them. - */ - String[] value() default {}; -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java deleted file mode 100644 index 330a30c..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheGroupsHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import java.util.Collection; - -import org.apache.cayenne.Persistent; - -import static java.util.Arrays.asList; - -/** - * @since 4.0 - */ -public class CacheGroupsHandler implements InvalidationHandler { - - /** - * Return invalidation function that returns values - * of {@link CacheGroups} annotations for the given type. - */ - @Override - public InvalidationFunction canHandle(Class<? extends Persistent> type) { - - CacheGroups a = type.getAnnotation(CacheGroups.class); - if (a == null) { - return null; - } - - String[] groups = a.value(); - if (groups.length == 0) { - return null; - } - - final Collection<String> groupsList = asList(groups); - return new InvalidationFunction() { - @Override - public Collection<String> apply(Persistent persistent) { - return groupsList; - } - }; - } - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java deleted file mode 100644 index f18a668..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationFilter.java +++ /dev/null @@ -1,154 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.cayenne.DataChannel; -import org.apache.cayenne.DataChannelFilter; -import org.apache.cayenne.DataChannelFilterChain; -import org.apache.cayenne.ObjectContext; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.QueryResponse; -import org.apache.cayenne.annotation.PrePersist; -import org.apache.cayenne.annotation.PreRemove; -import org.apache.cayenne.annotation.PreUpdate; -import org.apache.cayenne.cache.QueryCache; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.di.Provider; -import org.apache.cayenne.graph.GraphDiff; -import org.apache.cayenne.query.Query; - -/** - * <p> - * A {@link DataChannelFilter} that invalidates cache groups. - * Use custom rules for invalidation provided via DI. - * </p> - * <p> - * Default rule is based on entities' {@link CacheGroups} annotation. - * </p> - * <p> - * To add default filter: <pre> - * ServerRuntime.builder("cayenne-project.xml") - * .addModule(CacheInvalidationModuleBuilder.builder().build()); - * </pre> - * </p> - * - * @since 3.1 - * @see InvalidationHandler - * @see CacheInvalidationModuleBuilder - */ -public class CacheInvalidationFilter implements DataChannelFilter { - - @Inject - private Provider<QueryCache> cacheProvider; - - @Inject(CacheInvalidationModuleBuilder.INVALIDATION_HANDLERS_LIST) - private List<InvalidationHandler> handlers; - - private final Map<Class<? extends Persistent>, InvalidationFunction> mappedHandlers; - - private final InvalidationFunction skipHandler; - - private final ThreadLocal<Set<String>> groups; - - public CacheInvalidationFilter() { - mappedHandlers = new ConcurrentHashMap<>(); - skipHandler = new InvalidationFunction() { - @Override - public Collection<String> apply(Persistent p) { - return Collections.emptyList(); - } - }; - groups = new ThreadLocal<>(); - } - - public void init(DataChannel channel) { - // noop - } - - public QueryResponse onQuery(ObjectContext originatingContext, Query query, DataChannelFilterChain filterChain) { - return filterChain.onQuery(originatingContext, query); - } - - public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, - int syncType, DataChannelFilterChain filterChain) { - try { - GraphDiff result = filterChain.onSync(originatingContext, changes, syncType); - // no exceptions, flush... - Collection<String> groupSet = groups.get(); - if (groupSet != null && !groupSet.isEmpty()) { - QueryCache cache = cacheProvider.get(); - for (String group : groupSet) { - cache.removeGroup(group); - } - } - return result; - } finally { - groups.set(null); - } - } - - /** - * A callback method that records cache group to flush at the end of the commit. - */ - @PrePersist - @PreRemove - @PreUpdate - protected void preCommit(Object object) { - // TODO: for some reason we can't use Persistent as the argument type... (is it fixed in Cayenne 4.0.M4?) - Persistent p = (Persistent) object; - - InvalidationFunction invalidationFunction = mappedHandlers.get(p.getClass()); - if(invalidationFunction == null) { - invalidationFunction = skipHandler; - for (InvalidationHandler handler : handlers) { - InvalidationFunction function = handler.canHandle(p.getClass()); - if (function != null) { - invalidationFunction = function; - break; - } - } - mappedHandlers.put(p.getClass(), invalidationFunction); - } - - Collection<String> objectGroups = invalidationFunction.apply(p); - if (!objectGroups.isEmpty()) { - getOrCreateTxGroups().addAll(objectGroups); - } - } - - - protected Set<String> getOrCreateTxGroups() { - Set<String> txGroups = groups.get(); - if (txGroups == null) { - txGroups = new HashSet<>(); - groups.set(txGroups); - } - - return txGroups; - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java deleted file mode 100644 index 7b3b6bb..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/CacheInvalidationModuleBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import java.util.Collection; -import java.util.HashSet; - -import org.apache.cayenne.configuration.server.ServerModule; -import org.apache.cayenne.di.Binder; -import org.apache.cayenne.di.ListBuilder; -import org.apache.cayenne.di.Module; -import org.apache.cayenne.tx.TransactionFilter; - -/** - * @since 4.0 - */ -public class CacheInvalidationModuleBuilder { - - public static final String INVALIDATION_HANDLERS_LIST = "cayenne.querycache.invalidation_handlers"; - - private Collection<Class<? extends InvalidationHandler>> handlerTypes; - - private Collection<InvalidationHandler> handlerInstances; - - private boolean noCacheGroupsHandler; - - public static CacheInvalidationModuleBuilder builder() { - return new CacheInvalidationModuleBuilder(); - } - - private static ListBuilder<InvalidationHandler> contributeInvalidationHandler(Binder binder) { - return binder.bindList(InvalidationHandler.class, INVALIDATION_HANDLERS_LIST); - } - - CacheInvalidationModuleBuilder() { - this.handlerTypes = new HashSet<>(); - this.handlerInstances = new HashSet<>(); - } - - /** - * Disable {@link CacheGroupsHandler} based on {@link CacheGroups} annotation. - */ - public CacheInvalidationModuleBuilder noCacheGroupsHandler() { - noCacheGroupsHandler = true; - return this; - } - - public CacheInvalidationModuleBuilder invalidationHandler(Class<? extends InvalidationHandler> handlerType) { - handlerTypes.add(handlerType); - return this; - } - - public CacheInvalidationModuleBuilder invalidationHandler(InvalidationHandler handlerInstance) { - handlerInstances.add(handlerInstance); - return this; - } - - public Module build() { - return new Module() { - @Override - public void configure(Binder binder) { - ListBuilder<InvalidationHandler> handlers = contributeInvalidationHandler(binder); - - if(!noCacheGroupsHandler) { - handlers.add(CacheGroupsHandler.class); - } - handlers.addAll(handlerInstances); - - for(Class<? extends InvalidationHandler> handlerType : handlerTypes) { - handlers.add(handlerType); - } - - // want the filter to be INSIDE transaction - ServerModule.contributeDomainFilters(binder) - .insertBefore(CacheInvalidationFilter.class, TransactionFilter.class); - } - }; - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java deleted file mode 100644 index 8ae16db..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationFunction.java +++ /dev/null @@ -1,36 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import java.util.Collection; - -import org.apache.cayenne.Persistent; - -/** - * @since 4.0 - */ -public interface InvalidationFunction { - - /** - * @return collection of cache groups to invalidate for given object - */ - Collection<String> apply(Persistent persistent); - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/d71bfe1b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java deleted file mode 100644 index a667ead..0000000 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/cache/InvalidationHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -/***************************************************************** - * 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.lifecycle.cache; - -import org.apache.cayenne.Persistent; - -/** - * A pluggable handler to invalidate cache groups on changes in certain objects. - * @since 4.0 - */ -public interface InvalidationHandler { - - /** - * @return invalidation function or null if there is nothing to invalidate - */ - InvalidationFunction canHandle(Class<? extends Persistent> type); - -}