Switch from TimerTask to ScheduledExecutorService for more robustness

This commit is contained in:
Tom Morris 2013-08-18 11:31:03 -04:00
parent 25a3de410f
commit 4529310237
2 changed files with 27 additions and 30 deletions

View File

@ -41,8 +41,9 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Timer; import java.util.concurrent.Executors;
import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -80,23 +81,19 @@ public class RefineServlet extends Butterfly {
static final private Map<String, Command> commands = new HashMap<String, Command>(); static final private Map<String, Command> commands = new HashMap<String, Command>();
// timer for periodically saving projects // timer for periodically saving projects
static private Timer _timer = new Timer("autosave"); static private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
static final Logger logger = LoggerFactory.getLogger("refine"); static final Logger logger = LoggerFactory.getLogger("refine");
static final protected long s_autoSavePeriod = 1000 * 60 * 5; // 5 minutes static final protected long AUTOSAVE_PERIOD = 5; // 5 minutes
static protected class AutoSaveTimerTask extends TimerTask { static protected class AutoSaveTimerTask implements Runnable {
@Override @Override
public void run() { public void run() {
try { try {
ProjectManager.singleton.save(false); // quick, potentially incomplete save ProjectManager.singleton.save(false); // quick, potentially incomplete save
} finally { } catch (final Throwable e) {
if (_timer != null) { // Not the best, but we REALLY want this to keep trying
_timer.schedule(new AutoSaveTimerTask(), s_autoSavePeriod);
// we don't use scheduleAtFixedRate because that might result in
// bunched up events when the computer is put in sleep mode
}
} }
} }
} }
@ -134,7 +131,8 @@ public class RefineServlet extends Butterfly {
FileProjectManager.initialize(s_dataDir); FileProjectManager.initialize(s_dataDir);
ImportingManager.initialize(this); ImportingManager.initialize(this);
_timer.schedule(new AutoSaveTimerTask(), s_autoSavePeriod); service.scheduleWithFixedDelay(new AutoSaveTimerTask(), AUTOSAVE_PERIOD,
AUTOSAVE_PERIOD, TimeUnit.MINUTES);
logger.trace("< initialize"); logger.trace("< initialize");
} }

View File

@ -35,6 +35,8 @@ package com.google.refine.importing;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -43,8 +45,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.concurrent.Executors;
import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.json.JSONException; import org.json.JSONException;
@ -104,29 +107,25 @@ public class ImportingManager {
final static public Map<String, ImportingController> controllers = new HashMap<String, ImportingController>(); final static public Map<String, ImportingController> controllers = new HashMap<String, ImportingController>();
// timer for periodically deleting stale importing jobs // timer for periodically deleting stale importing jobs
static private Timer _timer; static private ScheduledExecutorService service;
final static private long s_timerPeriod = 1000 * 60 * 10; // 10 minutes final static private long TIMER_PERIOD = 10; // 10 minutes
final static private long s_stalePeriod = 1000 * 60 * 60; // 60 minutes final static private long STALE_PERIOD = 60; // 60 minutes
static private class CleaningTimerTask extends TimerTask { static private class CleaningTimerTask implements Runnable {
@Override @Override
public void run() { public void run() {
try { // An exception here will keep future runs of this task from happening,
cleanUpStaleJobs(); // but won't affect other timer tasks
} finally { cleanUpStaleJobs();
_timer.schedule(new CleaningTimerTask(), s_timerPeriod);
// we don't use scheduleAtFixedRate because that might result in
// bunched up events when the computer is put in sleep mode
}
} }
} }
static public void initialize(RefineServlet servlet) { static public void initialize(RefineServlet servlet) {
ImportingManager.servlet = servlet; ImportingManager.servlet = servlet;
_timer = new Timer("autosave"); service = Executors.newSingleThreadScheduledExecutor();
_timer.schedule(new CleaningTimerTask(), s_timerPeriod); service.scheduleWithFixedDelay(new CleaningTimerTask(), TIMER_PERIOD, TIMER_PERIOD, TimeUnit.MINUTES);
} }
static public void registerFormat(String format, String label) { static public void registerFormat(String format, String label) {
@ -289,13 +288,13 @@ public class ImportingManager {
static private void cleanUpStaleJobs() { static private void cleanUpStaleJobs() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
HashSet<Long> keys; Collection<Long> keys;
synchronized(jobs) { synchronized(jobs) {
keys = new HashSet<Long>(jobs.keySet()); keys = new ArrayList<Long>(jobs.keySet());
} }
for (Long id : keys) { for (Long id : keys) {
ImportingJob job = jobs.get(id); ImportingJob job = jobs.get(id);
if (job != null && !job.updating && now - job.lastTouched > s_stalePeriod) { if (job != null && !job.updating && now - job.lastTouched > STALE_PERIOD) {
job.dispose(); job.dispose();
jobs.remove(id); jobs.remove(id);
logger.info("Disposed " + id); logger.info("Disposed " + id);