blob: 5d3d7e2b397c5c3ee0e3c483f800b0ac9623cf6b [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.history.integration;
import com.intellij.history.*;
import com.intellij.history.core.*;
import com.intellij.history.core.tree.RootEntry;
import com.intellij.history.utils.LocalHistoryLog;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Clock;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
public class LocalHistoryImpl extends LocalHistory implements ApplicationComponent {
private ChangeList myChangeList;
private LocalHistoryFacade myVcs;
private IdeaGateway myGateway;
private LocalHistoryEventDispatcher myEventDispatcher;
private final AtomicBoolean isInitialized = new AtomicBoolean();
private Runnable myShutdownTask;
public static LocalHistoryImpl getInstanceImpl() {
return (LocalHistoryImpl)getInstance();
}
@Override
public void initComponent() {
if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
myShutdownTask = new Runnable() {
@Override
public void run() {
disposeComponent();
}
};
ShutDownTracker.getInstance().registerShutdownTask(myShutdownTask);
initHistory();
isInitialized.set(true);
}
protected void initHistory() {
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();
}
myChangeList = new ChangeList(storage);
myVcs = new LocalHistoryFacade(myChangeList);
myGateway = new IdeaGateway();
myEventDispatcher = new LocalHistoryEventDispatcher(myVcs, myGateway);
CommandProcessor.getInstance().addCommandListener(myEventDispatcher);
VirtualFileManager fm = VirtualFileManager.getInstance();
fm.addVirtualFileListener(myEventDispatcher);
fm.addVirtualFileManagerListener(myEventDispatcher);
if (ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode()) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
validateStorage();
}
});
}
}
private void validateStorage() {
if (ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode()) {
LocalHistoryLog.LOG.info("Checking local history storage...");
try {
long before = Clock.getTime();
myVcs.getChangeListInTests().getChangesInTests();
LocalHistoryLog.LOG.info("Local history storage seems to be ok (took " + ((Clock.getTime() - before) / 1000) + " sec)");
}
catch (Exception e) {
LocalHistoryLog.LOG.error(e);
}
}
}
public File getStorageDir() {
return new File(getSystemPath(), "LocalHistory");
}
protected String getSystemPath() {
return PathManager.getSystemPath();
}
@Override
public void disposeComponent() {
if (!isInitialized.getAndSet(false)) return;
long period = Registry.intValue("localHistory.daysToKeep") * 1000L * 60L * 60L * 24L;
VirtualFileManager fm = VirtualFileManager.getInstance();
fm.removeVirtualFileListener(myEventDispatcher);
fm.removeVirtualFileManagerListener(myEventDispatcher);
CommandProcessor.getInstance().removeCommandListener(myEventDispatcher);
validateStorage();
LocalHistoryLog.LOG.info("Purging local history...");
myChangeList.purgeObsolete(period);
validateStorage();
myChangeList.close();
LocalHistoryLog.LOG.info("Local history storage successfully closed.");
ShutDownTracker.getInstance().unregisterShutdownTask(myShutdownTask);
}
@TestOnly
public void cleanupForNextTest() {
disposeComponent();
FileUtil.delete(getStorageDir());
initComponent();
}
@Override
public LocalHistoryAction startAction(String name) {
if (!isInitialized()) return LocalHistoryAction.NULL;
LocalHistoryActionImpl a = new LocalHistoryActionImpl(myEventDispatcher, name);
a.start();
return a;
}
@Override
public Label putUserLabel(Project p, @NotNull String name) {
if (!isInitialized()) return Label.NULL_INSTANCE;
myGateway.registerUnsavedDocuments(myVcs);
return label(myVcs.putUserLabel(name, getProjectId(p)));
}
private String getProjectId(Project p) {
return p.getLocationHash();
}
@Override
public Label putSystemLabel(Project p, @NotNull String name, int color) {
if (!isInitialized()) return Label.NULL_INSTANCE;
myGateway.registerUnsavedDocuments(myVcs);
return label(myVcs.putSystemLabel(name, getProjectId(p), color));
}
private Label label(final LabelImpl impl) {
return new Label() {
@Override
public ByteContent getByteContent(final String path) {
return ApplicationManager.getApplication().runReadAction(new Computable<ByteContent>() {
@Override
public ByteContent compute() {
RootEntry root = myGateway.createTransientRootEntryForPathOnly(path);
return impl.getByteContent(root, path);
}
});
}
};
}
@Nullable
@Override
public byte[] getByteContent(final VirtualFile f, final FileRevisionTimestampComparator c) {
if (!isInitialized()) return null;
if (!myGateway.areContentChangesVersioned(f)) return null;
return ApplicationManager.getApplication().runReadAction(new Computable<byte[]>() {
@Override
public byte[] compute() {
return new ByteContentRetriever(myGateway, myVcs, f, c).getResult();
}
});
}
@Override
public boolean isUnderControl(VirtualFile f) {
return isInitialized() && myGateway.isVersioned(f);
}
private boolean isInitialized() {
return isInitialized.get();
}
@Override
@NonNls
@NotNull
public String getComponentName() {
return "Local History";
}
@Nullable
public LocalHistoryFacade getFacade() {
return myVcs;
}
@Nullable
public IdeaGateway getGateway() {
return myGateway;
}
}