IDEA-170230 Message "Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA" when leaving the IDEA window of a big project

Add LargeFileWriteRequestor marker interface to not assert file size.
This commit is contained in:
Vladimir Krivosheev
2017-03-29 18:52:19 +02:00
parent 694a452435
commit 34bd954333
6 changed files with 59 additions and 21 deletions
@@ -28,6 +28,7 @@ import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.fileEditor.impl.LoadTextUtil
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.ArrayUtil
@@ -196,7 +197,7 @@ fun writeFile(file: Path?, requestor: Any, virtualFile: VirtualFile?, element: E
virtualFile!!
}
if (LOG.isDebugEnabled || ApplicationManager.getApplication().isUnitTestMode) {
if ((LOG.isDebugEnabled || ApplicationManager.getApplication ().isUnitTestMode) && result.length < FileUtilRt.LARGE_FOR_CONTENT_LOADING) {
val content = element.toBufferExposingByteArray(lineSeparator.separatorString)
if (isEqualContent(result, lineSeparator, content, prependXmlProlog)) {
throw IllegalStateException("Content equals, but it must be handled not on this level: ${result.name}")
@@ -18,13 +18,14 @@ package com.intellij.configurationStore
import com.intellij.openapi.components.StateStorage
import com.intellij.openapi.util.JDOMExternalizable
import com.intellij.openapi.util.WriteExternalException
import com.intellij.openapi.vfs.LargeFileWriteRequestor
import com.intellij.openapi.vfs.SafeWriteRequestor
import com.intellij.reference.SoftReference
import com.intellij.util.xmlb.SkipDefaultsSerializationFilter
import com.intellij.util.xmlb.XmlSerializer
import org.jdom.Element
abstract class SaveSessionBase : StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor {
abstract class SaveSessionBase : StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor, LargeFileWriteRequestor {
override final fun setState(component: Any?, componentName: String, state: Any) {
val element: Element?
try {
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.ex.ProjectEx
import com.intellij.openapi.project.impl.ProjectImpl
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.project.stateStore
import com.intellij.testFramework.*
import com.intellij.testFramework.assertions.Assertions.assertThat
@@ -81,6 +82,13 @@ internal class ProjectStoreTest {
(ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedProjectFileAlarm()
assertThat(testComponent.state).isEqualTo(TestState("newValue"))
testComponent.state!!.value = "s".repeat(FileUtilRt.LARGE_FOR_CONTENT_LOADING + 1024)
project.saveStore()
// we should save twice (first call - virtual file size is not yet set)
testComponent.state!!.value = "b".repeat(FileUtilRt.LARGE_FOR_CONTENT_LOADING + 1024)
project.saveStore()
}
}
@@ -151,7 +159,7 @@ internal class ProjectStoreTest {
// test exact string - xml prolog, line separators, indentation and so on must be exactly the same
// todo get rid of default component states here
assertThat(file.readText()).startsWith(iprFileContent.replace("customValue", "foo").replace("</project>", ""))
return testComponent
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2017 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.openapi.vfs;
/**
* A marker interface for {@link VirtualFile#getOutputStream(Object)} to not assert file content size.
* @see com.intellij.openapi.util.io.FileUtilRt#isTooLarge
*/
public interface LargeFileWriteRequestor {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -22,6 +22,7 @@ package com.intellij.openapi.vfs.newvfs.impl;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileTooBigException;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.LargeFileWriteRequestor;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
@@ -34,7 +35,10 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
@@ -121,7 +125,7 @@ public class VirtualFileImpl extends VirtualFileSystemEntry {
@NotNull
@Override
public byte[] contentsToByteArray(boolean cacheContent) throws IOException {
checkNotTooLarge();
checkNotTooLarge(null);
final byte[] preloadedContent = getUserData(ourPreloadedContentKey);
if (preloadedContent != null) return preloadedContent;
return ourPersistence.contentsToByteArray(this, cacheContent);
@@ -130,19 +134,19 @@ public class VirtualFileImpl extends VirtualFileSystemEntry {
@Override
@NotNull
public OutputStream getOutputStream(final Object requestor, final long modStamp, final long timeStamp) throws IOException {
checkNotTooLarge();
checkNotTooLarge(requestor);
return VfsUtilCore.outputStreamAddingBOM(ourPersistence.getOutputStream(this, requestor, modStamp, timeStamp), this);
}
@Override
public void setBinaryContent(@NotNull byte[] content, long newModificationStamp, long newTimeStamp, Object requestor) throws IOException {
checkNotTooLarge();
checkNotTooLarge(requestor);
super.setBinaryContent(content, newModificationStamp, newTimeStamp, requestor);
}
@Override
public void setBinaryContent(@NotNull byte[] content, long newModificationStamp, long newTimeStamp) throws IOException {
checkNotTooLarge();
checkNotTooLarge(null);
super.setBinaryContent(content, newModificationStamp, newTimeStamp);
}
@@ -178,8 +182,8 @@ public class VirtualFileImpl extends VirtualFileSystemEntry {
return mySegment.changeUserMap(myId, oldMap, UserDataInterner.internUserData(newMap));
}
private void checkNotTooLarge() throws FileTooBigException {
if (isTooLarge()) throw new FileTooBigException(getPath());
private void checkNotTooLarge(@Nullable Object requestor) throws FileTooBigException {
if (!(requestor instanceof LargeFileWriteRequestor) && isTooLarge()) throw new FileTooBigException(getPath());
}
private boolean isTooLarge() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -15,6 +15,7 @@
*/
package com.intellij.configurationStore
import com.intellij.util.io.IOUtil
import org.jdom.*
import java.io.DataInputStream
import java.io.DataOutputStream
@@ -32,7 +33,7 @@ fun serializeElementToBinary(element: Element, out: OutputStream) {
fun readElement(input: InputStream) = readElement(DataInputStream(input))
fun readElement(input: DataInputStream): Element {
val element = Element(input.readUTF())
val element = Element(IOUtil.readUTF(input))
readAttributes(element, input)
readContent(element, input)
return element
@@ -42,15 +43,15 @@ private fun readContent(element: Element, input: DataInputStream) {
while (true) {
when (input.read()) {
TypeMarker.ELEMENT.ordinal -> element.addContent(readElement(input))
TypeMarker.TEXT.ordinal -> element.addContent(Text(input.readUTF()))
TypeMarker.CDATA.ordinal -> element.addContent(CDATA(input.readUTF()))
TypeMarker.TEXT.ordinal -> element.addContent(Text(IOUtil.readUTF(input)))
TypeMarker.CDATA.ordinal -> element.addContent(CDATA(IOUtil.readUTF(input)))
TypeMarker.ELEMENT_END.ordinal -> return
}
}
}
private fun writeElement(element: Element, out: DataOutputStream) {
out.writeUTF(element.name)
IOUtil.writeUTF(out, element.name)
writeAttributes(out, element.attributes)
@@ -63,12 +64,12 @@ private fun writeElement(element: Element, out: DataOutputStream) {
else if (item is Text) {
if (!isAllWhitespace(item)) {
out.writeByte(TypeMarker.TEXT.ordinal)
out.writeUTF(item.text)
IOUtil.writeUTF(out, item.text)
}
}
else if (item is CDATA) {
out.writeByte(TypeMarker.CDATA.ordinal)
out.writeUTF(item.text)
IOUtil.writeUTF(out, item.text)
}
}
out.writeByte(TypeMarker.ELEMENT_END.ordinal)
@@ -86,8 +87,8 @@ private fun writeAttributes(out: DataOutputStream, attributes: List<Attribute>?)
}
else {
for (attribute in attributes!!) {
out.writeUTF(attribute.name)
out.writeUTF(attribute.value)
IOUtil.writeUTF(out, attribute.name)
IOUtil.writeUTF(out, attribute.value)
}
}
}
@@ -95,7 +96,7 @@ private fun writeAttributes(out: DataOutputStream, attributes: List<Attribute>?)
private fun readAttributes(element: Element, input: DataInputStream) {
val size = input.readUnsignedByte()
for (i in 0..size - 1) {
element.setAttribute(Attribute(input.readUTF(), input.readUTF()))
element.setAttribute(Attribute(IOUtil.readUTF(input), IOUtil.readUTF(input)))
}
}