CORDA-704: Implement @DoNotImplement annotation (#1903)

* Enhance the API Scanner plugin to monitor class annotations.
* Implement @DoNotImplement annotation, and apply it.
* Update API definition.
* Update API change detection to handle @DoNotImplement.
* Document the `@DoNotImplement` annotation.
This commit is contained in:
Chris Rankin 2017-10-19 17:18:35 +01:00 committed by GitHub
parent 3dd09fd69b
commit 2c84d07e8e
29 changed files with 402 additions and 266 deletions

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,15 @@ if [ $removalCount -gt 0 ]; then
fi fi
# Adding new abstract methods could also break the API. # Adding new abstract methods could also break the API.
newAbstracts=$(echo "$diffContents" | grep "^+" | grep "\(public\|protected\) abstract") # However, first exclude anything with the @DoNotImplement annotation.
function forUserImpl() {
awk '/DoNotImplement/,/^##/{ next }{ print }' $1
}
userDiffContents=`diff -u <(forUserImpl $apiCurrent) <(forUserImpl $APIHOME/../build/api/api-corda-*.txt) | tail --lines=+3`
newAbstracts=$(echo "$userDiffContents" | grep "^+" | grep "\(public\|protected\) abstract")
abstractCount=`grep -v "^$" <<EOF | wc -l abstractCount=`grep -v "^$" <<EOF | wc -l
$newAbstracts $newAbstracts
EOF EOF

View File

@ -1,5 +1,6 @@
package net.corda.client.rpc package net.corda.client.rpc
import net.corda.core.DoNotImplement
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import java.io.Closeable import java.io.Closeable
@ -10,6 +11,7 @@ import java.io.Closeable
* [Closeable.close] may be used to shut down the connection and release associated resources. It is an * [Closeable.close] may be used to shut down the connection and release associated resources. It is an
* alias for [notifyServerAndClose]. * alias for [notifyServerAndClose].
*/ */
@DoNotImplement
interface RPCConnection<out I : RPCOps> : Closeable { interface RPCConnection<out I : RPCOps> : Closeable {
/** /**
* Holds a synthetic class that automatically forwards method calls to the server, and returns the response. * Holds a synthetic class that automatically forwards method calls to the server, and returns the response.

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=2.0.5 gradlePluginsVersion=2.0.6
kotlinVersion=1.1.50 kotlinVersion=1.1.50
guavaVersion=21.0 guavaVersion=21.0
bouncycastleVersion=1.57 bouncycastleVersion=1.57

View File

@ -0,0 +1,18 @@
package net.corda.core
import java.lang.annotation.Inherited
/**
* This annotation is for interfaces and abstract classes that provide Corda functionality
* to user applications. Future versions of Corda may add new methods to such interfaces and
* classes, but will not remove or modify existing methods.
*
* Adding new methods does not break Corda's API compatibility guarantee because applications
* should not implement or extend anything annotated with [DoNotImplement]. These classes are
* only meant to be implemented by Corda itself.
*/
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
@Inherited
annotation class DoNotImplement

View File

@ -1,5 +1,6 @@
package net.corda.core.cordapp package net.corda.core.cordapp
import net.corda.core.DoNotImplement
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
@ -17,13 +18,14 @@ import java.net.URL
* @property contractClassNames List of contracts * @property contractClassNames List of contracts
* @property initiatedFlows List of initiatable flow classes * @property initiatedFlows List of initiatable flow classes
* @property rpcFlows List of RPC initiable flows classes * @property rpcFlows List of RPC initiable flows classes
* @property serviceFlows List of [CordaService] initiable flows classes * @property serviceFlows List of [net.corda.core.node.services.CordaService] initiable flows classes
* @property schedulableFlows List of flows startable by the scheduler * @property schedulableFlows List of flows startable by the scheduler
* @property servies List of RPC services * @property services List of RPC services
* @property serializationWhitelists List of Corda plugin registries * @property serializationWhitelists List of Corda plugin registries
* @property customSchemas List of custom schemas * @property customSchemas List of custom schemas
* @property jarPath The path to the JAR for this CorDapp * @property jarPath The path to the JAR for this CorDapp
*/ */
@DoNotImplement
interface Cordapp { interface Cordapp {
val name: String val name: String
val contractClassNames: List<String> val contractClassNames: List<String>

View File

@ -1,11 +1,13 @@
package net.corda.core.cordapp package net.corda.core.cordapp
import net.corda.core.DoNotImplement
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
/** /**
* Provides access to what the node knows about loaded applications. * Provides access to what the node knows about loaded applications.
*/ */
@DoNotImplement
interface CordappProvider { interface CordappProvider {
/** /**
* Exposes the current CorDapp context which will contain information and configuration of the CorDapp that * Exposes the current CorDapp context which will contain information and configuration of the CorDapp that

View File

@ -1,6 +1,6 @@
package net.corda.core.flows package net.corda.core.flows
import net.corda.core.crypto.SecureHash import net.corda.core.DoNotImplement
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
/** /**
@ -8,6 +8,7 @@ import net.corda.core.serialization.CordaSerializable
* Typically this would be used from within the nextScheduledActivity method of a QueryableState to specify * Typically this would be used from within the nextScheduledActivity method of a QueryableState to specify
* the flow to run at the scheduled time. * the flow to run at the scheduled time.
*/ */
@DoNotImplement
interface FlowLogicRefFactory { interface FlowLogicRefFactory {
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
} }
@ -24,4 +25,5 @@ class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentEx
*/ */
// TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes) // TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes)
@CordaSerializable @CordaSerializable
@DoNotImplement
interface FlowLogicRef interface FlowLogicRef

View File

@ -1,6 +1,7 @@
package net.corda.core.flows package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.DoNotImplement
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@ -41,6 +42,7 @@ import net.corda.core.utilities.UntrustworthyData
* will become * will become
* otherSideSession.send(something) * otherSideSession.send(something)
*/ */
@DoNotImplement
abstract class FlowSession { abstract class FlowSession {
/** /**
* The [Party] on the other side of this session. In the case of a session created by [FlowLogic.initiateFlow] * The [Party] on the other side of this session. In the case of a session created by [FlowLogic.initiateFlow]

View File

@ -1,5 +1,6 @@
package net.corda.core.identity package net.corda.core.identity
import net.corda.core.DoNotImplement
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -10,6 +11,7 @@ import java.security.PublicKey
* the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case. * the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case.
*/ */
@CordaSerializable @CordaSerializable
@DoNotImplement
abstract class AbstractParty(val owningKey: PublicKey) { abstract class AbstractParty(val owningKey: PublicKey) {
/** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */ /** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */
override fun equals(other: Any?): Boolean = other === this || other is AbstractParty && other.owningKey == owningKey override fun equals(other: Any?): Boolean = other === this || other is AbstractParty && other.owningKey == owningKey

View File

@ -24,7 +24,6 @@ import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.*
@CordaSerializable @CordaSerializable
data class StateMachineInfo( data class StateMachineInfo(

View File

@ -1,5 +1,6 @@
package net.corda.core.messaging package net.corda.core.messaging
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -11,6 +12,7 @@ import rx.Observable
* @property id The started state machine's ID. * @property id The started state machine's ID.
* @property returnValue A [CordaFuture] of the flow's return value. * @property returnValue A [CordaFuture] of the flow's return value.
*/ */
@DoNotImplement
interface FlowHandle<A> : AutoCloseable { interface FlowHandle<A> : AutoCloseable {
val id: StateMachineRunId val id: StateMachineRunId
val returnValue: CordaFuture<A> val returnValue: CordaFuture<A>

View File

@ -1,9 +1,12 @@
package net.corda.core.messaging package net.corda.core.messaging
import net.corda.core.DoNotImplement
/** /**
* Base interface that all RPC servers must implement. Note: in Corda there's only one RPC interface. This base * Base interface that all RPC servers must implement. Note: in Corda there's only one RPC interface. This base
* interface is here in case we split the RPC system out into a separate library one day. * interface is here in case we split the RPC system out into a separate library one day.
*/ */
@DoNotImplement
interface RPCOps { interface RPCOps {
/** Returns the RPC protocol version. Exists since version 0 so guaranteed to be present. */ /** Returns the RPC protocol version. Exists since version 0 so guaranteed to be present. */
val protocolVersion: Int val protocolVersion: Int

View File

@ -1,5 +1,6 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.DoNotImplement
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
@ -19,6 +20,7 @@ import java.time.Clock
/** /**
* Part of [ServiceHub]. * Part of [ServiceHub].
*/ */
@DoNotImplement
interface StateLoader { interface StateLoader {
/** /**
* Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState]. * Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
@ -164,7 +166,7 @@ interface ServiceHub : ServicesForResolution {
@Throws(TransactionResolutionException::class) @Throws(TransactionResolutionException::class)
fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> { fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> {
val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash) val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
return stx.resolveBaseTransaction(this).outRef<T>(stateRef.index) return stx.resolveBaseTransaction(this).outRef(stateRef.index)
} }
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.DoNotImplement
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import java.io.IOException import java.io.IOException
@ -11,6 +12,7 @@ typealias AttachmentId = SecureHash
/** /**
* An attachment store records potentially large binary objects, identified by their hash. * An attachment store records potentially large binary objects, identified by their hash.
*/ */
@DoNotImplement
interface AttachmentStorage { interface AttachmentStorage {
/** /**
* Returns a handle to a locally stored attachment, or null if it's not known. The handle can be used to open * Returns a handle to a locally stored attachment, or null if it's not known. The handle can be used to open

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.DoNotImplement
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UpgradedContract import net.corda.core.contracts.UpgradedContract
import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.ContractUpgradeFlow
@ -9,6 +10,7 @@ import net.corda.core.flows.ContractUpgradeFlow
* a specified and mutually agreed (amongst participants) contract version. * a specified and mutually agreed (amongst participants) contract version.
* See also [ContractUpgradeFlow] to understand the workflow associated with contract upgrades. * See also [ContractUpgradeFlow] to understand the workflow associated with contract upgrades.
*/ */
@DoNotImplement
interface ContractUpgradeService { interface ContractUpgradeService {
/** Get contracts we would be willing to upgrade the suggested contract to. */ /** Get contracts we would be willing to upgrade the suggested contract to. */

View File

@ -1,6 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.CordaException import net.corda.core.CordaException
import net.corda.core.DoNotImplement
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.identity.* import net.corda.core.identity.*
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
@ -16,6 +17,7 @@ import java.security.cert.*
* whereas confidential identities are distributed only on a need to know basis (typically between parties in * whereas confidential identities are distributed only on a need to know basis (typically between parties in
* a transaction being built). See [NetworkMapCache] for retrieving well known identities from the network map. * a transaction being built). See [NetworkMapCache] for retrieving well known identities from the network map.
*/ */
@DoNotImplement
interface IdentityService { interface IdentityService {
val trustRoot: X509Certificate val trustRoot: X509Certificate
val trustAnchor: TrustAnchor val trustAnchor: TrustAnchor

View File

@ -1,6 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.DoNotImplement
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignableData
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
@ -11,6 +12,7 @@ import java.security.PublicKey
* The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example, * The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example,
* call out to a hardware security module that enforces various auditing and frequency-of-use requirements. * call out to a hardware security module that enforces various auditing and frequency-of-use requirements.
*/ */
@DoNotImplement
interface KeyManagementService { interface KeyManagementService {
/** /**
* Returns a snapshot of the current signing [PublicKey]s. * Returns a snapshot of the current signing [PublicKey]s.

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -43,6 +44,7 @@ interface NetworkMapCache : NetworkMapCacheBase {
} }
/** Subset of [NetworkMapCache] that doesn't depend on an [IdentityService]. */ /** Subset of [NetworkMapCache] that doesn't depend on an [IdentityService]. */
@DoNotImplement
interface NetworkMapCacheBase { interface NetworkMapCacheBase {
// DOCSTART 1 // DOCSTART 1
/** /**

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.DoNotImplement
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -8,6 +9,7 @@ import rx.Observable
/** /**
* Thread-safe storage of transactions. * Thread-safe storage of transactions.
*/ */
@DoNotImplement
interface TransactionStorage { interface TransactionStorage {
/** /**
* Return the transaction with the given [id], or null if no such transaction exists. * Return the transaction with the given [id], or null if no such transaction exists.

View File

@ -1,5 +1,6 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
@ -7,6 +8,7 @@ import net.corda.core.transactions.LedgerTransaction
* Provides verification service. The implementation may be a simple in-memory verify() call or perhaps an IPC/RPC. * Provides verification service. The implementation may be a simple in-memory verify() call or perhaps an IPC/RPC.
* @suppress * @suppress
*/ */
@DoNotImplement
interface TransactionVerifierService { interface TransactionVerifierService {
/** /**
* @param transaction The transaction to be verified. * @param transaction The transaction to be verified.

View File

@ -1,6 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -14,7 +15,6 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.NonEmptySet
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -151,6 +151,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
* *
* Note that transactions we've seen are held by the storage service, not the vault. * Note that transactions we've seen are held by the storage service, not the vault.
*/ */
@DoNotImplement
interface VaultService { interface VaultService {
/** /**
* Prefer the use of [updates] unless you know why you want to use this instead. * Prefer the use of [updates] unless you know why you want to use this instead.

View File

@ -2,6 +2,7 @@
package net.corda.core.node.services.vault package net.corda.core.node.services.vault
import net.corda.core.DoNotImplement
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
@ -144,6 +145,7 @@ sealed class QueryCriteria {
infix fun or(criteria: QueryCriteria): QueryCriteria = OrComposition(this, criteria) infix fun or(criteria: QueryCriteria): QueryCriteria = OrComposition(this, criteria)
} }
@DoNotImplement
interface IQueryCriteriaParser { interface IQueryCriteriaParser {
fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate> fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate>
fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate> fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate>

View File

@ -2,6 +2,7 @@
package net.corda.core.node.services.vault package net.corda.core.node.services.vault
import net.corda.core.DoNotImplement
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -10,6 +11,7 @@ import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.javaGetter import kotlin.reflect.jvm.javaGetter
@CordaSerializable @CordaSerializable
@DoNotImplement
interface Operator interface Operator
enum class BinaryLogicalOperator : Operator { enum class BinaryLogicalOperator : Operator {
@ -138,6 +140,7 @@ data class Sort(val columns: Collection<SortColumn>) {
} }
@CordaSerializable @CordaSerializable
@DoNotImplement
interface Attribute interface Attribute
enum class CommonStateAttribute(val attributeParent: String, val attributeChild: String?) : Attribute { enum class CommonStateAttribute(val attributeParent: String, val attributeChild: String?) : Attribute {

View File

@ -1,5 +1,6 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.DoNotImplement
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.castIfPossible import net.corda.core.internal.castIfPossible
@ -10,6 +11,7 @@ import java.util.function.Predicate
/** /**
* An abstract class defining fields shared by all transaction types in the system. * An abstract class defining fields shared by all transaction types in the system.
*/ */
@DoNotImplement
abstract class BaseTransaction : NamedByHash { abstract class BaseTransaction : NamedByHash {
/** The inputs of this transaction. Note that in BaseTransaction subclasses the type of this list may change! */ /** The inputs of this transaction. Note that in BaseTransaction subclasses the type of this list may change! */
abstract val inputs: List<*> abstract val inputs: List<*>

View File

@ -1,5 +1,6 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.DoNotImplement
import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.NamedByHash
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
@ -10,6 +11,7 @@ import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
/** An interface for transactions containing signatures, with logic for signature verification */ /** An interface for transactions containing signatures, with logic for signature verification */
@DoNotImplement
interface TransactionWithSignatures : NamedByHash { interface TransactionWithSignatures : NamedByHash {
val sigs: List<TransactionSignature> val sigs: List<TransactionSignature>

View File

@ -91,3 +91,14 @@ The following modules are available but we do not commit to their stability or c
Future releases will reject any CorDapps that use types from these packages. Future releases will reject any CorDapps that use types from these packages.
.. warning:: The web server module will be removed in future. You should call Corda nodes through RPC from your web server of choice e.g., Spring Boot, Vertx, Undertow. .. warning:: The web server module will be removed in future. You should call Corda nodes through RPC from your web server of choice e.g., Spring Boot, Vertx, Undertow.
The ``@DoNotImplement`` annotation
----------------------------------
Certain interfaces and abstract classes within the Corda API have been annotated
as ``@DoNotImplement``. While we undertake not to remove or modify any of these classes' existing
functionality, the annotation is a warning that we may need to extend them in future versions of Corda.
Cordapp developers should therefore just use these classes "as is", and *not* attempt to extend or implement any of them themselves.
This annotation is inherited by subclasses and subinterfaces.

View File

@ -16,6 +16,7 @@ import org.gradle.api.tasks.TaskAction;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@ -25,7 +26,7 @@ import java.net.URLClassLoader;
import java.util.*; import java.util.*;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static java.util.Collections.unmodifiableSet; import static java.util.Collections.*;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -228,9 +229,19 @@ public class ScanApi extends DefaultTask {
private void writeClass(PrintWriter writer, ClassInfo classInfo, int modifiers) { private void writeClass(PrintWriter writer, ClassInfo classInfo, int modifiers) {
if (classInfo.isAnnotation()) { if (classInfo.isAnnotation()) {
/*
* Annotation declaration.
*/
writer.append(Modifier.toString(modifiers & INTERFACE_MASK)); writer.append(Modifier.toString(modifiers & INTERFACE_MASK));
writer.append(" @interface ").print(classInfo); writer.append(" @interface ").print(classInfo);
} else if (classInfo.isStandardClass()) { } else if (classInfo.isStandardClass()) {
/*
* Class declaration.
*/
List<String> annotationNames = toNames(readClassAnnotationsFor(classInfo));
if (!annotationNames.isEmpty()) {
writer.append(asAnnotations(annotationNames));
}
writer.append(Modifier.toString(modifiers & CLASS_MASK)); writer.append(Modifier.toString(modifiers & CLASS_MASK));
writer.append(" class ").print(classInfo); writer.append(" class ").print(classInfo);
Set<ClassInfo> superclasses = classInfo.getDirectSuperclasses(); Set<ClassInfo> superclasses = classInfo.getDirectSuperclasses();
@ -242,6 +253,13 @@ public class ScanApi extends DefaultTask {
writer.append(" implements ").print(stringOf(interfaces)); writer.append(" implements ").print(stringOf(interfaces));
} }
} else { } else {
/*
* Interface declaration.
*/
List<String> annotationNames = toNames(readInterfaceAnnotationsFor(classInfo));
if (!annotationNames.isEmpty()) {
writer.append(asAnnotations(annotationNames));
}
writer.append(Modifier.toString(modifiers & INTERFACE_MASK)); writer.append(Modifier.toString(modifiers & INTERFACE_MASK));
writer.append(" interface ").print(classInfo); writer.append(" interface ").print(classInfo);
Set<ClassInfo> superinterfaces = classInfo.getDirectSuperinterfaces(); Set<ClassInfo> superinterfaces = classInfo.getDirectSuperinterfaces();
@ -253,7 +271,7 @@ public class ScanApi extends DefaultTask {
} }
private void writeMethods(PrintWriter writer, List<MethodInfo> methods) { private void writeMethods(PrintWriter writer, List<MethodInfo> methods) {
Collections.sort(methods); sort(methods);
for (MethodInfo method : methods) { for (MethodInfo method : methods) {
if (isVisible(method.getAccessFlags()) // Only public and protected methods if (isVisible(method.getAccessFlags()) // Only public and protected methods
&& isValid(method.getAccessFlags(), METHOD_MASK) // Excludes bridge and synthetic methods && isValid(method.getAccessFlags(), METHOD_MASK) // Excludes bridge and synthetic methods
@ -264,7 +282,7 @@ public class ScanApi extends DefaultTask {
} }
private void writeFields(PrintWriter output, List<FieldInfo> fields) { private void writeFields(PrintWriter output, List<FieldInfo> fields) {
Collections.sort(fields); sort(fields);
for (FieldInfo field : fields) { for (FieldInfo field : fields) {
if (isVisible(field.getAccessFlags()) && isValid(field.getAccessFlags(), FIELD_MASK)) { if (isVisible(field.getAccessFlags()) && isValid(field.getAccessFlags(), FIELD_MASK)) {
output.append(" ").println(field); output.append(" ").println(field);
@ -286,6 +304,36 @@ public class ScanApi extends DefaultTask {
return 0; return 0;
} }
private List<String> toNames(Collection<ClassInfo> classes) {
return classes.stream()
.map(ClassInfo::toString)
.filter(ScanApi::isApplicationClass)
.collect(toList());
}
private Set<ClassInfo> readClassAnnotationsFor(ClassInfo classInfo) {
Set<ClassInfo> annotations = new HashSet<>(classInfo.getAnnotations());
annotations.addAll(selectInheritedAnnotations(classInfo.getSuperclasses()));
annotations.addAll(selectInheritedAnnotations(classInfo.getImplementedInterfaces()));
return annotations;
}
private Set<ClassInfo> readInterfaceAnnotationsFor(ClassInfo classInfo) {
Set<ClassInfo> annotations = new HashSet<>(classInfo.getAnnotations());
annotations.addAll(selectInheritedAnnotations(classInfo.getSuperinterfaces()));
return annotations;
}
/**
* Returns those annotations which have themselves been annotated as "Inherited".
*/
private List<ClassInfo> selectInheritedAnnotations(Collection<ClassInfo> classes) {
return classes.stream()
.flatMap(cls -> cls.getAnnotations().stream())
.filter(ann -> ann.hasMetaAnnotation(Inherited.class.getName()))
.collect(toList());
}
private MethodInfo filterAnnotationsFor(MethodInfo method) { private MethodInfo filterAnnotationsFor(MethodInfo method) {
return new MethodInfo( return new MethodInfo(
method.getClassName(), method.getClassName(),
@ -319,6 +367,14 @@ public class ScanApi extends DefaultTask {
return items.stream().map(ClassInfo::toString).collect(joining(", ")); return items.stream().map(ClassInfo::toString).collect(joining(", "));
} }
private static String asAnnotations(Collection<String> items) {
return items.stream().collect(joining(" @", "@", " "));
}
private static boolean isApplicationClass(String typeName) {
return !typeName.startsWith("java.") && !typeName.startsWith("kotlin.");
}
private static URL toURL(File file) throws MalformedURLException { private static URL toURL(File file) throws MalformedURLException {
return file.toURI().toURL(); return file.toURI().toURL();
} }

View File

@ -2,7 +2,6 @@ package net.corda.node.services.statemachine
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import com.google.common.primitives.Primitives import com.google.common.primitives.Primitives
import net.corda.core.cordapp.CordappContext
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -34,7 +33,7 @@ data class FlowLogicRefImpl internal constructor(val flowLogicClassName: String,
*/ */
object FlowLogicRefFactoryImpl : SingletonSerializeAsToken(), FlowLogicRefFactory { object FlowLogicRefFactoryImpl : SingletonSerializeAsToken(), FlowLogicRefFactory {
// TODO: Replace with a per app classloader/cordapp provider/cordapp loader - this will do for now // TODO: Replace with a per app classloader/cordapp provider/cordapp loader - this will do for now
var classloader = javaClass.classLoader var classloader: ClassLoader = javaClass.classLoader
override fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef { override fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) { if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {