@@ -10,16 +10,12 @@ import com.r3.corda.lib.tokens.selection.memory.config.InMemorySelectionConfig
10
10
import com.r3.corda.lib.tokens.selection.memory.internal.Holder
11
11
import com.r3.corda.lib.tokens.selection.memory.internal.lookupExternalIdFromKey
12
12
import com.r3.corda.lib.tokens.selection.sortByStateRefAscending
13
- import com.r3.corda.lib.tokens.selection.sortByTimeStampAscending
14
- import io.github.classgraph.ClassGraph
15
- import io.github.classgraph.ScanResult
16
13
import net.corda.core.contracts.Amount
17
14
import net.corda.core.contracts.StateAndRef
18
15
import net.corda.core.internal.uncheckedCast
19
16
import net.corda.core.node.AppServiceHub
20
17
import net.corda.core.node.services.CordaService
21
18
import net.corda.core.node.services.Vault
22
- import net.corda.core.node.services.queryBy
23
19
import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM
24
20
import net.corda.core.node.services.vault.PageSpecification
25
21
import net.corda.core.node.services.vault.QueryCriteria
@@ -28,12 +24,8 @@ import net.corda.core.utilities.contextLogger
28
24
import rx.Observable
29
25
import java.time.Duration
30
26
import java.util.concurrent.*
31
- import java.util.concurrent.atomic.AtomicBoolean
32
- import java.util.concurrent.atomic.AtomicInteger
33
27
import java.util.concurrent.atomic.AtomicReference
34
28
import java.util.concurrent.locks.ReentrantReadWriteLock
35
- import java.util.function.Function
36
- import java.util.function.Supplier
37
29
import kotlin.concurrent.read
38
30
import kotlin.concurrent.write
39
31
@@ -55,41 +47,28 @@ class VaultWatcherService(
55
47
private val indexViewCreationLock: ReentrantReadWriteLock = ReentrantReadWriteLock ()
56
48
private val UPDATER = Executors .newSingleThreadScheduledExecutor()
57
49
58
- enum class IndexingType (val ownerType : Class <out Holder >) {
59
-
60
- EXTERNAL_ID (Holder .MappedIdentity ::class .java),
61
- PUBLIC_KEY (Holder .KeyIdentity ::class .java);
62
-
63
- companion object {
64
- fun fromHolder (holder : Class <out Holder >): IndexingType {
65
- return when (holder) {
66
- Holder .MappedIdentity ::class .java -> {
67
- EXTERNAL_ID
68
- }
69
-
70
- Holder .KeyIdentity ::class .java -> {
71
- PUBLIC_KEY
72
- }
73
- else -> throw IllegalArgumentException (" Unknown Holder type: $holder " )
74
- }
75
- }
76
- }
77
-
78
- }
79
-
80
50
constructor (appServiceHub: AppServiceHub ) : this (
81
51
getObservableFromAppServiceHub(appServiceHub),
82
52
InMemorySelectionConfig .parse(appServiceHub.getAppContext().config)
83
53
)
84
54
55
+ init {
56
+ addTokensToCache(tokenObserver.initialValues)
57
+ tokenObserver.source.doOnError {
58
+ LOG .error(" received error from observable" , it)
59
+ }
60
+ tokenObserver.source.subscribe(::onVaultUpdate)
61
+ tokenObserver.startLoading(::onVaultUpdate)
62
+ }
63
+
85
64
companion object {
86
65
val LOG = contextLogger()
87
66
88
67
private fun getObservableFromAppServiceHub (appServiceHub : AppServiceHub ): TokenObserver {
89
- val config = appServiceHub.cordappProvider.getAppContext().config
90
- val configOptions : InMemorySelectionConfig = InMemorySelectionConfig .parse(config )
68
+ val rawConfig = appServiceHub.cordappProvider.getAppContext().config
69
+ val parsedConfig : InMemorySelectionConfig = InMemorySelectionConfig .parse(rawConfig )
91
70
92
- if (! configOptions .enabled) {
71
+ if (! parsedConfig .enabled) {
93
72
LOG .info(" Disabling inMemory token selection - refer to documentation on how to enable" )
94
73
return TokenObserver (emptyList(), Observable .empty(), { _, _ ->
95
74
Holder .UnmappedIdentity ()
@@ -105,76 +84,19 @@ class VaultWatcherService(
105
84
}
106
85
}
107
86
}
108
-
109
87
val (_, vaultObservable) = appServiceHub.vaultService.trackBy(
110
88
contractStateType = FungibleToken ::class .java,
111
89
paging = PageSpecification (pageNumber = DEFAULT_PAGE_NUM , pageSize = 1 ),
112
90
criteria = QueryCriteria .VaultQueryCriteria (status = Vault .StateStatus .ALL ),
113
91
sorting = sortByStateRefAscending()
114
92
)
115
93
116
- val pageSize = configOptions.pageSize
117
- val asyncLoader = object : ((Vault .Update <FungibleToken >) -> Unit ) -> Unit {
118
- override fun invoke (callback : (Vault .Update <FungibleToken >) -> Unit ) {
119
- LOG .info(" Starting async token loading from vault" )
120
-
121
- val classGraph = ClassGraph ()
122
- classGraph.enableClassInfo()
123
-
124
- val scanResultFuture = CompletableFuture .supplyAsync {
125
- classGraph.scan()
126
- }
127
-
128
- scanResultFuture.thenApplyAsync { scanResult ->
129
- val subclasses: Set <Class <out FungibleToken >> = scanResult.getSubclasses(FungibleToken ::class .java.canonicalName)
130
- .map { it.name }
131
- .map { Class .forName(it) as Class <out FungibleToken > }.toSet()
132
-
133
- val enrichedClasses = (subclasses - setOf (FungibleToken ::class .java))
134
- LOG .info(" Enriching token query with types: $enrichedClasses " )
135
-
136
- val shouldLoop = AtomicBoolean (true )
137
- val pageNumber = AtomicInteger (DEFAULT_PAGE_NUM - 1 )
138
- val loadingFutures: List <CompletableFuture <Void >> = 0 .until(configOptions.loadingThreads).map {
139
- CompletableFuture .runAsync {
140
- try {
141
- while (shouldLoop.get()) {
142
- val newlyLoadedStates = appServiceHub.vaultService.queryBy<FungibleToken >(
143
- paging = PageSpecification (pageNumber = pageNumber.addAndGet(1 ), pageSize = pageSize),
144
- criteria = QueryCriteria .VaultQueryCriteria (contractStateTypes = subclasses),
145
- sorting = sortByTimeStampAscending()
146
- ).states.toSet()
147
- callback(Vault .Update (emptySet(), newlyLoadedStates))
148
- LOG .info(" publishing ${newlyLoadedStates.size} to async state loading callback" )
149
- shouldLoop.compareAndSet(newlyLoadedStates.isNotEmpty(), true )
150
- LOG .debug(" shouldLoop=${shouldLoop} " )
151
- if (configOptions.sleep > 0 ) {
152
- Thread .sleep(configOptions.sleep.toLong() * 1000 )
153
- }
154
-
155
- }
156
- LOG .info(" finished token loading" )
157
- } catch (t: Throwable ) {
158
- LOG .error(" Token Loading Failed due to: " , t)
159
- }
160
- }
161
- }
162
- }
163
- }
164
- }
165
-
94
+ val asyncLoader = ServiceHubAsyncLoader (appServiceHub, parsedConfig)
166
95
return TokenObserver (emptyList(), uncheckedCast(vaultObservable), ownerProvider, asyncLoader)
167
96
}
168
97
}
169
98
170
- init {
171
- addTokensToCache(tokenObserver.initialValues)
172
- tokenObserver.source.doOnError {
173
- LOG .error(" received error from observable" , it)
174
- }
175
- tokenObserver.startLoading(::onVaultUpdate)
176
- tokenObserver.source.subscribe(::onVaultUpdate)
177
- }
99
+
178
100
179
101
private fun processToken (token : StateAndRef <FungibleToken >, indexingType : IndexingType ): TokenIndex {
180
102
val owner = tokenObserver.ownerProvider(token, indexingType)
@@ -375,10 +297,10 @@ class VaultWatcherService(
375
297
}
376
298
377
299
class TokenObserver (
378
- val initialValues : List <StateAndRef <FungibleToken >>,
379
- val source : Observable <Vault .Update <FungibleToken >>,
380
- val ownerProvider : ((StateAndRef <FungibleToken >, VaultWatcherService . IndexingType ) -> Holder ),
381
- inline val asyncLoader : ((Vault .Update <FungibleToken >) -> Unit ) -> Unit = { _ -> }
300
+ val initialValues : List <StateAndRef <FungibleToken >>,
301
+ val source : Observable <Vault .Update <FungibleToken >>,
302
+ val ownerProvider : ((StateAndRef <FungibleToken >, IndexingType ) -> Holder ),
303
+ inline val asyncLoader : ((Vault .Update <FungibleToken >) -> Unit ) -> Unit = { _ -> }
382
304
) {
383
305
384
306
fun startLoading (loadingCallBack : (Vault .Update <FungibleToken >) -> Unit ) {
0 commit comments