使用 JNI 桥集成 IBM Business Process Manager V7.5 和 C API

本文提供了基于 Windows® 平台将 IBM® Business Process Manager V7.5 与一个 JNI 实现集成在一起的步骤,实现此集成的方法是:使用 JNI 创建一个本地 DLL 库,在 IBM BPM 中配置该库以生成 Java Integration Service,然后集成 Java™ 组件。本文还讨论了一个简单的操作场景,通过传递简单的、基于数组的数据来调用本地方法。

Sharad Chandra, 高级 WebSphere 顾问, IBM

Sharad Chandra 是 IBM 印度软件实验室 IBM PartnerWorld for WebSphere 中的一名高级顾问。他在使用 WebSphere Process Server、WebSphere Lombardi 和 SOA 产品套件开发解决方案方面拥有丰富的经验。在当前的职位上,Sharad 从事过多项涉及可伸缩且高度可用的 WebSphere 基础架构、解决方案开发、迁移、性能调优和故障排除的重要活动。



Sheng Ping Zhang, 软件工程师, IBM

Sheng Ping Zhang 是 IBM 中国软件开发实验室 WebSphere Lombardi Edition and Business Process Manager Level 2 Support 团队的一名软件工程师。她在为亚太和全球客户提供支持方面有很丰富的经验。



2012 年 9 月 19 日

简介

IBM Business Process Manager V7.5 是一个全面的、可使用的业务流程管理平台,提供业务流程的管理和可视性。它将来自 WebSphere® Process Server、WebSphere Lombardi Edition 和 IBM Integration Designer 的关键功能集成到一个统一的用户环境(包括统一的库、创建工具和运行时环境)中,以便设计、执行、监视和优化业务流程。IBM Business Process Manager V7.5 特别适用于帮助流程所有者和企业用户直接参与业务流程的改进。

IBM Business Process Manager (BPM) 提供了集成服务,通过集成外部服务来完成任务。集成服务包括 Web Services Integration 和 Java Integration 组件。然而,在许多场合,外部系统是基于 C 语言实现的,出于架构、性能或其他方面的原因,客户不希望重新构建他们现有的基于 C 的实现。这在销售基于 Java 的产品时成为瓶颈,因为集成是一个非常复杂的过程。

要集成基于 Java 的应用程序和基于 C 的API,需要在这两种编程语言 API 之间建立某种桥梁。Java Native Interface (JNI) 是一种本地编程接口,是 Java Software Development Kit (SDK) 的一部分。JNI 使 Java 代码能够使用其他语言编写的代码和代码库,如 C 和 C++。您可以创建共享的基于 JNI 的库实现,以便在两种不同的语言之间交换信息。这些库有两种格式可用:.dll(动态链接库),面向基于 Windows® 的程序,以及 .so(共享对象),面向基于 Unix 的平台。

本文将向您介绍在 Windows 平台上集成 IBM BPM 与 JNI 实现的一些步骤,包括使用 JNI 创建一个本地 DLL,在 Business Process Manager 中将库配置为可生成 Java Integration Services,并集成 Java 组件。

我们将讨论一个简单的操作场景,展示如何通过传递简单数据和基于数组的数据来调用本地方法。

先决条件

要执行本文中的步骤,您需要安装 IBM WebSphere Business Process Manager V7.5、IBM Integration Designer V7.5 和 IBM Process Designer V7.5。


下载文件

本文提供的 zip 文件 包含您可以下载并参考的工件,可以帮助您理解并执行本文的场景。


创建一个本地共享库

本节将介绍创建本地共享库(比如面向 Windows 环境 DLL)的步骤。您将执行以下操作:

创建一个独立的 Java 项目

要在 IBM Integration Designer(后面简称为 Integration Designer)中创建一个独立的 Java 项目,请进入 File > New > Java Project。为项目命名并按下 Next,如图 1 所示。将 “Default output folder” 设置为类目录,后者需要通过 Integration Designer 明确地进行创建。

图 1. 在 Integration Designer 中创建一个 Java 项目
在 Integration Designer 中创建一个 Java 项目

创建一个 Java 类

