diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheScheduler.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheScheduler.java index b17c02fde..6bee40660 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheScheduler.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheScheduler.java @@ -21,19 +21,23 @@ package org.apache.openjpa.datacache; import java.security.AccessController; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; -import java.util.concurrent.ConcurrentHashMap; import org.apache.openjpa.util.InvalidStateException; import org.apache.openjpa.util.UserException; + import serp.util.Strings; /** @@ -54,7 +58,7 @@ public class DataCacheScheduler private Map _caches = new ConcurrentHashMap(); private boolean _stop = false; - private int _interval = 2; + private int _interval = 1; private Log _log; private Thread _thread; @@ -63,14 +67,14 @@ public class DataCacheScheduler } /** - * The interval time in minutes between cache checks. Defaults to 2. + * The interval time in minutes between cache checks. Defaults to 1. */ public int getInterval() { return _interval; } /** - * The interval time in minutes between cache checks. Defaults to 2. + * The interval time in minutes between cache checks. Defaults to 1. */ public void setInterval(int interval) { _interval = interval; @@ -181,19 +185,46 @@ public class DataCacheScheduler final int[] min; public Schedule(String date) { - StringTokenizer token = new StringTokenizer(date, " \t"); - if (token.countTokens() != 5) - throw new UserException(_loc.get("bad-count", date)). - setFatal(true); - try { - min = parse(token.nextToken(), 0, 60); - hour = parse(token.nextToken(), 0, 24); - dayOfMonth = parse(token.nextToken(), 1, 31); - month = parse(token.nextToken(), 1, 13); - dayOfWeek = parse(token.nextToken(), 1, 8); - } catch (Throwable t) { - throw new UserException(_loc.get("bad-schedule", date), t). - setFatal(true); + int[] tmin = null; + if (date.startsWith("+")) { + Calendar cal = Calendar.getInstance(); + int interval = Integer.parseInt(date.substring(1)); + int currMin = cal.get(Calendar.MINUTE); + + tmin = new int[60/interval]; + for(int i = 0; i= 60 ){ + temp -= 60; + } + tmin[i]=temp; + } + Arrays.sort(tmin); + + min = tmin; + hour = WILDCARD; + dayOfMonth = WILDCARD; + month = WILDCARD; + dayOfWeek = WILDCARD; + }else{ + + StringTokenizer token = new StringTokenizer(date, " \t"); + if (token.countTokens() != 5) + throw new UserException(_loc.get("bad-count", date)).setFatal(true); + try { + min = parse(token.nextToken(), 0, 60); + hour = parse(token.nextToken(), 0, 24); + dayOfMonth = parse(token.nextToken(), 1, 31); + month = parse(token.nextToken(), 1, 13); + dayOfWeek = parse(token.nextToken(), 1, 8); + } catch (Throwable t) { + throw new UserException(_loc.get("bad-schedule", date), t).setFatal(true); + } } } diff --git a/openjpa-persistence-jdbc/pom.xml b/openjpa-persistence-jdbc/pom.xml index fff22d934..718fb2505 100644 --- a/openjpa-persistence-jdbc/pom.xml +++ b/openjpa-persistence-jdbc/pom.xml @@ -809,7 +809,6 @@ org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java org/apache/openjpa/persistence/datacache/TestCacheMultiThreadedLoad.java org/apache/openjpa/persistence/datacache/TestConcurrentDataCache.java - org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java org/apache/openjpa/persistence/datacache/TestDistributedKodoDataCache.java org/apache/openjpa/persistence/datacache/TestFlushDataCache.java org/apache/openjpa/persistence/datacache/TestJPQL2Queries.java diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java index 04ac62b55..16b609c5e 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java @@ -20,33 +20,17 @@ package org.apache.openjpa.persistence.datacache; import java.util.Calendar; import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.apache.openjpa.persistence.datacache.common.apps.ScheduledEviction; -import org.apache.openjpa.persistence.common.utils.AbstractTestCase; import org.apache.openjpa.conf.OpenJPAConfiguration; -import org.apache.openjpa.datacache.DataCache; -import org.apache.openjpa.datacache.DataCacheScheduler; import org.apache.openjpa.datacache.ConcurrentDataCache; -import org.apache.openjpa.persistence.JPAFacadeHelper; -import org.apache.openjpa.persistence.OpenJPAEntityManager; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.util.Id; +import org.apache.openjpa.datacache.DataCacheScheduler; +import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; +import org.apache.openjpa.persistence.datacache.common.apps.ScheduledEviction; +import org.apache.openjpa.persistence.test.SingleEMTestCase; -public class TestDataCacheScheduler - extends AbstractTestCase { +public class TestDataCacheScheduler extends SingleEMTestCase { - private static final String MINUTES = getMinutes(); - - public TestDataCacheScheduler(String str) { - super(str, "datacachecactusapp"); - } - - private static String getMinutes() { + private static String getMinutesString() { StringBuffer buf = new StringBuffer(); for (int i = 0; i < 60; i++) { if (i % 2 == 0) @@ -56,172 +40,86 @@ public class TestDataCacheScheduler } public void setUp() { - deleteAll(ScheduledEviction.class); + setUp(ScheduledEviction.class, CLEAR_TABLES + ); } - public void testRuntime() - throws Exception { - String sched = MINUTES + " * * * *"; - Map propsMap = new HashMap(); - propsMap.put("openjpa.DataCache", "true(EvictionSchedule=\"" + sched - + "\")"); - propsMap.put("openjpa.RemoteCommitProvider", "sjvm"); - propsMap.put("openjpa.FlushBeforeQueries", "true"); - propsMap.put("openjpa.BrokerImpl", CacheTestBroker.class.getName()); - OpenJPAEntityManagerFactory emf = - (OpenJPAEntityManagerFactory) getEmf(propsMap); - - ((OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.cast(emf)) - .getConfiguration().getDataCacheManagerInstance() - .getDataCacheScheduler().setInterval(1); - DataCache cache = JPAFacadeHelper.getMetaData(emf, - ScheduledEviction.class).getDataCache(); - - OpenJPAEntityManager em = (OpenJPAEntityManager) emf - .createEntityManager(); - startTx(em); - ScheduledEviction pc = new ScheduledEviction("Foo"); - em.persist(pc); - Object oid = em.getObjectId(pc); - Object oidwithclass = new Id(ScheduledEviction.class, oid.toString()); - endTx(em); - endEm(em); - - cache.clear();// clear and wait until next run. - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - if (cal.get(Calendar.MINUTE) % 2 == 0) - Thread.currentThread().sleep - ((60 - cal.get(Calendar.SECOND)) * 1000); - cal.setTime(new Date()); - assertTrue(cal.get(Calendar.MINUTE) % 2 == 1); - em = (OpenJPAEntityManager) emf.createEntityManager(); - em.find(ScheduledEviction.class, oid); - endEm(em); - assertTrue(cache.contains(oidwithclass)); - - Thread.currentThread().sleep(130 * 1000); - assertFalse(cache.contains(oidwithclass)); - emf.close(); - } - - /** - * too slow ! * - */ - //FIXME Seetha Sep 26,2006 - /*public void XXXtestRunnable() - throws Exception { - KodoPersistenceManager pm = getPM(); - OpenJPAConfiguration conf = pm.getConfiguration(); + public void testBasic() throws Exception { + OpenJPAConfiguration conf = ((OpenJPAEntityManagerSPI) em).getConfiguration(); DataCacheScheduler scheduler = new DataCacheScheduler(conf); + // Make the scheduler run every 1 minute scheduler.setInterval(1); + DummyCache cache1 = new DummyCache(); + DummyCache cache2 = new DummyCache(); Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); - int minute = (cal.get(Calendar.MINUTE) + 2) % 60; - StringBuffer sched = new StringBuffer(); - sched.append(minute).append(' '); - sched.append("* "); - sched.append("* "); - sched.append("* "); - sched.append("* "); - DummyCache cache = new DummyCache(); - scheduler.scheduleEviction(cache, sched.toString()); + int currMin = cal.get(Calendar.MINUTE); + int plusOne = currMin+1; + int plusTwo = plusOne+1; + if(plusOne>=60){ + plusOne-=60; + } + if(plusTwo>=60){ + plusTwo-=60; + } + // Schedule eviction to happen the next two minutes + scheduler.scheduleEviction(cache2, plusOne+","+plusTwo+" * * * *"); - Thread thread = new Thread(scheduler); - thread.setDaemon(true); - thread.start(); - // test that it did not run yet... - Thread.currentThread().sleep(90 * 1000); // 90 seconds - assertEquals(0, cache.clearCount); - // test that it ran... - Thread.currentThread().sleep(45 * 1000); // 45 seconds - assertEquals(1, cache.clearCount); - // test that it wasn't too eager - Thread.currentThread().sleep(50 * 1000); // 90 seconds - assertEquals(1, cache.clearCount); - scheduler.stop(); - }*/ - - /** - * too slow * - */ - /* public void XXXtestMonth() - throws Exception { - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - int month = cal.get(Calendar.MONTH); - int month2 = month + 1; - if (month2 > 12) - month2 = 1; - doTest("* * " + month + " *", "* * " + month2 + " *"); - }*/ - - /** - * too slow * - */ - /* public void XXXtestDayOfMonth() - throws Exception { - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - int dom = cal.get(Calendar.DAY_OF_MONTH); - doTest("* " + dom + " * *", "* " + (dom % 12 + 1) + " * *"); - }*/ - public void testDayOfWeek() - throws Exception { - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - int day = cal.get(Calendar.DAY_OF_WEEK); - doTest("* * * " + day, "* * * " + (day % 7 + 1)); - } - - public void testHour() - throws Exception { - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - int hour = cal.get(Calendar.HOUR_OF_DAY); - doTest(hour + " * * *", ((hour + 1) % 24) + " * * *"); + // Schedule eviction to happen every mintue on cache 1 + scheduler.scheduleEviction(cache1, ("+1")); + + Thread.currentThread().sleep(61000); + assertEquals(1,cache1.getClearCount()); + assertEquals(1,cache2.getClearCount()); + + Thread.currentThread().sleep(60000); + assertEquals(2,cache1.getClearCount()); + assertEquals(2,cache2.getClearCount()); + + Thread.currentThread().sleep(60000); + assertEquals(3,cache1.getClearCount()); + assertEquals(2,cache2.getClearCount()); } /** * Pass in 4 out of 5 tokens. */ - private void doTest(String valid, String invalid) - throws Exception { - - OpenJPAEntityManagerFactory emf = - (OpenJPAEntityManagerFactory) getEmf(); - OpenJPAConfiguration conf = - ((OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.cast(emf)) - .getConfiguration(); - - DataCacheScheduler scheduler = new DataCacheScheduler(conf); - scheduler.setInterval(1); - - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date()); - String sched = ((cal.get(Calendar.MINUTE) + 1) % 60) + " "; - DummyCache validCache = new DummyCache(); - scheduler.scheduleEviction(validCache, sched + valid); - DummyCache invalidCache = new DummyCache(); - scheduler.scheduleEviction(invalidCache, sched + invalid); - Thread thread = new Thread(scheduler); - thread.setDaemon(true); - thread.start(); - // test that it did not run yet... - Thread.currentThread().sleep(70 * 1000); // 70 seconds - scheduler.stop(); -// assertEquals(2, validCache.clearCount); - assertTrue("Wrong invocation count: " + validCache.clearCount, - validCache.clearCount == 1 || validCache.clearCount == 2); - assertEquals(0, invalidCache.clearCount); - } - + // private void doTest(String valid, String invalid) throws Exception { + // + // OpenJPAEntityManagerFactory emf = (OpenJPAEntityManagerFactory) getEmf(); + // OpenJPAConfiguration conf = ((OpenJPAEntityManagerFactorySPI) + // OpenJPAPersistence.cast(emf)).getConfiguration(); + // + // DataCacheScheduler scheduler = new DataCacheScheduler(conf); + // scheduler.setInterval(1); + // + // Calendar cal = Calendar.getInstance(); + // cal.setTime(new Date()); + // String sched = ((cal.get(Calendar.MINUTE) + 1) % 60) + " "; + // DummyCache validCache = new DummyCache(); + // scheduler.scheduleEviction(validCache, sched + valid); + // DummyCache invalidCache = new DummyCache(); + // scheduler.scheduleEviction(invalidCache, sched + invalid); + // Thread thread = new Thread(scheduler); + // thread.setDaemon(true); + // thread.start(); + // // test that it did not run yet... + // Thread.currentThread().sleep(70 * 1000); // 70 seconds + // scheduler.stop(); + // // assertEquals(2, validCache.clearCount); + // assertTrue("Wrong invocation count: " + validCache.clearCount, validCache.clearCount == 1 + // || validCache.clearCount == 2); + // assertEquals(0, invalidCache.clearCount); + // } private class DummyCache extends ConcurrentDataCache { int clearCount = 0; - public void clear() { + public synchronized int getClearCount(){ + return clearCount; + } + public synchronized void clear() { clearCount++; } } diff --git a/openjpa-project/src/doc/manual/ref_guide_caching.xml b/openjpa-project/src/doc/manual/ref_guide_caching.xml index be0f33150..cad4b83a3 100644 --- a/openjpa-project/src/doc/manual/ref_guide_caching.xml +++ b/openjpa-project/src/doc/manual/ref_guide_caching.xml @@ -277,7 +277,7 @@ for more information on the DataCache annotation. A cache can specify that it should be cleared at certain times rather than using data timeouts. The EvictionSchedule property of OpenJPA's -cache implementation accepts a cron style eviction schedule. +cache implementation can be input in two different formats. The first is a cron style eviction schedule. The format of this property is a whitespace-separated list of five tokens, where the * symbol (asterisk), indicates match all. The tokens are, in order: @@ -317,6 +317,20 @@ past 3 PM on Sunday. true(EvictionSchedule='15,45 15 * * 1') + +The second format for this property is an interval style eviction schedule. The +format of this property is a + followed by the number of minutes +between each time that the cache should be evicted. + + +For example, the following openjpa.DataCache setting schedules the default cache +to evict values from the cache every 120 minutes. + + + +true(EvictionSchedule='+120') + +
Distributing instances across cache partitions