OSGI服务JPA PersistenceContext未注入

时间:2012-12-03 17:24:24

标签: jpa osgi apache-karaf blueprint-osgi

我有一个核心库,它有一个我希望在Fuse ESB(Apache ServiceMix和Karaf)中作为OSGI服务公开的接口。目标是允许其他捆绑包使用它。该服务使用JPA(OpenJPA)和Spring。以下是界面:

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环境中,一切都很好。它使用felix maven-bundle-plugin,因此为了创建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>

成功部署捆绑包并注册服务。问题是当从另一个bundle引用PatientService时,实体管理器尚未注入,因此在NullPointerException方法中抛出了find(Integer id)。以下是消费者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 - org.apache.openjpa.persistence.PersistenceProviderImpl@24a5031d creating container org.apache.openjpa.persistence.EntityManagerFactoryImpl@4d6f77b6 for PU psu.

为了了解发生了什么,我在PatientServiceJpaImpl类的构造函数中记录了对象内存引用和堆栈跟踪。构造函数被调用两次(创建两个不同的对象):

  1. 第一个输出似乎来自osgi容器,从org.apache.felix开始,或多或少以org.apache.aries.blueprint结尾。

  2. 第二个输出似乎源自弹簧框架,从org.springframework.osgi开始,或多或少以org.springframework.beans.BeanUtils结尾。

  3. 当调用使用者服务时,它具有的引用是蓝图实例化对象,该对象没有注入的实体管理器。同样从日志中,在PatientServiceJpaImpl对象的蓝图实例化之后实例化持久性单元。

    我已经搜索并修改了这个问题很长一段时间,我的想法已经用完了。具有讽刺意味的是,它实际上在某一点上起作用,但是我做了很多改变才能让它发挥作用,因为它是一只老鼠窝,我无法成功地退出。

    为什么持久性上下文没有注入蓝图托管对象?任何想法将不胜感激。感谢。

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

处理此问题的一个好方法是使用Gemini JPA。使用Gemini JPA,您的JPA包将根据persistence.xml中的配置自动公开EntityManagerFactory OSGi服务。因此,在JPA客户端包中,蓝图只需要使用指定持久性单元名称的过滤条件来导入此服务。

以下是这些行的一个小实现:mvc-osgi

如果EMF服务不适合您,您可以使用Gemini JPA包公开的EntityManagerFactoryBuilder服务手动创建EMF。这需要调用“createEntityManagerFactory”。