IDEA-CR-12256 client should not close input stream

This commit is contained in:
Vladimir Krivosheev
2016-07-19 11:31:02 +02:00
parent f443213a74
commit 3f7b89e37c
10 changed files with 91 additions and 82 deletions
@@ -2,10 +2,19 @@
<item name='java.nio.file.FileSystem java.nio.file.Path getPath(java.lang.String, java.lang.String...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.nio.file.Files byte[] readAllBytes(java.nio.file.Path)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.nio.file.Files java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.nio.file.Files java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.nio.file.OpenOption...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.nio.file.Path java.nio.file.Path getParent()'>
<annotation name='org.jetbrains.annotations.Nullable'/>
</item>
<item name='java.nio.file.Path java.nio.file.Path resolve(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -259,11 +259,9 @@ class SchemeManagerImpl<T : Scheme, E : ExternalizableScheme>(val fileSpec: Stri
if (provider != null && provider.enabled) {
provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input, readOnly ->
catchAndLog(name) {
input.use {
val scheme = loadScheme(name, it, true)
if (readOnly && scheme != null) {
readOnlyExternalizableSchemes.put(scheme.name, scheme)
}
val scheme = loadScheme(name, input, true)
if (readOnly && scheme != null) {
readOnlyExternalizableSchemes.put(scheme.name, scheme)
}
}
true
@@ -277,7 +275,7 @@ class SchemeManagerImpl<T : Scheme, E : ExternalizableScheme>(val fileSpec: Stri
}
catchAndLog(file.fileName.toString()) { filename ->
file.inputStream()?.use { loadScheme(filename, it, true) }
file.inputStream().use { loadScheme(filename, it, true) }
}
}
}
@@ -20,10 +20,10 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import java.io.InputStream
interface StreamProvider {
open val enabled: Boolean
val enabled: Boolean
get() = true
open fun isApplicable(fileSpec: String, roamingType: RoamingType = RoamingType.DEFAULT): Boolean = true
fun isApplicable(fileSpec: String, roamingType: RoamingType = RoamingType.DEFAULT): Boolean = true
/**
* @param fileSpec
@@ -34,9 +34,6 @@ interface StreamProvider {
fun read(fileSpec: String, roamingType: RoamingType = RoamingType.DEFAULT): InputStream?
/**
* You must close passed input stream.
*/
fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean)
/**
@@ -1,53 +1,48 @@
package com.intellij.configurationStore
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.SmartList
import java.io.File
import java.io.FileInputStream
import com.intellij.util.*
import java.io.InputStream
import java.nio.file.NoSuchFileException
import java.nio.file.Path
class MockStreamProvider(private val myBaseDir: File) : StreamProvider {
class MockStreamProvider(private val dir: Path) : StreamProvider {
override fun write(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
FileUtil.writeToFile(File(myBaseDir, fileSpec), content, 0, size)
dir.resolve(fileSpec).write(content, 0, size)
}
override fun read(fileSpec: String, roamingType: RoamingType): InputStream? {
val file = File(myBaseDir, fileSpec)
//noinspection IOResourceOpenedButNotSafelyClosed
return if (file.exists()) FileInputStream(file) else null
val file = dir.resolve(fileSpec)
try {
return file.inputStream()
}
catch (e: NoSuchFileException) {
return null
}
}
private fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection<String> {
if (roamingType !== RoamingType.DEFAULT) {
return emptyList()
}
val files = File(myBaseDir, fileSpec).listFiles() ?: return emptyList()
val names = SmartList<String>()
for (file in files) {
names.add(file.name)
}
return names
}
/**
* You must close passed input stream.
*/
override fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean) {
for (name in listSubFiles(path, roamingType)) {
if (!filter(name)) {
continue
}
dir.resolve(path).directoryStreamIfExists({ filter(it.fileName.toString()) }) {
for (file in it) {
val attributes = file.basicAttributesIfExists()
if (attributes == null || attributes.isDirectory || file.isHidden()) {
continue
}
val input = read("$path/$name", roamingType)
if (input != null && !processor(name, input, false)) {
break
// we ignore empty files as well - delete if corrupted
if (attributes.size() == 0L) {
file.delete()
continue
}
if (!file.inputStream().use { processor(file.fileName.toString(), it, false) }) {
break
}
}
}
}
override fun delete(fileSpec: String, roamingType: RoamingType) {
FileUtil.delete(File(myBaseDir, fileSpec))
dir.resolve(fileSpec).delete()
}
}
@@ -324,7 +324,7 @@ internal class SchemeManagerTest {
}
private fun createAndLoad(): SchemeManagerImpl<TestScheme, TestScheme> {
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), MockStreamProvider(remoteBaseDir!!.toFile()), localBaseDir!!)
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), MockStreamProvider(remoteBaseDir!!), localBaseDir!!)
schemesManager.loadSchemes()
return schemesManager
}
@@ -21,13 +21,15 @@ import com.intellij.openapi.vfs.VfsUtil
import org.jetbrains.io.readCharSequence
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
fun Path.exists() = Files.exists(this)
fun Path.createDirectories() = Files.createDirectories(this)
fun Path.createDirectories(): Path = Files.createDirectories(this)
/**
* Opposite to Java, parent directories will be created
@@ -37,7 +39,7 @@ fun Path.outputStream(): OutputStream {
return Files.newOutputStream(this)
}
fun Path.inputStream() = Files.newInputStream(this)
fun Path.inputStream(): InputStream = Files.newInputStream(this)
/**
* Opposite to Java, parent directories will be created
@@ -71,7 +73,7 @@ fun Path.deleteRecursively(): Path = if (exists()) Files.walkFileTree (this, obj
}
}) else this
fun Path.lastModified() = Files.getLastModifiedTime(this)
fun Path.lastModified(): FileTime = Files.getLastModifiedTime(this)
val Path.systemIndependentPath: String
get() = toString().replace(File.separatorChar, '/')
@@ -79,9 +81,9 @@ val Path.systemIndependentPath: String
val Path.parentSystemIndependentPath: String
get() = parent!!.toString().replace(File.separatorChar, '/')
fun Path.readBytes() = Files.readAllBytes(this)
fun Path.readBytes(): ByteArray = Files.readAllBytes(this)
fun Path.readText() = readBytes().toString(Charsets.UTF_8)
fun Path.readText(): String = readBytes().toString(Charsets.UTF_8)
fun Path.readChars() = inputStream().reader().readCharSequence(size().toInt())
@@ -103,25 +105,24 @@ fun Path.write(data: String): Path {
fun Path.size() = Files.size(this)
fun Path.sizeOrNull(): Long {
val attributes: BasicFileAttributes
fun Path.basicAttributesIfExists(): BasicFileAttributes? {
try {
attributes = Files.readAttributes(this, BasicFileAttributes::class.java)
return Files.readAttributes(this, BasicFileAttributes::class.java)
}
catch (ignored: IOException) {
return -1
catch (ignored: NoSuchFileException) {
return null
}
return attributes.size()
}
fun Path.sizeOrNull() = basicAttributesIfExists()?.size() ?: -1
fun Path.isHidden() = Files.isHidden(this)
fun Path.isDirectory() = Files.isDirectory(this)
fun Path.isFile() = Files.isRegularFile(this)
fun Path.move(target: Path) = Files.move(this, target)
fun Path.move(target: Path): Path = Files.move(this, target)
/**
* Opposite to Java, parent directories will be created
@@ -152,7 +153,7 @@ inline fun <R> Path.directoryStreamIfExists(task: (stream: DirectoryStream<Path>
inline fun <R> Path.directoryStreamIfExists(noinline filter: ((path: Path) -> Boolean), task: (stream: DirectoryStream<Path>) -> R): R? {
try {
return Files.newDirectoryStream(this, { filter.invoke(it) }).use(task)
return Files.newDirectoryStream(this, filter).use(task)
}
catch (ignored: NoSuchFileException) {
}
@@ -26,9 +26,11 @@ import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.*
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
@@ -37,28 +39,35 @@ abstract class BaseRepositoryManager(protected val dir: Path) : RepositoryManage
protected val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
override fun processChildren(path: String, filter: (name: String) -> Boolean, processor: (name: String, inputStream: InputStream) -> Boolean) {
dir.resolve(path).directoryStreamIfExists {
dir.resolve(path).directoryStreamIfExists({ filter(it.fileName.toString()) }) {
for (file in it) {
if (file.isDirectory() || file.isHidden()) {
continue;
val attributes: BasicFileAttributes?
try {
attributes = file.basicAttributesIfExists()
}
catch (e: IOException) {
LOG.warn(e)
continue
}
if (attributes == null || attributes.isDirectory || file.isHidden()) {
continue
}
// we ignore empty files as well - delete if corrupted
if (file.size() == 0L) {
if (file.exists()) {
try {
LOG.warn("File $path is empty (length 0), will be removed")
delete(file, path)
}
catch (e: Exception) {
LOG.error(e)
}
if (attributes.size() == 0L) {
try {
LOG.warn("File $path is empty (length 0), will be removed")
delete(file, path)
}
continue;
catch (e: Exception) {
LOG.error(e)
}
continue
}
if (!processor(file.fileName.toString(), file.inputStream())) {
break;
if (!file.inputStream().use { processor(file.fileName.toString(), it) }) {
break
}
}
}
@@ -70,7 +70,7 @@ class IcsManager(dir: Path) {
}
}
val settingsFile = dir.resolve("config.json")
val settingsFile: Path = dir.resolve("config.json")
val settings: IcsSettings
val repositoryManager: RepositoryManager = GitRepositoryManager(credentialsStore, dir.resolve("repository"))
@@ -190,10 +190,10 @@ class IcsManager(dir: Path) {
// first of all we must load read-only schemes - scheme could be overridden if bundled or read-only, so, such schemes must be loaded first
for (repository in readOnlySourcesManager.repositories) {
repository.processChildren(fullPath, filter, { name, input -> processor(name, input, true) })
repository.processChildren(fullPath, filter) { name, input -> processor(name, input, true) }
}
repositoryManager.processChildren(fullPath, filter, { name, input -> processor(name, input, false) })
repositoryManager.processChildren(fullPath, filter) { name, input -> processor(name, input, false) }
}
override fun write(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
+4 -4
View File
@@ -97,7 +97,7 @@ fun createBareRepository(dir: Path): Repository {
return repository
}
fun createRepository(dir: Path): Repository {
fun createGitRepository(dir: Path): Repository {
val repository = FileRepositoryBuilder().setWorkTree(dir.toFile()).build()
repository.create()
return repository
@@ -250,8 +250,8 @@ fun Repository.processChildren(path: String, filter: ((name: String) -> Boolean)
continue
}
if (!processor(fileName, objectLoader.openStream())) {
break;
if (!objectLoader.openStream().use { processor(fileName, it) }) {
break
}
}
}
@@ -317,7 +317,7 @@ private class InputStreamWrapper(private val delegate: InputStream, private val
delegate.close()
}
finally {
reader.close();
reader.close()
}
}
}
@@ -21,10 +21,10 @@ import org.eclipse.jgit.lib.Repository
import org.jetbrains.jgit.dirCache.AddLoadedFile
import org.jetbrains.jgit.dirCache.edit
import org.jetbrains.settingsRepository.IcsManager
import org.jetbrains.settingsRepository.git.createGitRepository
import org.junit.Rule
import java.nio.file.FileSystem
import java.nio.file.Path
import org.jetbrains.settingsRepository.git.createRepository as createGitRepository
fun Repository.add(path: String, data: String) = add(path, data.toByteArray())