在继续创建 DLL 之前,要创建一个独立的 Java 类,其中包含需要链接到本地方法的方法,如清单 1 所示。

所示的方法是 NativeInvoke 类的一部分,将返回以下内容:

  • 两个整数值的和
  • 两个双精度值的和
  • 双精度数组的和
  • 一个数组,其中的值为双精度数组的两倍
清单 1. NativeInvoke 类
package com.ibm.test;

public class NativeInvoke {
	public static native int sum(int a, int b);
	public static native double sum(double a, double b);
	public static native double sum(double[] a);
	public static native double[] twice(double[] a);
}

使用基于 Eclipse 的工具(在本例中即为 Integration Designer)编译上面的类。这将生成 .class 文件,它是创建头文件的基本内容,后面的实现会用到头文件。完成类编译后,进入 Integration Designer 的输出文件夹(参见图 2),该文件夹就是生成类的位置。在命令提示符下将路径设置为软件包根目录中的 {JAVA_HOME}\bin 目录,如下所示:

set PATH=.;%PATH%;C:\BPM751\java\bin
图 2. Integration Designer 的输出文件夹
Integration Designer 的输出文件夹

创建头文件

要从编译的 Java 类创建一个头文件,请在 {APPSERVER_ROOT}/Java/bin 目录中对 Java 类运行 “javah” 命令,以便生成各自的 C 头文件(参见图 3)。该头文件构成了本地共享库的基本内容。

{APPSERVER_ROOT}\java\bin>javah -o header_file_name.h com.ibm.test.NativeInvoke
图 3. 创建一个头文件
创建一个头文件

创建一个 DLL 项目

通过在任何可用的 C/C++ 编辑器中创建一个 DLL 项目,您可以创建一个本地共享库。该项目有两个重要文件,扩展名分别为 .h (dll.h) 和 .c (dll.c),此外,它还包含由编辑器生成的其他相关文件。您需要将头文件代码合并到这个项目中来构建 DLL。图 4 显示了 C 编辑器。

图 4. C 编辑器中的 DLL 项目
C 编辑器中的 DLL 项目

合并和实现头文件代码

需要将 创建头文件 一节中生成的头文件的内容粘贴到 dll.h,首先从 NativeInvoke 类的 ifndef Include 指令开始(参见清单 2)。记得在合并后的代码的顶部添加 jni.h,然后,在编辑器中,将这个头文件在 WebSphere Application Server 中的路径设置为 {APPSERVER_ROOT}\Java\include,这样才能成功地构建代码。

清单 2. DLL 头文件的内容
#include <jni.h>
#ifndef _DLL_H_
#define _DLL_H_

#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void HelloWorld (void);
#endif /* _DLL_H_ */
			

/* Header for class com_ibm_test_NativeInvoke */
   
