1. 程式人生 > >python調用Java代碼,完畢JBPM工作流application

python調用Java代碼,完畢JBPM工作流application

6.0 star assigned classpath 邏輯 cif .class pla 自己

1.緣由

有一龐大Python django webproject,要引入工作流引擎,像OA一樣。方便的流程控制與管理。Python或django關於工作流的開源插件,稀少,並且弱爆了,終於選用java的開源框架JBPM。為了使Pythonproject整合Java框架,嘗試過jbpm-console提供的 REST, 以失敗告終,終於選用Python 直接調用Java代碼。操作JBPM.


1.1pythonprojectREST與JBPM Workbench交互 失敗

REST URL格式

http://host:8080/jbpm-console/rest/runtime/{deploymentId}

/process/{processDefId}/start

比如

http://localhost:8080/jbpm-console/rest/runtime/com.fun:test:1.0.0/process/test.TaskData/start
不是如文檔所言以 /runtime 打頭。REST接口是jbpm-console.war子項目提供的,必須部署此項目。

失敗原因:流程復雜而且須要處理復雜數據,JBPM內部能夠建立數據模型,使用POJO傳遞或存儲復雜數據,可是REST接口無法POST某種格式的數據。使jbpm-console自己主動映射成POJO,假設自己寫java代碼實現REST 數據轉POJO,可行度再說,既然都自己寫Java代碼了。就不是必需走REST,網絡慢。不如直接Java代碼完畢JBPM邏輯。

比如:JBPM創建了一個數據模型Person,形如

public class Person{
        String name = null;
        int age = 0;
        boolean isMan= false;
}
REST接口沒法POST數據,使JBPM能映射成一個Person類的實例,默認jbpm-console會當作三個零散變量(參數)處理,不會當一個對象的三個屬性。所以默認會在數據庫裏variableinstancelog表存入三行記錄,每行代表一個變量。達不到期望結果。僅僅有一條記錄,僅僅存一個對象的序列化信息。

假設某大神知道怎樣POST數據(或Python簡單處理POST數據後再發請求),jbpm-console能自己主動映射成POJO。請留言。銘謝!


2.Python 直接調用 Java

2.1 jPype 不採用

  • 坑(能夠人為避免)
    • 當Java方法或屬性是Pythonkeyword時,無法訪問
    • 無法訪問不在包以下(default包)的Java代碼
  • 缺點

    • 適合Python26。實測27版本號啟動jvm失敗

    • Java特殊方法不支持,如java.lang.Class.forName(String classname),硬傷(由於此方法在框架/容器代碼中十分頻繁)

    • 無法實例化匿名內部類。僅能實例化靜態內部類

    • 線程同步:明白支持synchronized類同步,notify, notifyAll, wait等未提及怎樣處理

    • 明白表示有較大性能影響:can impose rather large performance bottlenecks

2.2 pyjnius 採用

  • 長處

    • 封裝出色。文檔未提及也未發現有特殊Java語法或高級機制限制(明白支持重載)

    • 大量運用於Python自己主動化測試安卓應用,與Java語言親和度更高

    • 支持Python繼承/實現java的抽象類/接口(接口中靜態成員不被支持)

    • 提供一個近乎萬能的autoclass。反射出一個Java類的代理(能在Python層面實現Java的代理類)

  • 缺點

    • windows安裝十分困難,無預編譯版本號,預計必須GCC(MinGW, vs08, vs10均失敗)

3.python<-->java se <-->jbpm <-->hibernate<-->mysql原理簡述

  • use pyjnius connect python and java

  • java se code uses CLASSPATH include jbpm jars and hibernate jars(must do before import pyjnius)

  • jbpm uses hibernate in two step

    • jbpm uses jBPM.proporties to setup and register DataSource in specified db(we specify mysql)

    • jbpm calls hibernate to operate db, but hard code to act like use H2 db, we force hibernate act like use mysql by passing custom params when create it‘s entiy manager(more details in code)

  • hibernate operates db, I have find a way to directly control it‘s behavior completely, transparently

  • Codes speak louder. More details in example.



4. 附Python和Java代碼

完整project代碼地址http://download.csdn.net/detail/secretx/7814991


3.1Python代碼

#coding=utf8

import os
javapath = "/home/luodh/workspace/jbpm-6.0.0.Final-bin/*:/home/luodh/workspace/jbpm-6.0.0.Final-bin/lib/*"
os.environ[‘CLASSPATH‘] = javapath

"""call java code directly"""
from jnius import autoclass

ProcessMain = autoclass(‘com.sample.ProcessMain‘)  #.號分隔
ProcessMain.pythonCallMeStatic()     #ok,    before ProcessMain(), can call java static method
# ProcessMain.pythonCallMeInstance() #error, before ProcessMain(), can not call java instance method

process_main = ProcessMain()
process_main.pythonCallMeInstance()
process_main.pythonCallMeStatic()

ProcessMain.pythonCallMeStatic()
ProcessMain.pythonCallMeInstance()  # after ProcessMain(), python does not distinguish static/instance method
print "-----------------------------------------"



"""use a python proxy class"""
from jnius import JavaClass, MetaJavaClass, JavaMethod, JavaField
 
class PythonProxyJavaClass(JavaClass):
    __javaclass__ = "com/sample/ProcessMain"  # /分隔.
    __metaclass__ = MetaJavaClass  # must be this
