Help us understand the problem. What is going on with this article?

HULFT IoT EdgeStreamingでプラグインを作成してみた【開発編】(2/3)

はじめに

こんにちは。よろず相談担当 すぎもん:yum:です。
今回は、HULFT IoT EdgeStreamingのSDKを使用したコンポーネント(アダプタ)の開発をやってみようと思います。全3部作で完結するよう記載してます。

今回やること

今回は、第2弾としてHULFT IoT EdgeStreaming Plugin SDKを使用してプラグインを開発してみます。他にセットアップ編、実行編の記事もあるので是非参考にしてみてください。

:arrow_forward: HULFT IoT EdgeStreamingでプラグインを作成してみた【セットアップ編】(1/3)
:arrow_forward: HULFT IoT EdgeStreamingでプラグインを作成してみた【開発編】(2/3)
:arrow_forward: HULFT IoT EdgeStreamingでプラグインを作成してみた【実行編】(3/3)

プラグインの作成概要

EdgeStreamingのプラグイン作成の構成は、以下のようになります。
image.png

image.png

実行環境構成

Studioから指定されたStreaming処理を実行するRuntime部分になります。

SourceOperation
Streamingデータ(Tuple)を生成するオペレーションです。「入力処理」となります。
※JSON形式となります

SinkOperation
Streamingデータ(Tuple)を外部へ出力するオペレーションです。「出力処理」となります。

UDSFOperation
Streamingデータ(Tuple)の変換を行うオペレーションです。
また、変換されたStreming(Tuple)を出力します。「入出力(変換)処理」

開発環境構成

RunTimeにStreaming処理をどのように行うかを指示するBQL(SQLライクな文法)を生成します。

AdapterModuleComponent
コンポーネント(アダプタ)を表すクラスです。オペレーションと多対1となります。

BQLPluginSourceOperationFactory
SourceOperartionのプロパティを定義します。「入力処理」
Sourceオペレーションを生成するBQL(Create Source文)を出力します。

BQLPluginOutputOperationFactory
SinkOperationのプロパティを定義します。「出力処理」
Sinkオペレーションを生成するBQL(Create Sink文)を出力します。

BQLPluginUDSFOperationFactory
UDSFのプロパティを定義します。「入出力(変換)処理」
UDSFオペレーションを生成するBQL(Select文)を出力します。

プラグインの作成

今回は、以下のような形でプラグイン作成をしてみました。

・基本情報
image.png

・オペレーション情報
image.png

実行環境側(Golang)の実装

Studioから指定されたStreaming処理の実装をします。

環境準備
1. モジュールディレクトリの作成
$SDK_HOME/dev/go/src/github.sis.saison.co.jp/sherpa/es-agent/sample
 ※ここに、ソースファイルを格納しました。(source.go, sink.go, udsf.go)
2. pluginディレクトリの作成
$SDK_HOME/dev/go/src/github.sis.saison.co.jp/sherpa/es-agent/sample/plugin
3. externalディレクトリの作成
$SDK_HOME/dev/go/src/github.sis.saison.co.jp/sherpa/es-agent/sample/external

ソースファイルの作成
モジュールディレクトリ(ここではsample)に以下のファイル名でソースファイルを作成していってみます。

・ファイル構成は、以下のとおりです。

├─sample
│   ├─source.go(入力処理)
│   ├─sink.go(出力処理)
│   ├─udsf.go(入出力変換処理)
│   │ 
│   ├─external
│   │   ├─plugin_main.go(main関数)
│   │   │ 
│   └─plugin
│   │   ├─plugin.go(各sample配下のソースファイルを登録)
│   │   │ 

では、ソースファイルを作成していってみましょう。

・source.go(入力処理)
一定時間間隔で疑似乱数を生成する処理を作成します。
主な流れは、以下のとおりです。
1. 整数型のパラメータ”interval”を受け取る
2. intervalの時間間隔で疑似乱数を生成する
3. JSONスキーマのデータを持つTupleを生成する

Tuple
{
  "type": "object",
  "required": ["payload"],
  "properties": {
    "payload": {
      "type": "object",
      "required": ["value"],
      "properties": {
        "value": {
          "type": "number"
        }
      }
    }
  }
}

