diff --git a/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java b/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java index 3186e84535be..241ed223ece2 100644 --- a/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java +++ b/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java @@ -18,6 +18,10 @@ package com.intellij.history.core; import com.intellij.history.core.changes.ChangeSet; import com.intellij.history.utils.LocalHistoryLog; +import com.intellij.ide.BrowserUtil; +import com.intellij.ide.actions.ShowFilePathAction; +import com.intellij.notification.*; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.newvfs.ManagingFS; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; @@ -27,6 +31,7 @@ import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.event.HyperlinkEvent; import java.io.DataInputStream; import java.io.File; import java.io.IOException; @@ -36,19 +41,19 @@ public class ChangeListStorageImpl implements ChangeListStorage { private static final int VERSION = 5; private static final String STORAGE_FILE = "changes"; - private final LocalHistoryStorage myStorage; + private final File myStorageDir; + private LocalHistoryStorage myStorage; - public ChangeListStorageImpl(File storageDir) { - try { - myStorage = createStorage(storageDir); - } - catch (IOException e) { - throw handleError(e); - } + private boolean isCompletelyBroken = false; + + public ChangeListStorageImpl(File storageDir) throws IOException { + myStorageDir = storageDir; + myStorage = createStorage(myStorageDir); } private static LocalHistoryStorage createStorage(File storageDir) throws IOException { String path = storageDir.getPath() + "/" + STORAGE_FILE; + LocalHistoryStorage result = new LocalHistoryStorage(path); long fsTimestamp = getVFSTimestamp(); @@ -77,26 +82,63 @@ public class ChangeListStorageImpl implements ChangeListStorage { return ((PersistentFS)ManagingFS.getInstance()).getCreationTimestamp(); } - private RuntimeException handleError(Throwable e) { + private void handleError(Throwable e) { long storageTimestamp = -1; - try { - if (myStorage != null) { - myStorage.setVersion(-1); - myStorage.force(); - storageTimestamp = myStorage.getFSTimestamp(); - } - } - catch (Throwable ex) { - LocalHistoryLog.LOG.error("cannot mark storage as broken", ex); - } long vfsTimestamp = getVFSTimestamp(); long timestamp = System.currentTimeMillis(); - throw new RuntimeException("Local history is broken and will be rebuilt after restart " + - "(version:" + VERSION + - ",current timestamp:" + timestamp + - ",storage timestamp:" + storageTimestamp + - ",vfs timestamp:" + vfsTimestamp + ")", e); + + try { + storageTimestamp = myStorage.getFSTimestamp(); + } + catch (Exception ex) { + LocalHistoryLog.LOG.warn("cannot read storage timestamp", ex); + } + + LocalHistoryLog.LOG.warn("Local history is broken" + + "(version:" + VERSION + + ",current timestamp:" + timestamp + + ",storage timestamp:" + storageTimestamp + + ",vfs timestamp:" + vfsTimestamp + ")", e); + + myStorage.dispose(); + try { + FileUtil.delete(myStorageDir); + myStorage = createStorage(myStorageDir); + } + catch (Throwable ex) { + LocalHistoryLog.LOG.warn("cannot recreate storage", ex); + isCompletelyBroken = true; + } + + notifyUser("Local History storage file has become corrupted and was rebuilt."); + } + + public static void notifyUser(String message) { + final String logFile = PathManager.getLogPath(); + + Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, + "Local History is broken", + message + "
" + + "
" + + "Please create a YouTrack issue
" + + "and attach log files from
" + logFile, + NotificationType.ERROR, + new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + if ("url".equals(event.getDescription())) { + BrowserUtil.launchBrowser("http://youtrack.jetbrains.net/issues/#newissue=yes"); + } + else { + File file = new File(logFile); + ShowFilePathAction.open(file, new File(logFile)); + } + } + }), + NotificationDisplayType.STICKY_BALLOON, + null); } public synchronized void close() { @@ -104,11 +146,21 @@ public class ChangeListStorageImpl implements ChangeListStorage { } public synchronized long nextId() { - return myStorage.nextId(); + if (isCompletelyBroken) return 0; + + try { + return myStorage.nextId(); + } + catch (Throwable e) { + handleError(e); + return myStorage.nextId(); + } } @Nullable public synchronized ChangeSetHolder readPrevious(int id, TIntHashSet recursionGuard) { + if (isCompletelyBroken) return null; + try { int prevId = id == -1 ? myStorage.getLastRecord() : doReadPrevSafely(id, recursionGuard); if (prevId == 0) return null; @@ -116,7 +168,8 @@ public class ChangeListStorageImpl implements ChangeListStorage { return doReadBlock(prevId); } catch (Throwable e) { - throw handleError(e); + handleError(e); + return null; } } @@ -132,6 +185,8 @@ public class ChangeListStorageImpl implements ChangeListStorage { } public synchronized void writeNextSet(ChangeSet changeSet) { + if (isCompletelyBroken) return; + try { int id = myStorage.createNextRecord(); AbstractStorage.StorageDataOutput out = myStorage.writeStream(id); @@ -144,11 +199,13 @@ public class ChangeListStorageImpl implements ChangeListStorage { myStorage.force(); } catch (IOException e) { - throw handleError(e); + handleError(e); } } public synchronized void purge(long period, int intervalBetweenActivities, Consumer processor) { + if (isCompletelyBroken) return; + TIntHashSet recursionGuard = new TIntHashSet(1000); try { @@ -163,7 +220,7 @@ public class ChangeListStorageImpl implements ChangeListStorage { myStorage.force(); } catch (IOException e) { - throw handleError(e); + handleError(e); } } diff --git a/platform/lvcs-impl/testSrc/com/intellij/history/core/InMemoryChangeListStorage.java b/platform/lvcs-impl/src/com/intellij/history/core/InMemoryChangeListStorage.java similarity index 97% rename from platform/lvcs-impl/testSrc/com/intellij/history/core/InMemoryChangeListStorage.java rename to platform/lvcs-impl/src/com/intellij/history/core/InMemoryChangeListStorage.java index 4307f3d44248..5c9e04d5f89e 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/history/core/InMemoryChangeListStorage.java +++ b/platform/lvcs-impl/src/com/intellij/history/core/InMemoryChangeListStorage.java @@ -51,6 +51,5 @@ public class InMemoryChangeListStorage implements ChangeListStorage { @Override public void purge(long period, int intervalBetweenActivities, Consumer processor) { - throw new UnsupportedOperationException(); } } diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java index aa6196b17c7f..ff9d30eaecc1 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java @@ -70,7 +70,16 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone } protected void initHistory() { - myChangeList = new ChangeList(new ChangeListStorageImpl(getStorageDir())); + ChangeListStorage storage; + try { + storage = new ChangeListStorageImpl(getStorageDir()); + } + catch (Throwable e) { + LocalHistoryLog.LOG.warn("cannot create storage, in-memory implementation will be used", e); + storage = new InMemoryChangeListStorage(); + ChangeListStorageImpl.notifyUser("Local History storage file cannot be created. In-memory storage will be used."); + } + myChangeList = new ChangeList(storage); myVcs = new LocalHistoryFacade(myChangeList); myGateway = new IdeaGateway();