ServiceMix cannot find OSGI datasource

I dived into ServiceMix 5.4.0 and OSGi and ran into some rather strange behavior with OpenJPA.

I have a data source defined like this:

<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="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://localhost:5432/test"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
    </bean>

    <service interface="javax.sql.DataSource" ref="dataSource">
      <service-properties>
        <entry key="osgi.jndi.service.name" value="jdbc/test"/>
      </service-properties>
    </service>
</blueprint>

      

Using the jndi: names command, I can check that the datasource is visible:

karaf@root> jndi:names
JNDI Name            Class Name                                                  
osgi:service/jndi    org.apache.karaf.jndi.internal.JndiServiceImpl              
osgi:service/jdbc/test org.apache.commons.dbcp.BasicDataSource                     
karaf@root> 

      

My persistence.xml:

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="test" transaction-type="JTA">               
        <jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/test)</jta-data-source>

        <class>com.example.persistence.security.User</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>
            <property name="openjpa.jdbc.DBDictionary" value="postgres"/>                       
            <property name="openjpa.Log" value="slf4j"/>
        </properties>
    </persistence-unit>
</persistence>

      

Then I add a persistence block to the DAO class via Blueprint:

<?xml version="1.0" encoding="UTF-8"?>

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

    <bean id="securityDAO" class="com.example.security.dao.SecurityDAOImpl" init-method="init">
        <tx:transaction method="*" value="Required" />
        <jpa:context property="entityManager" unitname="test" />
    </bean>

    <service ref="securityDAO" interface="com.example.security.dao.SecurityDAO">
    </service>


</blueprint>

      

The persistence block is injected successfully, which I check in the init DAO method:

public void init() {
    if (em==null) {
        log.error("Entity manager not found. Check JPA configuration.");
        throw new RuntimeException("No EntityManager found");
    }

    log.info("Started SecurityDAO");
}

      

After all my hard work, ServiceMix rewards me with the following crypto exception when I call my DAO method from another bean:

....
public void setSecurityDAO (SecurityDAO dao) {
    this.dao = dao;
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String userName = req.getParameter("userName");
    String password = req.getParameter("password");

    // Invocation of injected DAO results in exception
    User u = dao.authenticateUser(userName, password);          

      

The result is the following:

Caused by: java.lang.RuntimeException: The DataSource osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/test) required by bundle persistence/0.0.1.SNAPSHOT could not be found.
    at org.apache.aries.jpa.container.unit.impl.JndiDataSource.getDs(JndiDataSource.java:87)
    at org.apache.aries.jpa.container.unit.impl.DelayedLookupDataSource.getConnection(DelayedLookupDataSource.java:36)
    at org.apache.openjpa.lib.jdbc.DelegatingDataSource.getConnection(DelegatingDataSource.java:116)
    at org.apache.openjpa.lib.jdbc.DecoratingDataSource.getConnection(DecoratingDataSource.java:93)
    at org.apache.openjpa.jdbc.schema.DataSourceFactory.installDBDictionary(DataSourceFactory.java:233)
    ... 54 more
Caused by: javax.naming.NoInitialContextException: Unable to find the InitialContextFactory org.eclipse.jetty.jndi.InitialContextFactory.
    at org.apache.aries.jndi.ContextHelper.getInitialContext(ContextHelper.java:148)
    at org.apache.aries.jndi.OSGiInitialContextFactoryBuilder.getInitialContext(OSGiInitialContextFactoryBuilder.java:49)
    at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
    at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
    at javax.naming.InitialContext.init(InitialContext.java:244)
    at javax.naming.InitialContext.<init>(InitialContext.java:216)
    at org.apache.aries.jpa.container.unit.impl.JndiDataSource.getDs(JndiDataSource.java:64)
    ... 58 more

      

Somehow the exported OSGi datasource doesn't find its way in the persistence set. The weird part is that when I added the following code to the init method to see if I can execute a test request, not only does OpenJPA not throw an exception in the init method, but the DAO call that fires the exception now also works:

public void init() {
    if (em==null) {
        log.error("Entity manager not found. Check JPA configuration.");
        throw new RuntimeException("No EntityManager found");
    }

    try {
        Query q = em.createNativeQuery("SELECT 1=1");           
        q.getFirstResult();         
    } catch (Exception ex) {
        log.error("Unable to execute test query against database", ex);
        throw new RuntimeException(ex);
    }

    log.info("Started SecurityDAO");
}

      

