mirror of
https://github.com/corda/corda.git
synced 2025-06-18 07:08:15 +00:00
CORDA-1099: Orchestrated clean shutdown from Shell (#2831)
This commit is contained in:
committed by
GitHub
parent
c964e50696
commit
7a077e76f0
@ -658,10 +658,25 @@ public static final class net.corda.core.contracts.UniqueIdentifier$Companion ex
|
|||||||
@org.jetbrains.annotations.NotNull public abstract List getServiceFlows()
|
@org.jetbrains.annotations.NotNull public abstract List getServiceFlows()
|
||||||
@org.jetbrains.annotations.NotNull public abstract List getServices()
|
@org.jetbrains.annotations.NotNull public abstract List getServices()
|
||||||
##
|
##
|
||||||
|
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappConfig
|
||||||
|
public abstract boolean exists(String)
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract Object get(String)
|
||||||
|
public abstract boolean getBoolean(String)
|
||||||
|
public abstract double getDouble(String)
|
||||||
|
public abstract float getFloat(String)
|
||||||
|
public abstract int getInt(String)
|
||||||
|
public abstract long getLong(String)
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract Number getNumber(String)
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract String getString(String)
|
||||||
|
##
|
||||||
|
public final class net.corda.core.cordapp.CordappConfigException extends java.lang.Exception
|
||||||
|
public <init>(String, Throwable)
|
||||||
|
##
|
||||||
public final class net.corda.core.cordapp.CordappContext extends java.lang.Object
|
public final class net.corda.core.cordapp.CordappContext extends java.lang.Object
|
||||||
public <init>(net.corda.core.cordapp.Cordapp, net.corda.core.crypto.SecureHash, ClassLoader, net.corda.core.cordapp.CordappConfig)
|
public <init>(net.corda.core.cordapp.Cordapp, net.corda.core.crypto.SecureHash, ClassLoader, net.corda.core.cordapp.CordappConfig)
|
||||||
@org.jetbrains.annotations.Nullable public final net.corda.core.crypto.SecureHash getAttachmentId()
|
@org.jetbrains.annotations.Nullable public final net.corda.core.crypto.SecureHash getAttachmentId()
|
||||||
@org.jetbrains.annotations.NotNull public final ClassLoader getClassLoader()
|
@org.jetbrains.annotations.NotNull public final ClassLoader getClassLoader()
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.CordappConfig getConfig()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.Cordapp getCordapp()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.Cordapp getCordapp()
|
||||||
##
|
##
|
||||||
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappProvider
|
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappProvider
|
||||||
@ -963,6 +978,8 @@ public static final class net.corda.core.crypto.PartialMerkleTree$Companion exte
|
|||||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.crypto.SecureHash$SHA256 sha256Twice(byte[])
|
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.crypto.SecureHash$SHA256 sha256Twice(byte[])
|
||||||
@org.jetbrains.annotations.NotNull public String toString()
|
@org.jetbrains.annotations.NotNull public String toString()
|
||||||
public static final net.corda.core.crypto.SecureHash$Companion Companion
|
public static final net.corda.core.crypto.SecureHash$Companion Companion
|
||||||
|
@org.jetbrains.annotations.NotNull public static final net.corda.core.crypto.SecureHash$SHA256 allOnesHash
|
||||||
|
@org.jetbrains.annotations.NotNull public static final net.corda.core.crypto.SecureHash$SHA256 zeroHash
|
||||||
##
|
##
|
||||||
public static final class net.corda.core.crypto.SecureHash$Companion extends java.lang.Object
|
public static final class net.corda.core.crypto.SecureHash$Companion extends java.lang.Object
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash$SHA256 getAllOnesHash()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash$SHA256 getAllOnesHash()
|
||||||
@ -1368,6 +1385,15 @@ public static final class net.corda.core.flows.NotarisationRequest$Companion ext
|
|||||||
public int hashCode()
|
public int hashCode()
|
||||||
public String toString()
|
public String toString()
|
||||||
##
|
##
|
||||||
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotarisationResponse extends java.lang.Object
|
||||||
|
public <init>(List)
|
||||||
|
@org.jetbrains.annotations.NotNull public final List component1()
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotarisationResponse copy(List)
|
||||||
|
public boolean equals(Object)
|
||||||
|
@org.jetbrains.annotations.NotNull public final List getSignatures()
|
||||||
|
public int hashCode()
|
||||||
|
public String toString()
|
||||||
|
##
|
||||||
@net.corda.core.flows.InitiatingFlow public final class net.corda.core.flows.NotaryChangeFlow extends net.corda.core.flows.AbstractStateReplacementFlow$Instigator
|
@net.corda.core.flows.InitiatingFlow public final class net.corda.core.flows.NotaryChangeFlow extends net.corda.core.flows.AbstractStateReplacementFlow$Instigator
|
||||||
public <init>(net.corda.core.contracts.StateAndRef, net.corda.core.identity.Party, net.corda.core.utilities.ProgressTracker)
|
public <init>(net.corda.core.contracts.StateAndRef, net.corda.core.identity.Party, net.corda.core.utilities.ProgressTracker)
|
||||||
@org.jetbrains.annotations.NotNull protected net.corda.core.flows.AbstractStateReplacementFlow$UpgradeTx assembleTx()
|
@org.jetbrains.annotations.NotNull protected net.corda.core.flows.AbstractStateReplacementFlow$UpgradeTx assembleTx()
|
||||||
@ -1375,10 +1401,12 @@ public static final class net.corda.core.flows.NotarisationRequest$Companion ext
|
|||||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.flows.NotaryError extends java.lang.Object
|
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.flows.NotaryError extends java.lang.Object
|
||||||
##
|
##
|
||||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$Conflict extends net.corda.core.flows.NotaryError
|
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$Conflict extends net.corda.core.flows.NotaryError
|
||||||
|
public <init>(net.corda.core.crypto.SecureHash, Map)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component1()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component1()
|
||||||
@org.jetbrains.annotations.NotNull public final Map component2()
|
@org.jetbrains.annotations.NotNull public final Map component2()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError$Conflict copy(net.corda.core.crypto.SecureHash, Map)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError$Conflict copy(net.corda.core.crypto.SecureHash, Map)
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
|
@org.jetbrains.annotations.NotNull public final Map getConsumedStates()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getTxId()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getTxId()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
@org.jetbrains.annotations.NotNull public String toString()
|
@org.jetbrains.annotations.NotNull public String toString()
|
||||||
@ -1431,6 +1459,7 @@ public static final class net.corda.core.flows.NotaryError$TimeWindowInvalid$Com
|
|||||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotaryException extends net.corda.core.flows.FlowException
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotaryException extends net.corda.core.flows.FlowException
|
||||||
public <init>(net.corda.core.flows.NotaryError, net.corda.core.crypto.SecureHash)
|
public <init>(net.corda.core.flows.NotaryError, net.corda.core.crypto.SecureHash)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError getError()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError getError()
|
||||||
|
@org.jetbrains.annotations.Nullable public final net.corda.core.crypto.SecureHash getTxId()
|
||||||
##
|
##
|
||||||
public final class net.corda.core.flows.NotaryFlow extends java.lang.Object
|
public final class net.corda.core.flows.NotaryFlow extends java.lang.Object
|
||||||
public <init>()
|
public <init>()
|
||||||
@ -1462,6 +1491,10 @@ public abstract static class net.corda.core.flows.NotaryFlow$Service extends net
|
|||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.TrustedAuthorityNotaryService getService()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.TrustedAuthorityNotaryService getService()
|
||||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull protected abstract net.corda.core.flows.TransactionParts validateRequest(net.corda.core.flows.NotarisationPayload)
|
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull protected abstract net.corda.core.flows.TransactionParts validateRequest(net.corda.core.flows.NotarisationPayload)
|
||||||
##
|
##
|
||||||
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotaryInternalException extends net.corda.core.flows.FlowException
|
||||||
|
public <init>(net.corda.core.flows.NotaryError)
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError getError()
|
||||||
|
##
|
||||||
public final class net.corda.core.flows.ReceiveStateAndRefFlow extends net.corda.core.flows.FlowLogic
|
public final class net.corda.core.flows.ReceiveStateAndRefFlow extends net.corda.core.flows.FlowLogic
|
||||||
public <init>(net.corda.core.flows.FlowSession)
|
public <init>(net.corda.core.flows.FlowSession)
|
||||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public List call()
|
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public List call()
|
||||||
@ -1514,6 +1547,15 @@ public @interface net.corda.core.flows.StartableByRPC
|
|||||||
##
|
##
|
||||||
public @interface net.corda.core.flows.StartableByService
|
public @interface net.corda.core.flows.StartableByService
|
||||||
##
|
##
|
||||||
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.StateConsumptionDetails extends java.lang.Object
|
||||||
|
public <init>(net.corda.core.crypto.SecureHash)
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component1()
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateConsumptionDetails copy(net.corda.core.crypto.SecureHash)
|
||||||
|
public boolean equals(Object)
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getHashOfTransactionId()
|
||||||
|
public int hashCode()
|
||||||
|
public String toString()
|
||||||
|
##
|
||||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.StateMachineRunId extends java.lang.Object
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.StateMachineRunId extends java.lang.Object
|
||||||
public <init>(UUID)
|
public <init>(UUID)
|
||||||
@org.jetbrains.annotations.NotNull public final UUID component1()
|
@org.jetbrains.annotations.NotNull public final UUID component1()
|
||||||
@ -1658,6 +1700,7 @@ public final class net.corda.core.identity.IdentityUtils extends java.lang.Objec
|
|||||||
@org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)
|
@org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)
|
||||||
@org.jetbrains.annotations.NotNull public abstract List registeredFlows()
|
@org.jetbrains.annotations.NotNull public abstract List registeredFlows()
|
||||||
public abstract void setFlowsDrainingModeEnabled(boolean)
|
public abstract void setFlowsDrainingModeEnabled(boolean)
|
||||||
|
public abstract void shutdown()
|
||||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowHandle startFlowDynamic(Class, Object...)
|
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowHandle startFlowDynamic(Class, Object...)
|
||||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlowDynamic(Class, Object...)
|
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlowDynamic(Class, Object...)
|
||||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.DataFeed stateMachineRecordedTransactionMappingFeed()
|
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.DataFeed stateMachineRecordedTransactionMappingFeed()
|
||||||
@ -1681,6 +1724,7 @@ public final class net.corda.core.identity.IdentityUtils extends java.lang.Objec
|
|||||||
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name)
|
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name)
|
||||||
##
|
##
|
||||||
public final class net.corda.core.messaging.CordaRPCOpsKt extends java.lang.Object
|
public final class net.corda.core.messaging.CordaRPCOpsKt extends java.lang.Object
|
||||||
|
@org.jetbrains.annotations.NotNull public static final net.corda.core.messaging.DataFeed pendingFlowsCount(net.corda.core.messaging.CordaRPCOps)
|
||||||
##
|
##
|
||||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.messaging.DataFeed extends java.lang.Object
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.messaging.DataFeed extends java.lang.Object
|
||||||
public <init>(Object, rx.Observable)
|
public <init>(Object, rx.Observable)
|
||||||
@ -1880,6 +1924,7 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
|||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.cordapp.CordappContext getAppContext()
|
||||||
@org.jetbrains.annotations.NotNull public abstract java.time.Clock getClock()
|
@org.jetbrains.annotations.NotNull public abstract java.time.Clock getClock()
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.KeyManagementService getKeyManagementService()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.KeyManagementService getKeyManagementService()
|
||||||
@ -2856,6 +2901,8 @@ public interface net.corda.core.schemas.StatePersistable
|
|||||||
public interface net.corda.core.serialization.ClassWhitelist
|
public interface net.corda.core.serialization.ClassWhitelist
|
||||||
public abstract boolean hasListed(Class)
|
public abstract boolean hasListed(Class)
|
||||||
##
|
##
|
||||||
|
public @interface net.corda.core.serialization.ConstructorForDeserialization
|
||||||
|
##
|
||||||
public @interface net.corda.core.serialization.CordaSerializable
|
public @interface net.corda.core.serialization.CordaSerializable
|
||||||
##
|
##
|
||||||
public @interface net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
public @interface net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
||||||
@ -2875,6 +2922,9 @@ public @interface net.corda.core.serialization.CordaSerializationTransformRename
|
|||||||
public @interface net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
public @interface net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||||
public abstract int version()
|
public abstract int version()
|
||||||
##
|
##
|
||||||
|
@net.corda.core.DoNotImplement public interface net.corda.core.serialization.EncodingWhitelist
|
||||||
|
public abstract boolean acceptEncoding(net.corda.core.serialization.SerializationEncoding)
|
||||||
|
##
|
||||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.serialization.MissingAttachmentsException extends net.corda.core.CordaException
|
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.serialization.MissingAttachmentsException extends net.corda.core.CordaException
|
||||||
public <init>(List)
|
public <init>(List)
|
||||||
@org.jetbrains.annotations.NotNull public final List getIds()
|
@org.jetbrains.annotations.NotNull public final List getIds()
|
||||||
@ -2895,6 +2945,8 @@ public final class net.corda.core.serialization.SerializationAPIKt extends java.
|
|||||||
##
|
##
|
||||||
@net.corda.core.DoNotImplement public interface net.corda.core.serialization.SerializationContext
|
@net.corda.core.DoNotImplement public interface net.corda.core.serialization.SerializationContext
|
||||||
@org.jetbrains.annotations.NotNull public abstract ClassLoader getDeserializationClassLoader()
|
@org.jetbrains.annotations.NotNull public abstract ClassLoader getDeserializationClassLoader()
|
||||||
|
@org.jetbrains.annotations.Nullable public abstract net.corda.core.serialization.SerializationEncoding getEncoding()
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.EncodingWhitelist getEncodingWhitelist()
|
||||||
public abstract boolean getObjectReferencesEnabled()
|
public abstract boolean getObjectReferencesEnabled()
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.utilities.ByteSequence getPreferredSerializationVersion()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.utilities.ByteSequence getPreferredSerializationVersion()
|
||||||
@org.jetbrains.annotations.NotNull public abstract Map getProperties()
|
@org.jetbrains.annotations.NotNull public abstract Map getProperties()
|
||||||
@ -2902,6 +2954,7 @@ public final class net.corda.core.serialization.SerializationAPIKt extends java.
|
|||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.ClassWhitelist getWhitelist()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.ClassWhitelist getWhitelist()
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withAttachmentsClassLoader(List)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withAttachmentsClassLoader(List)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withClassLoader(ClassLoader)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withClassLoader(ClassLoader)
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withEncoding(net.corda.core.serialization.SerializationEncoding)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withPreferredSerializationVersion(net.corda.core.utilities.ByteSequence)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withPreferredSerializationVersion(net.corda.core.utilities.ByteSequence)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withProperty(Object, Object)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withProperty(Object, Object)
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withWhitelisted(Class)
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.serialization.SerializationContext withWhitelisted(Class)
|
||||||
@ -2925,6 +2978,8 @@ public final class net.corda.core.serialization.SerializationDefaults extends ja
|
|||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT()
|
||||||
public static final net.corda.core.serialization.SerializationDefaults INSTANCE
|
public static final net.corda.core.serialization.SerializationDefaults INSTANCE
|
||||||
##
|
##
|
||||||
|
@net.corda.core.DoNotImplement public interface net.corda.core.serialization.SerializationEncoding
|
||||||
|
##
|
||||||
public abstract class net.corda.core.serialization.SerializationFactory extends java.lang.Object
|
public abstract class net.corda.core.serialization.SerializationFactory extends java.lang.Object
|
||||||
public <init>()
|
public <init>()
|
||||||
public final Object asCurrent(kotlin.jvm.functions.Function1)
|
public final Object asCurrent(kotlin.jvm.functions.Function1)
|
||||||
@ -3004,13 +3059,20 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
|
|||||||
@org.jetbrains.annotations.NotNull public final Map component2()
|
@org.jetbrains.annotations.NotNull public final Map component2()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(Map, Map)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(Map, Map)
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
|
@org.jetbrains.annotations.NotNull public final Map getHiddenComponents()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
||||||
@org.jetbrains.annotations.NotNull public List getInputs()
|
@org.jetbrains.annotations.NotNull public List getInputs()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
||||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
@org.jetbrains.annotations.NotNull public List getOutputs()
|
||||||
|
@org.jetbrains.annotations.NotNull public final Map getVisibleComponents()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
public String toString()
|
public String toString()
|
||||||
##
|
##
|
||||||
|
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent extends java.lang.Object
|
||||||
|
public <init>(net.corda.core.utilities.OpaqueBytes, net.corda.core.crypto.SecureHash)
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.OpaqueBytes getComponent()
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getNonce()
|
||||||
|
##
|
||||||
@net.corda.core.DoNotImplement public final class net.corda.core.transactions.ContractUpgradeLedgerTransaction extends net.corda.core.transactions.FullTransaction implements net.corda.core.transactions.TransactionWithSignatures
|
@net.corda.core.DoNotImplement public final class net.corda.core.transactions.ContractUpgradeLedgerTransaction extends net.corda.core.transactions.FullTransaction implements net.corda.core.transactions.TransactionWithSignatures
|
||||||
public <init>(List, net.corda.core.identity.Party, net.corda.core.contracts.Attachment, String, net.corda.core.contracts.Attachment, net.corda.core.crypto.SecureHash, net.corda.core.contracts.PrivacySalt, List, net.corda.core.node.NetworkParameters)
|
public <init>(List, net.corda.core.identity.Party, net.corda.core.contracts.Attachment, String, net.corda.core.contracts.Attachment, net.corda.core.crypto.SecureHash, net.corda.core.contracts.PrivacySalt, List, net.corda.core.node.NetworkParameters)
|
||||||
public void checkSignaturesAreValid()
|
public void checkSignaturesAreValid()
|
||||||
@ -3055,12 +3117,18 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
|
|||||||
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
||||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
@org.jetbrains.annotations.NotNull public List getOutputs()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt()
|
||||||
|
@org.jetbrains.annotations.NotNull public final List getSerializedComponents()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getUpgradedContractAttachmentId()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getUpgradedContractAttachmentId()
|
||||||
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
|
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
||||||
public String toString()
|
public String toString()
|
||||||
##
|
##
|
||||||
|
public static final class net.corda.core.transactions.ContractUpgradeWireTransaction$Component extends java.lang.Enum
|
||||||
|
protected <init>(String, int)
|
||||||
|
public static net.corda.core.transactions.ContractUpgradeWireTransaction$Component valueOf(String)
|
||||||
|
public static net.corda.core.transactions.ContractUpgradeWireTransaction$Component[] values()
|
||||||
|
##
|
||||||
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.transactions.CoreTransaction extends net.corda.core.transactions.BaseTransaction
|
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.transactions.CoreTransaction extends net.corda.core.transactions.BaseTransaction
|
||||||
public <init>()
|
public <init>()
|
||||||
@org.jetbrains.annotations.NotNull public abstract List getInputs()
|
@org.jetbrains.annotations.NotNull public abstract List getInputs()
|
||||||
@ -3192,6 +3260,7 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
|||||||
##
|
##
|
||||||
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.NotaryChangeWireTransaction extends net.corda.core.transactions.CoreTransaction
|
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.NotaryChangeWireTransaction extends net.corda.core.transactions.CoreTransaction
|
||||||
public <init>(List)
|
public <init>(List)
|
||||||
|
@kotlin.Deprecated public <init>(List, net.corda.core.identity.Party, net.corda.core.identity.Party)
|
||||||
@org.jetbrains.annotations.NotNull public final List component1()
|
@org.jetbrains.annotations.NotNull public final List component1()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction copy(List)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction copy(List)
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@ -3200,11 +3269,17 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
|||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getNewNotary()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getNewNotary()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
||||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
@org.jetbrains.annotations.NotNull public List getOutputs()
|
||||||
|
@org.jetbrains.annotations.NotNull public final List getSerializedComponents()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServiceHub, List)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServiceHub, List)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
||||||
public String toString()
|
public String toString()
|
||||||
##
|
##
|
||||||
|
public static final class net.corda.core.transactions.NotaryChangeWireTransaction$Component extends java.lang.Enum
|
||||||
|
protected <init>(String, int)
|
||||||
|
public static net.corda.core.transactions.NotaryChangeWireTransaction$Component valueOf(String)
|
||||||
|
public static net.corda.core.transactions.NotaryChangeWireTransaction$Component[] values()
|
||||||
|
##
|
||||||
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.SignedTransaction extends java.lang.Object implements net.corda.core.transactions.TransactionWithSignatures
|
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.SignedTransaction extends java.lang.Object implements net.corda.core.transactions.TransactionWithSignatures
|
||||||
public <init>(net.corda.core.serialization.SerializedBytes, List)
|
public <init>(net.corda.core.serialization.SerializedBytes, List)
|
||||||
public <init>(net.corda.core.transactions.CoreTransaction, List)
|
public <init>(net.corda.core.transactions.CoreTransaction, List)
|
||||||
@ -3348,6 +3423,7 @@ public final class net.corda.core.utilities.ByteArrays extends java.lang.Object
|
|||||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Object implements java.lang.Comparable
|
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Object implements java.lang.Comparable
|
||||||
public int compareTo(net.corda.core.utilities.ByteSequence)
|
public int compareTo(net.corda.core.utilities.ByteSequence)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence copy()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence copy()
|
||||||
|
@org.jetbrains.annotations.NotNull public final byte[] copyBytes()
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@org.jetbrains.annotations.NotNull public abstract byte[] getBytes()
|
@org.jetbrains.annotations.NotNull public abstract byte[] getBytes()
|
||||||
public final int getOffset()
|
public final int getOffset()
|
||||||
@ -3357,9 +3433,12 @@ public final class net.corda.core.utilities.ByteArrays extends java.lang.Object
|
|||||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.utilities.ByteSequence of(byte[], int)
|
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.utilities.ByteSequence of(byte[], int)
|
||||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.utilities.ByteSequence of(byte[], int, int)
|
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.utilities.ByteSequence of(byte[], int, int)
|
||||||
@org.jetbrains.annotations.NotNull public final java.io.ByteArrayInputStream open()
|
@org.jetbrains.annotations.NotNull public final java.io.ByteArrayInputStream open()
|
||||||
|
@org.jetbrains.annotations.NotNull public final java.nio.ByteBuffer putTo(java.nio.ByteBuffer)
|
||||||
|
@org.jetbrains.annotations.NotNull public final java.nio.ByteBuffer slice(int, int)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence subSequence(int, int)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence subSequence(int, int)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence take(int)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.ByteSequence take(int)
|
||||||
@org.jetbrains.annotations.NotNull public String toString()
|
@org.jetbrains.annotations.NotNull public String toString()
|
||||||
|
public final void writeTo(java.io.OutputStream)
|
||||||
public static final net.corda.core.utilities.ByteSequence$Companion Companion
|
public static final net.corda.core.utilities.ByteSequence$Companion Companion
|
||||||
##
|
##
|
||||||
public static final class net.corda.core.utilities.ByteSequence$Companion extends java.lang.Object
|
public static final class net.corda.core.utilities.ByteSequence$Companion extends java.lang.Object
|
||||||
@ -4126,6 +4205,7 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
|
|||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
||||||
|
@org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappContext getAppContext()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.services.MockAttachmentStorage getAttachments()
|
@org.jetbrains.annotations.NotNull public final net.corda.testing.services.MockAttachmentStorage getAttachments()
|
||||||
@org.jetbrains.annotations.NotNull public java.time.Clock getClock()
|
@org.jetbrains.annotations.NotNull public java.time.Clock getClock()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
||||||
@ -4180,6 +4260,7 @@ public static final class net.corda.testing.node.MockServicesKt$createMockCordaS
|
|||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
||||||
|
@org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappContext getAppContext()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments()
|
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments()
|
||||||
@org.jetbrains.annotations.NotNull public java.time.Clock getClock()
|
@org.jetbrains.annotations.NotNull public java.time.Clock getClock()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
||||||
@ -4284,18 +4365,22 @@ public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
|||||||
##
|
##
|
||||||
public static final class net.corda.client.rpc.CordaRPCClient$Companion extends java.lang.Object
|
public static final class net.corda.client.rpc.CordaRPCClient$Companion extends java.lang.Object
|
||||||
##
|
##
|
||||||
public final class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.Object
|
public interface net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
public <init>(java.time.Duration)
|
public abstract int getCacheConcurrencyLevel()
|
||||||
@org.jetbrains.annotations.NotNull public final java.time.Duration component1()
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration getConnectionMaxRetryInterval()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration)
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration getConnectionRetryInterval()
|
||||||
public boolean equals(Object)
|
public abstract double getConnectionRetryIntervalMultiplier()
|
||||||
@org.jetbrains.annotations.NotNull public final java.time.Duration getConnectionMaxRetryInterval()
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration getDeduplicationCacheExpiry()
|
||||||
public int hashCode()
|
public abstract int getMaxFileSize()
|
||||||
public String toString()
|
public abstract int getMaxReconnectAttempts()
|
||||||
|
public abstract int getMinimumServerProtocolVersion()
|
||||||
|
public abstract int getObservationExecutorPoolSize()
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration getReapInterval()
|
||||||
|
public abstract boolean getTrackRpcCallSites()
|
||||||
public static final net.corda.client.rpc.CordaRPCClientConfiguration$Companion Companion
|
public static final net.corda.client.rpc.CordaRPCClientConfiguration$Companion Companion
|
||||||
@org.jetbrains.annotations.NotNull public static final net.corda.client.rpc.CordaRPCClientConfiguration DEFAULT
|
|
||||||
##
|
##
|
||||||
public static final class net.corda.client.rpc.CordaRPCClientConfiguration$Companion extends java.lang.Object
|
public static final class net.corda.client.rpc.CordaRPCClientConfiguration$Companion extends java.lang.Object
|
||||||
|
@org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCClientConfiguration default()
|
||||||
##
|
##
|
||||||
@net.corda.core.DoNotImplement public final class net.corda.client.rpc.CordaRPCConnection extends java.lang.Object implements net.corda.client.rpc.RPCConnection
|
@net.corda.core.DoNotImplement public final class net.corda.client.rpc.CordaRPCConnection extends java.lang.Object implements net.corda.client.rpc.RPCConnection
|
||||||
public <init>(net.corda.client.rpc.RPCConnection)
|
public <init>(net.corda.client.rpc.RPCConnection)
|
||||||
|
@ -58,9 +58,9 @@ class NodeMonitorModel {
|
|||||||
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
|
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
|
||||||
val client = CordaRPCClient(
|
val client = CordaRPCClient(
|
||||||
nodeHostAndPort,
|
nodeHostAndPort,
|
||||||
CordaRPCClientConfiguration.DEFAULT.copy(
|
object : CordaRPCClientConfiguration {
|
||||||
connectionMaxRetryInterval = 10.seconds
|
override val connectionMaxRetryInterval = 10.seconds
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
val connection = client.start(username, password)
|
val connection = client.start(username, password)
|
||||||
val proxy = connection.proxy
|
val proxy = connection.proxy
|
||||||
|
@ -18,28 +18,28 @@ import net.corda.finance.flows.CashPaymentFlow
|
|||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
import net.corda.node.services.Permissions.Companion.all
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
|
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import rx.subjects.PublishSubject
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
|
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
|
||||||
private val rpcUser = User("user1", "test", permissions = setOf(
|
private val rpcUser = User("user1", "test", permissions = setOf(all())
|
||||||
startFlow<CashIssueFlow>(),
|
|
||||||
startFlow<CashPaymentFlow>(),
|
|
||||||
invokeRpc("vaultQueryBy"),
|
|
||||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
invokeRpc("vaultQueryByCriteria"))
|
|
||||||
)
|
)
|
||||||
private lateinit var node: StartedNode<Node>
|
private lateinit var node: StartedNode<Node>
|
||||||
private lateinit var identity: Party
|
private lateinit var identity: Party
|
||||||
@ -53,7 +53,9 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
|
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
|
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!, object : CordaRPCClientConfiguration {
|
||||||
|
override val maxReconnectAttempts = 5
|
||||||
|
})
|
||||||
identity = node.info.identityFromX500Name(ALICE_NAME)
|
identity = node.info.identityFromX500Name(ALICE_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +83,61 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `shutdown command stops the node`() {
|
||||||
|
|
||||||
|
val nodeIsShut: PublishSubject<Unit> = PublishSubject.create()
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
var successful = false
|
||||||
|
val maxCount = 20
|
||||||
|
var count = 0
|
||||||
|
CloseableExecutor(Executors.newSingleThreadScheduledExecutor()).use { scheduler ->
|
||||||
|
|
||||||
|
val task = scheduler.scheduleAtFixedRate({
|
||||||
|
try {
|
||||||
|
println("Checking whether node is still running...")
|
||||||
|
client.start(rpcUser.username, rpcUser.password).use {
|
||||||
|
println("... node is still running.")
|
||||||
|
if (count == maxCount) {
|
||||||
|
nodeIsShut.onError(AssertionError("Node does not get shutdown by RPC"))
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
} catch (e: ActiveMQNotConnectedException) {
|
||||||
|
println("... node is not running.")
|
||||||
|
nodeIsShut.onCompleted()
|
||||||
|
} catch (e: ActiveMQSecurityException) {
|
||||||
|
// nothing here - this happens if trying to connect before the node is started
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
nodeIsShut.onError(e)
|
||||||
|
}
|
||||||
|
}, 1, 1, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
nodeIsShut.doOnError { error ->
|
||||||
|
error.printStackTrace()
|
||||||
|
successful = false
|
||||||
|
task.cancel(true)
|
||||||
|
latch.countDown()
|
||||||
|
}.doOnCompleted {
|
||||||
|
successful = (node.internals.started == null)
|
||||||
|
task.cancel(true)
|
||||||
|
latch.countDown()
|
||||||
|
}.subscribe()
|
||||||
|
|
||||||
|
client.start(rpcUser.username, rpcUser.password).use { rpc -> rpc.proxy.shutdown() }
|
||||||
|
|
||||||
|
latch.await()
|
||||||
|
assertThat(successful).isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CloseableExecutor(private val delegate: ScheduledExecutorService) : AutoCloseable, ScheduledExecutorService by delegate {
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
delegate.shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `close-send deadlock and premature shutdown on empty observable`() {
|
fun `close-send deadlock and premature shutdown on empty observable`() {
|
||||||
println("Starting client")
|
println("Starting client")
|
||||||
@ -141,7 +198,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
|||||||
|
|
||||||
val updates = proxy.stateMachinesFeed().updates
|
val updates = proxy.stateMachinesFeed().updates
|
||||||
|
|
||||||
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0),identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||||
proxy.startFlow(::CashIssueFlow, 123.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
|
proxy.startFlow(::CashIssueFlow, 123.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
|
||||||
proxy.startFlowDynamic(CashIssueFlow::class.java, 1000.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
|
proxy.startFlowDynamic(CashIssueFlow::class.java, 1000.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import net.corda.client.rpc.internal.RPCClient
|
import net.corda.client.rpc.internal.RPCClient
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.context.Trace
|
import net.corda.core.context.Trace
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.internal.concurrent.fork
|
import net.corda.core.internal.concurrent.fork
|
||||||
@ -105,7 +105,7 @@ class RPCStabilityTests {
|
|||||||
Try.on {
|
Try.on {
|
||||||
startRpcClient<RPCOps>(
|
startRpcClient<RPCOps>(
|
||||||
server.get().broker.hostAndPort!!,
|
server.get().broker.hostAndPort!!,
|
||||||
configuration = RPCClientConfiguration.default.copy(minimumServerProtocolVersion = 1)
|
configuration = CordaRPCClientConfigurationImpl.default.copy(minimumServerProtocolVersion = 1)
|
||||||
).get()
|
).get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +240,7 @@ class RPCStabilityTests {
|
|||||||
val serverPort = startRpcServer<ReconnectOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
val serverPort = startRpcServer<ReconnectOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
||||||
serverFollower.unfollow()
|
serverFollower.unfollow()
|
||||||
// Set retry interval to 1s to reduce test duration
|
// Set retry interval to 1s to reduce test duration
|
||||||
val clientConfiguration = RPCClientConfiguration.default.copy(connectionRetryInterval = 1.seconds)
|
val clientConfiguration = CordaRPCClientConfigurationImpl.default.copy(connectionRetryInterval = 1.seconds)
|
||||||
val clientFollower = shutdownManager.follower()
|
val clientFollower = shutdownManager.follower()
|
||||||
val client = startRpcClient<ReconnectOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
val client = startRpcClient<ReconnectOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
||||||
clientFollower.unfollow()
|
clientFollower.unfollow()
|
||||||
@ -266,7 +266,7 @@ class RPCStabilityTests {
|
|||||||
val serverPort = startRpcServer<ReconnectOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
val serverPort = startRpcServer<ReconnectOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
||||||
serverFollower.unfollow()
|
serverFollower.unfollow()
|
||||||
// Set retry interval to 1s to reduce test duration
|
// Set retry interval to 1s to reduce test duration
|
||||||
val clientConfiguration = RPCClientConfiguration.default.copy(connectionRetryInterval = 1.seconds, maxReconnectAttempts = 5)
|
val clientConfiguration = CordaRPCClientConfigurationImpl.default.copy(connectionRetryInterval = 1.seconds, maxReconnectAttempts = 5)
|
||||||
val clientFollower = shutdownManager.follower()
|
val clientFollower = shutdownManager.follower()
|
||||||
val client = startRpcClient<ReconnectOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
val client = startRpcClient<ReconnectOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
||||||
clientFollower.unfollow()
|
clientFollower.unfollow()
|
||||||
@ -298,7 +298,7 @@ class RPCStabilityTests {
|
|||||||
val serverPort = startRpcServer<NoOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
val serverPort = startRpcServer<NoOps>(ops = ops).getOrThrow().broker.hostAndPort!!
|
||||||
serverFollower.unfollow()
|
serverFollower.unfollow()
|
||||||
|
|
||||||
val clientConfiguration = RPCClientConfiguration.default.copy(connectionRetryInterval = 500.millis, maxReconnectAttempts = 1)
|
val clientConfiguration = CordaRPCClientConfigurationImpl.default.copy(connectionRetryInterval = 500.millis, maxReconnectAttempts = 1)
|
||||||
val clientFollower = shutdownManager.follower()
|
val clientFollower = shutdownManager.follower()
|
||||||
val client = startRpcClient<NoOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
val client = startRpcClient<NoOps>(serverPort, configuration = clientConfiguration).getOrThrow()
|
||||||
clientFollower.unfollow()
|
clientFollower.unfollow()
|
||||||
|
@ -2,7 +2,7 @@ package net.corda.client.rpc
|
|||||||
|
|
||||||
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||||
import net.corda.client.rpc.internal.RPCClient
|
import net.corda.client.rpc.internal.RPCClient
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.context.Actor
|
import net.corda.core.context.Actor
|
||||||
import net.corda.core.context.Trace
|
import net.corda.core.context.Trace
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
@ -23,23 +23,46 @@ class CordaRPCConnection internal constructor(connection: RPCConnection<CordaRPC
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be used to configure the RPC client connection.
|
* Can be used to configure the RPC client connection.
|
||||||
*
|
|
||||||
* @property connectionMaxRetryInterval How much time to wait between connection retries if the server goes down. This
|
|
||||||
* time will be reached via exponential backoff.
|
|
||||||
*/
|
*/
|
||||||
data class CordaRPCClientConfiguration(val connectionMaxRetryInterval: Duration) {
|
interface CordaRPCClientConfiguration {
|
||||||
internal fun toRpcClientConfiguration(): RPCClientConfiguration {
|
|
||||||
return RPCClientConfiguration.default.copy(
|
/** The minimum protocol version required from the server */
|
||||||
connectionMaxRetryInterval = connectionMaxRetryInterval
|
val minimumServerProtocolVersion: Int get() = default().minimumServerProtocolVersion
|
||||||
)
|
/**
|
||||||
}
|
* If set to true the client will track RPC call sites. If an error occurs subsequently during the RPC or in a
|
||||||
|
* returned Observable stream the stack trace of the originating RPC will be shown as well. Note that
|
||||||
|
* constructing call stacks is a moderately expensive operation.
|
||||||
|
*/
|
||||||
|
val trackRpcCallSites: Boolean get() = default().trackRpcCallSites
|
||||||
|
/**
|
||||||
|
* The interval of unused observable reaping. Leaked Observables (unused ones) are detected using weak references
|
||||||
|
* and are cleaned up in batches in this interval. If set too large it will waste server side resources for this
|
||||||
|
* duration. If set too low it wastes client side cycles.
|
||||||
|
*/
|
||||||
|
val reapInterval: Duration get() = default().reapInterval
|
||||||
|
/** The number of threads to use for observations (for executing [Observable.onNext]) */
|
||||||
|
val observationExecutorPoolSize: Int get() = default().observationExecutorPoolSize
|
||||||
|
/**
|
||||||
|
* Determines the concurrency level of the Observable Cache. This is exposed because it implicitly determines
|
||||||
|
* the limit on the number of leaked observables reaped because of garbage collection per reaping.
|
||||||
|
* See the implementation of [com.google.common.cache.LocalCache] for details.
|
||||||
|
*/
|
||||||
|
val cacheConcurrencyLevel: Int get() = default().cacheConcurrencyLevel
|
||||||
|
/** The retry interval of artemis connections in milliseconds */
|
||||||
|
val connectionRetryInterval: Duration get() = default().connectionRetryInterval
|
||||||
|
/** The retry interval multiplier for exponential backoff */
|
||||||
|
val connectionRetryIntervalMultiplier: Double get() = default().connectionRetryIntervalMultiplier
|
||||||
|
/** Maximum retry interval */
|
||||||
|
val connectionMaxRetryInterval: Duration get() = default().connectionMaxRetryInterval
|
||||||
|
/** Maximum reconnect attempts on failover */
|
||||||
|
val maxReconnectAttempts: Int get() = default().maxReconnectAttempts
|
||||||
|
/** Maximum file size */
|
||||||
|
val maxFileSize: Int get() = default().maxFileSize
|
||||||
|
/** The cache expiry of a deduplication watermark per client. */
|
||||||
|
val deduplicationCacheExpiry: Duration get() = default().deduplicationCacheExpiry
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
fun default(): CordaRPCClientConfiguration = CordaRPCClientConfigurationImpl.default
|
||||||
* Returns the default configuration we recommend you use.
|
|
||||||
*/
|
|
||||||
@JvmField
|
|
||||||
val DEFAULT = CordaRPCClientConfiguration(connectionMaxRetryInterval = RPCClientConfiguration.default.connectionMaxRetryInterval)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,17 +95,17 @@ data class CordaRPCClientConfiguration(val connectionMaxRetryInterval: Duration)
|
|||||||
*/
|
*/
|
||||||
class CordaRPCClient private constructor(
|
class CordaRPCClient private constructor(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default(),
|
||||||
sslConfiguration: SSLConfiguration? = null,
|
sslConfiguration: SSLConfiguration? = null,
|
||||||
classLoader: ClassLoader? = null
|
classLoader: ClassLoader? = null
|
||||||
) {
|
) {
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(hostAndPort: NetworkHostAndPort, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(hostAndPort, configuration, null)
|
constructor(hostAndPort: NetworkHostAndPort, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default()) : this(hostAndPort, configuration, null)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal fun createWithSsl(
|
internal fun createWithSsl(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default(),
|
||||||
sslConfiguration: SSLConfiguration? = null
|
sslConfiguration: SSLConfiguration? = null
|
||||||
): CordaRPCClient {
|
): CordaRPCClient {
|
||||||
return CordaRPCClient(hostAndPort, configuration, sslConfiguration)
|
return CordaRPCClient(hostAndPort, configuration, sslConfiguration)
|
||||||
@ -90,7 +113,7 @@ class CordaRPCClient private constructor(
|
|||||||
|
|
||||||
internal fun createWithSslAndClassLoader(
|
internal fun createWithSslAndClassLoader(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default(),
|
||||||
sslConfiguration: SSLConfiguration? = null,
|
sslConfiguration: SSLConfiguration? = null,
|
||||||
classLoader: ClassLoader? = null
|
classLoader: ClassLoader? = null
|
||||||
): CordaRPCClient {
|
): CordaRPCClient {
|
||||||
@ -112,7 +135,7 @@ class CordaRPCClient private constructor(
|
|||||||
|
|
||||||
private val rpcClient = RPCClient<CordaRPCOps>(
|
private val rpcClient = RPCClient<CordaRPCOps>(
|
||||||
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = sslConfiguration),
|
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = sslConfiguration),
|
||||||
configuration.toRpcClientConfiguration(),
|
configuration,
|
||||||
if (classLoader != null) KRYO_RPC_CLIENT_CONTEXT.withClassLoader(classLoader) else KRYO_RPC_CLIENT_CONTEXT
|
if (classLoader != null) KRYO_RPC_CLIENT_CONTEXT.withClassLoader(classLoader) else KRYO_RPC_CLIENT_CONTEXT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,19 +2,32 @@ package net.corda.client.rpc.internal
|
|||||||
|
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
|
import net.corda.core.messaging.pendingFlowsCount
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
|
import rx.Observable
|
||||||
|
|
||||||
/** Utility which exposes the internal Corda RPC constructor to other internal Corda components */
|
/** Utility which exposes the internal Corda RPC constructor to other internal Corda components */
|
||||||
fun createCordaRPCClientWithSsl(
|
fun createCordaRPCClientWithSsl(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default(),
|
||||||
sslConfiguration: SSLConfiguration? = null
|
sslConfiguration: SSLConfiguration? = null
|
||||||
) = CordaRPCClient.createWithSsl(hostAndPort, configuration, sslConfiguration)
|
) = CordaRPCClient.createWithSsl(hostAndPort, configuration, sslConfiguration)
|
||||||
|
|
||||||
fun createCordaRPCClientWithSslAndClassLoader(
|
fun createCordaRPCClientWithSslAndClassLoader(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default(),
|
||||||
sslConfiguration: SSLConfiguration? = null,
|
sslConfiguration: SSLConfiguration? = null,
|
||||||
classLoader: ClassLoader? = null
|
classLoader: ClassLoader? = null
|
||||||
) = CordaRPCClient.createWithSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader)
|
) = CordaRPCClient.createWithSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader)
|
||||||
|
|
||||||
|
fun CordaRPCOps.drainAndShutdown(): Observable<Unit> {
|
||||||
|
|
||||||
|
setFlowsDrainingModeEnabled(true)
|
||||||
|
return pendingFlowsCount().updates
|
||||||
|
.doOnError { error ->
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
.doOnCompleted { shutdown() }.map { }
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.client.rpc.internal
|
package net.corda.client.rpc.internal
|
||||||
|
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
|
||||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
import net.corda.client.rpc.RPCConnection
|
import net.corda.client.rpc.RPCConnection
|
||||||
import net.corda.client.rpc.RPCException
|
import net.corda.client.rpc.RPCException
|
||||||
@ -27,40 +26,22 @@ import java.time.Duration
|
|||||||
/**
|
/**
|
||||||
* This configuration may be used to tweak the internals of the RPC client.
|
* This configuration may be used to tweak the internals of the RPC client.
|
||||||
*/
|
*/
|
||||||
data class RPCClientConfiguration(
|
data class CordaRPCClientConfigurationImpl(
|
||||||
/** The minimum protocol version required from the server */
|
override val minimumServerProtocolVersion: Int,
|
||||||
val minimumServerProtocolVersion: Int,
|
override val trackRpcCallSites: Boolean,
|
||||||
/**
|
override val reapInterval: Duration,
|
||||||
* If set to true the client will track RPC call sites. If an error occurs subsequently during the RPC or in a
|
override val observationExecutorPoolSize: Int,
|
||||||
* returned Observable stream the stack trace of the originating RPC will be shown as well. Note that
|
override val connectionRetryInterval: Duration,
|
||||||
* constructing call stacks is a moderately expensive operation.
|
override val connectionRetryIntervalMultiplier: Double,
|
||||||
*/
|
override val connectionMaxRetryInterval: Duration,
|
||||||
val trackRpcCallSites: Boolean,
|
override val maxReconnectAttempts: Int,
|
||||||
/**
|
override val maxFileSize: Int,
|
||||||
* The interval of unused observable reaping. Leaked Observables (unused ones) are detected using weak references
|
override val deduplicationCacheExpiry: Duration
|
||||||
* and are cleaned up in batches in this interval. If set too large it will waste server side resources for this
|
) : CordaRPCClientConfiguration {
|
||||||
* duration. If set too low it wastes client side cycles.
|
|
||||||
*/
|
|
||||||
val reapInterval: Duration,
|
|
||||||
/** The number of threads to use for observations (for executing [Observable.onNext]) */
|
|
||||||
val observationExecutorPoolSize: Int,
|
|
||||||
/** The retry interval of artemis connections in milliseconds */
|
|
||||||
val connectionRetryInterval: Duration,
|
|
||||||
/** The retry interval multiplier for exponential backoff */
|
|
||||||
val connectionRetryIntervalMultiplier: Double,
|
|
||||||
/** Maximum retry interval */
|
|
||||||
val connectionMaxRetryInterval: Duration,
|
|
||||||
/** Maximum reconnect attempts on failover */
|
|
||||||
val maxReconnectAttempts: Int,
|
|
||||||
/** Maximum file size */
|
|
||||||
val maxFileSize: Int,
|
|
||||||
/** The cache expiry of a deduplication watermark per client. */
|
|
||||||
val deduplicationCacheExpiry: Duration
|
|
||||||
) {
|
|
||||||
companion object {
|
companion object {
|
||||||
val unlimitedReconnectAttempts = -1
|
private const val unlimitedReconnectAttempts = -1
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val default = RPCClientConfiguration(
|
val default = CordaRPCClientConfigurationImpl(
|
||||||
minimumServerProtocolVersion = 0,
|
minimumServerProtocolVersion = 0,
|
||||||
trackRpcCallSites = false,
|
trackRpcCallSites = false,
|
||||||
reapInterval = 1.seconds,
|
reapInterval = 1.seconds,
|
||||||
@ -78,13 +59,13 @@ data class RPCClientConfiguration(
|
|||||||
|
|
||||||
class RPCClient<I : RPCOps>(
|
class RPCClient<I : RPCOps>(
|
||||||
val transport: TransportConfiguration,
|
val transport: TransportConfiguration,
|
||||||
val rpcConfiguration: RPCClientConfiguration = RPCClientConfiguration.default,
|
val rpcConfiguration: CordaRPCClientConfiguration = CordaRPCClientConfigurationImpl.default,
|
||||||
val serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
|
val serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
|
||||||
) {
|
) {
|
||||||
constructor(
|
constructor(
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
sslConfiguration: SSLConfiguration? = null,
|
sslConfiguration: SSLConfiguration? = null,
|
||||||
configuration: RPCClientConfiguration = RPCClientConfiguration.default,
|
configuration: CordaRPCClientConfiguration = CordaRPCClientConfigurationImpl.default,
|
||||||
serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
|
serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
|
||||||
) : this(tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration), configuration, serializationContext)
|
) : this(tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration), configuration, serializationContext)
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import com.github.benmanes.caffeine.cache.RemovalCause
|
|||||||
import com.github.benmanes.caffeine.cache.RemovalListener
|
import com.github.benmanes.caffeine.cache.RemovalListener
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||||
|
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
import net.corda.client.rpc.RPCException
|
import net.corda.client.rpc.RPCException
|
||||||
import net.corda.client.rpc.RPCSinceVersion
|
import net.corda.client.rpc.RPCSinceVersion
|
||||||
import net.corda.core.context.Actor
|
import net.corda.core.context.Actor
|
||||||
@ -70,7 +71,7 @@ import kotlin.reflect.jvm.javaMethod
|
|||||||
* The cleanup happens in batches using a dedicated reaper, scheduled on [reaperExecutor].
|
* The cleanup happens in batches using a dedicated reaper, scheduled on [reaperExecutor].
|
||||||
*/
|
*/
|
||||||
class RPCClientProxyHandler(
|
class RPCClientProxyHandler(
|
||||||
private val rpcConfiguration: RPCClientConfiguration,
|
private val rpcConfiguration: CordaRPCClientConfiguration,
|
||||||
private val rpcUsername: String,
|
private val rpcUsername: String,
|
||||||
private val rpcPassword: String,
|
private val rpcPassword: String,
|
||||||
private val serverLocator: ServerLocator,
|
private val serverLocator: ServerLocator,
|
||||||
@ -414,8 +415,8 @@ class RPCClientProxyHandler(
|
|||||||
sendingEnabled = false
|
sendingEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn("RPC server unavailable.")
|
log.warn("RPC server unavailable. RPC calls are being buffered.")
|
||||||
log.warn("Terminating observables and in flight RPCs.")
|
log.warn("Terminating observables.")
|
||||||
val m = observableContext.observableMap.asMap()
|
val m = observableContext.observableMap.asMap()
|
||||||
m.keys.forEach { k ->
|
m.keys.forEach { k ->
|
||||||
observationExecutorPool.run(k) {
|
observationExecutorPool.run(k) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
import net.corda.core.internal.concurrent.map
|
import net.corda.core.internal.concurrent.map
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
@ -44,7 +44,7 @@ open class AbstractRPCTest {
|
|||||||
inline fun <reified I : RPCOps> RPCDriverDSL.testProxy(
|
inline fun <reified I : RPCOps> RPCDriverDSL.testProxy(
|
||||||
ops: I,
|
ops: I,
|
||||||
rpcUser: User = rpcTestUser,
|
rpcUser: User = rpcTestUser,
|
||||||
clientConfiguration: RPCClientConfiguration = RPCClientConfiguration.default,
|
clientConfiguration: CordaRPCClientConfigurationImpl = CordaRPCClientConfigurationImpl.default,
|
||||||
serverConfiguration: RPCServerConfiguration = RPCServerConfiguration.default
|
serverConfiguration: RPCServerConfiguration = RPCServerConfiguration.default
|
||||||
): TestProxy<I> {
|
): TestProxy<I> {
|
||||||
return when (mode) {
|
return when (mode) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.internal.concurrent.fork
|
import net.corda.core.internal.concurrent.fork
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
@ -90,7 +90,7 @@ class RPCConcurrencyTests : AbstractRPCTest() {
|
|||||||
private fun RPCDriverDSL.testProxy(): TestProxy<TestOps> {
|
private fun RPCDriverDSL.testProxy(): TestProxy<TestOps> {
|
||||||
return testProxy<TestOps>(
|
return testProxy<TestOps>(
|
||||||
TestOpsImpl(pool),
|
TestOpsImpl(pool),
|
||||||
clientConfiguration = RPCClientConfiguration.default.copy(
|
clientConfiguration = CordaRPCClientConfigurationImpl.default.copy(
|
||||||
reapInterval = 100.millis
|
reapInterval = 100.millis
|
||||||
),
|
),
|
||||||
serverConfiguration = RPCServerConfiguration.default.copy(
|
serverConfiguration = RPCServerConfiguration.default.copy(
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import com.google.common.base.Stopwatch
|
import com.google.common.base.Stopwatch
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
import net.corda.core.utilities.days
|
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||||
@ -43,7 +42,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun RPCDriverDSL.testProxy(
|
private fun RPCDriverDSL.testProxy(
|
||||||
clientConfiguration: RPCClientConfiguration,
|
clientConfiguration: CordaRPCClientConfigurationImpl,
|
||||||
serverConfiguration: RPCServerConfiguration
|
serverConfiguration: RPCServerConfiguration
|
||||||
): TestProxy<TestOps> {
|
): TestProxy<TestOps> {
|
||||||
return testProxy<TestOps>(
|
return testProxy<TestOps>(
|
||||||
@ -56,7 +55,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
|||||||
private fun warmup() {
|
private fun warmup() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val proxy = testProxy(
|
val proxy = testProxy(
|
||||||
RPCClientConfiguration.default,
|
CordaRPCClientConfigurationImpl.default,
|
||||||
RPCServerConfiguration.default
|
RPCServerConfiguration.default
|
||||||
)
|
)
|
||||||
val executor = Executors.newFixedThreadPool(4)
|
val executor = Executors.newFixedThreadPool(4)
|
||||||
@ -86,7 +85,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
|||||||
measure(inputOutputSizes, (1..5)) { inputOutputSize, _ ->
|
measure(inputOutputSizes, (1..5)) { inputOutputSize, _ ->
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val proxy = testProxy(
|
val proxy = testProxy(
|
||||||
RPCClientConfiguration.default.copy(
|
CordaRPCClientConfigurationImpl.default.copy(
|
||||||
observationExecutorPoolSize = 2
|
observationExecutorPoolSize = 2
|
||||||
),
|
),
|
||||||
RPCServerConfiguration.default.copy(
|
RPCServerConfiguration.default.copy(
|
||||||
@ -125,7 +124,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
|||||||
rpcDriver {
|
rpcDriver {
|
||||||
val metricRegistry = startReporter(shutdownManager)
|
val metricRegistry = startReporter(shutdownManager)
|
||||||
val proxy = testProxy(
|
val proxy = testProxy(
|
||||||
RPCClientConfiguration.default.copy(
|
CordaRPCClientConfigurationImpl.default.copy(
|
||||||
reapInterval = 1.seconds
|
reapInterval = 1.seconds
|
||||||
),
|
),
|
||||||
RPCServerConfiguration.default.copy(
|
RPCServerConfiguration.default.copy(
|
||||||
@ -157,7 +156,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
|||||||
// TODO this hangs with more parallelism
|
// TODO this hangs with more parallelism
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val proxy = testProxy(
|
val proxy = testProxy(
|
||||||
RPCClientConfiguration.default,
|
CordaRPCClientConfigurationImpl.default,
|
||||||
RPCServerConfiguration.default
|
RPCServerConfiguration.default
|
||||||
)
|
)
|
||||||
val numberOfMessages = 1000
|
val numberOfMessages = 1000
|
||||||
|
@ -21,6 +21,7 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.Try
|
import net.corda.core.utilities.Try
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import rx.subjects.PublishSubject
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -365,6 +366,44 @@ interface CordaRPCOps : RPCOps {
|
|||||||
* @see setFlowsDrainingModeEnabled
|
* @see setFlowsDrainingModeEnabled
|
||||||
*/
|
*/
|
||||||
fun isFlowsDrainingModeEnabled(): Boolean
|
fun isFlowsDrainingModeEnabled(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts the node down. Returns immediately.
|
||||||
|
* This does not wait for flows to be completed.
|
||||||
|
*/
|
||||||
|
fun shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [DataFeed] that keeps track on the count of pending flows.
|
||||||
|
*/
|
||||||
|
fun CordaRPCOps.pendingFlowsCount(): DataFeed<Int, Pair<Int, Int>> {
|
||||||
|
|
||||||
|
val stateMachineState = stateMachinesFeed()
|
||||||
|
var pendingFlowsCount = stateMachineState.snapshot.size
|
||||||
|
var completedFlowsCount = 0
|
||||||
|
val updates = PublishSubject.create<Pair<Int, Int>>()
|
||||||
|
stateMachineState
|
||||||
|
.updates
|
||||||
|
.doOnNext { update ->
|
||||||
|
when (update) {
|
||||||
|
is StateMachineUpdate.Added -> {
|
||||||
|
pendingFlowsCount++
|
||||||
|
updates.onNext(completedFlowsCount to pendingFlowsCount)
|
||||||
|
}
|
||||||
|
is StateMachineUpdate.Removed -> {
|
||||||
|
completedFlowsCount++
|
||||||
|
updates.onNext(completedFlowsCount to pendingFlowsCount)
|
||||||
|
if (completedFlowsCount == pendingFlowsCount) {
|
||||||
|
updates.onCompleted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.subscribe()
|
||||||
|
if (completedFlowsCount == 0) {
|
||||||
|
updates.onCompleted()
|
||||||
|
}
|
||||||
|
return DataFeed(pendingFlowsCount, updates)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : ContractState> CordaRPCOps.vaultQueryBy(criteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(),
|
inline fun <reified T : ContractState> CordaRPCOps.vaultQueryBy(criteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(),
|
||||||
|
@ -116,7 +116,7 @@ class ContractUpgradeFlowTest {
|
|||||||
return startRpcClient<CordaRPCOps>(
|
return startRpcClient<CordaRPCOps>(
|
||||||
rpcAddress = startRpcServer(
|
rpcAddress = startRpcServer(
|
||||||
rpcUser = user,
|
rpcUser = user,
|
||||||
ops = SecureCordaRPCOps(node.services, node.smm, node.database, node.services)
|
ops = SecureCordaRPCOps(node.services, node.smm, node.database, node.services, { })
|
||||||
).get().broker.hostAndPort!!,
|
).get().broker.hostAndPort!!,
|
||||||
username = user.username,
|
username = user.username,
|
||||||
password = user.password
|
password = user.password
|
||||||
|
@ -7,6 +7,8 @@ Unreleased
|
|||||||
Here are brief summaries of what's changed between each snapshot release. This includes guidance on how to upgrade code
|
Here are brief summaries of what's changed between each snapshot release. This includes guidance on how to upgrade code
|
||||||
from the previous milestone release.
|
from the previous milestone release.
|
||||||
|
|
||||||
|
* Node can be shut down abruptly by ``shutdown`` function in `CordaRPCOps` or gracefully (draining flows first) through ``gracefulShutdown`` command from shell.
|
||||||
|
|
||||||
* Parsing of ``NodeConfiguration`` will now fail if unknown configuration keys are found.
|
* Parsing of ``NodeConfiguration`` will now fail if unknown configuration keys are found.
|
||||||
|
|
||||||
* The web server now has its own ``web-server.conf`` file, separate from ``node.conf``.
|
* The web server now has its own ``web-server.conf`` file, separate from ``node.conf``.
|
||||||
|
@ -18,6 +18,7 @@ the `CRaSH`_ shell and supports many of the same features. These features includ
|
|||||||
* Issuing SQL queries to the underlying database
|
* Issuing SQL queries to the underlying database
|
||||||
* Viewing JMX metrics and monitoring exports
|
* Viewing JMX metrics and monitoring exports
|
||||||
* UNIX style pipes for both text and objects, an ``egrep`` command and a command for working with columnular data
|
* UNIX style pipes for both text and objects, an ``egrep`` command and a command for working with columnular data
|
||||||
|
* Shutting the node down.
|
||||||
|
|
||||||
Permissions
|
Permissions
|
||||||
-----------
|
-----------
|
||||||
@ -197,6 +198,14 @@ Some RPCs return a stream of events that will be shown on screen until you press
|
|||||||
You can find a list of the available RPC methods
|
You can find a list of the available RPC methods
|
||||||
`here <https://docs.corda.net/api/kotlin/corda/net.corda.core.messaging/-corda-r-p-c-ops/index.html>`_.
|
`here <https://docs.corda.net/api/kotlin/corda/net.corda.core.messaging/-corda-r-p-c-ops/index.html>`_.
|
||||||
|
|
||||||
|
Shutting down the node
|
||||||
|
**********************
|
||||||
|
|
||||||
|
You can shut the node down via shell:
|
||||||
|
|
||||||
|
* ``gracefulShutdown`` will put node into draining mode, and shut down when there are no flows running
|
||||||
|
* ``shutdown`` will shut the node down immediately
|
||||||
|
|
||||||
Flow commands
|
Flow commands
|
||||||
*************
|
*************
|
||||||
|
|
||||||
|
@ -156,9 +156,9 @@ class Node(
|
|||||||
val user = config.users.first()
|
val user = config.users.first()
|
||||||
val address = config.nodeInterface
|
val address = config.nodeInterface
|
||||||
val targetHost = NetworkHostAndPort(address.host, address.rpcPort)
|
val targetHost = NetworkHostAndPort(address.host, address.rpcPort)
|
||||||
val config = CordaRPCClientConfiguration(
|
val config = object : CordaRPCClientConfiguration {
|
||||||
connectionMaxRetryInterval = 10.seconds
|
override val connectionMaxRetryInterval = 10.seconds
|
||||||
)
|
}
|
||||||
log.info("Establishing RPC connection to ${targetHost.host} on port ${targetHost.port} ...")
|
log.info("Establishing RPC connection to ${targetHost.host} on port ${targetHost.port} ...")
|
||||||
CordaRPCClient(targetHost, config).use(user.username, user.password) {
|
CordaRPCClient(targetHost, config).use(user.username, user.password) {
|
||||||
log.info("RPC connection to ${targetHost.host}:${targetHost.port} established")
|
log.info("RPC connection to ${targetHost.host}:${targetHost.port} established")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.modes.draining
|
package net.corda.node.modes.draining
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.client.rpc.internal.drainAndShutdown
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.map
|
import net.corda.core.internal.concurrent.map
|
||||||
@ -13,11 +14,13 @@ import net.corda.testing.core.singleIdentity
|
|||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.chooseIdentity
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
|
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -49,6 +52,7 @@ class P2PFlowsDrainingModeTest {
|
|||||||
fun `flows draining mode suspends consumption of initial session messages`() {
|
fun `flows draining mode suspends consumption of initial session messages`() {
|
||||||
|
|
||||||
driver(DriverParameters(isDebug = true, startNodesInProcess = false, portAllocation = portAllocation)) {
|
driver(DriverParameters(isDebug = true, startNodesInProcess = false, portAllocation = portAllocation)) {
|
||||||
|
|
||||||
val initiatedNode = startNode().getOrThrow()
|
val initiatedNode = startNode().getOrThrow()
|
||||||
val initiating = startNode(rpcUsers = users).getOrThrow().rpc
|
val initiating = startNode(rpcUsers = users).getOrThrow().rpc
|
||||||
val counterParty = initiatedNode.nodeInfo.singleIdentity()
|
val counterParty = initiatedNode.nodeInfo.singleIdentity()
|
||||||
@ -76,9 +80,37 @@ class P2PFlowsDrainingModeTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
@Test
|
||||||
@InitiatingFlow
|
fun `clean shutdown by draining`() {
|
||||||
class InitiateSessionFlow(private val counterParty: Party) : FlowLogic<String>() {
|
|
||||||
|
driver(DriverParameters(isDebug = true, startNodesInProcess = true, portAllocation = portAllocation)) {
|
||||||
|
|
||||||
|
val nodeA = startNode(rpcUsers = users).getOrThrow()
|
||||||
|
val nodeB = startNode(rpcUsers = users).getOrThrow()
|
||||||
|
var successful = false
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
nodeB.rpc.setFlowsDrainingModeEnabled(true)
|
||||||
|
IntRange(1, 10).forEach { nodeA.rpc.startFlow(::InitiateSessionFlow, nodeB.nodeInfo.chooseIdentity()) }
|
||||||
|
|
||||||
|
nodeA.rpc.drainAndShutdown()
|
||||||
|
.doOnError { error ->
|
||||||
|
error.printStackTrace()
|
||||||
|
successful = false
|
||||||
|
}
|
||||||
|
.doOnCompleted { successful = true }
|
||||||
|
.doAfterTerminate { latch.countDown() }
|
||||||
|
.subscribe()
|
||||||
|
nodeB.rpc.setFlowsDrainingModeEnabled(false)
|
||||||
|
latch.await()
|
||||||
|
|
||||||
|
assertThat(successful).isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class InitiateSessionFlow(private val counterParty: Party) : FlowLogic<String>() {
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): String {
|
override fun call(): String {
|
||||||
@ -87,10 +119,10 @@ class P2PFlowsDrainingModeTest {
|
|||||||
session.send("Hi there")
|
session.send("Hi there")
|
||||||
return session.receive<String>().unwrap { it }
|
return session.receive<String>().unwrap { it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@InitiatedBy(InitiateSessionFlow::class)
|
@InitiatedBy(InitiateSessionFlow::class)
|
||||||
class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit>() {
|
class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit>() {
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
@ -98,5 +130,4 @@ class P2PFlowsDrainingModeTest {
|
|||||||
val message = initiatingSession.receive<String>().unwrap { it }
|
val message = initiatingSession.receive<String>().unwrap { it }
|
||||||
initiatingSession.send("$message answer")
|
initiatingSession.send("$message answer")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -86,6 +86,7 @@ import java.time.Duration
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -145,6 +146,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
protected lateinit var networkMapUpdater: NetworkMapUpdater
|
protected lateinit var networkMapUpdater: NetworkMapUpdater
|
||||||
lateinit var securityManager: RPCSecurityManager
|
lateinit var securityManager: RPCSecurityManager
|
||||||
|
|
||||||
|
private val shutdownExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
/** Completes once the node has successfully registered with the network map service
|
/** Completes once the node has successfully registered with the network map service
|
||||||
* or has loaded network map data from local database */
|
* or has loaded network map data from local database */
|
||||||
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
|
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
|
||||||
@ -159,7 +162,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
/** The implementation of the [CordaRPCOps] interface used by this node. */
|
/** The implementation of the [CordaRPCOps] interface used by this node. */
|
||||||
open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence, smm: StateMachineManager): CordaRPCOps {
|
open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence, smm: StateMachineManager): CordaRPCOps {
|
||||||
return SecureCordaRPCOps(services, smm, database, flowStarter)
|
|
||||||
|
return SecureCordaRPCOps(services, smm, database, flowStarter, { shutdownExecutor.submit { stop() } })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initCertificate() {
|
private fun initCertificate() {
|
||||||
@ -712,6 +716,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
toRun()
|
toRun()
|
||||||
}
|
}
|
||||||
runOnStop.clear()
|
runOnStop.clear()
|
||||||
|
shutdownExecutor.shutdown()
|
||||||
_started = null
|
_started = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ internal class CordaRPCOpsImpl(
|
|||||||
private val services: ServiceHubInternal,
|
private val services: ServiceHubInternal,
|
||||||
private val smm: StateMachineManager,
|
private val smm: StateMachineManager,
|
||||||
private val database: CordaPersistence,
|
private val database: CordaPersistence,
|
||||||
private val flowStarter: FlowStarter
|
private val flowStarter: FlowStarter,
|
||||||
|
private val shutdownNode: () -> Unit
|
||||||
) : CordaRPCOps {
|
) : CordaRPCOps {
|
||||||
override fun networkMapSnapshot(): List<NodeInfo> {
|
override fun networkMapSnapshot(): List<NodeInfo> {
|
||||||
val (snapshot, updates) = networkMapFeed()
|
val (snapshot, updates) = networkMapFeed()
|
||||||
@ -298,6 +299,10 @@ internal class CordaRPCOpsImpl(
|
|||||||
return services.nodeProperties.flowsDrainingMode.isEnabled()
|
return services.nodeProperties.flowsDrainingMode.isEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun shutdown() {
|
||||||
|
shutdownNode.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
private fun stateMachineInfoFromFlowLogic(flowLogic: FlowLogic<*>): StateMachineInfo {
|
private fun stateMachineInfoFromFlowLogic(flowLogic: FlowLogic<*>): StateMachineInfo {
|
||||||
return StateMachineInfo(flowLogic.runId, flowLogic.javaClass.name, flowLogic.stateMachine.context.toFlowInitiator(), flowLogic.track(), flowLogic.stateMachine.context)
|
return StateMachineInfo(flowLogic.runId, flowLogic.javaClass.name, flowLogic.stateMachine.context.toFlowInitiator(), flowLogic.track(), flowLogic.stateMachine.context)
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,8 @@ class RpcAuthorisationProxy(private val implementation: CordaRPCOps, private val
|
|||||||
|
|
||||||
override fun isFlowsDrainingModeEnabled(): Boolean = guard("isFlowsDrainingModeEnabled", implementation::isFlowsDrainingModeEnabled)
|
override fun isFlowsDrainingModeEnabled(): Boolean = guard("isFlowsDrainingModeEnabled", implementation::isFlowsDrainingModeEnabled)
|
||||||
|
|
||||||
|
override fun shutdown() = guard("shutdown", implementation::shutdown)
|
||||||
|
|
||||||
// TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140
|
// TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140
|
||||||
private inline fun <RESULT> guard(methodName: String, action: () -> RESULT) = guard(methodName, emptyList(), action)
|
private inline fun <RESULT> guard(methodName: String, action: () -> RESULT) = guard(methodName, emptyList(), action)
|
||||||
|
|
||||||
|
@ -14,7 +14,8 @@ class SecureCordaRPCOps(services: ServiceHubInternal,
|
|||||||
smm: StateMachineManager,
|
smm: StateMachineManager,
|
||||||
database: CordaPersistence,
|
database: CordaPersistence,
|
||||||
flowStarter: FlowStarter,
|
flowStarter: FlowStarter,
|
||||||
val unsafe: CordaRPCOps = CordaRPCOpsImpl(services, smm, database, flowStarter)) : CordaRPCOps by RpcAuthorisationProxy(unsafe, ::rpcContext) {
|
shutdownNode: () -> Unit,
|
||||||
|
val unsafe: CordaRPCOps = CordaRPCOpsImpl(services, smm, database, flowStarter, shutdownNode)) : CordaRPCOps by RpcAuthorisationProxy(unsafe, ::rpcContext) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
|
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
|
||||||
|
@ -82,7 +82,7 @@ class CordaRPCOpsImplTest {
|
|||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||||
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services)
|
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services, { })
|
||||||
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))
|
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.corda.testing.node.internal
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
@ -14,8 +16,10 @@ import net.corda.core.utilities.seconds
|
|||||||
import net.corda.node.services.api.StartedNodeServices
|
import net.corda.node.services.api.StartedNodeServices
|
||||||
import net.corda.node.services.messaging.Message
|
import net.corda.node.services.messaging.Message
|
||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.internal.chooseIdentity
|
import net.corda.testing.internal.chooseIdentity
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||||
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.testContext
|
import net.corda.testing.node.testContext
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
@ -113,4 +117,4 @@ internal interface InternalMockMessagingService : MessagingService {
|
|||||||
fun stop()
|
fun stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CordaRPCClient.start(user: User) = start(user.username, user.password)
|
@ -3,7 +3,7 @@ package net.corda.testing.node.internal
|
|||||||
import net.corda.client.mock.Generator
|
import net.corda.client.mock.Generator
|
||||||
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||||
import net.corda.client.rpc.internal.RPCClient
|
import net.corda.client.rpc.internal.RPCClient
|
||||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.context.AuthServiceId
|
import net.corda.core.context.AuthServiceId
|
||||||
import net.corda.core.context.Trace
|
import net.corda.core.context.Trace
|
||||||
@ -59,7 +59,7 @@ import net.corda.nodeapi.internal.config.User as InternalUser
|
|||||||
inline fun <reified I : RPCOps> RPCDriverDSL.startInVmRpcClient(
|
inline fun <reified I : RPCOps> RPCDriverDSL.startInVmRpcClient(
|
||||||
username: String = rpcTestUser.username,
|
username: String = rpcTestUser.username,
|
||||||
password: String = rpcTestUser.password,
|
password: String = rpcTestUser.password,
|
||||||
configuration: RPCClientConfiguration = RPCClientConfiguration.default
|
configuration: CordaRPCClientConfigurationImpl = CordaRPCClientConfigurationImpl.default
|
||||||
) = startInVmRpcClient(I::class.java, username, password, configuration)
|
) = startInVmRpcClient(I::class.java, username, password, configuration)
|
||||||
|
|
||||||
inline fun <reified I : RPCOps> RPCDriverDSL.startRandomRpcClient(
|
inline fun <reified I : RPCOps> RPCDriverDSL.startRandomRpcClient(
|
||||||
@ -72,7 +72,7 @@ inline fun <reified I : RPCOps> RPCDriverDSL.startRpcClient(
|
|||||||
rpcAddress: NetworkHostAndPort,
|
rpcAddress: NetworkHostAndPort,
|
||||||
username: String = rpcTestUser.username,
|
username: String = rpcTestUser.username,
|
||||||
password: String = rpcTestUser.password,
|
password: String = rpcTestUser.password,
|
||||||
configuration: RPCClientConfiguration = RPCClientConfiguration.default
|
configuration: CordaRPCClientConfigurationImpl = CordaRPCClientConfigurationImpl.default
|
||||||
) = startRpcClient(I::class.java, rpcAddress, username, password, configuration)
|
) = startRpcClient(I::class.java, rpcAddress, username, password, configuration)
|
||||||
|
|
||||||
data class RpcBrokerHandle(
|
data class RpcBrokerHandle(
|
||||||
@ -253,7 +253,7 @@ data class RPCDriverDSL(
|
|||||||
rpcOpsClass: Class<I>,
|
rpcOpsClass: Class<I>,
|
||||||
username: String = rpcTestUser.username,
|
username: String = rpcTestUser.username,
|
||||||
password: String = rpcTestUser.password,
|
password: String = rpcTestUser.password,
|
||||||
configuration: RPCClientConfiguration = RPCClientConfiguration.default
|
configuration: CordaRPCClientConfigurationImpl = CordaRPCClientConfigurationImpl.default
|
||||||
): CordaFuture<I> {
|
): CordaFuture<I> {
|
||||||
return driverDSL.executorService.fork {
|
return driverDSL.executorService.fork {
|
||||||
val client = RPCClient<I>(inVmClientTransportConfiguration, configuration)
|
val client = RPCClient<I>(inVmClientTransportConfiguration, configuration)
|
||||||
@ -324,7 +324,7 @@ data class RPCDriverDSL(
|
|||||||
rpcAddress: NetworkHostAndPort,
|
rpcAddress: NetworkHostAndPort,
|
||||||
username: String = rpcTestUser.username,
|
username: String = rpcTestUser.username,
|
||||||
password: String = rpcTestUser.password,
|
password: String = rpcTestUser.password,
|
||||||
configuration: RPCClientConfiguration = RPCClientConfiguration.default
|
configuration: CordaRPCClientConfigurationImpl = CordaRPCClientConfigurationImpl.default
|
||||||
): CordaFuture<I> {
|
): CordaFuture<I> {
|
||||||
return driverDSL.executorService.fork {
|
return driverDSL.executorService.fork {
|
||||||
val client = RPCClient<I>(ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), rpcAddress, null), configuration)
|
val client = RPCClient<I>(ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), rpcAddress, null), configuration)
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
package net.corda.tools.shell;
|
package net.corda.tools.shell;
|
||||||
|
|
||||||
import net.corda.core.messaging.*;
|
import com.google.common.collect.Lists;
|
||||||
import net.corda.client.jackson.*;
|
import com.google.common.collect.Maps;
|
||||||
import org.crsh.cli.*;
|
import net.corda.client.jackson.StringToMethodCallParser;
|
||||||
import org.crsh.command.*;
|
import net.corda.core.messaging.CordaRPCOps;
|
||||||
|
import org.crsh.cli.Argument;
|
||||||
|
import org.crsh.cli.Command;
|
||||||
|
import org.crsh.cli.Man;
|
||||||
|
import org.crsh.cli.Usage;
|
||||||
|
import org.crsh.command.InvocationContext;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static java.util.Comparator.comparing;
|
||||||
|
|
||||||
// Note that this class cannot be converted to Kotlin because CRaSH does not understand InvocationContext<Map<?, ?>> which
|
// Note that this class cannot be converted to Kotlin because CRaSH does not understand InvocationContext<Map<?, ?>> which
|
||||||
// is the closest you can get in Kotlin to raw types.
|
// is the closest you can get in Kotlin to raw types.
|
||||||
|
|
||||||
@ -29,8 +37,7 @@ public class RunShellCommand extends InteractiveShellCommand {
|
|||||||
emitHelp(context, parser);
|
emitHelp(context, parser);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return InteractiveShell.runRPCFromString(command, out, context, ops(), objectMapper(), isSsh());
|
||||||
return InteractiveShell.runRPCFromString(command, out, context, ops(), objectMapper());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitHelp(InvocationContext<Map> context, StringToMethodCallParser<CordaRPCOps> parser) {
|
private void emitHelp(InvocationContext<Map> context, StringToMethodCallParser<CordaRPCOps> parser) {
|
||||||
@ -38,21 +45,37 @@ public class RunShellCommand extends InteractiveShellCommand {
|
|||||||
// Each element we emit is a map of column -> content.
|
// Each element we emit is a map of column -> content.
|
||||||
Set<Map.Entry<String, String>> entries = parser.getAvailableCommands().entrySet();
|
Set<Map.Entry<String, String>> entries = parser.getAvailableCommands().entrySet();
|
||||||
ArrayList<Map.Entry<String, String>> entryList = new ArrayList<>(entries);
|
ArrayList<Map.Entry<String, String>> entryList = new ArrayList<>(entries);
|
||||||
entryList.sort(Comparator.comparing(Map.Entry::getKey));
|
entryList.sort(comparing(Map.Entry::getKey));
|
||||||
for (Map.Entry<String, String> entry : entryList) {
|
for (Map.Entry<String, String> entry : entryList) {
|
||||||
// Skip these entries as they aren't really interesting for the user.
|
// Skip these entries as they aren't really interesting for the user.
|
||||||
if (entry.getKey().equals("startFlowDynamic")) continue;
|
if (entry.getKey().equals("startFlowDynamic")) continue;
|
||||||
if (entry.getKey().equals("getProtocolVersion")) continue;
|
if (entry.getKey().equals("getProtocolVersion")) continue;
|
||||||
|
|
||||||
// Use a LinkedHashMap to ensure that the Command column comes first.
|
|
||||||
Map<String, String> m = new LinkedHashMap<>();
|
|
||||||
m.put("Command", entry.getKey());
|
|
||||||
m.put("Parameter types", entry.getValue());
|
|
||||||
try {
|
try {
|
||||||
context.provide(m);
|
context.provide(commandAndDesc(entry.getKey(), entry.getValue()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lists.newArrayList(
|
||||||
|
commandAndDesc("shutdown", "Shuts node down (immediately)"),
|
||||||
|
commandAndDesc("gracefulShutdown", "Shuts node down gracefully, waiting for all flows to complete first.")
|
||||||
|
).forEach(stringStringMap -> {
|
||||||
|
try {
|
||||||
|
context.provide(stringStringMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Map<String, String> commandAndDesc(String command, String description) {
|
||||||
|
// Use a LinkedHashMap to ensure that the Command column comes first.
|
||||||
|
Map<String, String> abruptShutdown = Maps.newLinkedHashMap();
|
||||||
|
abruptShutdown.put("Command", command);
|
||||||
|
abruptShutdown.put("Parameter types", description);
|
||||||
|
return abruptShutdown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class CordaAuthenticationPlugin(private val rpcOps: (username: String, credentia
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val ops = rpcOps(username, credential)
|
val ops = rpcOps(username, credential)
|
||||||
return CordaSSHAuthInfo(true, ops)
|
return CordaSSHAuthInfo(true, ops, isSsh = true)
|
||||||
} catch (e: ActiveMQSecurityException) {
|
} catch (e: ActiveMQSecurityException) {
|
||||||
logger.warn(e.message)
|
logger.warn(e.message)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -6,7 +6,7 @@ import net.corda.tools.shell.InteractiveShell.createYamlInputMapper
|
|||||||
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
||||||
import org.crsh.auth.AuthInfo
|
import org.crsh.auth.AuthInfo
|
||||||
|
|
||||||
class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: CordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null) : AuthInfo {
|
class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: CordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null, val isSsh: Boolean = false) : AuthInfo {
|
||||||
override fun isSuccessful(): Boolean = successful
|
override fun isSuccessful(): Boolean = successful
|
||||||
|
|
||||||
val yamlInputMapper: ObjectMapper by lazy {
|
val yamlInputMapper: ObjectMapper by lazy {
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package net.corda.tools.shell
|
package net.corda.tools.shell
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
import com.fasterxml.jackson.core.JsonParser
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
import com.fasterxml.jackson.databind.*
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
import com.google.common.io.Closeables
|
|
||||||
import net.corda.client.jackson.JacksonSupport
|
import net.corda.client.jackson.JacksonSupport
|
||||||
import net.corda.client.jackson.StringToMethodCallParser
|
import net.corda.client.jackson.StringToMethodCallParser
|
||||||
|
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||||
|
import net.corda.client.rpc.CordaRPCConnection
|
||||||
import net.corda.client.rpc.PermissionException
|
import net.corda.client.rpc.PermissionException
|
||||||
import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
|
import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
@ -18,13 +21,9 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.concurrent.doneFuture
|
import net.corda.core.internal.concurrent.doneFuture
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.*
|
||||||
import net.corda.core.messaging.DataFeed
|
|
||||||
import net.corda.core.messaging.FlowProgressHandle
|
|
||||||
import net.corda.core.messaging.StateMachineUpdate
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
|
||||||
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
||||||
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
|
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
|
||||||
import org.crsh.command.InvocationContext
|
import org.crsh.command.InvocationContext
|
||||||
@ -50,12 +49,13 @@ import org.json.JSONObject
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscriber
|
import rx.Subscriber
|
||||||
import java.io.*
|
import java.io.FileDescriptor
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.PrintWriter
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.UndeclaredThrowableException
|
import java.lang.reflect.UndeclaredThrowableException
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
@ -73,69 +73,30 @@ import kotlin.concurrent.thread
|
|||||||
// TODO: Resurrect or reimplement the mail plugin.
|
// TODO: Resurrect or reimplement the mail plugin.
|
||||||
// TODO: Make it notice new shell commands added after the node started.
|
// TODO: Make it notice new shell commands added after the node started.
|
||||||
|
|
||||||
data class SSHDConfiguration(val port: Int) {
|
|
||||||
companion object {
|
|
||||||
internal const val INVALID_PORT_FORMAT = "Invalid port: %s"
|
|
||||||
private const val MISSING_PORT_FORMAT = "Missing port: %s"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a string of the form port into a [SSHDConfiguration].
|
|
||||||
* @throws IllegalArgumentException if the port is missing or the string is garbage.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun parse(str: String): SSHDConfiguration {
|
|
||||||
require(!str.isNullOrBlank()) { SSHDConfiguration.MISSING_PORT_FORMAT.format(str) }
|
|
||||||
val port = try {
|
|
||||||
str.toInt()
|
|
||||||
} catch (ex: NumberFormatException) {
|
|
||||||
throw IllegalArgumentException("Port syntax is invalid, expected port")
|
|
||||||
}
|
|
||||||
return SSHDConfiguration(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
require(port in (0..0xffff)) { INVALID_PORT_FORMAT.format(port) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ShellSslOptions(override val sslKeystore: Path, override val keyStorePassword: String, override val trustStoreFile:Path, override val trustStorePassword: String) : SSLConfiguration {
|
|
||||||
override val certificatesDirectory: Path get() = Paths.get("")
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ShellConfiguration(
|
|
||||||
val commandsDirectory: Path,
|
|
||||||
val cordappsDirectory: Path? = null,
|
|
||||||
var user: String = "",
|
|
||||||
var password: String = "",
|
|
||||||
val hostAndPort: NetworkHostAndPort,
|
|
||||||
val ssl: ShellSslOptions? = null,
|
|
||||||
val sshdPort: Int? = null,
|
|
||||||
val sshHostKeyDirectory: Path? = null,
|
|
||||||
val noLocalShell: Boolean = false) {
|
|
||||||
companion object {
|
|
||||||
const val SSH_PORT = 2222
|
|
||||||
const val COMMANDS_DIR = "shell-commands"
|
|
||||||
const val CORDAPPS_DIR = "cordapps"
|
|
||||||
const val SSHD_HOSTKEY_DIR = "ssh"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object InteractiveShell {
|
object InteractiveShell {
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
private lateinit var rpcOps: (username: String, credentials: String) -> CordaRPCOps
|
private lateinit var rpcOps: (username: String, credentials: String) -> CordaRPCOps
|
||||||
private lateinit var connection: CordaRPCOps
|
private lateinit var ops: CordaRPCOps
|
||||||
|
private lateinit var connection: CordaRPCConnection
|
||||||
private var shell: Shell? = null
|
private var shell: Shell? = null
|
||||||
private var classLoader: ClassLoader? = null
|
private var classLoader: ClassLoader? = null
|
||||||
|
private lateinit var shellConfiguration: ShellConfiguration
|
||||||
|
private var onExit: () -> Unit = {}
|
||||||
/**
|
/**
|
||||||
* Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node
|
* Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node
|
||||||
* internals.
|
* internals.
|
||||||
*/
|
*/
|
||||||
fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null) {
|
fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null) {
|
||||||
|
shellConfiguration = configuration
|
||||||
rpcOps = { username: String, credentials: String ->
|
rpcOps = { username: String, credentials: String ->
|
||||||
val client = createCordaRPCClientWithSslAndClassLoader(hostAndPort = configuration.hostAndPort,
|
val client = createCordaRPCClientWithSslAndClassLoader(hostAndPort = configuration.hostAndPort,
|
||||||
sslConfiguration = configuration.ssl, classLoader = classLoader)
|
configuration = object : CordaRPCClientConfiguration {
|
||||||
client.start(username, credentials).proxy
|
override val maxReconnectAttempts = 1
|
||||||
|
},
|
||||||
|
sslConfiguration = configuration.ssl,
|
||||||
|
classLoader = classLoader)
|
||||||
|
this.connection = client.start(username, credentials)
|
||||||
|
connection.proxy
|
||||||
}
|
}
|
||||||
InteractiveShell.classLoader = classLoader
|
InteractiveShell.classLoader = classLoader
|
||||||
val runSshDaemon = configuration.sshdPort != null
|
val runSshDaemon = configuration.sshdPort != null
|
||||||
@ -160,6 +121,7 @@ object InteractiveShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun runLocalShell(onExit: () -> Unit = {}) {
|
fun runLocalShell(onExit: () -> Unit = {}) {
|
||||||
|
this.onExit = onExit
|
||||||
val terminal = TerminalFactory.create()
|
val terminal = TerminalFactory.create()
|
||||||
val consoleReader = ConsoleReader("Corda", FileInputStream(FileDescriptor.`in`), System.out, terminal)
|
val consoleReader = ConsoleReader("Corda", FileInputStream(FileDescriptor.`in`), System.out, terminal)
|
||||||
val jlineProcessor = JLineProcessor(terminal.isAnsiSupported, shell, consoleReader, System.out)
|
val jlineProcessor = JLineProcessor(terminal.isAnsiSupported, shell, consoleReader, System.out)
|
||||||
@ -205,18 +167,18 @@ object InteractiveShell {
|
|||||||
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOps)
|
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val attributes = emptyMap<String,Any>()
|
val attributes = emptyMap<String, Any>()
|
||||||
val context = PluginContext(discovery, attributes, commandsFS, confFS, classLoader)
|
val context = PluginContext(discovery, attributes, commandsFS, confFS, classLoader)
|
||||||
context.refresh()
|
context.refresh()
|
||||||
this.config = config
|
this.config = config
|
||||||
start(context)
|
start(context)
|
||||||
connection = makeRPCOps(rpcOps, localUserName, localUserPassword)
|
ops = makeRPCOps(rpcOps, localUserName, localUserPassword)
|
||||||
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, connection, StdoutANSIProgressRenderer))
|
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, ops, StdoutANSIProgressRenderer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nodeInfo() = try {
|
fun nodeInfo() = try {
|
||||||
connection.nodeInfo()
|
ops.nodeInfo()
|
||||||
} catch (e: UndeclaredThrowableException) {
|
} catch (e: UndeclaredThrowableException) {
|
||||||
throw e.cause ?: e
|
throw e.cause ?: e
|
||||||
}
|
}
|
||||||
@ -411,14 +373,16 @@ object InteractiveShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, cordaRPCOps: CordaRPCOps, om: ObjectMapper): Any? {
|
fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, cordaRPCOps: CordaRPCOps, om: ObjectMapper, isSsh: Boolean = false): Any? {
|
||||||
val cmd = input.joinToString(" ").trim { it <= ' ' }
|
val cmd = input.joinToString(" ").trim { it <= ' ' }
|
||||||
if (cmd.toLowerCase().startsWith("startflow")) {
|
if (cmd.startsWith("startflow", ignoreCase = true)) {
|
||||||
// The flow command provides better support and startFlow requires special handling anyway due to
|
// The flow command provides better support and startFlow requires special handling anyway due to
|
||||||
// the generic startFlow RPC interface which offers no type information with which to parse the
|
// the generic startFlow RPC interface which offers no type information with which to parse the
|
||||||
// string form of the command.
|
// string form of the command.
|
||||||
out.println("Please use the 'flow' command to interact with flows rather than the 'run' command.", Color.yellow)
|
out.println("Please use the 'flow' command to interact with flows rather than the 'run' command.", Color.yellow)
|
||||||
return null
|
return null
|
||||||
|
} else if (cmd.substringAfter(" ").trim().equals("gracefulShutdown", ignoreCase = true)) {
|
||||||
|
return InteractiveShell.gracefulShutdown(out, cordaRPCOps, isSsh)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: Any? = null
|
var result: Any? = null
|
||||||
@ -457,6 +421,68 @@ object InteractiveShell {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun gracefulShutdown(userSessionOut: RenderPrintWriter, cordaRPCOps: CordaRPCOps, isSsh: Boolean = false) {
|
||||||
|
|
||||||
|
fun display(statements: RenderPrintWriter.() -> Unit) {
|
||||||
|
statements.invoke(userSessionOut)
|
||||||
|
userSessionOut.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
var isShuttingDown = false
|
||||||
|
try {
|
||||||
|
display {
|
||||||
|
println("Orchestrating a clean shutdown...")
|
||||||
|
println("...enabling draining mode")
|
||||||
|
}
|
||||||
|
cordaRPCOps.setFlowsDrainingModeEnabled(true)
|
||||||
|
display {
|
||||||
|
println("...waiting for in-flight flows to be completed")
|
||||||
|
}
|
||||||
|
cordaRPCOps.pendingFlowsCount().updates
|
||||||
|
.doOnError { error ->
|
||||||
|
log.error(error.message)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
.doOnNext { remaining ->
|
||||||
|
display {
|
||||||
|
println("...remaining: ${remaining.first}/${remaining.second}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.doOnCompleted {
|
||||||
|
if (isSsh) {
|
||||||
|
// print in the original Shell process
|
||||||
|
System.out.println("Shutting down the node via remote SSH session (it may take a while)")
|
||||||
|
}
|
||||||
|
display {
|
||||||
|
println("Shutting down the node (it may take a while)")
|
||||||
|
}
|
||||||
|
cordaRPCOps.shutdown()
|
||||||
|
isShuttingDown = true
|
||||||
|
connection.forceClose()
|
||||||
|
display {
|
||||||
|
println("...done, quitting standalone shell now.")
|
||||||
|
}
|
||||||
|
onExit.invoke()
|
||||||
|
}.toBlocking().single()
|
||||||
|
} catch (e: StringToMethodCallParser.UnparseableCallException) {
|
||||||
|
display {
|
||||||
|
println(e.message, Color.red)
|
||||||
|
println("Please try 'man run' to learn what syntax is acceptable")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (!isShuttingDown) {
|
||||||
|
display {
|
||||||
|
println("RPC failed: ${e.rootCause}", Color.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
InputStreamSerializer.invokeContext = null
|
||||||
|
InputStreamDeserializer.closeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun printAndFollowRPCResponse(response: Any?, out: PrintWriter): CordaFuture<Unit> {
|
private fun printAndFollowRPCResponse(response: Any?, out: PrintWriter): CordaFuture<Unit> {
|
||||||
|
|
||||||
val mapElement: (Any?) -> String = { element -> outputMapper.writerWithDefaultPrettyPrinter().writeValueAsString(element) }
|
val mapElement: (Any?) -> String = { element -> outputMapper.writerWithDefaultPrettyPrinter().writeValueAsString(element) }
|
||||||
@ -518,6 +544,7 @@ object InteractiveShell {
|
|||||||
return printNextElements(response.updates, printerFun, out)
|
return printNextElements(response.updates, printerFun, out)
|
||||||
}
|
}
|
||||||
if (response is Observable<*>) {
|
if (response is Observable<*>) {
|
||||||
|
|
||||||
return printNextElements(response, printerFun, out)
|
return printNextElements(response, printerFun, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,94 +559,4 @@ object InteractiveShell {
|
|||||||
return subscriber.future
|
return subscriber.future
|
||||||
}
|
}
|
||||||
|
|
||||||
//region Extra serializers
|
|
||||||
//
|
|
||||||
// These serializers are used to enable the user to specify objects that aren't natural data containers in the shell,
|
|
||||||
// and for the shell to print things out that otherwise wouldn't be usefully printable.
|
|
||||||
|
|
||||||
private object ObservableSerializer : JsonSerializer<Observable<*>>() {
|
|
||||||
override fun serialize(value: Observable<*>, gen: JsonGenerator, serializers: SerializerProvider) {
|
|
||||||
gen.writeString("(observable)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A file name is deserialized to an InputStream if found.
|
|
||||||
object InputStreamDeserializer : JsonDeserializer<InputStream>() {
|
|
||||||
// Keep track of them so we can close them later.
|
|
||||||
private val streams = Collections.synchronizedSet(HashSet<InputStream>())
|
|
||||||
|
|
||||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): InputStream {
|
|
||||||
val stream = object : BufferedInputStream(Files.newInputStream(Paths.get(p.text))) {
|
|
||||||
override fun close() {
|
|
||||||
super.close()
|
|
||||||
streams.remove(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streams += stream
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
|
|
||||||
fun closeAll() {
|
|
||||||
// Clone the set with toList() here so each closed stream can be removed from the set inside close().
|
|
||||||
streams.toList().forEach { Closeables.closeQuietly(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An InputStream found in a response triggers a request to the user to provide somewhere to save it.
|
|
||||||
private object InputStreamSerializer : JsonSerializer<InputStream>() {
|
|
||||||
var invokeContext: InvocationContext<*>? = null
|
|
||||||
|
|
||||||
override fun serialize(value: InputStream, gen: JsonGenerator, serializers: SerializerProvider) {
|
|
||||||
try {
|
|
||||||
val toPath = invokeContext!!.readLine("Path to save stream to (enter to ignore): ", true)
|
|
||||||
if (toPath == null || toPath.isBlank()) {
|
|
||||||
gen.writeString("<not saved>")
|
|
||||||
} else {
|
|
||||||
val path = Paths.get(toPath)
|
|
||||||
value.copyTo(path)
|
|
||||||
gen.writeString("<saved to: ${path.toAbsolutePath()}>")
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
value.close()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String value deserialized to [UniqueIdentifier].
|
|
||||||
* Any string value used as [UniqueIdentifier.externalId].
|
|
||||||
* If string contains underscore(i.e. externalId_uuid) then split with it.
|
|
||||||
* Index 0 as [UniqueIdentifier.externalId]
|
|
||||||
* Index 1 as [UniqueIdentifier.id]
|
|
||||||
* */
|
|
||||||
object UniqueIdentifierDeserializer : JsonDeserializer<UniqueIdentifier>() {
|
|
||||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UniqueIdentifier {
|
|
||||||
//Check if externalId and UUID may be separated by underscore.
|
|
||||||
if (p.text.contains("_")) {
|
|
||||||
val ids = p.text.split("_")
|
|
||||||
//Create UUID object from string.
|
|
||||||
val uuid: UUID = UUID.fromString(ids[1])
|
|
||||||
//Create UniqueIdentifier object using externalId and UUID.
|
|
||||||
return UniqueIdentifier(ids[0], uuid)
|
|
||||||
}
|
|
||||||
//Any other string used as externalId.
|
|
||||||
return UniqueIdentifier.fromString(p.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String value deserialized to [UUID].
|
|
||||||
* */
|
|
||||||
object UUIDDeserializer : JsonDeserializer<UUID>() {
|
|
||||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UUID {
|
|
||||||
//Create UUID object from string.
|
|
||||||
return UUID.fromString(p.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,5 @@ open class InteractiveShellCommand : BaseCommand() {
|
|||||||
fun ops() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).rpcOps
|
fun ops() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).rpcOps
|
||||||
fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer
|
fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer
|
||||||
fun objectMapper() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).yamlInputMapper
|
fun objectMapper() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).yamlInputMapper
|
||||||
|
fun isSsh() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).isSsh
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,5 @@ fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -> CordaRP
|
|||||||
// Unpack exception.
|
// Unpack exception.
|
||||||
throw e.targetException
|
throw e.targetException
|
||||||
}
|
}
|
||||||
}
|
}) as CordaRPCOps
|
||||||
) as CordaRPCOps
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package net.corda.tools.shell
|
||||||
|
|
||||||
|
data class SSHDConfiguration(val port: Int) {
|
||||||
|
companion object {
|
||||||
|
internal const val INVALID_PORT_FORMAT = "Invalid port: %s"
|
||||||
|
private const val MISSING_PORT_FORMAT = "Missing port: %s"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string of the form port into a [SSHDConfiguration].
|
||||||
|
* @throws IllegalArgumentException if the port is missing or the string is garbage.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun parse(str: String): SSHDConfiguration {
|
||||||
|
require(!str.isNullOrBlank()) { SSHDConfiguration.MISSING_PORT_FORMAT.format(str) }
|
||||||
|
val port = try {
|
||||||
|
str.toInt()
|
||||||
|
} catch (ex: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("Port syntax is invalid, expected port")
|
||||||
|
}
|
||||||
|
return SSHDConfiguration(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(port in (0..0xffff)) { INVALID_PORT_FORMAT.format(port) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package net.corda.tools.shell
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.google.common.io.Closeables
|
||||||
|
import net.corda.core.contracts.UniqueIdentifier
|
||||||
|
import net.corda.core.internal.copyTo
|
||||||
|
import org.crsh.command.InvocationContext
|
||||||
|
import rx.Observable
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
//region Extra serializers
|
||||||
|
//
|
||||||
|
// These serializers are used to enable the user to specify objects that aren't natural data containers in the shell,
|
||||||
|
// and for the shell to print things out that otherwise wouldn't be usefully printable.
|
||||||
|
|
||||||
|
object ObservableSerializer : JsonSerializer<Observable<*>>() {
|
||||||
|
override fun serialize(value: Observable<*>, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString("(observable)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String value deserialized to [UniqueIdentifier].
|
||||||
|
* Any string value used as [UniqueIdentifier.externalId].
|
||||||
|
* If string contains underscore(i.e. externalId_uuid) then split with it.
|
||||||
|
* Index 0 as [UniqueIdentifier.externalId]
|
||||||
|
* Index 1 as [UniqueIdentifier.id]
|
||||||
|
* */
|
||||||
|
object UniqueIdentifierDeserializer : JsonDeserializer<UniqueIdentifier>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UniqueIdentifier {
|
||||||
|
//Check if externalId and UUID may be separated by underscore.
|
||||||
|
if (p.text.contains("_")) {
|
||||||
|
val ids = p.text.split("_")
|
||||||
|
//Create UUID object from string.
|
||||||
|
val uuid: UUID = UUID.fromString(ids[1])
|
||||||
|
//Create UniqueIdentifier object using externalId and UUID.
|
||||||
|
return UniqueIdentifier(ids[0], uuid)
|
||||||
|
}
|
||||||
|
//Any other string used as externalId.
|
||||||
|
return UniqueIdentifier.fromString(p.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String value deserialized to [UUID].
|
||||||
|
* */
|
||||||
|
object UUIDDeserializer : JsonDeserializer<UUID>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UUID {
|
||||||
|
//Create UUID object from string.
|
||||||
|
return UUID.fromString(p.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An InputStream found in a response triggers a request to the user to provide somewhere to save it.
|
||||||
|
object InputStreamSerializer : JsonSerializer<InputStream>() {
|
||||||
|
var invokeContext: InvocationContext<*>? = null
|
||||||
|
|
||||||
|
override fun serialize(value: InputStream, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
|
||||||
|
value.use {
|
||||||
|
val toPath = invokeContext!!.readLine("Path to save stream to (enter to ignore): ", true)
|
||||||
|
if (toPath == null || toPath.isBlank()) {
|
||||||
|
gen.writeString("<not saved>")
|
||||||
|
} else {
|
||||||
|
val path = Paths.get(toPath)
|
||||||
|
it.copyTo(path)
|
||||||
|
gen.writeString("<saved to: ${path.toAbsolutePath()}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A file name is deserialized to an InputStream if found.
|
||||||
|
object InputStreamDeserializer : JsonDeserializer<InputStream>() {
|
||||||
|
// Keep track of them so we can close them later.
|
||||||
|
private val streams = Collections.synchronizedSet(HashSet<InputStream>())
|
||||||
|
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): InputStream {
|
||||||
|
val stream = object : BufferedInputStream(Files.newInputStream(Paths.get(p.text))) {
|
||||||
|
override fun close() {
|
||||||
|
super.close()
|
||||||
|
streams.remove(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streams += stream
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeAll() {
|
||||||
|
// Clone the set with toList() here so each closed stream can be removed from the set inside close().
|
||||||
|
streams.toList().forEach { Closeables.closeQuietly(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
@ -0,0 +1,28 @@
|
|||||||
|
package net.corda.tools.shell
|
||||||
|
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
data class ShellConfiguration(
|
||||||
|
val commandsDirectory: Path,
|
||||||
|
val cordappsDirectory: Path? = null,
|
||||||
|
var user: String = "",
|
||||||
|
var password: String = "",
|
||||||
|
val hostAndPort: NetworkHostAndPort,
|
||||||
|
val ssl: ShellSslOptions? = null,
|
||||||
|
val sshdPort: Int? = null,
|
||||||
|
val sshHostKeyDirectory: Path? = null,
|
||||||
|
val noLocalShell: Boolean = false) {
|
||||||
|
companion object {
|
||||||
|
const val SSH_PORT = 2222
|
||||||
|
const val COMMANDS_DIR = "shell-commands"
|
||||||
|
const val CORDAPPS_DIR = "cordapps"
|
||||||
|
const val SSHD_HOSTKEY_DIR = "ssh"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ShellSslOptions(override val sslKeystore: Path, override val keyStorePassword: String, override val trustStoreFile:Path, override val trustStorePassword: String) : SSLConfiguration {
|
||||||
|
override val certificatesDirectory: Path get() = Paths.get("")
|
||||||
|
}
|
@ -105,6 +105,7 @@ class StandaloneShell(private val configuration: ShellConfiguration) {
|
|||||||
configuration.sshdPort?.apply{ println("SSH server listening on port $this.") }
|
configuration.sshdPort?.apply{ println("SSH server listening on port $this.") }
|
||||||
|
|
||||||
exit.await()
|
exit.await()
|
||||||
|
// because we can't clean certain Crash Shell threads that block on read()
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ class CustomTypeJsonParsingTests {
|
|||||||
fun setup() {
|
fun setup() {
|
||||||
objectMapper = ObjectMapper()
|
objectMapper = ObjectMapper()
|
||||||
val simpleModule = SimpleModule()
|
val simpleModule = SimpleModule()
|
||||||
simpleModule.addDeserializer(UniqueIdentifier::class.java, InteractiveShell.UniqueIdentifierDeserializer)
|
simpleModule.addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer)
|
||||||
simpleModule.addDeserializer(UUID::class.java, InteractiveShell.UUIDDeserializer)
|
simpleModule.addDeserializer(UUID::class.java, UUIDDeserializer)
|
||||||
objectMapper.registerModule(simpleModule)
|
objectMapper.registerModule(simpleModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user