Added nice extension methods for Path, which are more readable than the static methods from Files

This commit is contained in:
Shams Asari
2016-11-04 11:33:43 +00:00
parent 2bc8f8414d
commit bd89da458b
17 changed files with 135 additions and 129 deletions

View File

@ -4,6 +4,7 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture
import com.r3corda.core.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.messaging.SingleMessageRecipient
@ -12,7 +13,6 @@ import com.r3corda.core.node.services.*
import com.r3corda.core.node.services.NetworkMapCache.MapChangeType
import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.core.protocols.ProtocolLogicRefFactory
import com.r3corda.core.seconds
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.serialization.deserialize
import com.r3corda.core.serialization.serialize
@ -47,7 +47,6 @@ import com.r3corda.protocols.sendRequest
import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
import java.time.Clock
@ -468,11 +467,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with
// the public key. Obviously in a real system this would need to be a certificate chain of some kind to ensure
// the legal name is actually validated in some way.
val privKeyFile = dir.resolve(privateKeyFileName)
val pubIdentityFile = dir.resolve(publicKeyFileName)
val privKeyFile = dir / privateKeyFileName
val pubIdentityFile = dir / publicKeyFileName
val identityName = if (serviceName == null) configuration.myLegalName else configuration.myLegalName + "|" + serviceName
val identityAndKey = if (!Files.exists(privKeyFile)) {
val identityAndKey = if (!privKeyFile.exists()) {
log.info("Identity key not found, generating fresh key!")
val keyPair: KeyPair = generateKeyPair()
keyPair.serialize().writeToFile(privKeyFile)
@ -485,12 +484,12 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// Check that the identity in the config file matches the identity file we have stored to disk.
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
// things up for us.
val myIdentity = Files.readAllBytes(pubIdentityFile).deserialize<Party>()
val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
if (myIdentity.name != identityName)
throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" +
"${identityName} vs ${myIdentity.name}")
"$identityName vs ${myIdentity.name}")
// Load the private key.
val keyPair = Files.readAllBytes(privKeyFile).deserialize<KeyPair>()
val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
Pair(myIdentity, keyPair)
}
partyKeys += identityAndKey.second
@ -500,17 +499,15 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
protected open fun generateKeyPair() = com.r3corda.core.crypto.generateKeyPair()
protected fun makeAttachmentStorage(dir: Path): NodeAttachmentService {
val attachmentsDir = dir.resolve("attachments")
val attachmentsDir = dir / "attachments"
try {
Files.createDirectory(attachmentsDir)
attachmentsDir.createDirectory()
} catch (e: FileAlreadyExistsException) {
}
return NodeAttachmentService(attachmentsDir, services.monitoringService.metrics)
}
protected fun createNodeDir() {
if (!Files.exists(configuration.basedir)) {
Files.createDirectories(configuration.basedir)
}
configuration.basedir.createDirectories()
}
}

View File

