Merge remote-tracking branch 'open/master' into os-merge-4f8a564

# Conflicts:
#	experimental/behave/src/main/kotlin/net/corda/behave/process/Command.kt
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
This commit is contained in:
Shams Asari 2018-09-07 14:49:52 +01:00
commit 46cbfefc77
14 changed files with 410 additions and 136 deletions

View File

@ -5,6 +5,10 @@ package net.corda.core.node.services.vault
import net.corda.core.DoNotImplement
import net.corda.core.internal.declaredField
import net.corda.core.internal.uncheckedCast
import net.corda.core.node.services.vault.CollectionOperator.*
import net.corda.core.node.services.vault.ColumnPredicate.*
import net.corda.core.node.services.vault.EqualityComparisonOperator.*
import net.corda.core.node.services.vault.LikenessOperator.*
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import java.lang.reflect.Field
@ -24,7 +28,9 @@ enum class BinaryLogicalOperator : Operator {
enum class EqualityComparisonOperator : Operator {
EQUAL,
NOT_EQUAL
NOT_EQUAL,
EQUAL_IGNORE_CASE,
NOT_EQUAL_IGNORE_CASE
}
enum class BinaryComparisonOperator : Operator {
@ -41,12 +47,16 @@ enum class NullOperator : Operator {
enum class LikenessOperator : Operator {
LIKE,
NOT_LIKE
NOT_LIKE,
LIKE_IGNORE_CASE,
NOT_LIKE_IGNORE_CASE
}
enum class CollectionOperator : Operator {
IN,
NOT_IN
NOT_IN,
IN_IGNORE_CASE,
NOT_IN_IGNORE_CASE
}
@CordaSerializable
@ -251,27 +261,45 @@ object Builder {
fun <R : Comparable<R>> Field.comparePredicate(operator: BinaryComparisonOperator, value: R) = info().comparePredicate(operator, value)
fun <R : Comparable<R>> FieldInfo.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
fun <O, R> KProperty1<O, R?>.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
fun <O, R> KProperty1<O, R?>.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
@JvmOverloads
fun <O, R> KProperty1<O, R?>.equal(value: R, exactMatch: Boolean = true) = predicate(Builder.equal(value, exactMatch))
@JvmOverloads
fun <O, R> KProperty1<O, R?>.notEqual(value: R, exactMatch: Boolean = true) = predicate(Builder.notEqual(value, exactMatch))
fun <O, R : Comparable<R>> KProperty1<O, R?>.lessThan(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN, value)
fun <O, R : Comparable<R>> KProperty1<O, R?>.lessThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
fun <O, R : Comparable<R>> KProperty1<O, R?>.greaterThan(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN, value)
fun <O, R : Comparable<R>> KProperty1<O, R?>.greaterThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
fun <O, R : Comparable<R>> KProperty1<O, R?>.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
fun <O, R : Comparable<R>> KProperty1<O, R?>.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
fun <O, R : Comparable<R>> KProperty1<O, R?>.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
@JvmOverloads
fun <O, R : Comparable<R>> KProperty1<O, R?>.`in`(collection: Collection<R>, exactMatch: Boolean = true) = predicate(Builder.`in`(collection, exactMatch))
@JvmOverloads
fun <O, R : Comparable<R>> KProperty1<O, R?>.notIn(collection: Collection<R>, exactMatch: Boolean = true) = predicate(Builder.notIn(collection, exactMatch))
@JvmStatic
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.equal(value: R) = info().equal(value)
@JvmStatic
fun <R> FieldInfo.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
fun <R> Field.equal(value: R, exactMatch: Boolean = true) = info().equal(value, exactMatch)
@JvmStatic
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.notEqual(value: R) = info().notEqual(value)
@JvmOverloads
fun <R> FieldInfo.equal(value: R, exactMatch: Boolean = true) = predicate(Builder.equal(value, exactMatch))
@JvmStatic
fun <R> FieldInfo.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.notEqual(value: R, exactMatch: Boolean = true) = info().notEqual(value, exactMatch)
@JvmStatic
@JvmOverloads
fun <R> FieldInfo.notEqual(value: R, exactMatch: Boolean = true) = predicate(Builder.equal(value, exactMatch))
@JvmStatic
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
@ -304,44 +332,77 @@ object Builder {
fun <R : Comparable<R>> FieldInfo.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
@JvmStatic
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.`in`(collection: Collection<R>) = info().`in`(collection)
fun <R : Comparable<R>> FieldInfo.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
fun <R : Comparable<R>> Field.`in`(collection: Collection<R>, exactMatch: Boolean = true) = info().`in`(collection, exactMatch)
@JvmStatic
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.notIn(collection: Collection<R>) = info().notIn(collection)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
@JvmOverloads
fun <R : Comparable<R>> FieldInfo.`in`(collection: Collection<R>, exactMatch: Boolean = true) = predicate(Builder.`in`(collection, exactMatch))
@JvmStatic
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.notIn(collection: Collection<R>, exactMatch: Boolean = true) = info().notIn(collection, exactMatch)
@JvmStatic
@JvmOverloads
fun <R : Comparable<R>> FieldInfo.notIn(collection: Collection<R>, exactMatch: Boolean = true) = predicate(Builder.notIn(collection, exactMatch))
@JvmOverloads
fun <R> equal(value: R, exactMatch: Boolean = true) = EqualityComparison(if (exactMatch) EQUAL else EQUAL_IGNORE_CASE, value)
@JvmOverloads
fun <R> notEqual(value: R, exactMatch: Boolean = true) = EqualityComparison(if (exactMatch) NOT_EQUAL else NOT_EQUAL_IGNORE_CASE, value)
fun <R> equal(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value)
fun <R> notEqual(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value)
fun <R : Comparable<R>> lessThan(value: R) = compare(BinaryComparisonOperator.LESS_THAN, value)
fun <R : Comparable<R>> lessThanOrEqual(value: R) = compare(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
fun <R : Comparable<R>> greaterThan(value: R) = compare(BinaryComparisonOperator.GREATER_THAN, value)
fun <R : Comparable<R>> greaterThanOrEqual(value: R) = compare(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
fun <R : Comparable<R>> between(from: R, to: R) = ColumnPredicate.Between(from, to)
fun <R : Comparable<R>> `in`(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection)
fun <R : Comparable<R>> notIn(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection)
fun like(string: String) = ColumnPredicate.Likeness(LikenessOperator.LIKE, string)
fun notLike(string: String) = ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string)
@JvmOverloads
fun <R : Comparable<R>> `in`(collection: Collection<R>, exactMatch: Boolean = true) = CollectionExpression(if (exactMatch) IN else IN_IGNORE_CASE, collection)
@JvmOverloads
fun <R : Comparable<R>> notIn(collection: Collection<R>, exactMatch: Boolean = true) = CollectionExpression(if (exactMatch) NOT_IN else NOT_IN_IGNORE_CASE, collection)
@JvmOverloads
fun like(string: String, exactMatch: Boolean = true) = Likeness(if (exactMatch) LIKE else LIKE_IGNORE_CASE, string)
@JvmOverloads
fun notLike(string: String, exactMatch: Boolean = true) = Likeness(if (exactMatch) NOT_LIKE else NOT_LIKE_IGNORE_CASE, string)
fun <R> isNull() = ColumnPredicate.NullExpression<R>(NullOperator.IS_NULL)
fun <R> isNotNull() = ColumnPredicate.NullExpression<R>(NullOperator.NOT_NULL)
@JvmOverloads
fun <O> KProperty1<O, String?>.like(string: String, exactMatch: Boolean = true) = predicate(Builder.like(string, exactMatch))
fun <O> KProperty1<O, String?>.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
@JvmStatic
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.like(string: String) = info().like(string)
@JvmStatic
fun FieldInfo.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
fun Field.like(string: String, exactMatch: Boolean = true) = info().like(string, exactMatch)
fun <O> KProperty1<O, String?>.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
@JvmStatic
@JvmOverloads
fun FieldInfo.like(string: String, exactMatch: Boolean = true) = predicate(Builder.like(string, exactMatch))
@JvmOverloads
fun <O> KProperty1<O, String?>.notLike(string: String, exactMatch: Boolean = true) = predicate(Builder.notLike(string, exactMatch))
@JvmStatic
@JvmOverloads
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.notLike(string: String) = info().notLike(string)
fun Field.notLike(string: String, exactMatch: Boolean = true) = info().notLike(string, exactMatch)
@JvmStatic
fun FieldInfo.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
@JvmOverloads
fun FieldInfo.notLike(string: String, exactMatch: Boolean = true) = predicate(Builder.notLike(string, exactMatch))
fun <O, R> KProperty1<O, R?>.isNull() = predicate(ColumnPredicate.NullExpression(NullOperator.IS_NULL))
@JvmStatic

View File

@ -166,6 +166,44 @@ An example of a custom query in Java is illustrated here:
where an anonymous party does not resolve to an X500 name via the ``IdentityService``, no query results will ever be
produced. For performance reasons, queries do not use ``PublicKey`` as search criteria.
Custom queries can be either case sensitive or case insensitive. They are defined via a ``Boolean`` as one of the function parameters of each operator function. By default each operator is case sensitive.
An example of a case sensitive custom query operator is illustrated here:
.. container:: codeset
.. sourcecode:: kotlin
val currencyIndex = PersistentCashState::currency.equal(USD.currencyCode, true)
.. note:: The ``Boolean`` input of ``true`` in this example could be removed since the function will default to ``true`` when not provided.
An example of a case insensitive custom query operator is illustrated here:
.. container:: codeset
.. sourcecode:: kotlin
val currencyIndex = PersistentCashState::currency.equal(USD.currencyCode, false)
An example of a case sensitive custom query operator in Java is illustrated here:
.. container:: codeset
.. sourcecode:: java
FieldInfo attributeCurrency = getField("currency", CashSchemaV1.PersistentCashState.class);
CriteriaExpression currencyIndex = Builder.equal(attributeCurrency, "USD", true);
An example of a case insensitive custom query operator in Java is illustrated here:
.. container:: codeset
.. sourcecode:: java
FieldInfo attributeCurrency = getField("currency", CashSchemaV1.PersistentCashState.class);
CriteriaExpression currencyIndex = Builder.equal(attributeCurrency, "USD", false);
Pagination
----------
The API provides support for paging where large numbers of results are expected (by default, a page size is set to 200

View File

@ -6,6 +6,9 @@ release, see :doc:`upgrade-notes`.
Unreleased
----------
* Case insensitive vault queries can be specified via a boolean on applicable SQL criteria builder operators. By default queries will be case sensitive.
* Getter added to ``CordaRPCOps`` for the node's network parameters.
* The RPC client library now checks at startup whether the server is of the client libraries major version or higher.

View File

@ -4,7 +4,7 @@ import net.corda.behave.await
import net.corda.behave.file.currentDirectory
import net.corda.behave.process.output.OutputListener
import net.corda.behave.waitFor
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds
import rx.Observable
@ -20,8 +20,10 @@ open class Command(
private val directory: Path = currentDirectory,
private val timeout: Duration = 2.minutes
): Closeable {
protected val log = loggerFor<Command>()
companion object {
private val WAIT_BEFORE_KILL: Duration = 5.seconds
private val log = contextLogger()
}
private val terminationLatch = CountDownLatch(1)
@ -36,17 +38,12 @@ open class Command(
var exitCode = -1
private set
val output: Observable<String> = Observable.create<String>({ emitter ->
val output: Observable<String> = Observable.create<String> { emitter ->
outputListener = object : OutputListener {
override fun onNewLine(line: String) {
emitter.onNext(line)
}
override fun onEndOfStream() {
emitter.onCompleted()
}
override fun onNewLine(line: String) = emitter.onNext(line)
override fun onEndOfStream() = emitter.onCompleted()
}
}).share()
}.share()
private val thread = Thread(Runnable {
try {
@ -132,12 +129,13 @@ open class Command(
}
override fun close() {
if (process?.isAlive == true) {
kill()
}
waitFor()
}
fun run() = use { _ -> }
fun use(action: (Command) -> Unit): Int {
fun run(action: (Command) -> Unit = { }): Int {
try {
start()
action(this)
@ -147,8 +145,8 @@ open class Command(
return exitCode
}
fun use(subscriber: Subscriber<String>, action: (Command, Observable<String>) -> Unit = { _, _ -> }): Int {
use {
fun run(subscriber: Subscriber<String>, action: (Command, Observable<String>) -> Unit = { _, _ -> }): Int {
run {
output.subscribe(subscriber)
start()
action(this, output)
@ -157,11 +155,4 @@ open class Command(
}
override fun toString() = "Command(${command.joinToString(" ")})"
companion object {
private val WAIT_BEFORE_KILL: Duration = 5.seconds
}
}

View File

@ -78,9 +78,7 @@ abstract class ContainerService(
override fun checkPrerequisites() {
if (!client.listImages().any { true == it.repoTags()?.contains(imageReference) }) {
log.info("Pulling image $imageReference ...")
client.pull(imageReference, { _ ->
run { }
})
client.pull(imageReference) { }
log.info("Image $imageReference downloaded")
}
}

View File

@ -20,7 +20,7 @@ class CommandTests {
@Test
fun `output stream for command can be observed`() {
val subscriber = TestSubscriber<String>()
val exitCode = Command(listOf("ls", "/")).use(subscriber) { _, _ ->
val exitCode = Command(listOf("ls", "/")).run(subscriber) { _, _ ->
subscriber.awaitTerminalEvent()
subscriber.assertCompleted()
subscriber.assertNoErrors()

View File

@ -12,6 +12,8 @@ import net.corda.nodeapi.internal.config.SslConfiguration
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
import org.slf4j.LoggerFactory
import java.nio.file.Path
import java.security.KeyPair
@ -25,11 +27,6 @@ import java.security.PublicKey
object DevIdentityGenerator {
private val log = LoggerFactory.getLogger(javaClass)
// TODO These don't need to be prefixes but can be the full aliases
// TODO Move these constants out of here as the node needs access to them
const val NODE_IDENTITY_ALIAS_PREFIX = "identity"
const val DISTRIBUTED_NOTARY_ALIAS_PREFIX = "distributed-notary"
/** Install a node key store for the given node directory using the given legal name. */
fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party {
val certificatesDirectory = nodeDir / "certificates"

View File

@ -48,6 +48,10 @@ object X509Utilities {
const val CORDA_CLIENT_TLS = "cordaclienttls"
const val CORDA_CLIENT_CA = "cordaclientca"
// TODO These don't need to be prefixes, but can be the full aliases.
const val NODE_IDENTITY_ALIAS_PREFIX = "identity"
const val DISTRIBUTED_NOTARY_ALIAS_PREFIX = "distributed-notary"
val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
/**

View File

@ -74,6 +74,8 @@ import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
import net.corda.nodeapi.internal.persistence.*
import net.corda.nodeapi.internal.storeLegalIdentity
import net.corda.tools.shell.InteractiveShell
@ -211,7 +213,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
/**
* 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() = networkMapCache.nodeReady.map { Unit }
@ -374,7 +376,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
}
/** Subclasses must override this to create a [StartedNode] of the desired type, using the provided machinery. */
/** Subclasses must override this to create a "started" node of the desired type, using the provided machinery. */
abstract fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): S
private fun verifyCheckpointsCompatible(tokenizableServices: List<Any>) {
@ -546,7 +548,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
private fun isNotaryService(serviceClass: Class<*>) = NotaryService::class.java.isAssignableFrom(serviceClass)
/**
* This customizes the ServiceHub for each CordaService that is initiating flows
* This customizes the ServiceHub for each CordaService that is initiating flows.
*/
// TODO Move this into its own file
private class AppServiceHubImpl<T : SerializeAsToken>(private val serviceHub: ServiceHub, private val flowStarter: FlowStarter) : AppServiceHub, ServiceHub by serviceHub {
@ -827,7 +829,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// Meanwhile, we let the remote service send us updates until the acknowledgment buffer overflows and it
// unsubscribes us forcibly, rather than blocking the shutdown process.
// Run shutdown hooks in opposite order to starting
// Run shutdown hooks in opposite order to starting.
for (toRun in runOnStop.reversed()) {
toRun()
}
@ -847,11 +849,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
val keyStore = configuration.signingCertificateStore.get()
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
// Node's main identity or if it's a single node notary
Pair(DevIdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
// Node's main identity or if it's a single node notary.
Pair(NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
} else {
// The node is part of a distributed notary whose identity must already be generated beforehand.
Pair(DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
Pair(DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
}
// TODO: Integrate with Key management service?
val privateKeyAlias = "$id-private-key"

View File

@ -7,6 +7,13 @@ import net.corda.core.internal.uncheckedCast
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultQueryException
import net.corda.core.node.services.vault.*
import net.corda.core.node.services.vault.BinaryComparisonOperator.*
import net.corda.core.node.services.vault.CollectionOperator.*
import net.corda.core.node.services.vault.ColumnPredicate.*
import net.corda.core.node.services.vault.EqualityComparisonOperator.*
import net.corda.core.node.services.vault.LikenessOperator.*
import net.corda.core.node.services.vault.NullOperator.IS_NULL
import net.corda.core.node.services.vault.NullOperator.NOT_NULL
import net.corda.core.node.services.vault.QueryCriteria.CommonQueryCriteria
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.PersistentStateRef
@ -54,54 +61,93 @@ abstract class AbstractQueryCriteriaParser<Q : GenericQueryCriteria<Q,P>, in P:
protected fun columnPredicateToPredicate(column: Path<out Any?>, columnPredicate: ColumnPredicate<*>): Predicate {
return when (columnPredicate) {
is ColumnPredicate.EqualityComparison -> {
val literal = columnPredicate.rightLiteral
when (columnPredicate.operator) {
EqualityComparisonOperator.EQUAL -> criteriaBuilder.equal(column, literal)
EqualityComparisonOperator.NOT_EQUAL -> criteriaBuilder.notEqual(column, literal)
}
}
is ColumnPredicate.BinaryComparison -> {
val literal: Comparable<Any?>? = uncheckedCast(columnPredicate.rightLiteral)
@Suppress("UNCHECKED_CAST")
column as Path<Comparable<Any?>?>
when (columnPredicate.operator) {
BinaryComparisonOperator.GREATER_THAN -> criteriaBuilder.greaterThan(column, literal)
BinaryComparisonOperator.GREATER_THAN_OR_EQUAL -> criteriaBuilder.greaterThanOrEqualTo(column, literal)
BinaryComparisonOperator.LESS_THAN -> criteriaBuilder.lessThan(column, literal)
BinaryComparisonOperator.LESS_THAN_OR_EQUAL -> criteriaBuilder.lessThanOrEqualTo(column, literal)
}
}
is ColumnPredicate.Likeness -> {
@Suppress("UNCHECKED_CAST")
column as Path<String?>
when (columnPredicate.operator) {
LikenessOperator.LIKE -> criteriaBuilder.like(column, columnPredicate.rightLiteral)
LikenessOperator.NOT_LIKE -> criteriaBuilder.notLike(column, columnPredicate.rightLiteral)
}
}
is ColumnPredicate.CollectionExpression -> {
when (columnPredicate.operator) {
CollectionOperator.IN -> column.`in`(columnPredicate.rightLiteral)
CollectionOperator.NOT_IN -> criteriaBuilder.not(column.`in`(columnPredicate.rightLiteral))
}
}
is ColumnPredicate.Between -> {
@Suppress("UNCHECKED_CAST")
column as Path<Comparable<Any?>?>
val fromLiteral: Comparable<Any?>? = uncheckedCast(columnPredicate.rightFromLiteral)
val toLiteral: Comparable<Any?>? = uncheckedCast(columnPredicate.rightToLiteral)
criteriaBuilder.between(column, fromLiteral, toLiteral)
}
is ColumnPredicate.NullExpression -> {
when (columnPredicate.operator) {
NullOperator.IS_NULL -> criteriaBuilder.isNull(column)
NullOperator.NOT_NULL -> criteriaBuilder.isNotNull(column)
}
}
is EqualityComparison -> equalityComparisonToPredicate(column, columnPredicate)
is BinaryComparison -> binaryComparisonToPredicate(column, columnPredicate)
is Likeness -> likeComparisonToPredicate(column, columnPredicate)
is CollectionExpression -> collectionComparisonToPredicate(column, columnPredicate)
is Between -> betweenComparisonToPredicate(column, columnPredicate)
is NullExpression -> nullComparisonToPredicate(column, columnPredicate)
else -> throw VaultQueryException("Not expecting $columnPredicate")
}
}
private fun equalityComparisonToPredicate(column: Path<out Any?>, columnPredicate: EqualityComparison<*>): Predicate {
val literal = columnPredicate.rightLiteral
return if (literal is String) {
@Suppress("UNCHECKED_CAST")
column as Path<String?>
when (columnPredicate.operator) {
EQUAL -> criteriaBuilder.equal(column, literal)
EQUAL_IGNORE_CASE -> criteriaBuilder.equal(criteriaBuilder.upper(column), literal.toUpperCase())
NOT_EQUAL -> criteriaBuilder.notEqual(column, literal)
NOT_EQUAL_IGNORE_CASE -> criteriaBuilder.notEqual(criteriaBuilder.upper(column), literal.toUpperCase())
}
} else {
when (columnPredicate.operator) {
EQUAL, EQUAL_IGNORE_CASE -> criteriaBuilder.equal(column, literal)
NOT_EQUAL, NOT_EQUAL_IGNORE_CASE -> criteriaBuilder.notEqual(column, literal)
}
}
}
private fun binaryComparisonToPredicate(column: Path<out Any?>, columnPredicate: BinaryComparison<*>): Predicate {
val literal: Comparable<Any?>? = uncheckedCast(columnPredicate.rightLiteral)
@Suppress("UNCHECKED_CAST")
column as Path<Comparable<Any?>?>
return when (columnPredicate.operator) {
GREATER_THAN -> criteriaBuilder.greaterThan(column, literal)
GREATER_THAN_OR_EQUAL -> criteriaBuilder.greaterThanOrEqualTo(column, literal)
LESS_THAN -> criteriaBuilder.lessThan(column, literal)
LESS_THAN_OR_EQUAL -> criteriaBuilder.lessThanOrEqualTo(column, literal)
}
}
private fun likeComparisonToPredicate(column: Path<out Any?>, columnPredicate: Likeness): Predicate {
@Suppress("UNCHECKED_CAST")
column as Path<String?>
return when (columnPredicate.operator) {
LIKE -> criteriaBuilder.like(column, columnPredicate.rightLiteral)
LIKE_IGNORE_CASE -> criteriaBuilder.like(criteriaBuilder.upper(column), columnPredicate.rightLiteral.toUpperCase())
NOT_LIKE -> criteriaBuilder.notLike(column, columnPredicate.rightLiteral)
NOT_LIKE_IGNORE_CASE -> criteriaBuilder.notLike(criteriaBuilder.upper(column), columnPredicate.rightLiteral.toUpperCase())
}
}
private fun collectionComparisonToPredicate(column: Path<out Any?>, columnPredicate: CollectionExpression<*>): Predicate {
val literal = columnPredicate.rightLiteral
return if (literal.any { it is String }) {
@Suppress("UNCHECKED_CAST")
column as Path<String?>
@Suppress("UNCHECKED_CAST")
literal as Collection<String>
when (columnPredicate.operator) {
IN -> column.`in`(literal)
IN_IGNORE_CASE -> criteriaBuilder.upper(column).`in`(literal.map { it.toUpperCase() })
NOT_IN -> criteriaBuilder.not(column.`in`(literal))
NOT_IN_IGNORE_CASE -> criteriaBuilder.not(criteriaBuilder.upper(column).`in`(literal.map { it.toUpperCase() }))
}
} else {
when (columnPredicate.operator) {
IN, IN_IGNORE_CASE -> column.`in`(literal)
NOT_IN, NOT_IN_IGNORE_CASE -> criteriaBuilder.not(column.`in`(literal))
}
}
}
private fun betweenComparisonToPredicate(column: Path<out Any?>, columnPredicate: Between<*>): Predicate {
@Suppress("UNCHECKED_CAST")
column as Path<Comparable<Any?>?>
val fromLiteral: Comparable<Any?>? = uncheckedCast(columnPredicate.rightFromLiteral)
val toLiteral: Comparable<Any?>? = uncheckedCast(columnPredicate.rightToLiteral)
return criteriaBuilder.between(column, fromLiteral, toLiteral)
}
private fun nullComparisonToPredicate(column: Path<out Any?>, columnPredicate: NullExpression<*>): Predicate {
return when (columnPredicate.operator) {
IS_NULL -> criteriaBuilder.isNull(column)
NOT_NULL -> criteriaBuilder.isNotNull(column)
}
}
}
class HibernateAttachmentQueryCriteriaParser(override val criteriaBuilder: CriteriaBuilder,
@ -474,7 +520,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
// state status
stateTypes = criteria.status
if (criteria.status != Vault.StateStatus.ALL) {
val predicateID = Pair(VaultSchemaV1.VaultStates::stateStatus.name, EqualityComparisonOperator.EQUAL)
val predicateID = Pair(VaultSchemaV1.VaultStates::stateStatus.name, EQUAL)
if (commonPredicates.containsKey(predicateID)) {
val existingStatus = ((commonPredicates[predicateID] as ComparisonPredicate).rightHandOperand as LiteralExpression).literal
if (existingStatus != criteria.status) {
@ -488,7 +534,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
// state relevance.
if (criteria.isRelevant != Vault.RelevancyStatus.ALL) {
val predicateID = Pair(VaultSchemaV1.VaultStates::isRelevant.name, EqualityComparisonOperator.EQUAL)
val predicateID = Pair(VaultSchemaV1.VaultStates::isRelevant.name, EQUAL)
if (commonPredicates.containsKey(predicateID)) {
val existingStatus = ((commonPredicates[predicateID] as ComparisonPredicate).rightHandOperand as LiteralExpression).literal
if (existingStatus != criteria.isRelevant) {
@ -503,7 +549,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
// contract state types
val contractStateTypes = deriveContractStateTypes(criteria.contractStateTypes)
if (contractStateTypes.isNotEmpty()) {
val predicateID = Pair(VaultSchemaV1.VaultStates::contractStateClassName.name, CollectionOperator.IN)
val predicateID = Pair(VaultSchemaV1.VaultStates::contractStateClassName.name, IN)
if (commonPredicates.containsKey(predicateID)) {
val existingTypes = (commonPredicates[predicateID]!!.expressions[0] as InPredicate<*>).values.map { (it as LiteralExpression).literal }.toSet()
if (existingTypes != contractStateTypes) {

View File

@ -800,6 +800,139 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
}
}
@Test
fun `logical operator case insensitive EQUAL`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal("gBp", false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(1)
}
}
@Test
fun `logical operator case insensitive EQUAL does not affect numbers`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.equal(10000, false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(3)
}
}
@Test
fun `logical operator case insensitive NOT_EQUAL does not return results containing the same characters as the case insensitive string`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notEqual("gBp", false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(2)
}
}
@Test
fun `logical operator case insensitive NOT_EQUAL does not affect numbers`() {
database.transaction {
listOf(USD, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
vaultFiller.fillWithSomeTestCash(AMOUNT(50, GBP), notaryServices, 1, DUMMY_CASH_ISSUER)
val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.notEqual(10000, false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(1)
}
}
@Test
fun `logical operator case insensitive IN`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val currencies = listOf("cHf", "gBp")
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.`in`(currencies, false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(2)
}
}
@Test
fun `logical operator case insensitive IN does not affect numbers`() {
database.transaction {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, USD), notaryServices, 1, DUMMY_CASH_ISSUER)
vaultFiller.fillWithSomeTestCash(AMOUNT(200, CHF), notaryServices, 1, DUMMY_CASH_ISSUER)
vaultFiller.fillWithSomeTestCash(AMOUNT(50, GBP), notaryServices, 1, DUMMY_CASH_ISSUER)
val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.`in`(listOf(10000L, 20000L), false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(2)
}
}
@Test
fun `logical operator case insensitive NOT IN does not return results containing the same characters as the case insensitive strings`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val currencies = listOf("cHf", "gBp")
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notIn(currencies, false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(1)
}
}
@Test
fun `logical operator case insensitive NOT_IN does not affect numbers`() {
database.transaction {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, USD), notaryServices, 1, DUMMY_CASH_ISSUER)
vaultFiller.fillWithSomeTestCash(AMOUNT(200, CHF), notaryServices, 1, DUMMY_CASH_ISSUER)
vaultFiller.fillWithSomeTestCash(AMOUNT(50, GBP), notaryServices, 1, DUMMY_CASH_ISSUER)
val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.notIn(listOf(10000L, 20000L), false) }
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(1)
}
}
@Test
fun `logical operator case insensitive LIKE`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.like("%bP", false) } // GPB
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(1)
}
}
@Test
fun `logical operator NOT LIKE does not return results containing the same characters as the case insensitive string`() {
database.transaction {
listOf(USD, GBP, CHF).forEach {
vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER)
}
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notLike("%bP", false) } // GPB
val criteria = VaultCustomQueryCriteria(logicalExpression)
val results = vaultService.queryBy<Cash.State>(criteria)
assertThat(results.states).hasSize(2)
}
}
@Test
fun `aggregate functions without group clause`() {
database.transaction {

View File

@ -16,11 +16,11 @@ import net.corda.core.internal.toX500Name
import net.corda.core.utilities.seconds
import net.corda.node.NodeRegistrationOption
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.createDevIntermediateCaCertPath
@ -168,7 +168,7 @@ class NetworkRegistrationHelperTest {
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key"
val serviceIdentityAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key"
nodeKeystore.run {
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
@ -233,7 +233,7 @@ class NetworkRegistrationHelperTest {
certService,
config.certificatesDirectory / networkRootTrustStoreFileName,
networkRootTrustStorePassword,
"${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key",
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
CertRole.SERVICE_IDENTITY)
else -> throw IllegalArgumentException("Unsupported cert role.")
}

View File

@ -2,9 +2,7 @@ description 'Corda Shell CLI'
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
gradlePluginPortal()
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:$shadow_version"
@ -12,15 +10,16 @@ buildscript {
}
apply plugin: 'application'
// We need to set mainClassName before applying the shadow plugin.
mainClassName = 'net.corda.tools.shell.StandaloneShellKt'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
mainClassName = 'net.corda.tools.shell.StandaloneShellKt'
dependencies {
compile project(':tools:shell')
compile group: 'org.slf4j', name: 'slf4j-simple', version: slf4j_version
compile "org.slf4j:slf4j-simple:$slf4j_version"
testCompile(project(':test-utils')) {
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
@ -36,10 +35,11 @@ shadowJar {
}
task buildShellCli(dependsOn: shadowJar)
assemble.dependsOn buildShellCli
artifacts {
publish shadowJar {
classifier ""
classifier = ""
}
}

View File

@ -61,24 +61,25 @@ dependencies {
compile "com.jcabi:jcabi-manifests:1.1"
// For logging, required for ANSIProgressRenderer.
compile "org.apache.logging.log4j:log4j-core:${log4j_version}"
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
// Unit testing helpers.
testCompile "junit:junit:$junit_version"
testCompile "org.assertj:assertj-core:${assertj_version}"
testCompile "org.assertj:assertj-core:$assertj_version"
testCompile project(':test-utils')
testCompile project(':finance')
// Integration test helpers.
integrationTestCompile "junit:junit:$junit_version"
integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
// Jsh: Testing SSH server.
integrationTestCompile "com.jcraft:jsch:$jsch_version"
integrationTestCompile project(':node-driver')
}
tasks.withType(JavaCompile) {
// Resolves a Gradle warning about not scanning for pre-processors.
options.compilerArgs << '-proc:none'
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath