Написание адаптеров - rsugio/cpi GitHub Wiki

Руководство по процедурам написания адаптеров для SAP CPI

версия 0.0.2 от 01 июня 2020г Илья Кузнецов [email protected] или [email protected]

Обсуждать можно в https://t.me/sapdevelopment

В этой серии вы увидите:

  • символы Ёё«»Ѣѣ
  • процедуры -- упор на то, какая последовательность работает, с дословной копипастой
  • установка средств разработки
  • пример с :camel:

TODO:

  1. Установка клауд-коннектора
  2. Ссылка на документ по Operations API

Рабочее место разработчика

Java

Ставим JDK 8 - обязательно 64 бита. Прописываем JAVA_HOME. На момент статьи это 1.8.0_251. Восьмёрка -- потому что слоупоки детектед текущий CPI работает на SAP JVM 8, равно как и CloudConnector.

Системы сборки

Maven

https://maven.apache.org/download.html и устанавливаем локальный мавен, он пригодится для генерации проектов и вообще для многих дел. У меня это C:\int\apache\apache-maven-3.6.2\, прописан в путях пользователя как C:\int\apache\apache-maven-3.6.2\bin. Проверяем запуск:

> mvn -version
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T18:06:16+03:00)
Maven home: C:\int\apache\apache-maven-3.6.2\bin\..
Java version: 1.8.0_251, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk1.8.0_251\jre
Default locale: en_US, platform encoding: Cp1251
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Gradle

https://gradle.org/install/ и ставим локальный гредл. Это в целом в индустрии разработки необязательно но мне так сильно проще. Прописываем в путях пользователя, проверяем:

> gradle -version
------------------------------------------------------------
Gradle 6.4
------------------------------------------------------------

Build time:   2020-05-05 19:18:55 UTC
Revision:     42f7c3d0c3066b7b38bd0726760d4881e86fd19f

Kotlin:       1.3.71
Groovy:       2.5.10
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          1.8.0_251 (Oracle Corporation 25.251-b08)
OS:           Windows 10 10.0 amd64

Затмение курильщика

Заходим на https://tools.hana.ondemand.com/#cloudintegration и по ссылке https://www.eclipse.org/downloads/packages/release/oxygen/3a/eclipse-jee-oxygen-3a скачиваем именно эту версию. Распаковываем, прописываем в eclipse.ini первыми двумя строками:

-vm
C:/Program Files/Java/jdk1.8.0_251/bin

Запускаем, добавляем сайт обновления https://tools.hana.ondemand.com/oxygen и устанавливаем такие штуки:

  • SAP Cloud Platform Integration Tools
    • Adapter Development Kit
    • Operations

Вы можете поставить также SAP Cloud Platform Tools, здесь это не требуется.

Для проверки открываем перспективу Integration Operations и в свойствах прописываем Operations Server:

URL = https://d0451-tmn.hci.eu1.hana.ondemand.com <-- конечно здесь у вас другой будет

Вводим логин/пароль и проверяем соединение. Данный плагин использует Operations API, исторически первый для HCI и FSM, и позволяет делать очень много интересных штук -- про это /* TODO */ отдельный документ.

Вторая проверка -- создаём новый проект по Ctrl-N и выбираем SAP Cloud Platform Integration / Adapter Project, смотрим появляется ли этот диалог.

Я также рекомендую отдельно скачать этот плагин для исследований так:

@echo off
set URL=https://tools.hana.ondemand.com/oxygen/
set DST=./cpi-oxygen
rem путь к вашему эклипсу здесь
set ECL=c:\int\Eclipses\eclipse_oxygen\eclipsec.exe 
%ECL% -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source %URL% -destination %DST%
%ECL% -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source %URL% -destination %DST%

Как вы уже знаете по опыту наземного ява-стека, без изучения существующих решений (артефакты и система сборки) рассчитывать на продуктивную работу наивно.

Cloud Connector