So, to summarize, if I call a method from a package other than my DAO, OpenJPA throws an exception indicating that it cannot find InitialNamingContext and shows no sign in the log it started. If I execute a request inside my DAO before the external component arrives, somehow OpenJPA can find the InitialNamingContext, OpenJPA shows up in the log, and subsequent calls from outside the DAO package start working.

Obviously, I missed something basic. Any help or thoughtful explanation of what is breaking or what I am doing wrong would be greatly appreciated.

EDIT:

I didn't notice last night, but when I added to the test query, the following lines appear in the log. They are missing when I comment out this request:

... | Runtime | 220 - org.apache.openjpa - 2.3.0 | Starting OpenJPA 2.3.0
... | JDBC    | 220 - org.apache.openjpa - 2.3.0 | Using dictionary class "org.apache.openjpa.jdbc.sql.PostgresDictionary".
... | JDBC    | 220 - org.apache.openjpa - 2.3.0 | Connected to PostgreSQL version 9.9 using JDBC driver PostgreSQL Native Driver version PostgreSQL 9.3 JDBC4.1 (build 1102).

      

EDIT 2:

Tried this on plain vanilla Karaf 3.0.3 and got the same error. As a workaround, I created a separate bean in the bundle that runs the above test request. Apparently, as long as the only bean in the package makes a call to OpenJPA before a bean outside of the package tries to make the call, OpenJPA will be properly initialized.

Since this is mentioned nowhere, I don't see it in the OpenJPA / ServiceMix docs, I can only assume that I am doing something wrong elsewhere in my configuration.

EDIT 3:

Per John Forth, here's MANIFEST.MF

Manifest-Version: 1.0
Bnd-LastModified: 1430533396366
Build-Jdk: 1.8.0_45
Built-By: somedude
Bundle-Blueprint: OSGI-INF/blueprint/blueprint.xml
Bundle-Description: Database access layer for Peer Review product
Bundle-ManifestVersion: 2
Bundle-Name: Example :: Persistence
Bundle-SymbolicName: persistence-jpa
Bundle-Version: 0.0.1.SNAPSHOT
Created-By: Apache Maven Bundle Plugin
Export-Package: com.example.persistence.security;version="0.0.1.SNAPSHOT",co
 m.example.security.dao;version="0.0.1.SNAPSHOT";uses:="com.example.persistence.
 security,javax.persistence"
Export-Service: com.example.security.dao.SecurityDAO
Import-Package: javax.persistence;version="[1.1,2)",org.osgi.service.blu
 eprint;version="[1.0.0,2.0.0)",org.slf4j;version="[1.7,2)"
Meta-Persistence: META-INF/persistence.xml
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Tool: Bnd-2.3.0.201405100607

      

And, as it might be related, the pom.xml of the JPA package is:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>example</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>persistence-jpa</artifactId>
    <packaging>bundle</packaging>

    <name>Example :: Persistence</name>

    <dependencies>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jpa_2.0_spec</artifactId>
            <version>1.1</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.5.3</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Bundle-Version>${project.version}</Bundle-Version>                     
                        <Import-Package>*</Import-Package>
                        <Export-Package>com.example.persistence*,com.example.security.*;version=${project.version}</Export-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

      

+3


source to share


1 answer


If you are using OSGI, class visibility is defined in the MANIFEST.MF files.

This way the persistence package can see and load the classes that are imported into its MANIFEST.MF.

The correct way to extend an existing package is to define a chunk that is attached to an existing package. This way you can provide classes (like DAO) and files (like persistence.xml) and make it visible to the fragment node.

Now MANIFEST.MF looks like



Bundle-ManifestVersion: 2
Bundle-Name: foo.bar.openjpa-fragment
Bundle-SymbolicName: foo.bar.openjpa-fragment;singleton:=true
Bundle-Version: 0.0.1.SNAPSHOT
Bundle-Vendor: foo bar
Fragment-Host: org.apache.openjpa-bundle
Bundle-ClassPath: .

      

Please note that this is just an example.

OSGI stands for Proper Visibility.

You can add more than one snippet to an existing package eg. to keep the configuration in a separate kit, making it easy to set up the configuration.

0


source







All Articles