#     __javaconstructor__ = (  # 構造函數,源於反編譯
#         "()V",
#     )
# 構造函數規則尚未確定,文檔有誤
    pythonCallMeStatic = JavaMethod("()V", static=True)
    pythonCallMeInstance = JavaMethod("()V")
    s1 = JavaField("Ljava/lang/String;", static=True)
    s2 = JavaField("Ljava/lang/String;")
 
PythonProxyJavaClass.pythonCallMeStatic()
# PythonProxyJavaClass.pythonCallMeInstance() # 實例化對象之前,不可調用實例方法
 
PythonProxyJavaClass().pythonCallMeStatic() # 實例化對象之後,python不區分靜態(類)方法和非靜態(實例)方法
PythonProxyJavaClass().pythonCallMeInstance()
 
PythonProxyJavaClass.pythonCallMeStatic()
PythonProxyJavaClass.pythonCallMeInstance()
 
"""python嚴格區分靜態/非靜態的Field"""
print PythonProxyJavaClass.s1
# print PythonProxyJavaClass.s2  # error, 類不與成員變量綁定
print PythonProxyJavaClass().s1  # none,實例不與靜態變量綁定
print PythonProxyJavaClass().s2

3.2 Java代碼(jbpm相關)

package com.sample;



import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Properties;



import javax.persistence.EntityManagerFactory;

import javax.persistence.Persistence;



import org.jbpm.test.JBPMHelper;

import org.kie.api.KieBase;

import org.kie.api.KieServices;

import org.kie.api.runtime.KieContainer;

import org.kie.api.runtime.KieSession;

import org.kie.api.runtime.manager.RuntimeEngine;

import org.kie.api.runtime.manager.RuntimeEnvironmentBuilder;

import org.kie.api.runtime.manager.RuntimeManager;

import org.kie.api.runtime.manager.RuntimeManagerFactory;

import org.kie.api.task.TaskService;

import org.kie.api.task.model.TaskSummary;





public class ProcessMain {

	public static String s1 = "I‘m static field";

	public String s2 = "I‘m instance field";

	public static RuntimeManager manager = null;







	public static void main(String[] args) {

		callJBPM(false);

		System.exit(0);

	}

	

	public void pythonCallMeInstance(){

		callJBPM(true);

	}

	

	public static void pythonCallMeStatic(){

		callJBPM(true);

	}

	

	public static void callJBPM(boolean fake){

		if (fake){

			System.out.println("I‘m java, python call me to play jbpm. I don‘t know how to include jbpm jars.");

			return;

		}

		KieServices ks = KieServices.Factory.get();

		KieContainer kContainer = ks.getKieClasspathContainer();//載入kmodule.xml

		KieBase kbase = kContainer.getKieBase("kbase");// 依據暴露的名字(kbase標簽的name屬性)獲取知識庫對象



		if(manager == null) {

			manager = createRuntimeManager(kbase);// use synchronized static block

		}

		RuntimeEngine engine = manager.getRuntimeEngine(null);

		KieSession ksession = engine.getKieSession();

		TaskService taskService = engine.getTaskService();// engine一共3接口 getAuditLogService



		ksession.startProcess("com.sample.bpmn.hello");



		// let john execute Task 1

		List<TaskSummary> list = taskService.getTasksAssignedAsPotentialOwner("john", "en-UK");

		TaskSummary task = list.get(0);

		System.out.println("John is executing task " + task.getName());

		taskService.start(task.getId(), "john");

		taskService.complete(task.getId(), "john", null);



		// let mary execute Task 2

		list = taskService.getTasksAssignedAsPotentialOwner("mary", "en-UK");

		task = list.get(0);

		System.out.println("Mary is executing task " + task.getName());

		taskService.start(task.getId(), "mary");

		taskService.complete(task.getId(), "mary", null);



		manager.disposeRuntimeEngine(engine);

//		System.exit(0);

	}



	private static RuntimeManager createRuntimeManager(KieBase kbase) {

		//因為要獲取task,須要TaskService進行入庫操作,需提前啟動數據庫,準備數據源

//		JBPMHelper.startH2Server();//不用h2,則不啟動

		JBPMHelper.setupDataSource();

		Map<String, String> map = configHibernate();

		EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa", map);

		RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get()

			.newDefaultBuilder().entityManagerFactory(emf)

			.knowledgeBase(kbase);

		

		return RuntimeManagerFactory.Factory.get()

			.newSingletonRuntimeManager(builder.get(), "com.sample:example:1.0");//any_non_blank_string

	}

	

	private static Map<String, String> configHibernate(){

		Map<String, String> map = new HashMap<String, String>();

		Properties properties = new Properties();

        try {

            properties.load(JBPMHelper.class.getResourceAsStream("/jBPM.properties"));

        } catch (Throwable t) {

            // do nothing, use defaults

        }

        

		map.put("hibernate.dialect", properties.getProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"));

		map.put("hibernate.hbm2ddl.auto", properties.getProperty("hibernate.hbm2ddl.auto", "update"));

		map.put("hibernate.show_sql", properties.getProperty("hibernate.show_sql", "false"));

		map.put("hibernate.format_sql", properties.getProperty("hibernate.format_sql", "true"));

		return map;

	}



}




python調用Java代碼,完畢JBPM工作流application