LH: simplified storage

LH: dumb component in headless environment
This commit is contained in:
Anton Makeev
2010-08-05 13:10:08 +04:00
parent dc7f38b834
commit e5ab7a0ebc
7 changed files with 57 additions and 120 deletions
@@ -21,10 +21,9 @@ import com.intellij.history.core.changes.Change;
import com.intellij.history.core.changes.ChangeSet;
import com.intellij.history.core.changes.ChangeVisitor;
import com.intellij.history.core.storage.Content;
import com.intellij.openapi.application.Application;
import com.intellij.history.utils.LocalHistoryLog;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.TestOnly;
import java.util.ArrayList;
@@ -34,24 +33,23 @@ import java.util.List;
public class ChangeList {
private final ChangeListStorage myStorage;
private ChangeSetBlock myCurrentBlock;
private ChangeSet myCurrentChangeSet;
private int myChangeSetDepth;
private ChangeSet myCurrentChangeSet;
private int myIntervalBetweenActivities = 12 * 60 * 60 * 1000; // one day
public ChangeList(ChangeListStorage storage) {
myStorage = storage;
myCurrentBlock = storage.createNewBlock();
}
public synchronized void save() {
flushChanges(true);
myStorage.flush();
}
public synchronized void close() {
flushChanges(true);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
LocalHistoryLog.LOG.assertTrue(myCurrentChangeSet == null, "current changes won't be saved: " + myCurrentChangeSet);
}
myStorage.close();
}
@@ -73,7 +71,6 @@ public class ChangeList {
private void doBeginChangeSet() {
myCurrentChangeSet = new ChangeSet(myStorage.nextId(), Clock.getCurrentTimestamp());
myCurrentBlock.add(myCurrentChangeSet);
}
public synchronized boolean forceBeginChangeSet() {
@@ -86,7 +83,7 @@ public class ChangeList {
}
public synchronized boolean endChangeSet(String name) {
assert myChangeSetDepth > 0;
LocalHistoryLog.LOG.assertTrue(myChangeSetDepth > 0, "not balanced 'begin/end-change set' calls");
myChangeSetDepth--;
if (myChangeSetDepth > 0) return false;
@@ -96,14 +93,14 @@ public class ChangeList {
private boolean doEndChangeSet(String name) {
if (myCurrentChangeSet.getChanges().isEmpty()) {
myCurrentBlock.removeLast();
myCurrentChangeSet = null;
return false;
}
myCurrentChangeSet.setName(name);
myStorage.writeNextSet(myCurrentChangeSet);
myCurrentChangeSet = null;
flushChanges(false);
return true;
}
@@ -116,13 +113,12 @@ public class ChangeList {
return result;
}
// todo synchronization issue: changeset may me modified while being iterated
public synchronized Iterable<ChangeSet> iterChanges() {
return new Iterable<ChangeSet>() {
public Iterator<ChangeSet> iterator() {
return new Iterator<ChangeSet>() {
private ChangeSetBlock currentBlock;
private Iterator<ChangeSet> currentIter;
private ChangeSetHolder currentBlock;
private ChangeSet next = fetchNext();
public boolean hasNext() {
@@ -138,19 +134,21 @@ public class ChangeList {
private ChangeSet fetchNext() {
if (currentBlock == null) {
synchronized (ChangeList.this) {
currentBlock = myCurrentBlock;
List<ChangeSet> copy = new ArrayList<ChangeSet>(currentBlock.changes);
currentIter = ContainerUtil.iterateBackward(copy).iterator();
if (myCurrentChangeSet != null) {
currentBlock = new ChangeSetHolder(-1, myCurrentChangeSet);
}
else {
currentBlock = myStorage.readPrevious(-1);
}
}
}
while (!currentIter.hasNext()) {
else {
synchronized (ChangeList.this) {
currentBlock = myStorage.readPrevious(currentBlock);
currentBlock = myStorage.readPrevious(currentBlock.id);
}
if (currentBlock == null) return null;
currentIter = ContainerUtil.iterateBackward(currentBlock.changes).iterator();
}
return currentIter.next();
if (currentBlock == null) return null;
return currentBlock.changeSet;
}
public void remove() {
@@ -161,20 +159,6 @@ public class ChangeList {
};
}
private void flushChanges(boolean force) {
if (myChangeSetDepth > 0) return;
if (myCurrentBlock.shouldFlush(force) || flushEveryChangeSetInTests()) {
myStorage.writeNextBlock(myCurrentBlock);
myCurrentBlock = myStorage.createNewBlock();
}
myStorage.flush();
}
private boolean flushEveryChangeSetInTests() {
Application app = ApplicationManager.getApplication();
return app == null || app.isUnitTestMode();
}
public void accept(ChangeVisitor v) {
try {
for (ChangeSet change : iterChanges()) {
@@ -20,21 +20,17 @@ import com.intellij.history.core.changes.ChangeSet;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface ChangeListStorage {
void close();
long nextId();
ChangeSetBlock createNewBlock();
@Nullable
ChangeSetBlock readPrevious(ChangeSetBlock block);
ChangeSetHolder readPrevious(int id);
void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor);
void writeNextBlock(ChangeSetBlock block);
void writeNextSet(ChangeSet changeSet);
void flush();
}
@@ -27,21 +27,16 @@ import com.intellij.util.io.storage.AbstractStorage;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ChangeListStorageImpl implements ChangeListStorage {
private static final int VERSION = 3;
private static final int VERSION = 4;
private static final String STORAGE_FILE = "changes";
private final File myStorageDir;
private final LinkedStorage myStorage;
public ChangeListStorageImpl(File storageDir) {
myStorageDir = storageDir;
try {
myStorage = createStorage(myStorageDir);
myStorage = createStorage(storageDir);
}
catch (IOException e) {
throw handleError(e);
@@ -90,27 +85,18 @@ public class ChangeListStorageImpl implements ChangeListStorage {
return myStorage.nextId();
}
public synchronized ChangeSetBlock createNewBlock() {
return new ChangeSetBlock(0);
}
public synchronized ChangeSetBlock readPrevious(ChangeSetBlock block) {
int prevId = block.id == 0 ? myStorage.getLastRecord() : myStorage.getPrevRecord(block.id);
public synchronized ChangeSetHolder readPrevious(int id) {
int prevId = id == -1 ? myStorage.getLastRecord() : myStorage.getPrevRecord(id);
if (prevId == 0) return null;
assert prevId != block.id;
return doReadBlock(prevId);
}
private ChangeSetBlock doReadBlock(int id) {
private ChangeSetHolder doReadBlock(int id) {
try {
DataInputStream in = myStorage.readStream(id);
try {
int size = in.readInt();
List<ChangeSet> changes = new ArrayList<ChangeSet>(size);
while (size-- > 0) {
changes.add(new ChangeSet(in));
}
return new ChangeSetBlock(id, changes);
return new ChangeSetHolder(id, new ChangeSet(in));
}
finally {
in.close();
@@ -121,15 +107,12 @@ public class ChangeListStorageImpl implements ChangeListStorage {
}
}
public synchronized void writeNextBlock(ChangeSetBlock block) {
public synchronized void writeNextSet(ChangeSet changeSet) {
try {
block.id = myStorage.createNextRecord();
AbstractStorage.StorageDataOutput out = myStorage.writeStream(block.id);
int id = myStorage.createNextRecord();
AbstractStorage.StorageDataOutput out = myStorage.writeStream(id);
try {
out.writeInt(block.changes.size());
for (ChangeSet each : block.changes) {
each.write(out);
}
changeSet.write(out);
}
finally {
out.close();
@@ -143,10 +126,8 @@ public class ChangeListStorageImpl implements ChangeListStorage {
public synchronized void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor) {
int eachBlockId = findFirstObsoleteBlock(period, intervalBetweenActivities);
try {
while(eachBlockId != 0) {
for (ChangeSet eachChangeSet : doReadBlock(eachBlockId).changes) {
processor.consume(eachChangeSet);
}
while (eachBlockId != 0) {
processor.consume(doReadBlock(eachBlockId).changeSet);
myStorage.deleteRecord(eachBlockId);
eachBlockId = myStorage.getPrevRecord(eachBlockId);
}
@@ -20,37 +20,12 @@ import com.intellij.history.core.changes.ChangeSet;
import java.util.ArrayList;
import java.util.List;
public class ChangeSetBlock {
private static final int BLOCK_SIZE = 1000;
public int id;
public final List<ChangeSet> changes;
public class ChangeSetHolder {
public final int id;
public final ChangeSet changeSet;
public ChangeSetBlock(int id) {
public ChangeSetHolder(int id, ChangeSet changeSet) {
this.id = id;
this.changes = new ArrayList<ChangeSet>(BLOCK_SIZE);
}
ChangeSetBlock(int id, List<ChangeSet> changes) {
this.id = id;
this.changes = changes;
}
public void add(ChangeSet changeSet) {
changes.add(changeSet);
}
public void removeLast() {
changes.remove(changes.size() - 1);
}
public boolean shouldFlush(boolean force) {
int count = 0;
for (ChangeSet each : changes) {
count += each.getChanges().size();
if (count >= BLOCK_SIZE) return true;
if (force && count > 0) return true;
}
return false;
this.changeSet = changeSet;
}
}
@@ -35,6 +35,7 @@ import com.intellij.openapi.vfs.ex.VirtualFileManagerEx;
import com.intellij.util.io.storage.HeavyProcessLatch;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.File;
@@ -58,6 +59,8 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
}
public void initComponent() {
if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
myShutdownTask = new Runnable() {
public void run() {
disposeComponent();
@@ -193,10 +196,12 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
return "Local History";
}
@Nullable
public LocalHistoryFacade getFacade() {
return myVcs;
}
@Nullable
public IdeaGateway getGateway() {
return myGateway;
}
@@ -23,6 +23,7 @@ import com.intellij.history.core.changes.ContentChange;
import com.intellij.history.core.changes.StructuralChange;
import com.intellij.history.integration.IdeaGateway;
import com.intellij.history.integration.LocalHistoryImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -50,11 +51,12 @@ public class FileUndoProvider extends VirtualFileAdapter implements UndoProvider
}
public FileUndoProvider(Project project) {
myProject = project;
myProject = project;
if (myProject == null) return;
myLocalHistory = LocalHistoryImpl.getInstanceImpl().getFacade();
myGateway = LocalHistoryImpl.getInstanceImpl().getGateway();
if (myLocalHistory == null || myGateway == null) return; // local history was not initialized (e.g. in headless environment)
getFileManager().addVirtualFileListener(this, project);
myLocalHistory.addListener(new LocalHistoryFacade.Listener() {
@@ -23,7 +23,7 @@ import java.util.List;
public class InMemoryChangeListStorage implements ChangeListStorage {
private int myCurrentId;
private List<ChangeSetBlock> myBlocks = new ArrayList<ChangeSetBlock>();
private List<ChangeSet> mySets = new ArrayList<ChangeSet>();
public void close() {
}
@@ -32,20 +32,14 @@ public class InMemoryChangeListStorage implements ChangeListStorage {
return myCurrentId++;
}
public ChangeSetBlock createNewBlock() {
return new ChangeSetBlock(-1);
public ChangeSetHolder readPrevious(int id) {
if (mySets.isEmpty()) return null;
if (id == -1) return new ChangeSetHolder(mySets.size() - 1, mySets.get(mySets.size() - 1));
return id == 0 ? null : new ChangeSetHolder(id -1, mySets.get(id - 1));
}
public ChangeSetBlock readPrevious(ChangeSetBlock block) {
if (myBlocks.isEmpty()) return null;
if (block.id == 0) return null;
if (block.id == -1) return myBlocks.get(myBlocks.size() - 1);
return myBlocks.get(block.id - 1);
}
public void writeNextBlock(ChangeSetBlock block) {
myBlocks.add(block);
block.id = myBlocks.size() - 1;
public void writeNextSet(ChangeSet changeSet) {
mySets.add(changeSet);
}
public void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor) {