EclipseLink is a JPA implementation like Hibernate, OpenJPA and others. It belongs to the Eclipse foundation and plays well with other Eclipse and OSGi technologies.

2. Exercise - Setting up a target definition

In this exercise you define the dependencies, which are necessary to create a EclipseLink JPA application.

2.1. Creating a target definition project

Create a general project called com.vogella.jpa.target.

Inside this project a target definition file called com.vogella.jpa.target.target should be created.

target definition project

The following update sites are supposed to be used:

target definition contents

2.2. Validate

Press Set as Active Target Platform once the update sites are resolved and ready. In the following exercises the bundles from these update sites are used.

3. Exercise - Creating a JPA model

3.1. Target

Specify a Todo model together with a service interface, which will be used to get Todo objects.

3.2. Creating the model bundle

Create a plug-in project called com.vogella.jpa.model with javax.persistence as bundle dependency.

Then create a Todo and TodoService class inside the com.vogella.jpa.model package.

package com.vogella.jpa.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Todo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String summary;
    private String description;

    // empty constructor for JPA instantiation
    public Todo() {
    }

    public Todo(int id) {
        this(id, "", "");
    }

    public Todo(int id, String summary, String description) {
        this.id = id;
        this.summary = summary;
        this.description = description;
    }

    public int getId() {
        return id;
    }

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

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Todo [id=" + id + ", summary=" + summary + ", description=" + description + "]";
    }

}
package com.vogella.jpa.model;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

public interface TodoService {

    void getTodos(Consumer<List<Todo>> todosConsumer);

    boolean saveTodo(Todo newTodo);

    Optional<Todo> getTodo(int id);

    boolean deleteTodo(int id);

    void printTodos();
}

Do not forget to export the com.vogella.jpa.model package, so that it can be used by other plugins.

4. Exercise - Creating a JPA service implementation

4.1. Target

Provide a service implementation of the previously created TodoService, which makes use of Eclipse Link, a H2 Database Engine and JPA.

4.2. Creating the service bundle

Create a plugin project called com.vogella.jpa.service with the following bundle dependencies:

  • javax.persistence

  • org.eclipse.persistence.jpa

  • org.h2

and package imports

  • com.vogella.jpa.model

  • org.osgi.service.component.annotations (as optional dependency)

service dependencies

Please activate the Generate descriptors from annotated sources preference, so that the component xml file will be created automatically for the TodoServiceImpl.

ds preferences

The TodoServiceImpl, which impelements the TodoService interface should look like this:

package com.vogella.jpa.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.jpa.PersistenceProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;

import com.vogella.jpa.model.Todo;
import com.vogella.jpa.model.TodoService;

@Component(service = TodoService.class, property = { "osgi.command.scope=test", "osgi.command.function=printTodos" })
public class TodoServiceImpl implements TodoService {

    private static AtomicInteger current = new AtomicInteger(1);
    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;

    @Activate
    @SuppressWarnings("unchecked")
    protected void activateComponent() {
        @SuppressWarnings("rawtypes")
        Map map = new HashMap();
        map.put(PersistenceUnitProperties.CLASSLOADER, getClass().getClassLoader());

        PersistenceProvider persistenceProvider = new PersistenceProvider();
        entityManagerFactory = persistenceProvider.createEntityManagerFactory("h2-eclipselink", map);
        entityManager = entityManagerFactory.createEntityManager();

        getTodos(todos -> {
            if(todos.isEmpty()) {
                List<Todo> initialModel = createInitialModel();
                initialModel.forEach(this::saveTodo);
            }
        });
    }

    @Deactivate
    protected void deactivateComponent() {
        entityManager.close();
        entityManagerFactory.close();
        entityManager = null;
        entityManagerFactory = null;
    }

    public TodoServiceImpl() {
    }

    @Override
    public void getTodos(Consumer<List<Todo>> todosConsumer) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Todo> cq = cb.createQuery( Todo.class );
        Root<Todo> rootTodo = cq.from( Todo.class );
        CriteriaQuery<Todo> allTodos = cq.select( rootTodo );
        TypedQuery<Todo> todosQuery = entityManager.createQuery( allTodos );