@ -1,6 +1,7 @@
package com.r3corda.node.internal
import com.codahale.metrics.JmxReporter
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.ServiceInfo
@ -366,7 +367,7 @@ class Node(override val configuration: FullNodeConfiguration, networkMapAddress:
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
// exists, we try to take the file lock first before replacing it and if that fails it means we're being started
// twice with the same directory: that's a user error and we should bail out.
val pidPath = configuration.basedir.resolve("process-id")
val pidPath = configuration.basedir / "process-id"
val file = pidPath.toFile()
if (!file.exists()) {
file.createNewFile()

View File

@ -1,7 +1,7 @@
package com.r3corda.node.services
import com.r3corda.core.exists
import com.r3corda.core.use
import com.r3corda.core.read
import java.nio.file.Path
import java.util.*
@ -25,7 +25,7 @@ class PropertiesFileRPCUserService(file: Path) : RPCUserService {
init {
_users = if (file.exists()) {
val properties = Properties()
file.use {
file.read {
properties.load(it)
}
properties.map {

View File

@ -1,7 +1,10 @@
package com.r3corda.node.services.config
import com.google.common.net.HostAndPort
import com.r3corda.core.copyTo
import com.r3corda.core.createDirectories
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.div
import com.r3corda.core.exists
import com.r3corda.core.utilities.loggerFor
import com.typesafe.config.Config
@ -29,7 +32,7 @@ object ConfigHelper {
val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false))
val normalisedBaseDir = baseDirectoryPath.normalize()
val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir.resolve("node.conf")).toFile()
val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir / "node.conf").toFile()
val appConfig = ConfigFactory.parseFile(configFile, ConfigParseOptions.defaults().setAllowMissing(allowMissingConfig))
val overridesMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map.
@ -89,10 +92,9 @@ fun Config.getProperties(path: String): Properties {
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/
fun NodeSSLConfiguration.configureWithDevSSLCertificate() {
Files.createDirectories(certificatesPath)
certificatesPath.createDirectories()
if (!trustStorePath.exists()) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"),
trustStorePath)
javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStorePath)
}
if (!keyStorePath.exists()) {
val caKeyStore = X509Utilities.loadKeyStore(

View File

@ -7,7 +7,7 @@ import com.r3corda.core.crypto.toBase58String
import com.r3corda.core.messaging.MessageRecipients
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.use
import com.r3corda.core.read
import com.r3corda.node.services.config.NodeSSLConfiguration
import com.r3corda.node.services.config.configureWithDevSSLCertificate
import org.apache.activemq.artemis.api.core.SimpleString
@ -110,10 +110,10 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
* unfortunately Artemis tends to bury the exception when the password is wrong.
*/
fun checkStorePasswords() {
config.keyStorePath.use {
config.keyStorePath.read {
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
}
config.trustStorePath.use {
config.trustStorePath.read {
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
}
}

View File

@ -5,9 +5,9 @@ import com.google.common.annotations.VisibleForTesting
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream
import com.r3corda.core.*
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.extractZipFile
import com.r3corda.core.node.services.AttachmentStorage
import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.api.AcceptsFileUpload
@ -35,7 +35,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
}
// Just count all non-directories in the attachment store, and assume the admin hasn't dumped any junk there.
private fun countAttachments() = Files.list(storePath).filter { Files.isRegularFile(it) }.count()
private fun countAttachments() = storePath.list { it.filter { it.isRegularFile() }.count() }
/**
* If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for
@ -45,7 +45,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
@Volatile var automaticallyExtractAttachments = false
init {
require(Files.isDirectory(storePath)) { "$storePath must be a directory" }
require(storePath.isDirectory()) { "$storePath must be a directory" }
}
class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() {
@ -66,7 +66,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
private val counter: CountingInputStream = CountingInputStream(input),
private val stream: HashingInputStream = HashingInputStream(Hashing.sha256(), counter)) : FilterInputStream(stream) {
private val expectedSize = Files.size(filePath)
private val expectedSize = filePath.size
override fun close() {
super.close()
@ -94,8 +94,8 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
}
override fun openAttachment(id: SecureHash): Attachment? {
val path = storePath.resolve(id.toString())
if (!Files.exists(path)) return null
val path = storePath / id.toString()
if (!path.exists()) return null
return AttachmentImpl(id, path, checkAttachmentsOnLoad)
}
@ -103,28 +103,28 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
override fun importAttachment(jar: InputStream): SecureHash {
require(jar !is JarInputStream)
val hs = HashingInputStream(Hashing.sha256(), jar)
val tmp = storePath.resolve("tmp.${UUID.randomUUID()}")
Files.copy(hs, tmp)
val tmp = storePath / "tmp.${UUID.randomUUID()}"
hs.copyTo(tmp)
checkIsAValidJAR(tmp)
val id = SecureHash.SHA256(hs.hash().asBytes())
val finalPath = storePath.resolve(id.toString())
val finalPath = storePath / id.toString()
try {
// Move into place atomically or fail if that isn't possible. We don't want a half moved attachment to
// be exposed to parallel threads. This gives us thread safety.
if (!Files.exists(finalPath)) {
if (!finalPath.exists()) {
log.info("Stored new attachment $id")
attachmentCount.inc()
} else {
log.info("Replacing attachment $id - only bother doing this if you're trying to repair file corruption")
}
Files.move(tmp, finalPath, StandardCopyOption.ATOMIC_MOVE)
tmp.moveTo(finalPath, StandardCopyOption.ATOMIC_MOVE)
} finally {
Files.deleteIfExists(tmp)
tmp.deleteIfExists()
}
if (automaticallyExtractAttachments) {
val extractTo = storePath.resolve("$id.jar")
val extractTo = storePath / "$id.jar"
try {
Files.createDirectory(extractTo)
extractTo.createDirectory()
extractZipFile(finalPath, extractTo)
} catch(e: FileAlreadyExistsException) {
log.trace("Did not extract attachment jar to directory because it already exists")
@ -138,9 +138,10 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
private fun checkIsAValidJAR(path: Path) {
// Just iterate over the entries with verification enabled: should be good enough to catch mistakes.
JarInputStream(Files.newInputStream(path), true).use { stream ->
path.read {
val jar = JarInputStream(it)
while (true) {
val cursor = stream.nextJarEntry ?: break
val cursor = jar.nextJarEntry ?: break
val entryPath = Paths.get(cursor.name)
// Security check to stop zips trying to escape their rightful place.
if (entryPath.isAbsolute || entryPath.normalize() != entryPath || '\\' in cursor.name)

View File

@ -1,13 +1,12 @@
package com.r3corda.node.utilities.certsigning
import com.r3corda.core.*
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY
import com.r3corda.core.crypto.X509Utilities.CORDA_ROOT_CA
import com.r3corda.core.crypto.X509Utilities.addOrReplaceCertificate
import com.r3corda.core.crypto.X509Utilities.addOrReplaceKey
import com.r3corda.core.div
import com.r3corda.core.minutes
import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
@ -15,7 +14,6 @@ import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.getValue
import joptsimple.OptionParser
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths
import java.security.KeyPair
import java.security.cert.Certificate
@ -34,7 +32,7 @@ class CertificateSigner(val config: NodeConfiguration, val certService: Certific
}
fun buildKeyStore() {
Files.createDirectories(config.certificatesPath)
config.certificatesPath.createDirectories()
val caKeyStore = X509Utilities.loadOrCreateKeyStore(config.keyStorePath, config.keyStorePassword)
@ -101,15 +99,15 @@ class CertificateSigner(val config: NodeConfiguration, val certService: Certific
private fun submitCertificateSigningRequest(keyPair: KeyPair): String {
val requestIdStore = config.certificatesPath / "certificate-request-id.txt"
// Retrieve request id from file if exists, else post a request to server.
return if (!Files.exists(requestIdStore)) {
return if (!requestIdStore.exists()) {
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.nearestCity, config.emailAddress, keyPair)
// Post request to signing server via http.
val requestId = certService.submitRequest(request)
// Persists request ID to file in case of node shutdown.
Files.write(requestIdStore, listOf(requestId), Charsets.UTF_8)
requestIdStore.writeLines(listOf(requestId))
requestId
} else {
Files.readAllLines(requestIdStore).first()
requestIdStore.readLines { it.findFirst().get() }
}
}
}

View File

@ -5,7 +5,7 @@ import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.sha256
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.services.ServiceInfo
import com.r3corda.core.serialization.OpaqueBytes
import com.r3corda.core.write
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.persistence.NodeAttachmentService
@ -18,9 +18,6 @@ import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.security.KeyPair
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
@ -103,9 +100,7 @@ class AttachmentTests {
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
// Corrupt its store.
val writer = Files.newByteChannel(network.filesystem.getPath("/nodes/0/attachments/$id"), StandardOpenOption.WRITE)
writer.write(ByteBuffer.wrap(OpaqueBytes.of(99, 99, 99, 99).bits))
writer.close()
network.filesystem.getPath("/nodes/0/attachments/$id").write { it.write(byteArrayOf(99, 99, 99, 99)) }
// Get n1 to fetch the attachment. Should receive corrupted bytes.
network.runNetwork()

View File

@ -4,12 +4,19 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.use
import com.r3corda.core.crypto.sha256
import com.r3corda.core.div
import com.r3corda.core.read
import com.r3corda.core.readAll
import com.r3corda.core.write
import com.r3corda.node.services.persistence.NodeAttachmentService
import org.junit.Before
import org.junit.Test
import java.nio.charset.Charset
import java.nio.file.*
import java.nio.file.FileAlreadyExistsException
import java.nio.file.FileSystem
import java.nio.file.Path
import java.nio.file.StandardOpenOption.WRITE
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import kotlin.test.assertEquals
@ -28,10 +35,10 @@ class NodeAttachmentStorageTest {
@Test
fun `insert and retrieve`() {
val testJar = makeTestJar()
val expectedHash = SecureHash.sha256(Files.readAllBytes(testJar))
val expectedHash = testJar.readAll().sha256()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
val id = testJar.use { storage.importAttachment(it) }
val id = testJar.read { storage.importAttachment(it) }
assertEquals(expectedHash, id)
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
@ -48,9 +55,9 @@ class NodeAttachmentStorageTest {
fun `duplicates not allowed`() {
val testJar = makeTestJar()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
testJar.use { storage.importAttachment(it) }
testJar.read { storage.importAttachment(it) }
assertFailsWith<FileAlreadyExistsException> {
testJar.use { storage.importAttachment(it) }
testJar.read { storage.importAttachment(it) }
}
}
@ -58,15 +65,15 @@ class NodeAttachmentStorageTest {
fun `corrupt entry throws exception`() {
val testJar = makeTestJar()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
val id = testJar.use { storage.importAttachment(it) }
val id = testJar.read { storage.importAttachment(it) }
// Corrupt the file in the store.
Files.write(fs.getPath("/", id.toString()), "arggghhhh".toByteArray(), StandardOpenOption.WRITE)
fs.getPath("/", id.toString()).write(options = WRITE) { it.write("arggghhhh".toByteArray()) }
val e = assertFailsWith<NodeAttachmentService.OnDiskHashMismatch> {
storage.openAttachment(id)!!.open().use { it.readBytes() }
}
assertEquals(e.file, storage.storePath.resolve(id.toString()))
assertEquals(e.file, storage.storePath / id.toString())
// But if we skip around and read a single entry, no exception is thrown.
storage.openAttachment(id)!!.openAsJAR().use {
@ -78,15 +85,16 @@ class NodeAttachmentStorageTest {
private var counter = 0
private fun makeTestJar(): Path {
counter++
val f = fs.getPath("$counter.jar")
JarOutputStream(Files.newOutputStream(f)).use {
it.putNextEntry(JarEntry("test1.txt"))
it.write("This is some useful content".toByteArray())
it.closeEntry()
it.putNextEntry(JarEntry("test2.txt"))
it.write("Some more useful content".toByteArray())
it.closeEntry()
val file = fs.getPath("$counter.jar")
file.write {
val jar = JarOutputStream(it)
jar.putNextEntry(JarEntry("test1.txt"))
jar.write("This is some useful content".toByteArray())
jar.closeEntry()
jar.putNextEntry(JarEntry("test2.txt"))
jar.write("Some more useful content".toByteArray())
jar.closeEntry()
}
return f
return file
}
}

View File

@ -2,11 +2,11 @@ package com.r3corda.node.services
import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs
import com.r3corda.core.writeLines
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Test
import java.nio.file.Files
class PropertiesFileRPCUserServiceTest {
@ -75,7 +75,7 @@ class PropertiesFileRPCUserServiceTest {
}
private fun loadWithContents(vararg lines: String): PropertiesFileRPCUserService {
Files.write(file, lines.asList())
file.writeLines(lines.asList())
return PropertiesFileRPCUserService(file)
}
}

View File

@ -6,11 +6,12 @@ import com.nhaarman.mockito_kotlin.mock
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.div
import com.r3corda.core.exists
import com.r3corda.core.readLines
import com.r3corda.node.services.config.NodeConfiguration
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.nio.file.Files
import java.nio.file.Path
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@ -47,13 +48,13 @@ class CertificateSignerTest {
override val trustStorePassword: String = "trustpass"
}
assertFalse(Files.exists(config.keyStorePath))
assertFalse(Files.exists(config.trustStorePath))
assertFalse(config.keyStorePath.exists())
assertFalse(config.trustStorePath.exists())
CertificateSigner(config, certService).buildKeyStore()
assertTrue(Files.exists(config.keyStorePath))
assertTrue(Files.exists(config.trustStorePath))
assertTrue(config.keyStorePath.exists())
assertTrue(config.trustStorePath.exists())
X509Utilities.loadKeyStore(config.keyStorePath, config.keyStorePassword).run {
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY))
@ -73,7 +74,7 @@ class CertificateSignerTest {
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY))
}
assertEquals(id, Files.readAllLines(config.certificatesPath / "certificate-request-id.txt").first())
assertEquals(id, (config.certificatesPath / "certificate-request-id.txt").readLines { it.findFirst().get() })
}
}