出力されるTupleのJSONデータは、以下のような形になります。

{"payload": {"value": 3.5423242}}

source.goは、以下のように作成してみました。

source.go
package sample

import (
    "math/rand"
    "time"

    "gopkg.in/sensorbee/sensorbee.v0/bql"
    "gopkg.in/sensorbee/sensorbee.v0/core"
    "gopkg.in/sensorbee/sensorbee.v0/data"
)

type source struct {
    interval time.Duration
    term     chan struct{}
}

func (s *source) GenerateStream(ctx *core.Context, w core.Writer) error {
    rand.Seed(time.Now().UnixNano())

    next := time.Now()
    for {
        val := rand.Float64()
        m := data.Map{"value": data.Float(val)}
        t := core.NewTuple(data.Map{"payload": m})
        if s.interval > 0 {
            t.Timestamp = next
        }
        ctx.Log().Debug("generation: ", val)
        if err := w.Write(ctx, t); err != nil {
            return err
        }

        if s.interval > 0 {
            now := time.Now()
            next = next.Add(s.interval)
            if next.Before(now) {
                next = now.Add(s.interval)
            }

            select {
            case <-s.term:
                return core.ErrSourceStopped
            case <-time.After(next.Sub(now)):
            }
        }
    }
    return nil
}

func (s *source) Stop(ctx *core.Context) error {
    s.term <- struct{}{}
    return nil
}

func CreateSource(ctx *core.Context, ioParams *bql.IOParams, params data.Map) (core.Source, error) {
    interval, err := getInterval(params)
    if err != nil {
        return nil, err
    }

    return &source{
        interval: interval,
        term:     make(chan struct{}),
    }, nil
}

func getInterval(params data.Map) (time.Duration, error) {
    interval := 1 * time.Second
    if v, ok := params["interval"]; ok {
        i, err := data.ToDuration(v)
        if err != nil {
            return interval, err
        }
        interval = i
    }
    return interval, nil
}

・sink.go(出力処理)
有効小数点桁数で切り捨ててログに出力する処理を作成します。

