blob: df861dd7fade54d65e0aa36ae81b9f15e1e73e07 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 Tasktop Technologies and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui;
import java.util.Map;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.internal.tasks.core.ITaskDataStorage;
import org.eclipse.mylyn.internal.tasks.core.TaskDataState;
/**
* @deprecated Do not use. This class is pending for removal: see bug 237552.
*/
@Deprecated
public class OfflineCachingStorage implements ITaskDataStorage {
private static final int DEFAULT_FLUSH_INTERVAL = 60 * 1000;
private static final int MAX_READ_QUEUE_SIZE = 80;
private final Map<String, Map<String, TaskDataState>> readCache = new ConcurrentHashMap<String, Map<String, TaskDataState>>();
private final Map<String, Map<String, TaskDataState>> writeCache = new ConcurrentHashMap<String, Map<String, TaskDataState>>();
private final Queue<TaskDataState> lruQueue = new ConcurrentLinkedQueue<TaskDataState>();
private final ITaskDataStorage storage;
private CacheFlushJob cacheFlushJob;
private Timer cacheFlushTimer;
public OfflineCachingStorage(ITaskDataStorage storage) {
this.storage = storage;
}
/**
* FOR TESTING PURPOSES DESTROYS ALL DATA IN STORAGE
*/
public void clear() {
if (cacheFlushJob != null) {
cacheFlushJob.waitSaveCompleted();
}
readCache.clear();
writeCache.clear();
lruQueue.clear();
storage.clear();
}
public void flush() {
cacheFlushJob.waitSaveCompleted();
persistToStorage();
}
public TaskDataState get(String repositoryUrl, String id) {
TaskDataState result = null;
result = retrieveFromCache(writeCache, repositoryUrl, id);
if (result == null) {
result = retrieveFromCache(readCache, repositoryUrl, id);
}
if (result == null) {
result = retrieveFromStorage(repositoryUrl, id);
}
if (result != null) {
pushRead(result);
}
return result;
}
private TaskDataState retrieveFromCache(Map<String, Map<String, TaskDataState>> cache, String repositoryUrl,
String id) {
Map<String, TaskDataState> idMap = cache.get(repositoryUrl);
if (idMap != null) {
return idMap.get(id);
}
return null;
}
private TaskDataState retrieveFromStorage(String repositoryUrl, String id) {
TaskDataState result = null;
synchronized (readCache) {
Map<String, TaskDataState> idMap = readCache.get(repositoryUrl);
if (idMap == null) {
idMap = new ConcurrentHashMap<String, TaskDataState>();
readCache.put(repositoryUrl, idMap);
} else {
result = idMap.get(id);
}
if (result == null) {
result = storage.get(repositoryUrl, id);
if (result != null) {
idMap.put(id, result);
}
}
}
return result;
}
public void put(TaskDataState taskDataState) {
putReadCache(taskDataState);
putWriteCache(taskDataState);
if (cacheFlushJob != null) {
cacheFlushJob.requestSave();
}
}
public void remove(String repositoryUrl, String id) {
Map<String, TaskDataState> idMap = writeCache.get(repositoryUrl);
if (idMap != null) {
idMap.remove(id);
}
idMap = readCache.get(repositoryUrl);
if (idMap != null) {
idMap.remove(id);
}
lruQueue.remove(new TaskDataState(repositoryUrl, id));
storage.remove(repositoryUrl, id);
}
public void start() throws Exception {
storage.start();
if (cacheFlushTimer == null && cacheFlushJob == null) {
cacheFlushTimer = new Timer();
cacheFlushTimer.schedule(new RequestSaveTimerTask(), DEFAULT_FLUSH_INTERVAL, DEFAULT_FLUSH_INTERVAL);
cacheFlushJob = new CacheFlushJob();
cacheFlushJob.schedule();
}
}
public void stop() throws Exception {
cacheFlushTimer.cancel();
cacheFlushJob.cancel();
this.flush();
cacheFlushTimer = null;
cacheFlushJob = null;
storage.stop();
}
private void pushRead(TaskDataState state) {
lruQueue.remove(state);
lruQueue.add(state);
if (lruQueue.size() > MAX_READ_QUEUE_SIZE) {
flushReadCache(false);
}
}
private void putReadCache(TaskDataState taskDataState) {
if (taskDataState == null) {
return;
}
synchronized (readCache) {
Map<String, TaskDataState> idMap = readCache.get(taskDataState.getUrl());
if (idMap == null) {
idMap = new ConcurrentHashMap<String, TaskDataState>();
readCache.put(taskDataState.getUrl(), idMap);
}
idMap.put(taskDataState.getId(), taskDataState);
}
pushRead(taskDataState);
}
private synchronized void putWriteCache(TaskDataState taskDataState) {
if (taskDataState == null) {
return;
}
Map<String, TaskDataState> idMap = writeCache.get(taskDataState.getUrl());
if (idMap == null) {
idMap = new ConcurrentHashMap<String, TaskDataState>();
writeCache.put(taskDataState.getUrl(), idMap);
}
idMap.put(taskDataState.getId(), taskDataState);
}
private void persistToStorage() {
synchronized (writeCache) {
for (Map<String, TaskDataState> idMap : writeCache.values()) {
for (TaskDataState state : idMap.values()) {
storage.put(state);
}
idMap.clear();
}
}
}
private class CacheFlushJob extends Job {
private volatile boolean saveRequested = false;
private volatile boolean saveCompleted = true;
CacheFlushJob() {
super("Flush Cache Job");
setPriority(Job.LONG);
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
while (true) {
if (saveRequested) {
saveRequested = false;
saveCompleted = false;
try {
persistToStorage();
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Error saving offline cache", t));
}
}
if (!saveRequested) {
synchronized (this) {
saveCompleted = true;
notifyAll();
try {
wait();
} catch (InterruptedException ex) {
// ignore
}
}
}
}
}
void requestSave() {
saveRequested = true;
}
void runRequested() {
synchronized (this) {
notifyAll();
}
}
void waitSaveCompleted() {
while (!saveCompleted) {
synchronized (this) {
try {
wait();
} catch (InterruptedException ex) {
// ignore
}
}
}
}
}
private class RequestSaveTimerTask extends TimerTask {
@Override
public void run() {
if (!Platform.isRunning()) {
return;
} else {
flushReadCache(false);
cacheFlushJob.runRequested();
}
}
}
/**
* @param reset
* if true all read cached data is dropped if false only remove until cache lower than
* MAX_READ_QUEUE_SIZE
*/
public void flushReadCache(boolean reset) {
if (reset) {
lruQueue.clear();
readCache.clear();
} else {
while (lruQueue.size() > MAX_READ_QUEUE_SIZE / 2) {
TaskDataState state = lruQueue.poll();
if (state != null) {
Map<String, TaskDataState> tasksMap = readCache.get(state.getUrl());
if (tasksMap != null) {
tasksMap.remove(state.getId());
}
}
}
}
}
/**
* For testing...
*/
public Queue<TaskDataState> getReadQueue() {
return lruQueue;
}
public Map<String, Map<String, TaskDataState>> getReadCache() {
return readCache;
}
public Map<String, Map<String, TaskDataState>> getWriteCache() {
return writeCache;
}
}