Simple working JPA project in OSGi Environment

Goal

create a very simple working JPA persistence project to be understood and enable people to build in into a more complex solution.
This article will concern mainly JPA client bundle configuration and we assume knowledge of OSGi platforms and JPA specification.

Limitations

  • We will keep the example simple (a single entity class, single service), but keeping OSGi modularization in mind.
  • In this example we will use non-jta data source and we let the implementation bean to manage its local transactions.
  • We will hardcode values in the blueprint configuration, no OSGi ConfigAdmin (or other external configuration) used

Note: this blog serves as my notepad, don’t take anything granted..

Used components

Apache Karaf ( Apache ServiceMix 4.4.1-fuse-08-15)
OSGi platform was originally intended to be very lightweight runtime container and a user could deploy and configure modules as needed. On the other hand – complex functionality, such as providing secure web services, data persistence, enterprise integration, etc requires set of modules playing together. So we advice to use an off-shelve bundled OSGi container with already prepared and tested features, such as – FuseSource, Talend Runtime, Apache ServiceMix, …

DerbyDB
A small and lightweight database, very good to start with.

OpenJPA Persistence
Apache OpenJPA is a Java persistence project at The Apache Software Foundation that can be used as a stand-alone POJO persistence layer or integrated into any Java EE compliant container and many other lightweight frameworks. We chose this framework for this example as it requires somehow less configuration comparing to EclipseLink (of cause – there’s a price of robustness and options to configure).

There is a nice overview of JPA framework options: tutorial-using-jpa-in-an-osgi-environment

Note:
Generally – there are 3 main frameworks to implement object persistence, in this article we will create a sample for Apache Aries JPA with OpenJPA Persistence Manager

  • SpringDM ORM (jpa-hibernate OSGi feature)
  • Apache Aries (jpa feature)
    Mainstream persistence managers for Apache Aries JPA
    - OpenJPA
    - Eclipse Gemini
    - Hibernate JPA
  • NoSQL custom projects (such as OrientDB Object)

There’s a rule to remember – DON’T MIX THEM!!!

In this example we will create 4 modules:
blogjpa project modules:

  • blogjpa-commons – entity classes and data access service interface
  • blogjpa-datasource – exposes javax.sql.DataSource service
  • blogjpa-store – data access service implementation with a test method
  • blogjpa-feature – OSGi features configuration, so we can install all at once. As well this file shows dependencies in the runtime environment

Note: blog-jpa is named because the example is intended for the blog

blogjpa-commons

A module with entity classes, service interface and persistence unit definition.
Person – an example entity

package com.apogado.blogjpa.commons;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

/**
* example entity object
* @author Gabriel Vince
*/
@Entity(name = "Person")
@NamedQuery(name = "Person.findById",query = "SELECT p FROM Person p WHERE p.id = :id")
public class Person implements Serializable {

    public static final String QUERY_FIND_BY_ID = "Person.findById";

    private Integer id;
    private String name;
    private String address;

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "personname")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

PersonService – an example service interface

Persistence unit definition:
META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="blogjpa" transaction-type="RESOURCE_LOCAL" >
    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
    <non-jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/test)</non-jta-data-source>
    <class>com.apogado.blogjpa.commons.Person</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="openjpa.Log" value="DefaultLevel=INFO, Tool=INFO"/>
      <property name="openjpa.jdbc.DBDictionary" value="derby"/>
      <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
    </properties>
  </persistence-unit>
</persistence>

Notes:

  • using JNDI feature allows OpenJPA to find an osgi service by JNDI lookup as a notation: osgi:service/<interface>/<filter> A data source as service is exposed by a blogjpa-datasource module.
  • OpenJPA requires the entity classes to be enhanced, so META-INF/persistence.xml must come along the entity classes http://openjpa.apache.org/entity-enhancement.html Personally I don’t like the idea that data source references and database specific configuration (such as dialect) should come with data classes (in this the Person entity class)
  • From the architectural point of view I still suggest to create a separate module containing data classes and interfaces. I consider very heavy to bundle everything into a single module or the other way – it is too cumbersome to split entity classes into definition and implementation modules, mainly for very small project.

Important:
-

  • OpenJPA needs to enhance entity classes. It can be done during build time and as well dynamically, when classes are loaded. From my experience it is more reliable to do it during build time
  • Declaring Meta-Persistence element in the manifest, the Aries JPA exposes an EntityManagerFactory from this module.

pom.xml

<?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>blogjpa</artifactId>
        <groupId>com.apogado.blogjpa</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <groupId>com.apogado.blogjpa</groupId>
    <artifactId>blogjpa-commons</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>bundle</packaging>

    <name>blogjpa-commons</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.3.7</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
			  <Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>
                        <Export-Package>com.apogado.blogjpa.commons</Export-Package>
                        <Import-Package>
                            org.apache.openjpa.enhance,
                            org.apache.openjpa.util,                            
                            serp.*,
                            *                            
                        </Import-Package>
                    </instructions>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.7</version>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <configuration>
                            <tasks>
                                <path id="enhance.path.ref">
                                    <fileset dir="${project.build.outputDirectory}">
                                        <include name="Person.class" />
                                    </fileset>
                                </path>
                                <pathconvert property="enhance.files" refid="enhance.path.ref" pathsep=" " />
                                <java classname="org.apache.openjpa.enhance.PCEnhancer">
                                    <arg line="-p persistence.xml" />
                                    <arg line="${enhance.files}" />
                                    <classpath>
                                        <path refid="maven.dependency.classpath" />
                                        <path refid="maven.compile.classpath" />
                                    </classpath>
                                </java>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>                       
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>2.2.0</version>
        </dependency>
    </dependencies>
