Bir süredir blog yazılarına ara vermiştim. Bu süre zarfında BusinessProcessManagement kabiliyetinin mevcut altyapımıza
kazandırılması için çalışmalar yaptık. BPM için tercihimiz JBPM’den yana oldu. Bu ve devamındaki yazılarımda JBPM ile
ilgili tecrübelerimizi, JBPM’in JPA, SpringWebFlow, AcegiSecurity gibi diğer frameworklerin de yer aldığı
altyapımıza entegre edilmesi ile ilgili geliştirdiğimiz çözümleri, JBPM’in gömülü biçimde kurumsal web uygulamalarında
kullanışını, yönetim arayüzünün mevcut uygulamalara nasıl entegre edilebileceğini anlatacağım.
İlk olarak JBPM JPA entegrasyonu ile konumuza başlayalım. JBPM persistence işlemleri için doğrudan Hibernate’i
kullanmaktadır. Biz ise geliştirdiğimiz uygulamalarda JPA’yı, JPA implementasyonu olarak da Hibernate’i
kullanmaktayız. Bu durumda uygulamalarımızın gerçekleştirdiği persistence işlemleri ile JPBM’in gerçekleştirdiği
persistence işlemlerinin farklı iki transaction context içerisinde çalışması durumu ortaya çıkmaktadır. Bu da doğal
olarak veriler üzerinde tutarsızlığa yol açacaktır.
JPA kullanan bir uygulama ile JBPM‘in persistence işlemlerini aynı transaction context içinde gerçekleştirmelerini
sağlamak için ilk akla gelen çözüm JTA’nın kullanılmasıdır. Aslında uygulamalarımızın ve JBPM’in aynı veritabanını
kullanmalarına rağmen JTA kullanma zorunluluğu tahmin ediyorum sizin de kulağınıza garip gelmiştir. Şu ana kadar lokal
transaction’larla uygulama sunucularının herhangi bir servisine ihtiyaç duymadan standalone test edilebilen ve üretim
hattında çalıştırılabilen uygulamalarımızın JTA kullanmaya başlamaları ile sahip olduğumuz esnekliği kaybetmek doğrusu
benimde hiç içime sinmedi. Oysa her iki taraf da ortak bir veri tabanını, hatta aynı ORM implementasyonunu paylaşıyordu.
Aslında JPA implementasyonu olarak Hibernate’i kullanıyorsanız uygulama tarafında JPA API’si ile muhatap olsanız da
arka tarafta persistence sürecini yine core Hibernate implementasyonu yönetmektedir. Yani uygulamanız bir
EntityManagerFactory oluşturduğunda aslında Hibernate native SessionFactory nesnesini oluşturup kullanmaktadır.
Aynı şekilde EntityManagerFactory üzerinden yeni bir EntityManager oluşturduğumuz vakit yine bu EntityManager
nesnesi native bir Hibernate Session’ı üzerinden persistence işlemlerini gerçekleştirmektedir. Eğer JBPM’in
persistence işlemleri için JPA’nın yönettiği SessionFactory ve Session nesnelerini kullanması mümkün olursa
uygulamamız içerisinde gerçekleşen ve JBPM’in gerçekleştirdiği persistence işlemlerin JTA’ya gerek kalmadan aynı
transaction context içerisinde yer alması sağlanabilir.
Bunun için yapılması gereken iki temel işlem söz konusudur. Birincisi JBPM’in ihtiyaç duyduğu SessionFactory’nin
JPA EntityManagerFactory tarafından expose edilmesidir. Bu noktada ayrıca JBPM’in hibernate.cfg.xml içerisindeki
mapping tanımlarının JPA persistence.xml içerisinde tanımlanması gerekir. JBPM’in mapping-file tanımlarını belirli
bir sırada sağlamazsanız (jar-file tanımı kullanıldığı vakit bu durum ortaya çıkmaktadır)
EntityManagerFactory/SessionFactory ayağa kaldırılırken problem oluşmaktadır.
EntityManagerFactory’nin native Hibernate SessionFactory nesnesini expose etmesi için basit bir Spring FactoryBean
yazmamız yeterli oldu.
public class EntityManagerFactoryToSessionFactoryBean extends AbstractFactoryBean {
private EntityManagerFactory entityManagerFactory;
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
@Required
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
protected Object createInstance() throws Exception {
return HibernateEntityManagerFactory)getEntityManagerFactory(?.getSessionFactory();
}
public Class getObjectType() {
return SessionFactory.class;
}
}
<bean id="sessionFactory" class="jbpm.jpa.integration.EntityManagerFactoryToSessionFactoryBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Uygulamalarımızda JBPM konfigürasyonunu ayağa kaldırmak ve JBPM işlemlerini gerçekleştirmek için Spring Modules
projesinden yararlandık. Spring Modules JBPM işlemleri için Spring’in klasikleşmiş XXXTemplate callback yapısına
uygun JbpmTemplate sınıfını sunmaktadır. JbpmTemplate’in kullanılabilmesi için yapılması gereken bir
JbpmConfiguration nesnesinin oluşturulmasıdır. Spring Modules’ün LocalJbpmConfigurationFactoryBean sınıfı bir
JbpmConfiguration nesnesi oluşturmaktadır. Bu FactoryBean, JbpmConfiguration nesnesi üzerinden JBPM persistence
servisinin (DbPersistenceService) kullandığı SessionFactory nesnesini EntityManagerFactory’den expose ettiğimiz
SessionFactory nesnesi olarak set etmektedir.
<bean id="jbpmConfiguration" class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="configuration" value="classpath:jbpm.cfg.xml"/>
<property name="createSchema" value="false"/>
</bean>
<bean id="jbpmTemplate" class="org.springmodules.workflow.jbpm31.JbpmTemplate">
<constructor-arg index="0" ref="jbpmConfiguration"/>
<property name="hibernateTemplate" ref="hibernateTemplate"/>
</bean>
Gelelim ikinci adımımıza. Bu adımda yapmamız gereken uygulamamızın persistence işlemler için kullandığı
EntityManager’ın native Hibernate Session nesnesinin JBPM’in persistence işlemler için kullanmasını sağlamalıyız.
Hibernate 3.1’den itibaren “contextual session” kabiliyetini sunmaktadır. Bu sayede SessionFactory’ye
Hibernate Session’ı nereden ve nasıl temin edeceğini pluggable biçimde belirtebilmekteyiz. Hibernate’in konfigürasyon
parametrelerinden “hibernate.current_session_context_class “ bunun için tahsis edilmiştir. Bu prametreye Hibernate’in
CurrentSessionContext interface’ini implement eden bir sınıfın full paket adını değer olarak girmemiz yeterlidir.
CurrentSessionContext implementasyonu bizim için o anda mevcut olan thread bound EntityManager’ın native
Hibernate Session’ını döndürecektir.
public class EntityManagerAwareCurrentSessionContext implements CurrentSessionContext {
private EntityManagerFactory emf;
private ThreadLocalSessionContext threadLocalSessionContext;
public Session currentSession() throws HibernateException {
EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(getEmf());
if(em != null){
return (Session)((HibernateEntityManager) em).getSession();
} else {
return threadLocalSessionContext.currentSession();
}
}
public EntityManagerAwareCurrentSessionContext(SessionFactoryImplementor factory) {
threadLocalSessionContext = new ThreadLocalSessionContext(factory);
}
private synchronized EntityManagerFactory getEmf() {
if(emf == null) {
emf = (EntityManagerFactory)SpringUtils.getBean("entityManagerFactory");
}
return emf;
}
}
EntityManagerAwareCurrentSessionContext eğer thread bound bir EntityManager varsa bunun kullandığı Session’ı
yoksa fallback olarak ThreadLocalSessionContext’i kullanarak yeni bir Session’ı döndürmektedir. Yukarıdaki
CurrentSessionContext implementasyonunun aktif olması için persistence.xml dosyasının içerisinde
<properties>
...
<property name="hibernate.current_session_context_class" value="jbpm.jpa.integration.EntityManagerAwareCurrentSessionContext" />
</properties>
şeklinde bir tanım yapmamız yeterli olacaktır.
JBPM’in DbPersistenceService sınıfına Hibernate SessionFactory’nin getCurrentSession() metodu ile o an mevcut
Session’ı kullanması (isCurrentSessionEnabled) söylenebilir. Ancak bu değer default false olarak set edilidir. Bu
property’de değişiklik yapmadan da DbPersistenceService’in mevcut Session’ı kullanması sağlanabilir. Nasıl mı?
Okumaya devam…
Spring Modüles’ün JbpmTemplate sınıfı, bütün JBPM işlemlerini HibernateTemplate içinde yürütmektedir.
JbpmTemplate herhangi bir operasyon gerçekleşmeden evvel HibernateTemplate’dan gelen Session’ı JbpmContext’e set
etmektedir. Böylece DbPersistenceService’in yeni Session yaratmak yerine JbpmContext’deki mevcut Session’ı
kullanması sağlanır. Bu arada HibernateTemplate’a da her seferinde yeni bir Session yaratmak yerine halihazırdaki
SessionFactory’nin getCurrentSession() metodunu çağırarak mevcut Hibernate Session’ı kullanması (allowCreate=false)
söylenmelidir.
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
<property name="allowCreate" value="false"/>
</bean>
Artık JBPM’in halihazırda thread bound bir EntityManager’dan expose edilmiş native Hibernate Session’ı kullanması
sağlanmış olur. Bir sonraki yazımda JBPM ve Spring WebFlow’un entegrasyonu üzerinde duracağım.