OPENJPA-1531: Adding support for a interval style syntax in the openjpa.DataCache.EvictionSchedule property. Added code changes, test case, and doc.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@916052 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Richard G. Curtis 2010-02-25 00:11:33 +00:00
parent c9e2d17fd5
commit be33af366c
4 changed files with 132 additions and 190 deletions

View File

@ -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<tmin.length;i++){
int temp;
if(i==0){
temp=currMin+interval;
}else{
temp=tmin[i-1]+interval;
}
if(temp >= 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);
}
}
}

View File

@ -809,7 +809,6 @@
<exclude>org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestCacheMultiThreadedLoad.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestConcurrentDataCache.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestDataCacheScheduler.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestDistributedKodoDataCache.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestFlushDataCache.java</exclude>
<exclude>org/apache/openjpa/persistence/datacache/TestJPQL2Queries.java</exclude>

View File

@ -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++;
}
}

View File

@ -277,7 +277,7 @@ for more information on the <classname>DataCache</classname> annotation.
</indexterm>
A cache can specify that it should be cleared at certain times rather than using
data timeouts. The <literal>EvictionSchedule</literal> property of OpenJPA's
cache implementation accepts a <literal>cron</literal> style eviction schedule.
cache implementation can be input in two different formats. The first is a <literal>cron</literal> style eviction schedule.
The format of this property is a whitespace-separated list of five tokens, where
the <literal>*</literal> symbol (asterisk), indicates match all. The tokens are,
in order:
@ -317,6 +317,20 @@ past 3 PM on Sunday.
<programlisting>
true(EvictionSchedule='15,45 15 * * 1')
</programlisting>
<para>
The second format for this property is an interval style eviction schedule. The
format of this property is a <literal>+</literal> followed by the number of minutes
between each time that the cache should be evicted.
</para>
<para>
For example, the following openjpa.DataCache setting schedules the default cache
to evict values from the cache every 120 minutes.
</para>
<para>
<programlisting>
true(EvictionSchedule='+120')
</programlisting>
</para>
<section id="ref_guide_cache_distribution">
<title>Distributing instances across cache partitions</title>
<para>