Статьи

Corda Services 101

Я нахожусь в настроении, чтобы написать короткую и основную статью сегодня. Мне действительно любопытно, как быстро я могу опубликовать это. Итак, начнем.

Этот пост о Corda Services (используется Corda версии 3.2 ). Кто они такие? Как разработчик, который часто использует Spring, я бы сказал, что они похожи на Beans. Spring Beans может сделать больше, но на базовом уровне они очень похожи. В любом случае, давайте прекратим говорить о весне и сосредоточимся на Корде.

услуги корда

Минимум, который вам нужно знать

Сервисы Corda — это классы, внешние по отношению к потокам, которые в настоящее время могут вызываться только из исполняющего потока или из другой службы (которая, в свою очередь, вызывается потоком). Подобно subFlow , они позволяют повторно использовать код, но должны использоваться по разным причинам. Например, набор функций запроса хранилища или инициирование trackBy внутри узла. Это то, для чего я склонен пользоваться услугами в любом случае.

Сервисы Corda определяются с помощью аннотации @CordaService вместе с расширением SingletonSerializeAsToken . Как только это будет сделано, когда ваш Cordapp загружен и узел запускается, служба, которую вы только что определили, будет инициализирована:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@CordaService
class MessageRepository(private val serviceHub: AppServiceHub) : SingletonSerializeAsToken() {
 
    private companion object {
        val log = loggerFor()
    }
 
    init {
        log.info("I am alive!")
    }
 
    fun findAll(pageSpec: PageSpecification): Vault.Page =
        serviceHub.vaultService.queryBy(QueryCriteria.LinearStateQueryCriteria(), pageSpec)
}

serviceHub предоставляет доступ ко всему, что вам нужно. В этом примере служба обращается к vaultService для получения состояний из хранилища узла.

Теперь он готов к использованию из ваших потоков или других служб, если это необходимо. Фрагмент ниже взят из одного из моих потоков:

1
private fun repository() = serviceHub.cordaService(MessageRepository::class.java)

serviceHub доступен во всех потоках и обеспечивает функцию cordaService . Для ввода требуется класс службы, которую вы пытаетесь получить. В этом случае MessageRepository загружается.

Чуть-чуть больше информации

Это все, что вам нужно, чтобы начать пользоваться Corda Services. Но. Я дам вам немного больше информации, чтобы вы не повторяли те же ошибки, что и я.

Урок первый. При звонке на услугу из потока. Не вводите его внутрь конструктора потока. Вместо этого вызывайте его изнутри функции call или любых других, использованных с этой точки. Если вы этого не сделаете, вы увидите следующее сообщение об ошибке:

1
java.lang.IllegalStateException: This can only be done after the flow has been started.

Выше ошибка, которую вы получите при вызове потока из теста. Если вы звоните из RPC, вы получите что-то вроде этого:

1
2
Caused by: java.lang.reflect.InvocationTargetException: null
Caused by: java.lang.IllegalStateException: This can only be done after the flow has been started.

Вероятно, с длинной трассировкой стека в зависимости от выбранного веб-фреймворка.

Не совсем ясно, что внедрение службы в этот момент вызывает эти ошибки, и вы можете обнаружить, что они появляются по другим причинам. Но я думаю, что можно с уверенностью сказать, по крайней мере в Corda 3.2 , что вы не должны ничего делать внутри конструктора или во время инициализации потока.

Просто чтобы сделать это еще яснее, ниже приведен код, который сопровождал более ранний фрагмент кода, где я внедрил службу:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@InitiatingFlow
@StartableByRPC
class ReplyToMessagesFlow : FlowLogic<List>() {
 
    @Suspendable
    override fun call(): List {
        return messages().map { reply(it) }
    }
 
    private fun messages() =
        repository().findAll(PageSpecification(1, 100))
            .states
            .filter { it.state.data.recipient == ourIdentity }
 
    private fun repository() = serviceHub.cordaService(MessageRepository::class.java)
 
    @Suspendable
    private fun reply(message: StateAndRef) = subFlow(SendMessageFlow(response(message), message))
 
    private fun response(message: StateAndRef): MessageState {
        val state = message.state.data
        return state.copy(
            contents = "Thanks for your message: ${state.contents}",
            recipient = state.sender,
            sender = state.recipient
        )
    }
}

Как видите, сервис внедряется в функцию repository которая в свою очередь call . Следуя такой структуре, все будет работать нормально.

Урок второй Не забудьте включить serviceHub: AppServiceHub в конструктор вашей службы (вы можете вызывать serviceHub как угодно). Если вы этого не сделаете, служба не будет создана, и при попытке доступа к ней появится следующая ошибка:

1
Caused by: java.lang.IllegalArgumentException: Corda service com.lankydanblog.tutorial.services.MessageRepository does not exist

Хотя в этой ситуации есть луч надежды … Маловероятно, что вы это сделаете. Потому что без экземпляра AppServiceHub вы мало что можете сделать с собственным сервисом. У вас не будет доступа к хранилищу или другим встроенным службам. Итак, в конце дня этот урок немного бессмысленный, но я все же попал в эту ловушку …

Это все?

Блин, я думаю, что на самом деле я написал короткий пост на этот раз! Это хорошо или плохо? Я не уверен на 100% …

Во всяком случае, я очень стараюсь придумать еще несколько фрагментов информации. Но я не могу. Минимум, чтобы заставить Corda Service работать, действительно хорош и прост.

Тем не менее, за последние несколько недель я узнал, что есть некоторые очень интересные и полезные вещи, которые вы можете делать в сервисах, которые нельзя сделать в Flows. Это тема, которую я надеюсь охватить в какой-то момент!

Вывод

Сервисы Corda позволяют вам создавать классы, внешние по отношению к потокам, где вы можете логически группировать код, который не имеет прямого отношения к выполнению потока. Мой любимый способ использовать сервис — это сгруппировать функции запросов хранилища в один класс (почти то же, что я делал бы в мире Spring). Есть несколько шагов, которые необходимо предпринять, чтобы убедиться, что вы правильно создали свой сервис. Во-первых, аннотируйте его с помощью @CordaService и @CordaService SingletonSerializeAsToken . Во-вторых, убедитесь, что вы правильно внедрили их в свои потоки, что практически везде, кроме конструктора (или init в Kotlin). Наконец, не забудьте включить AppServiceHub в конструктор службы. Как только вы сможете использовать сервисы Corda, вы сможете отделить код от ваших потоков. Это не только делает потоки короче, но и облегчает их понимание, увеличивая возможность повторного использования кода, на который вы потратили свое драгоценное время.

Код, используемый для этого поста, можно найти на моем GitHub . В этом хранилище есть еще много чего, чего не было в этом посте.

Если вы нашли этот пост полезным, вы можете подписаться на меня в Twitter на @LankyDanDev, чтобы не отставать от моих новых сообщений.

Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . Смотреть оригинальную статью здесь: Corda Services 101

Мнения, высказанные участниками Java Code Geeks, являются их собственными.