Custom exceptions in corda, should either derive from an appropriate closely related java exception, or CordaException, or CordaRuntimeException. They should not inherit just from Exception, or RuntimeException.

Handle PR comments

Add nicer constructors to CordaException and CordaRuntimeException

(cherry picked from commit 89478c8)

Fix ambiguous defaulted constructor

(cherry picked from commit ec9bafe)

Address PR comment

Update a few more custom exceptions
This commit is contained in:
Matthew Nesbit 2017-09-28 13:25:08 +01:00
parent d13bf77473
commit 5fa7381883
24 changed files with 68 additions and 44 deletions

View File

@ -6,6 +6,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.google.common.collect.HashMultimap
import com.google.common.collect.Multimap
import net.corda.client.jackson.StringToMethodCallParser.ParsedMethodCall
import net.corda.core.CordaException
import org.slf4j.LoggerFactory
import java.lang.reflect.Constructor
import java.lang.reflect.Method
@ -146,7 +147,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
}
}
open class UnparseableCallException(command: String, cause: Throwable? = null) : Exception("Could not parse as a command: $command", cause) {
open class UnparseableCallException(command: String, cause: Throwable? = null) : CordaException("Could not parse as a command: $command", cause) {
class UnknownMethod(val methodName: String) : UnparseableCallException("Unknown command name: $methodName")
class MissingParameter(methodName: String, val paramName: String, command: String) : UnparseableCallException("Parameter $paramName missing from attempt to invoke $methodName in command: $command")
class TooManyParameters(methodName: String, command: String) : UnparseableCallException("Too many parameters provided for $methodName: $command")

View File

@ -1,10 +1,10 @@
package net.corda.client.rpc
import net.corda.core.CordaRuntimeException
import net.corda.core.serialization.CordaSerializable
/**
* Thrown to indicate that the calling user does not have permission for something they have requested (for example
* calling a method).
*/
@CordaSerializable
class PermissionException(msg: String) : RuntimeException(msg)
class PermissionException(msg: String) : CordaRuntimeException(msg)

View File

@ -15,10 +15,11 @@ interface CordaThrowable {
open class CordaException internal constructor(override var originalExceptionClassName: String? = null,
private var _message: String? = null,
private var _cause: Throwable? = null) : Exception(null, null, true, true), CordaThrowable {
constructor(message: String?,
cause: Throwable?) : this(null, message, cause)
constructor(message: String?) : this(null, message, null)
override val message: String?
get() = if (originalExceptionClassName == null) originalMessage else {
if (originalMessage == null) "$originalExceptionClassName" else "$originalExceptionClassName: $originalMessage"
@ -59,10 +60,12 @@ open class CordaException internal constructor(override var originalExceptionCla
}
open class CordaRuntimeException(override var originalExceptionClassName: String?,
private var _message: String? = null,
private var _cause: Throwable? = null) : RuntimeException(null, null, true, true), CordaThrowable {
private var _message: String?,
private var _cause: Throwable?) : RuntimeException(null, null, true, true), CordaThrowable {
constructor(message: String?, cause: Throwable?) : this(null, message, cause)
constructor(message: String?) : this(null, message, null)
override val message: String?
get() = if (originalExceptionClassName == null) originalMessage else {
if (originalMessage == null) "$originalExceptionClassName" else "$originalExceptionClassName: $originalMessage"

View File

@ -1,11 +1,12 @@
package net.corda.core.crypto
import net.corda.core.CordaException
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.CordaSerializable
import java.util.*
@CordaSerializable
class MerkleTreeException(val reason: String) : Exception("Partial Merkle Tree exception. Reason: $reason")
class MerkleTreeException(val reason: String) : CordaException("Partial Merkle Tree exception. Reason: $reason")
/**
* Building and verification of Partial Merkle Tree.

View File

@ -2,6 +2,7 @@ package net.corda.core.internal
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.serialization.CordaSerializable
@ -63,7 +64,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
}
@CordaSerializable
class ExcessivelyLargeTransactionGraph : Exception()
class ExcessivelyLargeTransactionGraph : FlowException()
/** Transaction for fetch attachments for */
private var signedTransaction: SignedTransaction? = null

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services
import net.corda.core.CordaException
import net.corda.core.contracts.PartyAndReference
import net.corda.core.identity.*
import java.security.InvalidAlgorithmParameterException
@ -105,4 +106,4 @@ interface IdentityService {
fun partiesFromName(query: String, exactMatch: Boolean): Set<Party>
}
class UnknownAnonymousPartyException(msg: String) : Exception(msg)
class UnknownAnonymousPartyException(msg: String) : CordaException(msg)

View File

@ -1,7 +1,8 @@
package net.corda.core.serialization
import net.corda.core.CordaException
import net.corda.core.crypto.SecureHash
/** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found. */
@CordaSerializable
class MissingAttachmentsException(val ids: List<SecureHash>) : Exception()
class MissingAttachmentsException(val ids: List<SecureHash>) : CordaException()

View File

@ -1,5 +1,6 @@
package net.corda.core.transactions
import net.corda.core.CordaException
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.identity.Party
@ -263,11 +264,11 @@ data class FilteredComponentGroup(override val groupIndex: Int, override val com
* @param reason information about the exception.
*/
@CordaSerializable
class ComponentVisibilityException(val id: SecureHash, val reason: String) : Exception("Component visibility error for transaction with id:$id. Reason: $reason")
class ComponentVisibilityException(val id: SecureHash, val reason: String) : CordaException("Component visibility error for transaction with id:$id. Reason: $reason")
/** Thrown when [FilteredTransaction.verify] fails.
* @param id transaction's id.
* @param reason information about the exception.
*/
@CordaSerializable
class FilteredTransactionVerificationException(val id: SecureHash, val reason: String) : Exception("Transaction with id:$id cannot be verified. Reason: $reason")
class FilteredTransactionVerificationException(val id: SecureHash, val reason: String) : CordaException("Transaction with id:$id cannot be verified. Reason: $reason")

View File

@ -2,6 +2,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionState
import net.corda.core.flows.FlowException
import net.corda.core.serialization.CordaSerializable
/**
@ -11,4 +12,4 @@ import net.corda.core.serialization.CordaSerializable
*/
@CordaSerializable
class MissingContractAttachments(val states: List<TransactionState<ContractState>>)
: Exception("Cannot find contract attachments for ${states.map { it.contract }.distinct() }")
: FlowException("Cannot find contract attachments for ${states.map { it.contract }.distinct() }")

View File

@ -12,6 +12,7 @@ import net.corda.core.contracts.CommandData
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.TokenizableAssetInfo
import net.corda.core.flows.FlowException
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
@ -199,7 +200,7 @@ enum class Frequency(val annualCompoundCount: Int, val offset: LocalDate.(Long)
@CordaSerializable
open class BusinessCalendar (val holidayDates: List<LocalDate>) {
@CordaSerializable
class UnknownCalendar(name: String) : Exception("$name not found")
class UnknownCalendar(name: String) : FlowException("$name not found")
companion object {
val calendars = listOf("London", "NewYork")

View File

@ -67,7 +67,7 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<T
logger.warn("Unexpected exception de-serializing throwable: ${proxy.exceptionClass}. Converting to CordaRuntimeException.", e)
}
// If the criteria are not met or we experience an exception constructing the exception, we fall back to our own unchecked exception.
return CordaRuntimeException(proxy.exceptionClass).apply {
return CordaRuntimeException(proxy.exceptionClass, null, null).apply {
this.setMessage(proxy.message)
this.setCause(proxy.cause)
this.stackTrace = proxy.stackTrace

View File

@ -1,11 +1,14 @@
package net.corda.nodeapi.internal.serialization.carpenter
class DuplicateNameException : RuntimeException(
import net.corda.core.CordaException
import net.corda.core.CordaRuntimeException
class DuplicateNameException : CordaRuntimeException(
"An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.")
class InterfaceMismatchException(msg: String) : RuntimeException(msg)
class InterfaceMismatchException(msg: String) : CordaRuntimeException(msg)
class NullablePrimitiveException(msg: String) : RuntimeException(msg)
class NullablePrimitiveException(msg: String) : CordaRuntimeException(msg)
class UncarpentableException(name: String, field: String, type: String) :
Exception("Class $name is loadable yet contains field $field of unknown type $type")
CordaException("Class $name is loadable yet contains field $field of unknown type $type")

View File

@ -6,6 +6,7 @@ import com.google.common.collect.MutableClassToInstanceMap
import com.google.common.util.concurrent.MoreExecutors
import net.corda.confidential.SwapIdentitiesFlow
import net.corda.confidential.SwapIdentitiesHandler
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.cordapp.CordappProvider
import net.corda.core.flows.*
@ -231,7 +232,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
}
private class ServiceInstantiationException(cause: Throwable?) : Exception(cause)
private class ServiceInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause)
private fun installCordaServices() {
cordappProvider.cordapps.flatMap { it.services }.forEach {
@ -465,7 +466,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
// Specific class so that MockNode can catch it.
class DatabaseConfigurationException(msg: String) : Exception(msg)
class DatabaseConfigurationException(msg: String) : CordaException(msg)
protected open fun <T> initialiseDatabasePersistence(insideTransaction: () -> T): T {
val props = configuration.dataSourceProperties

View File

@ -1,6 +1,7 @@
package net.corda.node.internal
import com.codahale.metrics.JmxReporter
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate
@ -384,6 +385,6 @@ open class Node(override val configuration: FullNodeConfiguration,
}
}
class ConfigurationException(message: String) : Exception(message)
class ConfigurationException(message: String) : CordaException(message)
data class NetworkMapInfo(val address: NetworkHostAndPort, val legalName: CordaX500Name)

View File

@ -1,5 +1,6 @@
package net.corda.node.services.api
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowInitiator
@ -62,9 +63,9 @@ interface NetworkMapCacheInternal : NetworkMapCache {
}
@CordaSerializable
sealed class NetworkCacheError : Exception() {
sealed class NetworkCacheException : CordaException("Network Cache Error") {
/** Indicates a failure to deregister, because of a rejected request from the remote node */
class DeregistrationFailed : NetworkCacheError()
class DeregistrationFailed : NetworkCacheException()
}
interface ServiceHubInternal : ServiceHub {

View File

@ -1,5 +1,6 @@
package net.corda.node.services.network
import net.corda.core.CordaException
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.isFulfilledBy
@ -189,7 +190,7 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal,
}
private fun addSubscriber(subscriber: MessageRecipients) {
if (subscriber !is SingleMessageRecipient) throw NodeMapError.InvalidSubscriber()
if (subscriber !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber()
subscribers.locked {
if (!containsKey(subscriber)) {
put(subscriber, LastAcknowledgeInfo(mapVersion))
@ -198,12 +199,12 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal,
}
private fun removeSubscriber(subscriber: MessageRecipients) {
if (subscriber !is SingleMessageRecipient) throw NodeMapError.InvalidSubscriber()
if (subscriber !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber()
subscribers.locked { remove(subscriber) }
}
private fun processAcknowledge(request: UpdateAcknowledge): Unit {
if (request.replyTo !is SingleMessageRecipient) throw NodeMapError.InvalidSubscriber()
if (request.replyTo !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber()
subscribers.locked {
val lastVersionAcked = this[request.replyTo]?.mapVersion
if ((lastVersionAcked ?: 0) < request.mapVersion) {
@ -360,13 +361,13 @@ class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalS
}
@CordaSerializable
sealed class NodeMapError : Exception() {
sealed class NodeMapException : CordaException("Network Map Protocol Error") {
/** Thrown if the signature on the node info does not match the public key for the identity */
class InvalidSignature : NodeMapError()
class InvalidSignature : NodeMapException()
/** Thrown if the replyTo of a subscription change message is not a single message recipient */
class InvalidSubscriber : NodeMapError()
class InvalidSubscriber : NodeMapException()
}
@CordaSerializable

View File

@ -21,7 +21,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.parsePublicKeyBase58
import net.corda.core.utilities.toBase58String
import net.corda.node.services.api.NetworkCacheError
import net.corda.node.services.api.NetworkCacheException
import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.messaging.MessagingService
@ -135,7 +135,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal)
data = NetworkMapService.UpdateAcknowledge(req.mapVersion, network.myAddress).serialize().bytes)
network.send(ackMessage, req.replyTo)
processUpdatePush(req)
} catch (e: NodeMapError) {
} catch (e: NodeMapException) {
logger.warn("Failure during node map update due to bad update: ${e.javaClass.name}")
} catch (e: Exception) {
logger.error("Exception processing update from network map service", e)
@ -202,7 +202,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal)
val address = getPartyInfo(mapParty)?.let { network.getAddressOfParty(it) } ?:
throw IllegalArgumentException("Can't deregister for updates, don't know the party: $mapParty")
val future = network.sendRequest<SubscribeResponse>(NetworkMapService.SUBSCRIPTION_TOPIC, req, address).map {
if (it.confirmed) Unit else throw NetworkCacheError.DeregistrationFailed()
if (it.confirmed) Unit else throw NetworkCacheException.DeregistrationFailed()
}
_registrationFuture.captureLater(future.map { null })
return future
@ -213,7 +213,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal)
val reg = req.wireReg.verified()
processRegistration(reg)
} catch (e: SignatureException) {
throw NodeMapError.InvalidSignature()
throw NodeMapException.InvalidSignature()
}
}

View File

@ -6,6 +6,7 @@ import com.google.common.hash.HashCode
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream
import net.corda.core.CordaRuntimeException
import net.corda.core.internal.AbstractAttachment
import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash
@ -58,7 +59,7 @@ class NodeAttachmentService(metrics: MetricRegistry) : AttachmentStorage, Single
}
@CordaSerializable
class HashMismatchException(val expected: SecureHash, val actual: SecureHash) : RuntimeException("File $expected hashed to $actual: corruption in attachment store?")
class HashMismatchException(val expected: SecureHash, val actual: SecureHash) : CordaRuntimeException("File $expected hashed to $actual: corruption in attachment store?")
/**
* Wraps a stream and hashes data as it is read: if the entire stream is consumed, then at the end the hash of

View File

@ -9,6 +9,7 @@ import com.codahale.metrics.Gauge
import com.esotericsoftware.kryo.KryoException
import com.google.common.collect.HashMultimap
import com.google.common.util.concurrent.MoreExecutors
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
@ -641,6 +642,6 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
class SessionRejectException(val rejectMessage: String, val logMessage: String) : Exception() {
class SessionRejectException(val rejectMessage: String, val logMessage: String) : CordaException(rejectMessage) {
constructor(message: String) : this(message, message)
}

View File

@ -23,6 +23,7 @@ import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.utilities.loggerFor
import net.corda.client.jackson.JacksonSupport
import net.corda.client.jackson.StringToMethodCallParser
import net.corda.core.CordaException
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
@ -258,7 +259,7 @@ object InteractiveShell {
}
}
class NoApplicableConstructor(val errors: List<String>) : Exception() {
class NoApplicableConstructor(val errors: List<String>) : CordaException(this.toString()) {
override fun toString() = (listOf("No applicable constructor for flow. Problems were:") + errors).joinToString(System.lineSeparator())
}

View File

@ -1,5 +1,6 @@
package net.corda.node.utilities.registration
import net.corda.core.CordaException
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import java.security.cert.Certificate
@ -14,4 +15,4 @@ interface NetworkRegistrationService {
}
@CordaSerializable
class CertificateRequestException(message: String) : Exception(message)
class CertificateRequestException(message: String) : CordaException(message)

View File

@ -3,6 +3,7 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatingFlow
@ -44,8 +45,7 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
fun tracker(fixName: String) = ProgressTracker(QUERYING(fixName), WORKING, SIGNING)
}
@CordaSerializable
class FixOutOfRange(@Suppress("unused") val byAmount: BigDecimal) : Exception("Fix out of range by $byAmount")
class FixOutOfRange(@Suppress("unused") val byAmount: BigDecimal) : FlowException("Fix out of range by $byAmount")
@CordaSerializable
data class QueryRequest(val queries: List<FixOf>)

View File

@ -9,6 +9,7 @@ import net.corda.client.rpc.CordaRPCClient
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformNode
import net.corda.cordform.NodeDefinition
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.concurrent.firstOf
import net.corda.core.identity.CordaX500Name
@ -411,7 +412,7 @@ fun getTimestampAsDirectoryName(): String {
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(UTC).format(Instant.now())
}
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) : Exception("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) : CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
/**
* @throws ListenProcessDeathException if [listenProcess] dies before the check succeeds, i.e. the check can't succeed as intended.

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.*
import net.corda.core.crypto.NullKeys.NULL_SIGNATURE
import net.corda.core.flows.FlowException
import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast
import net.corda.core.node.ServiceHub
@ -52,9 +53,9 @@ sealed class EnforceVerifyOrFail {
internal object Token : EnforceVerifyOrFail()
}
class DuplicateOutputLabel(label: String) : Exception("Output label '$label' already used")
class DoubleSpentInputs(ids: List<SecureHash>) : Exception("Transactions spend the same input. Conflicting transactions ids: '$ids'")
class AttachmentResolutionException(attachmentId: SecureHash) : Exception("Attachment with id $attachmentId not found")
class DuplicateOutputLabel(label: String) : FlowException("Output label '$label' already used")
class DoubleSpentInputs(ids: List<SecureHash>) : FlowException("Transactions spend the same input. Conflicting transactions ids: '$ids'")
class AttachmentResolutionException(attachmentId: SecureHash) : FlowException("Attachment with id $attachmentId not found")
/**
* This interpreter builds a transaction, and [TransactionDSL.verifies] that the resolved transaction is correct. Note