Я рекомендую также установить на свой компьютер КК и зарегистрировать его. Это много удобнее и совершенно бесплатно. /* TODO */

Идея здорового человека

Устанавливаем IntelliJ Idea, бесплатный Community Edition вполне достаточен. Все настройки по умолчанию, если вы не знаете что выбрать.

Я даже рекомендую по своему опыту начать установку не с IDE а по шагам:

  1. Создать сквозной аккаунт на https://jetbrains.com и https://hyperskill.org
  2. Скачать https://www.jetbrains.com/toolbox-app/, установить тулбокс и в нём залогиниться в аккаунт
  3. В Тулбоксе сделать настройки, куда ставить продукты JetBrains (я ставлю в C:\int\jetbrains) и выбрать Community Edition к установке

В дальнейшем ToolBox вам сильно облегчит жизнь и запуск проектов.

Для IDEA есть отличный Camel-plugin от Клауса Ибсена (автора Camel), я рекомендую поставить его, тогда вы увидите подсказки по параметрам стандартных компонент.


Первый подход к снаряду: создаём Camel-компонент

Это всё очень хорошо изложено в Camel in Action, раздел 8.4 Developing custom components.

Есть папка C:\webinars\2020-06-02-cpiadapter в которой батник generate.bat:

mvn archetype:generate -B ^
  -DarchetypeGroupId=org.apache.camel.archetypes -DarchetypeArtifactId=camel-archetype-component -DarchetypeVersion=2.17.4 ^
  -DgroupId=demo.cpi -DartifactId=coffee_comp -Dversion=0.0.1 ^
  -Dname=CpiCoffee -Dscheme=coffee 
cd coffee_comp && mvn install
rem Для господ юниксоидов - заменяйте концевые ^ на \

Это должно у вас работать из командной строки, при первом запуске будет вытягивать много артефактов и складывать их в %USERPROFILE%/.m2 и куда-то ещё. Если у вас прокси -- пропишите его где-то в настройках мэвена.

  • org.apache.camel.archetypes и camel-archetype-component указывают, какой проект надо создать по шаблону, версия 2.17.4 взята с учётом рантайма SAP CPI.
  • demo.cpi, coffee_comp и 0.0.1 это составное имя нового артефакта -- компонента Camel
  • scheme=coffee означает что наш компонент будет обрабатывать адреса вида coffee://espresso
  • name=CpiCoffee будет использован для создания файлов CpiCoffee*.java с логикой работы коннектора

Запустили, накачались артефакты и в случае успеха:


[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:3.1.2:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:3.1.2:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO] 
[INFO] 
[INFO] --- maven-archetype-plugin:3.1.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] Archetype repository not defined. Using the one from [org.apache.camel.archetypes:camel-archetype-component:3.3.0] found in catalog remote
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: camel-archetype-component:2.17.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: demo.cpi
[INFO] Parameter: artifactId, Value: coffee_comp
[INFO] Parameter: version, Value: 0.0.1
[INFO] Parameter: package, Value: demo.cpi
[INFO] Parameter: packageInPathFormat, Value: demo/cpi
[INFO] Parameter: name, Value: CpiCoffee
[INFO] Parameter: maven-bundle-plugin-version, Value: 2.3.7
[INFO] Parameter: scheme, Value: coffee
[INFO] Parameter: groupId, Value: demo.cpi
[INFO] Parameter: maven-resources-plugin-version, Value: 2.6
[INFO] Parameter: maven-compiler-plugin-version, Value: 3.5.1
[INFO] Parameter: slf4j-version, Value: 1.7.21
[INFO] Parameter: version, Value: 0.0.1
[INFO] Parameter: log4j-version, Value: 1.2.17
[INFO] Parameter: camel-version, Value: 2.17.4
[INFO] Parameter: package, Value: demo.cpi
[INFO] Parameter: artifactId, Value: coffee_comp
[INFO] Project created from Archetype in dir: C:\webinars\2020-06-02-cpiadapter\coffee_comp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.183 s
[INFO] Finished at: 2020-05-31T12:04:05+03:00
[INFO] ------------------------------------------------------------------------

