mirror of https://github.com/apache/openjpa.git
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:
parent
c9e2d17fd5
commit
be33af366c
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue