diff --git a/platform/configuration-store-impl/src/FileBasedStorage.kt b/platform/configuration-store-impl/src/FileBasedStorage.kt index 1a8c483cf3f9..7b4e399e7c37 100644 --- a/platform/configuration-store-impl/src/FileBasedStorage.kt +++ b/platform/configuration-store-impl/src/FileBasedStorage.kt @@ -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}") diff --git a/platform/configuration-store-impl/src/SaveSessionBase.kt b/platform/configuration-store-impl/src/SaveSessionBase.kt index 75fad25736bd..837eca66e3bc 100644 --- a/platform/configuration-store-impl/src/SaveSessionBase.kt +++ b/platform/configuration-store-impl/src/SaveSessionBase.kt @@ -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 { diff --git a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt index 69c6bbb29a32..cd141054fd3f 100644 --- a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt @@ -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("", "")) - + return testComponent } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/LargeFileWriteRequestor.java b/platform/platform-api/src/com/intellij/openapi/vfs/LargeFileWriteRequestor.java new file mode 100644 index 000000000000..0b0c5da4d135 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/vfs/LargeFileWriteRequestor.java @@ -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 { +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java index 87431391ef7e..7e04ee3d3c89 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java @@ -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() { diff --git a/platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt b/platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt index 35ddf8f71407..bb90ebcfd677 100644 --- a/platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt +++ b/platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt @@ -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?) } 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?) 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))) } }