Файловая структура такая будет:

coffee_comp
│   pom.xml
│   ReadMe.txt
│   
├───src
│   ├───data
│   ├───main
│   │   ├───java
│   │   │   └───demo
│   │   │       └───cpi
│   │   │               CpiCoffeeComponent.java
│   │   │               CpiCoffeeConsumer.java
│   │   │               CpiCoffeeEndpoint.java
│   │   │               CpiCoffeeProducer.java
│   │   │               
│   │   └───resources
│   │       └───META-INF
│   │           └───services
│   │               └───org
│   │                   └───apache
│   │                       └───camel
│   │                           └───component
│   │                                   coffee
│   │                                   
│   └───test
│       ├───java
│       │   └───demo
│       │       └───cpi
│       │               CpiCoffeeComponentTest.java
│       │               
│       └───resources
│               log4j.properties
│               
└───target
        coffee_comp-0.0.1.jar

Для экспериментов открываем этот проект в IdeaJ: Open or Import, C:\webinars\2020-06-02-cpiadapter\coffee_comp.

Запуск теста CpiCoffeeComponentTest.java у вас явно пройдёт успешно, но для консольных примеров мы напишем такой C:\webinars\2020-06-02-cpiadapter\coffee_comp\src\test\java\demo\cpi\Main.java:

package demo.cpi;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

class WebinarDemo extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("coffee://x?name=espresso&option=1")
                .to("coffee://y?option=2")
                .to("file://tmp");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        CamelContext ctx = new DefaultCamelContext();
        ctx.addRoutes(new WebinarDemo());
        ctx.start();
        Thread.sleep(5000);
        ctx.stop();
    }
}

Наша кофеварка будет периодически отправлять сообщения из coffee://x в coffee://y и файловую папку C:\webinars\2020-06-02-cpiadapter\coffee_comp\tmp. В данном примере по умолчанию мэвен-плагин создаёт Endpoint с параметрами:

  • name (обязательный, строка). В нашем примере обязательность на Producer нарушена, но ошибка не возникает
  • option (по умолчанию 10, число)

Штатно X и Y никуда не сохраняются, и чтобы их получать, впишем в CpiCoffeeEndpoint.java новый параметр host с сеттером и геттером:

public class CpiCoffeeEndpoint extends DefaultEndpoint {
    private String host = null;
    void setHost(String host) {
        this.host = host;
    }
    public String getHost() {
        return host;
    }
    ...

А в CpiCoffeeComponent.java:

protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
    CpiCoffeeEndpoint endpoint = new CpiCoffeeEndpoint(uri, this);   // меняем класс у endpoint, чтобы был виден setHost
    setProperties(endpoint, parameters);
    endpoint.setHost(remaining);   // __добавляем руками__
    return endpoint;
}

Как видим, стандартный UriEndpointComponent передаёт полный путь в uri, параметры в parameters а неразобранный остаток в remaining, который мы интерпретируем как host.

Для настройки интервала опроса и формирования содержимого, пропишем в CpiCoffeeConsumer.java:


public CpiCoffeeConsumer(CpiCoffeeEndpoint endpoint, Processor processor) {
    super(endpoint, processor);
    this.endpoint = endpoint;
    this.setDelay(1000L);       // интервал поллинга
}

