21.3. 使用自定义客户端 API 扩展 KIE 服务器客户端

KIE 服务器使用预定义的客户端 API,您可以与之交互来使用 KIE 服务器服务。您可以使用自定义客户端 API 扩展 KIE 服务器客户端,以满足您的业务需求。

例如,这个流程将自定义客户端 API 添加到 KIE Server 以容纳自定义数据传输(之前为这种情况配置),它基于 Apache MINA,它是一个开源 Java 网络应用程序框架。

流程

  1. 创建一个空的 Maven 项目,并在项目的 pom.xml 文件中定义以下打包类型和依赖项:

    示例项目中的 pom.xml 文件示例

    <packaging>jar</packaging>
    
    <properties>
       <version.org.kie>7.59.0.Final-redhat-00006</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 {
    
    }

    需要一个特定的接口,因为您必须根据接口注册客户端实现,且对于给定接口只能有一个实施。

    在本例中,自定义基于 MINA 的数据传输使用 Drools 扩展,因此本例 RulesMinaServicesClient 接口会扩展 Drools 扩展的现有 RuleServicesClient 客户端 API。

  3. 实施 KIE 服务器可以用来为新的 MINA 传输提供额外的客户端功能的 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 指定为 marshalling 格式
    • 需要收到的消息是以打开的 bracket {开头的 JSON 对象。
    • 在等待响应的第一行时,使用与阻塞 API 的直接套接字通信,然后读取所有可用的行
    • 不使用流模式,因此在调用命令后断开 KIE 服务器会话
  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 服务器客户端基础架构提供额外的客户端 API
    2
    定义客户端使用的 KIE 服务器功能(分机)
    3
    提供客户端实施的映射,其中键是接口,值是完全初始化的实施
  5. 要使新的客户端 API 发现 KIE 服务器客户端,请在 Maven 项目中创建一个 META-INF/services/org.kie.server.client.helper.KieServicesClientBuilder 文件,并在文件中添加 KieServicesClientBuilder 实施类的完全限定名称。在本例中,文件包含一行 org.kie.server.ext.mina.client.MinaClientBuilderImpl
  6. 构建项目并将生成的 JAR 文件复制到项目的 ~/kie-server.war/WEB-INF/lib 目录中。例如,在红帽 JBoss EAP 上,此目录的路径为 EAP_HOME/standalone/deployments/kie-server.war/WEB-INF/lib
  7. 启动 KIE 服务器,并将构建的项目部署到正在运行的 KIE 服务器。您可以使用 Business Central 接口或 KIE 服务器 REST API 部署项目( PUT 请求 http://SERVER:PORT/kie-server/services/rest/server/containers/{containerId})。

    在运行的 KIE Server 上部署项目后,您可以开始与新的 KIE Server 客户端交互。您可以通过创建客户端配置和客户端实例、按类型检索服务客户端并调用客户端方法,按照标准 KIE 服务器客户端使用新客户端。

    在本例中,您可以创建一个 RulesMinaServiceClient 客户端实例,并通过 MINA 传输在 KIE 服务器上调用操作:

    创建 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 传输调用操作的示例配置

    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());