</project>

blogjpa-store

Implementation of the service interface. Simple and straightforward:
blueprint.xml

<?xml version="1.0" encoding="UTF-8"?>
<blueprint 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.1.0"
           xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
                        http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint-2.8.0.xsd
                        http://aries.apache.org/xmlns/transactions/v1.0.0 http://aries.apache.org/schemas/transaction/transactionv10.xsd
                        http://aries.apache.org/xmlns/jpa/v1.1.0 http://aries.apache.org/schemas/jpa/jpa_110.xsd"
>
    <bean id="personServiceBean" class="com.apogado.blogjpa.store.PersonServiceImpl" init-method="test"  >
        <jpa:unit property="entityManagerFactory" unitname="blogjpa" />
    </bean>
    <service interface="com.apogado.blogjpa.commons.PersonService" ref="personServiceBean"/> 

</blueprint>

Note:
jpa:unit injects an EntityManagerFactory
jpa:content injects an EntityManager

Example Service implementation

blogjpa-features

This module defines a Karaf feature – modules and their dependencies. Here I may deeply disappoint people doing things “lightly”, but as I wrote before, it is faster to use configured and tested features than fighting with dependencies.

In the resources there’s a link to knowhowlab.tips with exact dependency configuration and as well configuration for other persistence managers.
features.xml

<?xml version="1.0" encoding="utf-8"?>
<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="ocm-features">
    <feature name="blogjpa" version="1.0.0-SNAPSHOT">

<!-- feature url: mvn:com.apogado.blogjpa/blogjpa-feature/1.0.0-SNAPSHOT/xml/features -->

        <!-- feature dependencies -->
        <feature>jpa</feature>
        <feature>http</feature>
        <feature>jndi</feature>
        <feature>transaction</feature>

        <!-- openjpa library dependencies -->
        <bundle>mvn:commons-collections/commons-collections/3.2.1</bundle>
        <bundle>mvn:commons-pool/commons-pool/1.5</bundle>
        <bundle>mvn:commons-dbcp/commons-dbcp/1.4</bundle>
        <bundle>mvn:commons-lang/commons-lang/2.5</bundle>
        <bundle>mvn:net.sourceforge.serp/com.springsource.serp/1.13.1</bundle>
        <bundle>mvn:org.osgi/org.osgi.enterprise/5.0.0</bundle>
        <bundle>mvn:org.osgi/org.osgi.compendium/4.3.0</bundle>

        <!-- install openjpa -->
        <bundle>mvn:org.apache.openjpa/openjpa/2.2.0</bundle>

        <!-- install db client -->
        <bundle>mvn:org.apache.derby/derbyclient/10.9.1.0</bundle>

        <!-- application bundles -->
        <bundle>mvn:com.apogado.blogjpa/blogjpa-commons/1.0.0-SNAPSHOT</bundle>
        <bundle>mvn:com.apogado.blogjpa/blogjpa-datasource/1.0.0-SNAPSHOT</bundle>
        <bundle>mvn:com.apogado.blogjpa/blogjpa-store/1.0.0-SNAPSHOT</bundle>

    </feature>
</features>

and pom.xml

Deployment

Build a project – here we assume a common maven repository between build maven and Apache Karaf. If the repositories are not the same, you need to configure the maven repository in the Karaf environment (org.ops4j.pax.url.mvn.cfg)

  1. start a database server and create a database startNetworkServer
  2. add a feature configuration
    features:addurl mvn:com.apogado.blogjpa/blogjpa-feature/1.0.0-SNAPSHOT/xml/features
  3. install feature
    features:install -c -v blogjpa
  4. check log, should say
    15:38:47,831 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | running
    15:38:47,831 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | entity manager factory available
    15:38:57,417 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Created Person id 1
    15:38:57,447 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person persisted with id: 1
    15:38:57,597 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person found with name: a9f0ed64-afaa-4e3c-9c5d-72c94424cbc3
    15:38:57,597 | INFO  | rint Extender: 1 | PersonServiceImpl                | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person found with name: a9f0ed64-afaa-4e3c-9c5d-72c94424cbc3

    Resources:

    example project: https://github.com/gusto2/blog-sample-openjpa/tree/master/blogjpa

    https://github.com/dpishchukhin/org.knowhowlab.tips.jpa

    http://jaxenter.com/tutorial-using-jpa-in-an-osgi-environment-36661.html

    http://planet.jboss.org/post/jpa_osgi_example_in_fuse_esb_4

    http://aries.apache.org/modules/samples/blog-sample.html

Author works as a senior consultant for Apogado. Apogado is a vendor-independent consultancy company focusing on sustainable, future-proof ICT architecture and enterprise architecture.

About these ads

, ,

  1. #1 by Neil stockton on March 28, 2013 - 19:24

    Comparing to “Eclipse”? Eclipse is an IDE for development. Perhaps you mean EclipseLink?

    • #2 by Gabriel on March 28, 2013 - 23:46

      You are right.. comparing to EclipseLink. Thank you for the update.

  2. #3 by Gabriel on April 5, 2013 - 09:34

    Aries JPA has currently an outstanding issue – when injecting a JPA unit (EntityManagerFactory) to a bean when the bean uses managed properties (defined in cm:placeholder), it will produce a ClassCastException. Until Apache Aries is fixed, I recommend to create a separate bean providing an entity manager and do not mix placeholder and jpa properties in the same bean.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 219 other followers

%d bloggers like this: