22.3. カスタムクライアント API を使用した KIE Server のクライアント拡張

KIE Server は、KIE Server サービスの使用時に対話可能な、事前定義済みのクライアント API を使用します。カスタムのクライアント API で KIE Server クライアントを拡張して、ビジネスのニーズに KIE Server サービスを適合させます。

たとえば、以下の手順では、カスタムのクライアント API を KIE Server に追加して、Apache MINA (オープンソースの Java ネットワークアプリケーションフレームワーク) をもとにした、カスタムのデータトランスポートに対応します (このシナリオ向けにすでに設定済み)。

手順

  1. 空の Maven プロジェクトを作成して、以下のパッケージタイプと依存関係を、プロジェクトの pom.xml ファイルに定義します。

    サンプルプロジェクトの pom.xml ファイルの例

    <packaging>jar</packaging>
    
    <properties>
       <version.org.kie>7.52.0.Final-redhat-00007</version.org.kie>
     </properties>
    
     <dependencies>
       <dependency>
         <groupId>org.kie.server</groupId>
         <artifactId>kie-server-api</artifactId>
         <version>${version.org.kie}</version>
       </dependency>
       <dependency>
          <groupId>org.kie.server</groupId>
          <artifactId>kie-server-client</artifactId>
          <version>${version.org.kie}</version>
        </dependency>
       <dependency>
         <groupId>org.drools</groupId>
         <artifactId>drools-compiler</artifactId>
         <version>${version.org.kie}</version>
       </dependency>
     </dependencies>

  2. 以下の例のように、プロジェクトの Java クラスに、関連する ServicesClient インターフェイスを実装します。

    RulesMinaServicesClient インターフェイスの例

    public interface RulesMinaServicesClient extends RuleServicesClient {
    
    }

    インターフェイスをもとにクライアントの実装を登録する必要があるため、特定のインターフェイスが必要です。また、指定のインターフェイスには実装は 1 つしか指定できません。

    この例では、カスタムの MINA ベースのデータトランスポートが Drools 拡張を使用し、この RulesMinaServicesClient インターフェイスの例は、Drools 拡張から、既存の RuleServicesClient クライアント API を拡張します。

  3. 以下の例のように、新規の MINA トランスポートのクライアント機能を追加するのに KIE Server が使用可能な RulesMinaServicesClient インターフェイスを実装します。

    RulesMinaServicesClient インターフェイスの実装例

    public class RulesMinaServicesClientImpl implements RulesMinaServicesClient {
    
        private String host;
        private Integer port;
    
        private Marshaller marshaller;
    
        public RulesMinaServicesClientImpl(KieServicesConfiguration configuration, ClassLoader classloader) {
            String[] serverDetails = configuration.getServerUrl().split(":");
    
            this.host = serverDetails[0];
            this.port = Integer.parseInt(serverDetails[1]);
    
            this.marshaller = MarshallerFactory.getMarshaller(configuration.getExtraJaxbClasses(), MarshallingFormat.JSON, classloader);
        }
    
        public ServiceResponse<String> executeCommands(String id, String payload) {
    
            try {
                String response = sendReceive(id, payload);
                if (response.startsWith("{")) {
                    return new ServiceResponse<String>(ResponseType.SUCCESS, null, response);
                } else {
                    return new ServiceResponse<String>(ResponseType.FAILURE, response);
                }
            } catch (Exception e) {
                throw new KieServicesException("Unable to send request to KIE Server", e);
            }
        }
    
        public ServiceResponse<String> executeCommands(String id, Command<?> cmd) {
            try {
                String response = sendReceive(id, marshaller.marshall(cmd));
                if (response.startsWith("{")) {
                    return new ServiceResponse<String>(ResponseType.SUCCESS, null, response);
                } else {
                    return new ServiceResponse<String>(ResponseType.FAILURE, response);
                }
            } catch (Exception e) {
                throw new KieServicesException("Unable to send request to KIE Server", e);
            }
        }
    
        protected String sendReceive(String containerId, String content) throws Exception {
    
            // Flatten the content to be single line:
            content = content.replaceAll("\\n", "");
    
            Socket minaSocket = null;
            PrintWriter out = null;
            BufferedReader in = null;
    
            StringBuffer data = new StringBuffer();
            try {
                minaSocket = new Socket(host, port);
                out = new PrintWriter(minaSocket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(minaSocket.getInputStream()));
    
                // Prepare and send data:
                out.println(containerId + "|" + content);
                // Wait for the first line:
                data.append(in.readLine());
                // Continue as long as data is available:
                while (in.ready()) {
                    data.append(in.readLine());
                }
    
                return data.toString();
            } finally {
                out.close();
                in.close();
                minaSocket.close();
            }
        }
    }

    この実装例は、以下のデータおよび動作を指定します。

    • ソケットベースの通信を使用して簡素化します。
    • KIE Server クライアントのデフォルト設定に依存し、ServerUrl を使用して MINA サーバーのホストとポートを提供します。
    • マーシャリング形式で JSON を指定します。
    • 受信メッセージは左波括弧 { で始まる JSON オブジェクトでなければなりません。
    • 応答の最初の行を待機中に、ブロッキング API と直接、ソケット通信を使用してから、利用可能なすべての行を読み取ります。
    • ストリームモード を使用しないため、コマンドの呼び出し後に KIE Server セッションを切断します。
  4. 以下の例のように、プロジェクトの Java クラスに org.kie.server.client.helper.KieServicesClientBuilder インターフェイスを実装します。

    KieServicesClientBuilder インターフェイスの実装例

    public class MinaClientBuilderImpl implements KieServicesClientBuilder {  1
    
        public String getImplementedCapability() {  2
            return "BRM-Mina";
        }
    
        public Map<Class<?>, Object> build(KieServicesConfiguration configuration, ClassLoader classLoader) {  3
            Map<Class<?>, Object> services = new HashMap<Class<?>, Object>();
    
            services.put(RulesMinaServicesClient.class, new RulesMinaServicesClientImpl(configuration, classLoader));
    
            return services;
        }
    
    }

    1
    一般の KIE Server クライアントインフラストラクチャーにクライアント API を追加できます。
    2
    クライアントが使用する KIE Server 機能 (拡張) を定義します。
    3
    クライアントの実装のマッピングを提供します。キーはインターフェイス、値は完全な初期実装です。
  5. 新規のクライアント API を KIE Server クライアントで検出できるようにするには、Maven プロジェクトで META-INF/services/org.kie.server.client.helper.KieServicesClientBuilder ファイルを作成し、このファイルに KieServicesClientBuilder 実装クラスの完全修飾名を追加します。たとえば、このファイルには org.kie.server.ext.mina.client.MinaClientBuilderImpl の 1 行が含まれます。
  6. プロジェクトを構築して、作成された JAR ファイルをプロジェクトの ~/kie-server.war/WEB-INF/lib ディレクトリーにコピーします。たとえば、Red Hat JBoss EAP ではこのディレクトリーへのパスは EAP_HOME/standalone/deployments/kie-server.war/WEB-INF/lib です。
  7. KIE Server を起動して、実行中の KIE Server に構築したプロジェクトをデプロイします。プロジェクトは、Business Central インターフェイスまたは KIE Server REST API (http://SERVER:PORT/kie-server/services/rest/server/containers/{containerId} への PUT 要求) を使用してデプロイできます。

    実行中の KIE Server にプロジェクトをデプロイしたあとに、新規の KIE Server クライアントと対話を開始できます。標準の KIE Server クライアントと同じ方法で、クライアント設定とクライアントインスタンスを作成して、タイプ別にサービスクライアントを取得し、クライアントメソッドを呼び出して、新しいクライアントを使用します。

    たとえば、RulesMinaServiceClient クライアントインスタンスを作成して、MINA トランスポートを使用して KIE Server で操作を呼び出すことができます。

    RulesMinaServiceClient クライアント作成の実装例

    protected RulesMinaServicesClient buildClient() {
        KieServicesConfiguration configuration = KieServicesFactory.newRestConfiguration("localhost:9123", null, null);
        List<String> capabilities = new ArrayList<String>();
        // Explicitly add capabilities (the MINA client does not respond to `get-server-info` requests):
        capabilities.add("BRM-Mina");
    
        configuration.setCapabilities(capabilities);
        configuration.setMarshallingFormat(MarshallingFormat.JSON);
    
        configuration.addJaxbClasses(extraClasses);
    
        KieServicesClient kieServicesClient =  KieServicesFactory.newKieServicesClient(configuration);
    
        RulesMinaServicesClient rulesClient = kieServicesClient.getServicesClient(RulesMinaServicesClient.class);
    
        return rulesClient;
    }

    MINA トランスポートを使用して KIE Server 上で操作を呼び出す設定例

    RulesMinaServicesClient rulesClient = buildClient();
    
    List<Command<?>> commands = new ArrayList<Command<?>>();
    BatchExecutionCommand executionCommand = commandsFactory.newBatchExecution(commands, "defaultKieSession");
    
    Person person = new Person();
    person.setName("mary");
    commands.add(commandsFactory.newInsert(person, "person"));
    commands.add(commandsFactory.newFireAllRules("fired"));
    
    ServiceResponse<String> response = rulesClient.executeCommands(containerId, executionCommand);
    Assert.assertNotNull(response);
    
    Assert.assertEquals(ResponseType.SUCCESS, response.getType());
    
    String data = response.getResult();
    
    Marshaller marshaller = MarshallerFactory.getMarshaller(extraClasses, MarshallingFormat.JSON, this.getClass().getClassLoader());
    
    ExecutionResultImpl results = marshaller.unmarshall(data, ExecutionResultImpl.class);
    Assert.assertNotNull(results);
    
    Object personResult = results.getValue("person");
    Assert.assertTrue(personResult instanceof Person);
    
    Assert.assertEquals("mary", ((Person) personResult).getName());
    Assert.assertEquals("JBoss Community", ((Person) personResult).getAddress());
    Assert.assertEquals(true, ((Person) personResult).isRegistered());