        List<Todo> todoList = todosQuery.getResultList();
        todosConsumer.accept(todoList);
    }

    // create or update an existing instance of Todo
    @Override
    public synchronized boolean saveTodo(Todo newTodo) {
        // hold the Optional object as reference to determine, if the Todo is
        // newly created or not
        Optional<Todo> todoOptional = getTodo(newTodo.getId());

        // get the actual todo or create a new one
        Todo todo = todoOptional.orElse(new Todo(current.getAndIncrement()));
        todo.setSummary(newTodo.getSummary());
        todo.setDescription(newTodo.getDescription());

        // send out events
        if (todoOptional.isPresent()) {
            entityManager.getTransaction().begin();
            entityManager.merge(todo);
            entityManager.getTransaction().commit();
        } else {
            entityManager.getTransaction().begin();
            entityManager.persist(todo);
            entityManager.getTransaction().commit();
        }
        return true;
    }

    @Override
    public Optional<Todo> getTodo(int id) {
        entityManager.getTransaction().begin();
        Todo find = entityManager.find(Todo.class, id);
        entityManager.getTransaction().commit();

        return Optional.ofNullable(find);
    }

    @Override
    public boolean deleteTodo(int id) {
        entityManager.getTransaction().begin();
        Todo find = entityManager.find(Todo.class, id);
        entityManager.remove(find);
        entityManager.getTransaction().commit();

        return true;
    }

    @Override
    public void printTodos() {
        getTodos(todoList -> todoList.forEach(System.out::println));
    }

    private List<Todo> createInitialModel() {
        List<Todo> list = new ArrayList<>();
        list.add(createTodo("Application model", "Flexible and extensible"));
        list.add(createTodo("DI", "@Inject as programming mode"));
        list.add(createTodo("OSGi", "Services"));
        list.add(createTodo("SWT", "Widgets"));
        list.add(createTodo("JFace", "Especially Viewers!"));
        list.add(createTodo("CSS Styling", "Style your application"));
        list.add(createTodo("Eclipse services", "Selection, model, Part"));
        list.add(createTodo("Renderer", "Different UI toolkit"));
        list.add(createTodo("Compatibility Layer", "Run Eclipse 3.x"));
        return list;
    }

    private Todo createTodo(String summary, String description) {
        return new Todo(current.getAndIncrement(), summary, description);
    }

}

The osgi.command.scope and osgi.command.function properties in the @Component definition allows to call certain methods, e.g., the printTodos method, from the command line by using the felix gogo shell.

The @Activate and @Deactivate annotations ensure that the activateComponent and deactivateComponent methods will be called once the service is activated or deactivated.

In order to persist Todo objects in the H2 database certain properties for the so called persistence-unit have to be specified in a persistence.xml file, which is supposed to be located in the META-INF folder of the projects right besides the MANIFEST.MF file.

The contents of the persistence.xml should look like this:

<?xml version="1.0" encoding="UTF-8" ?>
<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"
    xmlns="http://java.sun.com/xml/ns/persistence"
    version="2.0">
    <persistence-unit name="h2-eclipselink" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>com.vogella.jpa.model.Todo</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:~/h2db/vogellaDB" />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" />
            <property name="eclipselink.logging.level" value="FINE" />
        </properties>
    </persistence-unit>
</persistence>

Make sure that all the files like persistence.xml and the com.vogella.jpa.service.TodoServiceImpl.xml are also mentioned in the build.properties file.

4.3. Validate

Go to the Debug Configurations and create a new OSGi Framework configuration.

Simply select the com.vogella.jpa.model and com.vogella.jpa.service bundles from the workspace and hit the Add Required Bundles button.

OSGi framework debug configurations

When running the new OSGi Framework configuration the OSGi console should appear in the Console view, where you can type the following:

osgi> test:printTodos

The console should show the following output:

console print todos

The test:printTodos command is related to the properties in the @Component annotation in the TodoServiceImpl class.

5. Exercise - Using the JPA service as standalone application

In the Creating a JPA service implementation chapter an OSGi Framework configuration has been used to start the OSGi runtime together with the JPA service from the IDE. In this exercise the OSGi runtime will be started outside the Eclipse IDE.

See OSGi Tutorial for further information.

5.1. Creating a product with proper configuration

Usually products are used to create rich client applications, but for the sake of generating the predefined structure and aggregation of necessary bundles a slightly tweaked product can be used.

Create a general project called com.vogella.jpa.product and create a Product Configuration called com.vogella.jpa.product.

Switch to the contents tab and add the following bundles to it:

  • com.vogella.jpa.model

  • com.vogella.jpa.service

  • org.eclipse.equinox.console

  • org.apache.felix.gogo.command

  • org.apache.felix.gogo.runtime

  • org.apache.felix.gogo.shell

  • org.eclipse.equinox.simpleconfigurator

  • org.apache.felix.src

After that press the Add Required Plug-ins button to add all necessary dependencies.

Create a config.ini file with the following contents in the root folder besides the com.vogella.jpa.product file:

osgi.bundles=reference\:file\:plugins/org.eclipse.equinox.simpleconfigurator_1.1.200.v20160504-1450.jar@1\:start
org.eclipse.equinox.simpleconfigurator.configUrl=file\:org.eclipse.equinox.simpleconfigurator/bundles.info
osgi.bundles.defaultStartLevel=4
osgi.noShutdown=true
eclipse.ignoreApp=true

On the Configuration tab of the product this config.ini file can be referenced.

product configuration

Now the product can be exported.

product export

A proper Destination has to be chosen and Generate p2 repository is not necessary and Syncronize before exporting won’t work because there is no product specifying bundle.

As final step before the exported product can be started the org.eclipse.osgi bundle, e.g., org.eclipse.osgi_3.11.2.v20161107-1947.jar, has to be copied into the root folder of the exported product.

Currently custom config.ini files are not considered by the product export. See Bug 284732. So please ensure that the exported product’s config.ini file looks like specified above.

5.2. Validate

After making all these changes the folder structure of the exported product should look similar to this:

product export folder structure

Now the application can be run from the command line by starting it like this:

java -jar org.eclipse.osgi_3.11.2.v20161107-1947.jar -console

After executing this the OSGi console should appear.

In order to check whether the JPA service is properly working the test:printTodos command can be invoked again.

osgi standalone jpa service

If you need more assistance we offer Online Training and Onsite training as well as consulting