#ifndef _Included_com_ibm_test_NativeInvoke
#define _Included_com_ibm_test_NativeInvoke
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ibm_test_NativeInvoke_sum__II
 (JNIEnv *, jclass, jint, jint);

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: (DD)D
*/
JNIEXPORT jdouble JNICALL Java_com_ibm_test_NativeInvoke_sum__DD
 (JNIEnv *, jclass, jdouble, jdouble);

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: ([D)D
*/
JNIEXPORT jdouble JNICALL Java_com_ibm_test_NativeInvoke_sum___3D
 (JNIEnv *, jclass, jdoubleArray);

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    twice
 * Signature: ([D)[D
*/
JNIEXPORT jdoubleArray JNICALL Java_com_ibm_test_NativeInvoke_twice
 (JNIEnv *, jclass, jdoubleArray);

#ifdef __cplusplus
}
#endif
#endif

DLL 头文件中定义的代码需要在 DLL C 实现类中实现,这个类是在编辑器中与头文件一起生成的。参见清单 3。

清单 3. DLL C 实现类的内容
/* Replace "dll.h" with the name of your header */
#include "dll.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

DLLIMPORT void HelloWorld ()
{
  MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
}

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ibm_test_NativeInvoke_sum__II
  (JNIEnv *env, jobject jobj, jint j1, jint j2){
  return j1 + j2;        
  }

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: (DD)D
*/
JNIEXPORT jdouble JNICALL Java_com_ibm_test_NativeInvoke_sum__DD
  (JNIEnv *env, jobject jobj, jdouble j1, jdouble j2){
  return j1 + j2;
  }

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    sum
 * Signature: ([D)D
 */
JNIEXPORT jdouble JNICALL Java_com_ibm_test_NativeInvoke_sum___3D
  (JNIEnv *env, jobject jobj, jdoubleArray jarray){     
  jdouble sum = 0.0;
  int i;
  jsize length = (*env)->GetArrayLength(env, jarray);
  jdouble *body = (*env)->GetDoubleArrayElements(env,jarray, 0);
  for (i=0;i < length; i+)
  sum = sum + body[i];
  (*env)->ReleaseDoubleArrayElements(env,jarray, body, 0);
  return sum;                 
}

/*
 * Class:     com_ibm_test_NativeInvoke
 * Method:    twice
 * Signature: ([D)[D
*/
JNIEXPORT jdoubleArray JNICALL Java_com_ibm_test_NativeInvoke_twice
  (JNIEnv *env, jobject jobj, jdoubleArray jarray){
  jdouble sum = 0.0;
  jdoubleArray result;      
  jint fill[256];  
  int i;
  jsize length= (*env)->GetArrayLength(env, jarray);
  result = (*env)->NewDoubleArray(env, len); 
  jdouble *body = (*env)->GetDoubleArrayElements(env,jarray, 0);
  for (i=0;i < length; i+)
  (*env)->SetDoubleArrayRegion(env, result, 0, len,body);
  return result;          
}

BOOL APIENTRY DllMain (HINSTANCE hInst  
                        /* Library instance handle. */ ,
					   DWORD reason
                        /* Reason this function is being called. */ ,
                       LPVOID reserved
                        /* Not used. */ )
{
  switch (reason)
  {
  case DLL_PROCESS_ATTACH:
    break;

  case DLL_PROCESS_DETACH:
    break;

  case DLL_THREAD_ATTACH:
    break;

  case DLL_THREAD_DETACH:
    break;
  }

   /* Returns TRUE on success, FALSE on failure */
      return TRUE;
}

构建 DLL

在文件系统的项目文件夹中构建代码,生成 .dll 实现(参见图 5)。这些 DLL 需要从编辑器路径复制到 BPM 安装位置,并进行相应配置。将 DLL 复制到 {WAS_ROOT}/jnilib 中,其中的 “jnilib” 是显式创建的。

图 5. 生成的 DLL 文件
生成的 DLL 文件

使用 IBM BPM 配置本地库

生成 DLL 后,您需要在 IBM BPM 运行时中配置它,使之能够从应用程序中调用。这些配置需要在 IBM BPM 的基础 WebSphere Application Server 管理控制台中完成。

设置 DLL 目录路径

在服务器的 JVM 属性中,按照如下所示设置定制 JVM 属性,加载该 DLL:server > serverTypes > WebSphere Application Server > server1 > Java and Process Management > Process Definition > Java Virtual Machine > Custom Properties。图 6 显示了如何将本地库路径配置为基础 WebSphere Application Server 的 JVM 定制属性。

图 6. 定制 JVM 属性设置
定制 JVM 属性设置

设置完路径后,您需要重启服务器。

编写包装器

在 Integration Designer 中以 InvokeNativeWrapper 为名称创建一个独立的 Java 项目和一个类(参见清单 4)。在该项目中复制包含方法声明的 Java 接口。定义一些方法,用这些方法从 BPM 流程接收数据,并将它们传递给本地方法调用。注意,IBM BPM 会将所有数字值都作为包装器进行传递。

清单 4. Wrapper 类
package com.ibm.test;

public class InvokeNativeWrapper {
	static{
			System.load("WASIntegrationSharedLib");
		  }
				
		  public static Integer doSimpleSum(Integer num1,Integer num2){
		   int result=0;
		   if(num1!=null && num2!=null){
			 int val1=num1.intValue();
			 int val2=num2.intValue();
			 result=NativeInvoke.sum(val1, val2);
		   }
		   return new Integer(result);
		  }
				
		  public static Double doDoubleSum(Double num1,Double num2){
		    double result=0;
		    if(num1!=null && num2!=null){
		    double val1=num1.intValue();
		    double val2=num2.intValue();
		    result=NativeInvoke.sum(val1, val2);
		    }
		    return new Double(result);
		  }
				
		  public static Double doDoubleArraySum(Double[] arr){
			double[] darr=new double[arr.length];
			for(int i=0;i<darr.length;i++){
			  darr[i]=arr[i].doubleValue();
			}
			double result=NativeInvoke.sum(darr);
			return new Double(result);
		   }
				
		   public static Double[] doubleTheArray(Double[] arr){
			 double[] darr=new double[arr.length];
			 for(int i=0;i<darr.length;i++){
			   darr[i]=arr[i].doubleValue();
			 }
			 double[] result=NativeInvoke.twice(darr);
			 Double[] finalResult=new Double[result.length];
			 for(int k=0;k<result.length;k++){
			   finalResult[k]=new Double(result[k]);
			 }
		     return finalResult;
			}
		   }

实现了该类后,可以将 Java 项目导出为一个 JAR 文件,并将该文件导入到 Process Designer 中,从而将它作为一个 Java Integration Service 用于业务流程(参见图 7)。

图 7. 将 InvokeNativeWrapper 导出为 JAR
将 InvokeNativeWrapper 导出为 JAR

集成 IBM BPM 和 Java Service

确保 IBM BPM 处于运行状态。启动 Process Designer 并以 “tw_admin” 身份登录,密码为 “tw_admin”。

  1. 选择 Files (the + symbol) > Server File,如图 8 所示,然后查找前面步骤中导出的 JAR 文件。按下 Finish 按钮。
    图 8. 添加 Java Service JAR
    添加 Java Service JAR

    注意:成功完成导入后,您会在单击 Files 之后看到 JAR 文件,如图 9 所示。

    图 9. 添加 Java Service JAR
    添加 Java Service JAR
  2. 定义 Integration Service 和本地 JAR 中与该活动相关的方法,如图 10 所示。
    图 10. 定义 Integration Service
    定义 Integration Service
  3. 将 Java Integration Service 与您希望调用的方法关联起来,如图 11 所示。
    图 11. 关联 Java Integration Service 和 JAR 文件
    关联 Java Integration Service 和 JAR 文件
  4. 创建 Business Process Definition (BPD) 和人类服务 (human service),并将 Coach 与 Java Integration Service 进行关联,以便调用本地方法,如图 12、图 13 和图 14 所示。
    图 12. 定义 Business Process Definition
    定义 Business Process Definition
    图 13. 人类服务包含一个简单的数据服务
    人类服务包含一个简单的数据服务
    图 14. 定义 Business Process Definition
    定义 Business Process Definition

测试解决方案

本节将展示两个使用了加法和数组的测试用例。

简单的数据测试

  1. 在 Process Designer 中运行人类服务,其中包括简单的数据服务。在每个字段中输入两个数(参见图 15)并单击 OK 按钮。
    图 15. 在文本字段中输入数据
    在文本字段中输入数据
  2. Once the service ran successfully, you see the "Final Result" in the output text box as shown in 图 16.
    图 16. 在文本字段中显示结果
    在文本字段中显示结果

数组数据测试

  1. 调试人类服务,它包含数组数据相加服务,然后您将对数组进行初始化(参见图 17)。
    图 17. 数组初始化
    数组初始化
  2. 单击 Step,您将看到如图 18 所示的相加结果。
    图 18. 数组数据的相加结果
    数组数据的相加结果

结束语

本文讨论了如何集成 IBM Business Process Manager 和 JNI 实现。本文还介绍了如何使用 JNI 创建一个本地 DLL 库,如何在 Business Process Manager 中配置该库,以生成 Java Integration Services,以及如何与 Java 组件集成。最后,本文在 Process Designer 中对解决方案进行了测试。


下载

描述名字大小
样例代码文件code_sample.zip936KB
样例代码文件JNITestProject.zip16KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=835902
ArticleTitle=使用 JNI 桥集成 IBM Business Process Manager V7.5 和 C API
publish-date=09192012