主な流れは、以下のとおりです。
1. 整数型のパラメータ「decimal」を受け取る
2. 「decimal」の有効小数点桁数で、受け取った値を標準出力に出力
3. JSONデータのスキーマのデータを持つTupleを受け取る`

Tuple
{
  "type": "object",
  "required": ["payload"],
  "properties": {
    "payload": {
      "type": "object",
      "required": ["value", "formula"],
      "properties": {
        "value": {
          "type": "number"
        }
        "formula": {
          "type": "string"
        }
      }
    }
  }
}

sink.goは、以下のように作成してみました。

sink.go
package sample

import (
    "fmt"
    "math"

    "gopkg.in/sensorbee/sensorbee.v0/bql"
    "gopkg.in/sensorbee/sensorbee.v0/core"
    "gopkg.in/sensorbee/sensorbee.v0/data"
)

type sink struct {
    decimal int
}

func (s *sink) Write(ctx *core.Context, tuple *core.Tuple) error {
    p, ok := tuple.Data["payload"]
    if !ok {
        return fmt.Errorf("the tuple doesn't have the required field: payload")
    }

    payload, err := data.AsMap(p)
    if err != nil {
        return err
    }

    v, ok := payload["value"]
    if !ok {
        return fmt.Errorf("the tuple doesn't have the required field: value")
    }

    value, err := data.AsFloat(v)
    if err != nil {
        return err
    }

    f, ok := payload["formula"]
    if !ok {
        return fmt.Errorf("the tuple doesn't have the required field: formula")
    }

    formula, err := data.AsString(f)
    if err != nil {
        return err
    }
    shift := math.Pow(10, float64(s.decimal))
    value = math.Floor(value*shift) / shift
    ctx.Log().Infof("formula: %s", formula)
    ctx.Log().Infof("value: %f", value)
    return nil
}

func (s *sink) Close(ctx *core.Context) error {
    return nil
}

func CreateSink(ctx *core.Context, ioParams *bql.IOParams, params data.Map) (core.Sink, error) {
    decimal, err := getDecimal(params)
    if err != nil {
        return nil, err
    }

    return &sink{
        decimal: decimal,
    }, nil
}

func getDecimal(params data.Map) (int, error) {
    node, ok := params["decimal"]
    if !ok {
        return 0, fmt.Errorf("decimal is required")
    }
    decimal, err := data.AsInt(node)
    if err != nil {
        return 0, fmt.Errorf("decimal must be a int:%s", err)
    }
    return int(decimal), nil
}

・udsf.go(入出力変換処理)
udsfオブジェクトは、以下をデータを受け取ります。
- 文字列型のパラメータ:stream_name(入力Stream名)
- operaror(演算子)
- 浮動小数点型パラメータ: initial_value(初期値)

udsfオブジェクトは、受け取ったTupleのvalue要素の値を、現在の値(スタート時はinitial_value(初期値))に指定した演算子で、演算し続けます。

udsf.goは、以下のように作成してみました。

udsf.go
package sample

import (
    "fmt"

    "gopkg.in/sensorbee/sensorbee.v0/bql/udf"
    "gopkg.in/sensorbee/sensorbee.v0/core"
    "gopkg.in/sensorbee/sensorbee.v0/data"
)

type operator byte

const (
    none    = ' '
    plus    = '+'
    minus   = '-'
    times   = '*'
    divided = '/'
)

type udsf struct {
    cur float64
    ope operator
}

func (u *udsf) Process(ctx *core.Context, tuple *core.Tuple, w core.Writer) error {
    p, ok := tuple.Data["payload"]
    if !ok {
        return fmt.Errorf("the tuple doesn't have the required field: payload")
    }

    payload, err := data.AsMap(p)
    if err != nil {
        return err
    }

    v, ok := payload["value"]
    if !ok {
        return fmt.Errorf("the tuple doesn't have the required field: value")
    }

    value, err := data.AsFloat(v)
    if err != nil {
        return err
    }

    var formula string
    newVal := u.cur
    switch u.ope {
    case plus:
        newVal += value
    case minus:
        newVal -= value
    case times:
        newVal *= value
    case divided:
        newVal /= value
    }
    formula = fmt.Sprintf("%f %s %f", u.cur, string(u.ope), value)
    ctx.Log().Debug("calculate: " + formula)
    m := data.Map{
        "value":   data.Float(newVal),
        "formula": data.String(formula),
    }
    if err := w.Write(ctx, core.NewTuple(data.Map{"payload": m})); err != nil {
        return err
    }
    u.cur = newVal
    return nil
}

func (u *udsf) Terminate(ctx *core.Context) error {
    return nil
}

func CreateUDSF(decl udf.UDSFDeclarer, params data.Map) (udf.UDSF, error) {
    inputStream, err := getStreamName(params)
    if err != nil {
        return nil, err
    }

    operator, err := getOperator(params)
    if err != nil {
        return nil, err
    }

    initialValue, err := getInitialValue(params)
    if err != nil {
        return nil, err
    }

    if err := decl.Input(inputStream, nil); err != nil {
        return nil, err
    }

    return &udsf{
        ope: operator,
        cur: initialValue,
    }, nil
}

func getStreamName(params data.Map) (string, error) {
    node, ok := params["stream_name"]
    if !ok {
        return "", fmt.Errorf("stream_name is required")
    }
    streamName, err := data.AsString(node)
    if err != nil {
        return "", fmt.Errorf("stream_name must be a string:%s", err)
    }
    return streamName, nil
}

func getOperator(params data.Map) (operator, error) {
    node, ok := params["operator"]
    if !ok {
        return none, fmt.Errorf("operator is required")
    }
    operatorStr, err := data.AsString(node)
    if err != nil {
        return none, fmt.Errorf("operator must be a string:%s", err)
    }

    switch operatorStr {
    case "plus":
        return plus, nil
    case "minus":
        return minus, nil
    case "times":
        return times, nil
    case "divided":
        return divided, nil
    default:
        return none, fmt.Errorf("invalid oparator")
    }
}

func getInitialValue(params data.Map) (float64, error) {
    initialValue := 0.0
    node, ok := params["initial_value"]
    if !ok {
        return initialValue, nil
    }
    initialValue, err := data.AsFloat(node)
    if err != nil {
        return initialValue, fmt.Errorf("initial_value is invalid")
    }
    return initialValue, nil
}

ソースファイルの登録
pluginディレクトリに、plugin.goを作成し、Source、Sink、 UDSF Operationの登録処理(BQLとして使用するため)を実装します。

image.png

plugin.goは、以下のように作成してみました。

plugin.go
package plugin

import (
    "github.sis.saison.co.jp/sherpa/es-agent/sample"
    "gopkg.in/sensorbee/sensorbee.v0/bql"
    "gopkg.in/sensorbee/sensorbee.v0/bql/udf"
)

func init() {
    bql.MustRegisterGlobalSourceCreator("sample_source", bql.SourceCreatorFunc(sample.CreateSource))
    bql.MustRegisterGlobalSinkCreator("sample_sink", bql.SinkCreatorFunc(sample.CreateSink))
    udf.MustRegisterGlobalUDSFCreator("sample_udsf", udf.MustConvertToUDSFCreator(sample.CreateUDSF))
}

main関数の作成
最後に、単体の実行モジュールとして処理を呼び出すmain関数をexternalディレクトリに、plugin_main.goを作成し実装します。

plugin_main.goは、以下のように作成してみました。

plugin_main.go
package main

import (
    "os"

    "github.sis.saison.co.jp/sherpa/es-agent/external/plugin"
    _ "github.sis.saison.co.jp/sherpa/es-agent/sample/plugin"
)

func main() {
    if err := plugin.NewServer().ListenAndServe(); err != nil {
        os.Exit(1)
    }
}

ここまでで、Runtime側の準備が完了しました。
次に、開発環境側の実装をしていきます。

開発環境側(Java)の実装

RuntimeにStreaming処理をどのように行うかを指示するBQL(SQLライクな文法)の生成を実装します。

環境準備
モジュールディレクトリの作成
$SDK_HOME/dev/sample_adapterディレクトリを作成します。

ファイルのコピー
$SDK_HOME/dev/confディレクトリにある、build.xml、config.propertiesをモジュールディレクトリにコピーします。
$SDK_HOME/dev/conf/build.xml
$SDK_HOME/dev/sample_adapter/build.xml
$SDK_HOME/dev/conf/config.properties
$SDK_HOME/dev/sample_adapter/config.propertites

コピーしたconfig.propertiesファイルを編集します。

Implementation-Title=SampleAdapter
Implementation-Vendor=sugimon
Implementation-Version=0

module.category=Sample
module.label=Sample Plugin
display.name=Sample Plugin Adapter

plugin.name=sample_plugin
esagent.plugin.package=github.sis.saison.co.jp/sherpa/es-agent/sample

ソースファイル用ディレクトリを作成
$SDK_HOME/dev/sample_adapterにsrcディレクトリを作成します。
($SDK_HOME/dev/sample_adapter/src)

  次に作成するjavaファイルのパッケージcom/appresso/ds/dp/modules/adapter/sample
になるように、以下のようなパッケージ用のディレクトリを作成します。
($SDK_HOME/dev/sample_adapter/src/com/appresso/ds/dp/modules/adapter/sample)

ソースファイルの作成
パッケージディレクトリに以下のファイル名でソースファイルを作成します。

・SampleAdapterModuleComponent.java
・SampleSinkOperationFactory.java
・SampleSourceOperationFactory.java
・SampleUDSFOperationFactory.java

├─ sample_adapter
│      │  build.xml
│      │  config.properties
│      ├─ src
│      │   └com
│      │     └appresso
│      │       └ds
│      │         └dp
│      │           └modules
│      │              └adapter
│      │                 └sample
│      │                    SampleAdapterModuleComponent.java
│      │                    SampleSinkOperationFactory.java
│      │                    SampleSourceOperationFactory.java
│      │                    SampleUDSFOperationFactory.java

こちらもそれぞれ、ソースファイルを作成していってみましょう。

・SampleSourceOperationFactory.java(入力処理)
Sourceオペレーションのプロパティを保持するオブジェクトを返したり、オペレーションオブジェクトを返したりします。(継承元クラス:BQLPluginSourceOperationFactoryクラス)

SampleSourceOperationFactory.javaは、以下のように作成してみました。

SampleSourceOperationFactory.java
package com.appresso.ds.dp.modules.adapter.sample;

import com.appresso.ds.common.spi.constraint.NumberFillin;
import com.appresso.ds.common.spi.param.SimpleParameter;
import com.appresso.ds.common.xmlfw.xml.XmlHandler;
import com.appresso.ds.dp.share.adapter.bql.common.BQLPluginSourceOperationFactory;
import com.appresso.ds.dp.spi.OperationConfiguration;
import com.appresso.ds.dp.spi.OperationConfigurator;
import org.xml.sax.SAXException;

import static com.appresso.ds.common.bql.BQLSimpleParameterType.FLOAT;

public class SampleSourceOperationFactory extends BQLPluginSourceOperationFactory {

    @Override
    protected String getLabel() {
        return "Sample source";
    }

    @Override
    protected String getPluginName() {
        return "sample_plugin";
    }

    @Override
    public String getOperationName() {
        return "sample_source";
    }

    @Override
    protected String getTypeName() {
        return "sample_source";
    }

    @Override
    protected void setupOperationConfigurator(OperationConfigurator operationConfigurator) {
        operationConfigurator.addSimpleParameter(createIntervalParameter());
    }

    @Override
    protected void setupOutputSchema(XmlHandler handler, OperationConfiguration conf) throws Exception {
        handler.startElement("", "payload", "payload", EMPTY_ATTRIBUTE);
        writeElement(handler, "value");
        handler.endElement("", "payload", "payload");
    }

    protected void writeElement(XmlHandler handler, String name) throws SAXException {
        handler.startElement("", name, name, EMPTY_ATTRIBUTE);
        handler.endElement("", name, name);
    }

    static SimpleParameter createIntervalParameter() {
        NumberFillin fillin = new NumberFillin();
        fillin.setMinValue(0.001);
        fillin.setMaxValue(1314000);
        fillin.setAllowMin(true);
        fillin.setAllowMax(true);
        fillin.setPrecision(10);
        fillin.setDecimal(3);
        fillin.setAllowDouble(true);
        fillin.setLabel("Interval[sec]");
        fillin.setRequired(true);
        return new SimpleParameter(FLOAT.toParameterKey("interval"), fillin);
    }
}

・SampleSinkOperationFactory.java(出力処理)
Sinkオペレーションのプロパティを保持するオブジェクトを返したり、オペレーションオブジェクトを返したりします。(継承元クラス:BQLPluginSinkOperationFactoryクラス)

SampleSinkOperationFactory.javaは、以下のように作成してみました。

SampleSinkOperationFactory.java
package com.appresso.ds.dp.modules.adapter.sample;

import com.appresso.ds.common.spi.constraint.NumberFillin;
import com.appresso.ds.common.spi.param.SimpleParameter;
import com.appresso.ds.common.xmlfw.xml.XmlHandler;
import com.appresso.ds.dp.share.adapter.bql.common.BQLPluginOutputOperationFactory;
import com.appresso.ds.dp.spi.OperationConfiguration;
import com.appresso.ds.dp.spi.OperationConfigurator;
import com.appresso.ds.dp.spi.OperationContext;
import org.xml.sax.SAXException;

import static com.appresso.ds.common.bql.BQLSimpleParameterType.INTEGER;

public class SampleSinkOperationFactory extends BQLPluginOutputOperationFactory {
    @Override
    protected String getLabel() {
        return "Sample sink";
    }

    @Override
    protected String getPluginName() {
        return "sample_plugin";
    }

    @Override
    public String getOperationName() {
        return "sample_sink";
    }

    @Override
    protected String getTypeName() {
        return "sample_sink";
    }

    @Override
    protected void setupOperationConfigurator(OperationConfigurator operationConfigurator) {
        operationConfigurator.addSimpleParameter(createDecimalParameter());
    }

    protected void setupInputSchema(XmlHandler handler, OperationConfiguration conf, OperationContext context)
            throws Exception {
        handler.startElement("", "payload", "payload", EMPTY_ATTRIBUTE);
        writeElement(handler, "formula");
        writeElement(handler, "value");
        handler.endElement("", "payload", "payload");
    }

    protected void writeElement(XmlHandler handler, String name) throws SAXException {
        handler.startElement("", name, name, EMPTY_ATTRIBUTE);
        handler.endElement("", name, name);
    }

    static SimpleParameter createDecimalParameter() {
        NumberFillin fillin = new NumberFillin();
        fillin.setMinValue(0);
        fillin.setMaxValue(10);
        fillin.setAllowMin(true);
        fillin.setAllowMax(true);
        fillin.setLabel("Decimal");
        fillin.setRequired(true);
        return new SimpleParameter(INTEGER.toParameterKey("decimal"), fillin);
    }
}


・SampleUDSFOperationFactory
UDSFオペレーションのプロパティを保持するオブジェクトを返したり、オペレーションオブジェクトを返したりします。

SampleUDSFOperationFactory.javaは、以下のように作成してみました。

SampleUDSFOperationFactory.java
package com.appresso.ds.dp.modules.adapter.sample;

import com.appresso.ds.common.bql.UDSFFromArgument;
import com.appresso.ds.common.bql.UDSFFromTemplate;
import com.appresso.ds.common.spi.constraint.Item;
import com.appresso.ds.common.spi.constraint.Multi;
import com.appresso.ds.common.spi.constraint.NumberFillin;
import com.appresso.ds.common.spi.param.SimpleParameter;
import com.appresso.ds.common.xmlfw.xml.XmlHandler;
import com.appresso.ds.dp.share.adapter.bql.common.BQLPluginUDSFOperationFactory;
import com.appresso.ds.dp.spi.OperationConfiguration;
import com.appresso.ds.dp.spi.OperationConfigurator;
import com.appresso.ds.dp.spi.OperationContext;
import org.xml.sax.SAXException;

import java.util.stream.Stream;

import static com.appresso.ds.common.bql.BQLSimpleParameterType.FLOAT;
import static com.appresso.ds.common.bql.BQLSimpleParameterType.STRING;

public class SampleUDSFOperationFactory extends BQLPluginUDSFOperationFactory {
    @Override
    protected String getLabel() {
        return "Sample UDSF";
    }

    @Override
    public String getPluginName() {
        return "sample_plugin";
    }

    @Override
    protected String getTypeName() {
        return "sample_udsf";
    }

    @Override
    public String getOperationName() {
        return "sample_udsf";
    }

    @Override
    protected void addArgs(UDSFFromTemplate template) {
        template.addArg(new UDSFFromArgument(STRING.toParameterKey("operator")));
        template.addArg(new UDSFFromArgument(FLOAT.toParameterKey("initial_value")));
    }

    @Override
    protected void setupOperationConfigurator(OperationConfigurator operationConfigurator) {
        setStreamConfigurationParameter(operationConfigurator);
        operationConfigurator.addSimpleParameter(createOperatorParameter());
        operationConfigurator.addSimpleParameter(createInitialValueParameter());
    }

    @Override
    protected void setupInputSchema(XmlHandler handler, OperationConfiguration conf, OperationContext context)
            throws Exception {
        handler.startElement("", "payload", "payload", EMPTY_ATTRIBUTE);
        writeElement(handler, "value");
        handler.endElement("", "payload", "payload");
    }

    @Override
    protected void setupOutputSchema(XmlHandler handler, OperationConfiguration conf, OperationContext context)
            throws Exception {
        handler.startElement("", "payload", "payload", EMPTY_ATTRIBUTE);
        writeElement(handler, "formula");
        writeElement(handler, "value");
        handler.endElement("", "payload", "payload");
    }

    protected void writeElement(XmlHandler handler, String name) throws SAXException {
        handler.startElement("", name, name, EMPTY_ATTRIBUTE);
        handler.endElement("", name, name);
    }

    static SimpleParameter createInitialValueParameter() {
        NumberFillin fillin = new NumberFillin();
        fillin.setPrecision(10);
        fillin.setDecimal(3);
        fillin.setAllowDouble(true);
        fillin.setLabel("Initial value");
        fillin.setRequired(true);
        return new SimpleParameter(FLOAT.toParameterKey("initial_value"), fillin);
    }

    static SimpleParameter createOperatorParameter(){
        Multi multi = new Multi(Operator.getItems());
        multi.setLabel("Operator");
        multi.setRequired(true);
        SimpleParameter param = new SimpleParameter(STRING.toParameterKey("operator"), multi);
        return param;
    }

    enum Operator {
        Plus("+","plus"),
        Minus("-","minus"),
        Times("*","times"),
        Divided("/","divided");

        public String getDisplayName() {
            return displayName;
        }

        public String getValue() {
            return value;
        }

        private final String displayName;

        private final String value;

        private Operator(String displayName, String value) {
            this.displayName = displayName;
            this.value=value;
        }

        Item toItem(){
            return new Item(value,displayName);
        }

        static Item[] getItems(){
            return Stream.of(Operator.values()).map(s->s.toItem()).toArray(Item[]::new);
        }
    }
}

・SampleAdapterModuleComponent
コンポーネント(アダプタ)を表すクラスです。

SampleAdapterModuleComponent.javaは、以下のように作成してみました。

SampleAdapterModuleComponent.java
package com.appresso.ds.dp.modules.adapter.sample;

import java.util.ArrayList;
import java.util.List;

import com.appresso.ds.common.kernel.modules.LicenseManager;
import com.appresso.ds.common.license.LicensePackageType;
import com.appresso.ds.dp.spi.AdapterModuleComponent;
import com.appresso.ds.dp.spi.OperationFactory;
import com.appresso.ds.dp.spi.ResourceFactory;

public class SampleAdapterModuleComponent extends AdapterModuleComponent {

    private static final String MODULE_COMPONENT_NAME = "Sample Adapter";

    @Override
    public OperationFactory[] getOperationFactories() throws Exception {
        List<OperationFactory> operationFactories = new ArrayList<>();
        operationFactories.add(new SampleSourceOperationFactory());
        operationFactories.add(new SampleUDSFOperationFactory());
        operationFactories.add(new SampleSinkOperationFactory());
        return operationFactories.toArray(new OperationFactory[operationFactories.size()]);
    }

    @Override
    public ResourceFactory[] getResourceFactories() throws Exception {
        return new ResourceFactory[]{};
    }

    public void checkLicense() throws Exception {
        LicenseManager licenseManager = getContext().getProxy(LicenseManager.class);
        licenseManager.checkLicense(getModuleComponentName(), getPermittedPackageType());
    }

    private String getModuleComponentName() {
        return MODULE_COMPONENT_NAME;
    }

    private int[] getPermittedPackageType() {
        return new int[]{LicensePackageType.TYPE_BASIC_SERVER};
    }
}

最後に

今回は、実際に実行環境側と開発環境側に分けて処理の実装をしてみました。ここまででプラグインの作成処理が完了しました。次回はこれらをビルドして実行をしてみたいと思います。

このブログでは、今後も技術の「よろず相談窓口」で相談を受けた内容や、誕生したワザをご紹介していけたらな、と思っています。

これからも是非チェックいただき、宜しければフォローをお願いします。

それでは、また!

sugimon
はじめまして。 皆様に役立ちそうな技術情報をフランクに投稿させていただきます。 ※掲載内容は個人の見解であり、所属する企業を代表するものではありません。 ※各製品のサポート窓口等にお問い合わせいただいても、お答えすることができませんので、あらかじめご了承ください。
saison_information_systems
モード1(守りのIT)・モード2(攻めのIT)を兼ね備えたバイモーダル・インテグレーターとしてデータ連携プラットフォームのHULFTシリーズ, リンケージサービス, 流通ITサービス, フィナンシャルITサービスを提供します。
https://home.saison.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away