Archive for March, 2013

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.

Advertisements

, ,

5 Comments

Web Service CXF Interceptor replacing request data

Goal of this exercise is to build a web service replacing a value in the request with an arbitrary value. This can be handy to fill e.g. SOAP header or HTTP header data to the message payload and make the data accessible in the service bean. Intended to use with declarative web services (OSGI blueprint / Spring configuration)

Prerequisites – knowledge of OSGi blueprint, CXF web services

Using Karaf 2.2.2-fuse-08-15, CXF 2.4.2

Web Service invocation chain

CXF invocation chain

CXF invocation chain

The main idea is to use interceptor to intercept incoming message and a value in the payload. For easy of use we will  deserialize request objects and try to use POJO approach. This approach may have some overhead, but we consider it to be reasonable price to pay for easy usage.

Blueprint

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
           xmlns:cxf="http://cxf.apache.org/blueprint/core"
           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
             http://cxf.apache.org/blueprint/jaxws 
               http://cxf.apache.org/schemas/blueprint/jaxws.xsd
             http://cxf.apache.org/blueprint/core 
               http://cxf.apache.org/schemas/blueprint/core.xsd
             http://cxf.apache.org/blueprint/jaxws 
               http://cxf.apache.org/schemas/blueprint/jaxws.xsd
             http://cxf.apache.org/blueprint/core 
               http://cxf.apache.org/schemas/blueprint/core.xsd
">

    <bean 
        id="serviceBean" 
        class="com.apogado.test.interceptortest.InterceptorTestService" />
    <bean 
        id="interceptor" 
        class="com.apogado.test.interceptortest.TestInterceptor" />

    <jaxws:server id="InterceptorTestService"
                  address="/InterceptorTestService"  
                  serviceClass="com.apogado.test.interceptortest.service.Test1Service"
                  serviceBean="#serviceBean">
        <jaxws:inInterceptors>
            <ref component-id="interceptor" />
        </jaxws:inInterceptors>
    </jaxws:server>
</blueprint>

Interceptor

package com.apogado.test.interceptortest;

import com.apogado.test.interceptortest.data.TDataRequest;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

/**
 * a test interceptor to alter payload data
 *
 * @author Gabriel Vince
 */
public class TestInterceptor extends AbstractPhaseInterceptor {

    private static final Logger logger = Logger.getLogger(TestInterceptor.class.getName());

    public TestInterceptor() {
        // the PRE_INVKE phase allows us access payload as objects and we don't 
        // need to worry some other object will mess it up
        // security should be already checked
        super(Phase.PRE_INVOKE);
    }

    /**
     * get the request object and alter data there
     * @param t
     * @throws Fault 
     */
    public void handleMessage(Message t) throws Fault {

        try
        {
            HttpServletRequest req = (HttpServletRequest)t.get("HTTP.REQUEST");
            String queryString = req.getQueryString();
            if (queryString != null
                    && (queryString.contains("wsdl") || queryString.contains("WSDL"))
                    && req.getMethod().equals("GET")) {
                return; //let the schema request pass
            }
            logger.info("Test interceptor - processing data");

            // Content formats available: org.w3c.dom.Node , 
            // org.apache.cxf.io.DelegatingInputStream
            //  java.io.InputStream , java.util.List ,
            // javax.xml.stream.XMLStreamReader 
            // seems only we reach payload as objects are using the List class
            List list = t.getContent(List.class); // message parts
            // TDataRequest is a request object generated from WSDL and XSD
            // and used as a request message payload part
            TDataRequest requestData = (TDataRequest) list.get(0);
            requestData.setValue("An arbitrary value");
        }
        catch(Exception ex)
        {
            logger.log(Level.SEVERE, "test interceptor",ex);
        }
    }

Project link: intercepttest.zip

,

1 Comment