はじめに
こんにちは。よろず相談担当 すぎもんです。
今回は、HULFT IoT EdgeStreamingのSDKを使用したコンポーネント(アダプタ)の開発をやってみようと思います。全3部作で完結するよう記載してます。
今回やること
今回は、第2弾としてHULFT IoT EdgeStreaming Plugin SDKを使用してプラグインを開発してみます。他にセットアップ編、実行編の記事もあるので是非参考にしてみてください。
[HULFT IoT EdgeStreamingでプラグインを作成してみた【セットアップ編】(1/3)]
(https://qiita.com/sugimon/items/cf7503479c6e4c46c3b3)
HULFT IoT EdgeStreamingでプラグインを作成してみた【開発編】(2/3)
HULFT IoT EdgeStreamingでプラグインを作成してみた【実行編】(3/3)
プラグインの作成概要
EdgeStreamingのプラグイン作成の構成は、以下のようになります。
実行環境構成
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文)を出力します。
プラグインの作成
今回は、以下のような形でプラグイン作成をしてみました。
実行環境側(Golang)の実装
Studioから指定されたStreaming処理の実装をします。
環境準備
- モジュールディレクトリの作成
・$SDK_HOME/dev/go/src/github.sis.saison.co.jp/sherpa/es-agent/sample
※ここに、ソースファイルを格納しました。(source.go, sink.go, udsf.go) - pluginディレクトリの作成
・$SDK_HOME/dev/go/src/github.sis.saison.co.jp/sherpa/es-agent/sample/plugin
- 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(入力処理)
一定時間間隔で疑似乱数を生成する処理を作成します。
主な流れは、以下のとおりです。
- 整数型のパラメータ”interval”を受け取る
- intervalの時間間隔で疑似乱数を生成する
- JSONスキーマのデータを持つTupleを生成する
{
"type": "object",
"required": ["payload"],
"properties": {
"payload": {
"type": "object",
"required": ["value"],
"properties": {
"value": {
"type": "number"
}
}
}
}
}
出力されるTupleのJSONデータは、以下のような形になります。
{"payload": {"value": 3.5423242}}
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(出力処理)
有効小数点桁数で切り捨ててログに出力する処理を作成します。
主な流れは、以下のとおりです。
- 整数型のパラメータ「decimal」を受け取る
- 「decimal」の有効小数点桁数で、受け取った値を標準出力に出力
- JSONデータのスキーマのデータを持つTupleを受け取る`
{
"type": "object",
"required": ["payload"],
"properties": {
"payload": {
"type": "object",
"required": ["value", "formula"],
"properties": {
"value": {
"type": "number"
}
"formula": {
"type": "string"
}
}
}
}
}
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は、以下のように作成してみました。
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として使用するため)を実装します。
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は、以下のように作成してみました。
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は、以下のように作成してみました。
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は、以下のように作成してみました。
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は、以下のように作成してみました。
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は、以下のように作成してみました。
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};
}
}
最後に
今回は、実際に実行環境側と開発環境側に分けて処理の実装をしてみました。ここまででプラグインの作成処理が完了しました。次回はこれらをビルドして実行をしてみたいと思います。
このブログでは、今後も技術の「よろず相談窓口」で相談を受けた内容や、誕生したワザをご紹介していけたらな、と思っています。
これからも是非チェックいただき、宜しければフォローをお願いします。
それでは、また!