2012-12-03 42 views
3

我有一個核心庫,它具有一個我想在Fuse ESB(Apache ServiceMix和Karaf)中公開爲OSGI服務的接口。目標是允許其他軟件包使用它。該服務使用JPA(OpenJPA)和Spring。以下是該接口:OSGI服務未注入JPA PersistenceContext

public interface PatientService { 
    public Patient find(Integer id); 
} 

和類:

@Repository 
public class PatientServiceJpaImpl implements PatientService { 
    @PersistenceContext(unitName="psu") 
    private EntityManager entityManager; 

    @Override 
    public Patient find(Integer id) { 
     return entityManager.find(Patient.class, id); 
    } 
} 

下面是一個簡化的META-INF/spring/beans.xml

<beans xmlns="http://www.springframework.org/schema/beans" ...> 
    <context:annotation-config /> 
    <context:component-scan base-package="..." /> 

    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="emf" /> 
    </bean> 

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="persistenceUnitName" value="psu" /> 
     <property name="jpaVendorAdapter" ref="jpaAdapter" /> 
     <property name="dataSource" ref="dataSource" /> 
    </bean> 

    <bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter" /> 

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
     <property name="driverClassName" value="${database.driver}" /> 
     <property name="url" value="${database.url}" /> 
     <property name="username" value="${database.username}" /> 
     <property name="password" value="${database.password}" /> 
    </bean> 

    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> 
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> 
</beans> 

而且META-INF/persistence.xml(也縮寫):

<persistence xmlns="http://java.sun.com/xml/ns/persistence" ...> 
    <persistence-unit name="psu" transaction-type="RESOURCE_LOCAL"> 
     <class>...</class> 
</persistence> 

在非OSGi環境中,一切都很好。它採用了菲利克斯行家束-插件,所以創建OSGi服務,我增加了以下OSGI-INF/blueprint/osgi-context.xml

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 
    http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"> 

    <bean id="patientService" class="com.test.service.PatientServiceJpaImpl" /> 
    <service id="osgiPatientService" ref="patientService" interface="com.test.service.PatientService" /> 

</blueprint> 

束成功部署和服務被註冊。問題是,當PatientService被另一個包引用時,實體管理器沒有被注入,因此在find(Integer id)方法中拋出NullPointerException。以下是消費者的META-INF/spring/consumer-context.xml一個片段:

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:jaxws="http://cxf.apache.org/jaxws" 
xmlns:osgi="http://www.springframework.org/schema/osgi" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd 
    http://cxf.apache.org/jaxws 
    http://cxf.apache.org/schemas/jaxws.xsd 
    http://www.springframework.org/schema/osgi 
    http://www.springframework.org/schema/osgi/spring-osgi.xsd"> 

    <bean id="patientServiceImpl" class="com.test.ws.PatientWebServiceImpl" > 
     <property name="patientService"> 
      <osgi:reference interface="com.test.service.PatientService"/> 
     </property> 
    </bean> 

    ... 

</beans> 

只是要明確,PatientService在消費束被注入,但是實體管理器是不是在提供束注入。此外,從原來的服務時,它不會出現與持久化單元的問題,由於下面的日誌輸出:

125 psu TRACE [SpringOsgiExtenderThread-14] openjpa.Runtime - [email protected] creating container [email protected] for PU psu. 

爲了得到一個想法是怎麼回事,我記錄的對象內存引用和堆棧跟蹤PatientServiceJpaImpl類的構造函數中。構造函數被調用兩次(產生兩個不同的對象):

  1. 第一輸出出現從org.apache.felix和更或更少org.apache.aries.blueprint結束開始OSGi容器發起。

  2. 第二個輸出似乎源自彈簧框架從org.springframework.osgi開始,或多或少以org.springframework.beans.BeanUtils結尾。

當消費者服務被調用,它具有參考的藍圖實例化對象,它沒有注入實體管理器。同樣從日誌中,持久性單元在PatientServiceJpaImpl對象的藍圖實例化之後被實例化。

我已經搜索了這個問題並修改了很長一段時間,並且我已經用完了想法。具有諷刺意味的是,它確實在某一時刻起作用,但我做了很多改變才能使它起作用,以至於我無法成功退出,這是一個老鼠巢。

爲什麼持久性上下文沒有注入到藍圖管理對象中?任何想法將不勝感激。謝謝。

回答

2

我不確定這是否會起作用,因爲您正在將藍圖與春天混合。我有一個基於藍圖的工作應用程序,我很高興。對於你的用例,我建議至少使用藍圖至少爲你的JPA部分。你仍然可以使用spring-dm來使用jpa類作爲服務。

<blueprint default-activation="eager" 
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.0.0" xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"> 

<bean id="patientService" class="com.test.service.PatientServiceJpaImpl" > 
    <jpa:context property="em" unitname="dn1" /> 
<tx:transaction method="*" value="Required" /> 
</bean> 
<service id="osgiPatientService" ref="patientService" interface="com.test.service.PatientService" /> 
</blueprint> 

PatientServiceJPAImpl我會改變不包含任何註釋。

public class PatientServiceJpaImpl implements PatientService { 
    protected EntityManager em; 

    @Override 
    public Patient find(Integer id) { 
     return em.find(Patient.class, id); 
    } 
} 
+0

感謝您的回覆。該庫也將被其他開發人員在非OSGI環境中使用。有沒有辦法維護這兩種解決方案?換句話說,在藍圖環境中保留「PatientServiceJpaImpl」註釋有什麼作用(如果有的話)? –

+0

在這種情況下,請確保您不是通過藍圖導出服務,而是使用spring-dm導出服務。如果你必須使用spring-dm的彈簧棒,並且通過藍圖來引用你的服務,那麼兩者的混合都不起作用。 –

+0

我最初通過在spring上下文xml文件中定義osgi服務來嘗試這種方法。但是,在使用felix maven-bundle-plugin構建包之後,我沒有在MANIFEST-MF中看到Export-Service聲明,並且部署該包沒有註冊該服務。自從春天DM似乎已經停止以後,我決定不採取後續行動。是否可以在karaf中使用spring-dm(我正在使用Fuse ESB發行版)?如果是這樣,在道路上這樣做是否有意義?謝謝。 –

0

解決此問題的好方法是使用Gemini JPA。使用Gemini JPA,您的JPA包將根據您的persistence.xml中的配置自動公開一個EntityManagerFactory OSGi服務。因此,在JPA客戶端軟件包中,藍圖只需要用指定持久性單元名稱的過濾條件導入該服務。

這裏的那些線小的實現:mvc-osgi

如果EMF服務不適合你,你可以使用由雙子座JPA暴露EntityManagerFactoryBuilder服務捆綁手動創建EMF。這需要調用「createEntityManagerFactory」。