@Override
protected int poll() throws Exception {
    Exchange exchange = endpoint.createExchange();

    // create a message body
    Date now = new Date();
    /* Вот это вписываем */ 
    Message msg = exchange.getIn();
    msg.setHeader("CoffeeStart", now.toString());
    msg.setHeader("CoffeeName", endpoint.getName());
    msg.setHeader("CoffeeHost", endpoint.getHost());
    String s = MessageFormat.format("Coffee started: host={0} name={1} option={2} version={3}", endpoint.getHost(), endpoint.getName(), endpoint.getOption(), endpoint.getVersion());
    msg.setBody(s);
    exchange.setProperty("CoffeeVersion", endpoint.getVersion());
...

Отправитель-consumer шлёт сообщение, плюс три заголовка и Exchange-property CoffeeVersion.

На стороне получателя-producer CpiCoffeeProducer.java немного меняем метод получения, вместо System.out:


public void process(Exchange exchange) throws Exception {
    LOG.info(exchange.getIn().getBody().toString());    // выводим через sfl4j
//    System.out.println(exchange.getIn().getBody());       было по шаблону
}

Теперь запуск Main.main() отработает примерно так:

[                          main] DefaultCamelContext            INFO  Apache Camel 2.17.4 (CamelContext: camel-1) is starting
[                          main] ManagedManagementStrategy      INFO  JMX is enabled
[                          main] DefaultTypeConverter           INFO  Loaded 184 type converters
[                          main] DefaultRuntimeEndpointRegistry INFO  Runtime endpoint registry is in extended mode gathering usage statistics of all incoming and outgoing endpoints (cache limit: 1000)
[                          main] DefaultCamelContext            INFO  AllowUseOriginalMessage is enabled. If access to the original message is not needed, then its recommended to turn this option off as it may improve performance.
[                          main] DefaultCamelContext            INFO  StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
[                          main] DefaultCamelContext            INFO  Route: route1 started and consuming from: Endpoint[coffee://x?name=espresso&option=1]
[                          main] DefaultCamelContext            INFO  Total 1 routes, of which 1 are started.
[                          main] DefaultCamelContext            INFO  Apache Camel 2.17.4 (CamelContext: camel-1) started in 0.904 seconds
[amel-1) thread #0 - coffee://x] CpiCoffeeProducer              INFO  Coffee started: host=x name=espresso option=1 version=2.17.4
[amel-1) thread #0 - coffee://x] CpiCoffeeConsumer              INFO  Coffee consumer ID-MOWN34097792A-52631-1590921427160-0-2
[amel-1) thread #0 - coffee://x] CpiCoffeeProducer              INFO  Coffee started: host=x name=espresso option=1 version=2.17.4
[amel-1) thread #0 - coffee://x] CpiCoffeeConsumer              INFO  Coffee consumer ID-MOWN34097792A-52631-1590921427160-0-4
[amel-1) thread #0 - coffee://x] CpiCoffeeProducer              INFO  Coffee started: host=x name=espresso option=1 version=2.17.4
[amel-1) thread #0 - coffee://x] CpiCoffeeConsumer              INFO  Coffee consumer ID-MOWN34097792A-52631-1590921427160-0-6
[amel-1) thread #0 - coffee://x] CpiCoffeeProducer              INFO  Coffee started: host=x name=espresso option=1 version=2.17.4
[amel-1) thread #0 - coffee://x] CpiCoffeeConsumer              INFO  Coffee consumer ID-MOWN34097792A-52631-1590921427160-0-8
[                          main] DefaultCamelContext            INFO  Apache Camel 2.17.4 (CamelContext: camel-1) is shutting down
[                          main] DefaultShutdownStrategy        INFO  Starting to graceful shutdown 1 routes (timeout 300 seconds)
[el-1) thread #1 - ShutdownTask] DefaultShutdownStrategy        INFO  Route: route1 shutdown complete, was consuming from: Endpoint[coffee://x?name=espresso&option=1]
[                          main] DefaultShutdownStrategy        INFO  Graceful shutdown of 1 routes completed in 0 seconds
[                          main] DefaultCamelContext            INFO  Apache Camel 2.17.4 (CamelContext: camel-1) uptime 5.922 seconds
[                          main] DefaultCamelContext            INFO  Apache Camel 2.17.4 (CamelContext: camel-1) is shutdown in 0.009 seconds

Process finished with exit code 0

В C:\webinars\2020-06-02-cpiadapter\coffee_comp\tmp\ появится 4-5 файлов с содержимым: Coffee started: host=x name=espresso option=1 version=2.17.4


«Всё это хорошо, прекрасно», отвѣтилъ онъ: "но всё это меня что-то не веселитъ"


ADK в Eclipse

От прекрасного и понятного перейдём к ADK (Adapter Development Kit).

Создаём проект с параметрами:

Project Name = CpiCoffee
Adapter Details
  Name = CpiCoffee
  ID = CpiCoffee
  Version = 1.0.0
  Vendor = cpi.demo
[x] Enable Maven
Component Details
  Name = CpiCoffee
  Package = cpicoffee 

Созданный проект (шаблон - см. com.sap.cloud.adk:com.sap.cloud.adk.build.archive:1.30.0) в целом аналогичен camel-archetype-component но есть ряд SAP-специфичных файлов:

CpiCofee
│   .classpath
│   .project
│   config.adk                                          <-- файл со свойствами для ADK
│   pom.xml
│   
├───src
│   ├───main
│   │   ├───java
│   │   │   └───cpicoffee
│   │   │           CpiCoffeeComponent.java
│   │   │           CpiCoffeeConsumer.java
│   │   │           CpiCoffeeEndpoint.java
│   │   │           CpiCoffeeProducer.java
│   │   │           
│   │   └───resources
│   │       ├───META-INF
│   │       │   └───services
│   │       │       └───org
│   │       │           └───apache
│   │       │               └───camel
│   │       │                   └───component
│   │       │                           sap-sample      <-- к сожалению, это имя зашито в плагине
│   │       │                           
│   │       └───metadata
│   │               metadata.xml
│   │               
│   └───test
│       └───java
│           └───cpicoffee
│                   CpiCoffeeComponentTest.java
│                   
└───target

Я рекомендую сравнить этот проект с набором файлов, созданным из camel-archetype-component, и внести некоторые изменения в SAP-вариант:

  1. Переименовать sap-sample в coffee а также ссылки на него в CpiCoffeeEndpoint.java, CpiCoffeeComponentTest.java, metadata.xml
  2. TODO

Ошибки и их причины

Blueprint metadata

[ERROR] Blueprint Metadata is not referred in the Component Metadata
[ERROR] Adapter build failed due to: Validation Failed:  Blueprint Metadata is not referred in the Component Metadata

Необходимо декомпилировать com.sap.cloud.adk.build_1.30.0.jar из плагина для Eclipse, и найти \com\sap\cloud\adk\checks\AdapterProjectChecks.java, метод isReferencedComponentPresentInMetadata:

AdapterMetadataBuildUtil.getVariantIds -- из XML вытаскивает список /ComponentMetadata/Variant/@VariantId

List bpMetadataVariantIds = AdapterMetadataBuildUtil.getVariantIds(AdapterMetadataBuildUtil.getDocumentFromFile(bpMetadataFile));
ctype::AdapterVariant/cname::CpiCoffee2/vendor::cpi.demo/tp::sap-sample/mp::sap-sample/direction::Sender
ctype::AdapterVariant/cname::CpiCoffee2/vendor::cpi.demo/tp::sap-sample/mp::sap-sample/direction::Receiver

Точно в отладчике пока воспроизвести не смог но побороть получилось - просто удалить bpMetadata.xml с тем чтобы он создался заново при необходимости.


Ссылки

[1] Camel in action, второе издание, параграф 8.4 про разработку

[2] Запись видео /* TODO */

[3] Отличный пример по BlogADKAdapter - https://blogs.sap.com/2018/06/05/sap-cloud-platform-integration-new-adapter-api-for-adapter-development/

[4] Устаревший подход (до Maven) - https://blogs.sap.com/2017/06/23/extension-of-runtime-capabilities-using-blueprint-metadata-in-cloud-platform-integration-sdk/

[5] Developing Adapters, документация по CPI