From 2c2e78149dd64bb5ce7e42687e5b55a1865eb838 Mon Sep 17 00:00:00 2001 From: Michael Dick Date: Fri, 1 May 2009 15:47:33 +0000 Subject: [PATCH] OPENJPA-1045. L2 cache included / excluded types may be specified via configuration properties git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@770729 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/datacache/AbstractDataCache.java | 78 ++++++ .../apache/openjpa/meta/ClassMetaData.java | 35 ++- .../openjpa/persistence/datacache/Item.java | 71 ++++++ .../openjpa/persistence/datacache/Order.java | 87 +++++++ .../persistence/datacache/Purchase.java | 68 +++++ .../datacache/TestCacheExclusions.java | 233 ++++++++++++++++++ 6 files changed, 569 insertions(+), 3 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Item.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Order.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Purchase.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestCacheExclusions.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java index aa6059b65..eddaa5280 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java @@ -19,13 +19,17 @@ package org.apache.openjpa.datacache; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.event.RemoteCommitEvent; import org.apache.openjpa.event.RemoteCommitListener; @@ -35,6 +39,8 @@ import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager; +import serp.util.Strings; + /** * Abstract {@link DataCache} implementation that provides various * statistics, logging, and timeout functionality common across cache @@ -68,6 +74,9 @@ public abstract class AbstractDataCache private String _name = null; private boolean _closed = false; private String _schedule = null; + + protected Set _includedTypes; + protected Set _excludedTypes; public String getName() { return _name; @@ -461,4 +470,73 @@ public abstract class AbstractDataCache log.warn(s_loc.get("exp-listener-ex"), e); } } + + public Set getTypes() { + return _includedTypes; + } + + public Set getExcludedTypes() { + return _excludedTypes; + } + + public void setTypes(Set types) { + _includedTypes = types; + } + + public void setTypes(String types) { + _includedTypes = + StringUtils.isEmpty(types) ? null : new HashSet(Arrays + .asList(Strings.split(types, ";", 0))); + } + + public void setExcludedTypes(Set types) { + _excludedTypes = types; + } + + public void setExcludedTypes(String types) { + _excludedTypes = + StringUtils.isEmpty(types) ? null : new HashSet(Arrays + .asList(Strings.split(types, ";", 0))); + } + + /** + * Determine whether a provided class can be applied to this cache. + * + *

+ * The algorithm used to determine which types apply is as follows: + *

    + *
  • If neither included nor excluded types are found all types will be + * used.
  • + *
  • If included types are specified and excluded types are not specified + * only the included types will be used.
  • + *
  • If included types are not specified and excluded types are specified + * all types will be used except those which are explicitly excluded. + *
  • + *
  • If both included types and excluded types are specified then + * only the included types will be used. If an included type is also + * an excluded type the excluded setting will take precedence (ie + * the type will not be used).
  • + *
+ * + * @param className + * A class which may be used by this plugin. + * @return True if the type should be used, otherwise false. + */ + public boolean isCacheableType(String classname) { + boolean rval = true; + if(rval) { + System.out.format("ABDC"); + } + if (_includedTypes != null && ! _includedTypes.isEmpty()) { + if(!_includedTypes.contains(classname)) { + rval = false; + } + } + if (_excludedTypes != null && ! _excludedTypes.isEmpty()) { + if(_excludedTypes.contains(classname)) { + rval = false; + } + } + return rval; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java index 38ed3bb57..82e50497b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java @@ -38,6 +38,7 @@ import java.util.TreeMap; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.conf.OpenJPAConfiguration; +import org.apache.openjpa.datacache.AbstractDataCache; import org.apache.openjpa.datacache.DataCache; import org.apache.openjpa.enhance.PCRegistry; import org.apache.openjpa.enhance.Reflection; @@ -1395,14 +1396,23 @@ public class ClassMetaData } /** - * The name of the datacache to use for this class, or null if none. + * The name of the datacache to use for this class. If this class is not + * eligible for caching based its annotation or the cache configuration + * null will be returned. + * + * @return The cache name, or null if this type should not be cached. */ public String getDataCacheName() { if (DEFAULT_STRING.equals(_cacheName)) { - if (_super != null) + if (_super != null) { _cacheName = getPCSuperclassMetaData().getDataCacheName(); - else + } + else { _cacheName = DataCache.NAME_DEFAULT; + } + if(!isCacheable(_cacheName)) { + _cacheName = null; + } } return _cacheName; } @@ -2553,4 +2563,23 @@ public class ClassMetaData public void setAbstract(boolean flag) { _abstract = flag; } + + /** + * Determine whether this Type should be included in the DataCache (if one + * is provided) based on the DataCache's configuration. + * + * @return true if the DataCache will accept this type, otherwise false. + */ + private boolean isCacheable(String candidateCacheName) { + boolean rval = true; + DataCache cache = + getRepository().getConfiguration().getDataCacheManagerInstance() + .getDataCache(candidateCacheName); + if (cache != null && (cache instanceof AbstractDataCache)) { + AbstractDataCache adc = (AbstractDataCache) cache; + if (!adc.isCacheableType(getDescribedType().getName())) + rval = false; + } + return rval; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Item.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Item.java new file mode 100644 index 000000000..0c96a738d --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Item.java @@ -0,0 +1,71 @@ +/* + * 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.openjpa.persistence.datacache; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Version; + +@Entity +@Table(name="CACHE_EXC_ITEM") +public class Item { + + @Id + private int id; + + @Version + private int version; + + private String name; + + private String type; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Order.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Order.java new file mode 100644 index 000000000..8ef925649 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Order.java @@ -0,0 +1,87 @@ +/* + * 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.openjpa.persistence.datacache; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.Version; + +@Entity +@Table(name="CACHE_EXC_ORDR") +public class Order { + + @Id + @GeneratedValue + private int id; + + @Version + private int version; + + private int quantity; + + @OneToOne + private Item item; + + @ManyToOne + private Purchase purchase; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public Item getItem() { + return item; + } + + public void setItem(Item item) { + this.item = item; + } + + public Purchase getPurchase() { + return purchase; + } + + public void setPurchase(Purchase purchase) { + this.purchase = purchase; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Purchase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Purchase.java new file mode 100644 index 000000000..a028de590 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/Purchase.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.openjpa.persistence.datacache; + +import java.util.Collection; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Version; + +@Entity +@Table(name="CACHE_EXC_PURC") +public class Purchase { + + @Id + @GeneratedValue + private int id; + + @Version + private int version; + + @OneToMany(mappedBy = "purchase", cascade = CascadeType.ALL) + private Collection orders; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public Collection getOrders() { + return orders; + } + + public void setOrders(Collection orders) { + this.orders = orders; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestCacheExclusions.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestCacheExclusions.java new file mode 100644 index 000000000..d47d99fd6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestCacheExclusions.java @@ -0,0 +1,233 @@ +/* + * 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.openjpa.persistence.datacache; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.openjpa.jdbc.meta.ClassMapping; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.StoreCache; +import org.apache.openjpa.persistence.test.PersistenceTestCase; + +public class TestCacheExclusions extends PersistenceTestCase { + + private OpenJPAEntityManagerFactorySPI emf = null; + + private static String[] ITEM_NAMES = + { "Cup", "pen", "pencil", "phone", "laptop", "keyboard", "mouse" }; + + private static final String _tSep = ";"; + + Item[] items = new Item[ITEM_NAMES.length]; + Order o1, o2; + Purchase p; + + public void populate() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + int n = 0; + for (String s : ITEM_NAMES) { + items[n] = new Item(); + items[n].setName(s); + items[n].setId(n); + em.persist(items[n++]); + } + p = new Purchase(); + p.setOrders(new ArrayList()); + o1 = new Order(); + o1.setItem(em.find(Item.class, 1)); + o1.setQuantity(2); + o1.setPurchase(p); + p.getOrders().add(o1); + + o2 = new Order(); + o2.setItem(em.find(Item.class, 4)); + o2.setQuantity(23); + o2.setPurchase(p); + p.getOrders().add(o2); + + em.persist(p); + em.getTransaction().commit(); + em.close(); + } + + public void tearDown() throws Exception { + if (emf != null) { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + for (ClassMapping mapping : ((ClassMapping[]) emf + .getConfiguration().getMetaDataRepositoryInstance() + .getMetaDatas())) { + Query q = + em.createNativeQuery("DROP TABLE " + + mapping.getTable().getName()); + q.executeUpdate(); + } + em.getTransaction().commit(); + em.close(); + + emf.close(); + } + emf = null; + super.tearDown(); + } + + public void testCacheAll() { + getEntityManagerFactoryCacheSettings(null, null); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, true, true, true); + } + + public void testCacheItems() { + getEntityManagerFactoryCacheSettings(new Class[] { Item.class }, null); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, false, false, true); + } + + public void testCacheItemsAndPurchases() { + getEntityManagerFactoryCacheSettings(new Class[] { Item.class, + Purchase.class }, null); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, true, false, true); + } + + public void testCacheItemsAndOrders() { + getEntityManagerFactoryCacheSettings(new Class[] { Item.class, + Order.class }, null); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, false, true, true); + } + + public void testCachePurchasesAndOrders() { + getEntityManagerFactoryCacheSettings(new Class[] { Purchase.class, + Order.class }, null); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, true, true, false); + } + + public void testExcludePurchases() { + getEntityManagerFactoryCacheSettings(null, + new Class[] { Purchase.class }); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, false, true, true); + } + + public void testExcludeOrders() { + getEntityManagerFactoryCacheSettings(null, new Class[] { Order.class }); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, true, false, true); + } + + public void testExcludeItems() { + getEntityManagerFactoryCacheSettings(null, new Class[] { Item.class }); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, true, true, false); + } + + public void testExcludeOrdersAndPurchases() { + getEntityManagerFactoryCacheSettings(null, new Class[] { Order.class, + Purchase.class }); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, false, false, true); + } + + public void testIncludePurchaseItemExcludePurchase() { + getEntityManagerFactoryCacheSettings(new Class[] { Purchase.class, + Item.class }, new Class[] { Purchase.class }); + populate(); + StoreCache cache = emf.getStoreCache(); + assertCacheContents(cache, false, false, true); + } + + public OpenJPAEntityManagerFactorySPI getEntityManagerFactoryCacheSettings( + Class[] includedTypes, Class[] excludedTypes) { + StringBuilder includes = new StringBuilder(); + if (includedTypes != null && includedTypes.length > 0) { + includes.append("Types="); + for (Class c : includedTypes) { + includes.append(c.getName()); + includes.append(_tSep); + } + includes.setLength(includes.length() - 1); // remove last semicolon + } + StringBuilder excludes = new StringBuilder(); + if (excludedTypes != null && excludedTypes.length > 0) { + excludes.append("ExcludedTypes="); + for (Class c : excludedTypes) { + excludes.append(c.getName()); + excludes.append(_tSep); + } + excludes.setLength(excludes.length() - 1); // remove last semicolon + } + StringBuilder dataCacheSettings = new StringBuilder(); + dataCacheSettings.append("true"); + if (includes.length() > 0 || excludes.length() > 0) { + dataCacheSettings.append("("); + dataCacheSettings.append(includes); + if (includes.length() > 0 && excludes.length() > 0) { + dataCacheSettings.append(","); + } + dataCacheSettings.append(excludes); + dataCacheSettings.append(")"); + } + Map props = new HashMap(); + props.put("openjpa.DataCache", dataCacheSettings.toString()); + props.put("openjpa.RemoteCommitProvider", "sjvm"); + props.put("openjpa.MetaDataFactory", "jpa(Types=" + + Item.class.getName() + _tSep + Purchase.class.getName() + _tSep + + Order.class.getName() + ")"); + emf = + (OpenJPAEntityManagerFactorySPI) javax.persistence.Persistence + .createEntityManagerFactory("test", props); + return emf; + } + + public void assertCacheContents(StoreCache cache, boolean expectPurchase, + boolean expectOrders, boolean expectItems) { + assertEquals("Expected purchases to " + (expectPurchase ? "" : "not ") + + "exist in the cache", expectPurchase, cache.contains( + Purchase.class, p.getId())); + assertEquals("Expected Orders to " + (expectOrders ? "" : "not ") + + "exist in the cache", expectOrders, cache.contains(Order.class, + o1.getId())); + assertEquals("Expected Orders to " + (expectOrders ? "" : "not ") + + "exist in the cache", expectOrders, cache.contains(Order.class, + o2.getId())); + for (int i = 0; i < ITEM_NAMES.length; i++) { + assertEquals("Expected Items to " + (expectItems ? "" : "not ") + + "exist in the cache", expectItems, cache.contains(Item.class, + items[i].getId())); + } + } +}