Albert Lacambra BasilAlbert Lacambra Basil

Creating an empty DB on memory:

Create an H2 empty DB is quite fast if we are deploying an application. However, if using for hour jpa test or integration test, the time that it is taking is significant.

The following code creates a fresh DB each time a test run.

 @BeforeEach
  void setUp() {
        EntityManager em = Persistence.createEntityManagerFactory("SCHEMA_NAME").createEntityManager();
  }

The output when creating a new factory is as follows:

class TestOfTest {

  @BeforeEach
  void init() {
    EntityManager em = Persistence.createEntityManagerFactory("SCHEMA_NAME").createEntityManager();
  }

  @Test
  void t1() {}

  @Test
  void t2() {}

  @Test
  void t3() {}
}
h2 load test time

As we see in the image, a complete empty test needs about three seconds to load everything.

The solution?

Just load the factory once, and per each test empty the DB. So easy…​

Find all tables and execute delete queries

Calling the function em.getMetamodel().getEntities() gives us all the JPA entites being created buy the current PersistenceFactory. Now we just need to execute a DELETE query to clean up all the H2 database.

SET REFERENTIAL_INTEGRITY FALSE;
for (EntityType<?> entity : em.getMetamodel().getEntities()) {
    final String className = entity.getName();
    Query q = em.createQuery("delete from " + className + " c");
    q.executeUpdate();
}

Disable referencial integrity (foreign keys)˚

To be able to cleanly delete all entites without any Referencial Integrity error we need firstly to diable foreign key constraints.

Restart autoincrement counters

The following query, gives us the name of all H2 current sequences:

SELECT * FROM INFORMATION_SCHEMA.SEQUENCES;
h2 sequences query

The element on position2 of the array sequence[2] is the name of the sequence. Now we can just restart it using an ALTER command:

ALTER SEQUENCE HIBERNATE_SEQUENCE RESTART WITH 1;
Restart sequences. ALl together.
List<Object[]> sequences = em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.SEQUENCES").getResultList();

for (Object[] sequence : sequences) {
    String seqName = (String) sequence[2];
    em.createNativeQuery("ALTER SEQUENCE " + seqName + " RESTART WITH 1").executeUpdate();
}

Share the Entity_Manager_Factory

You nneed to share the EntityManagerFactory in order to only create it once and save this 1 second per test. You can do it in several ways, but a simple one is to use a helper class with static variables:

public class EntityManagerFactoryHelper {

    private static EntityManagerFactory entityManagerFactory;

    public static EntityManagerFactory getEntityManagerFactory() {
        if (entityManagerFactory == null) {
        entityManagerFactory = Persistence.createEntityManagerFactory("schema");
        }
    }
}

This version is not using any lock, so the test cannot be multithreaded or parallel.

Use H2 on memory with Hibernate

Add maven dependencies. H2 on meory database and hibernate entiy manager:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.197</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.3.7.Final</version>
    <scope>test</scope>
</dependency>

Create persitance.xml file with dsesired params on src/test/resources/META-INF/persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

<persistence-unit name="{{db-schema}}" transaction-type="RESOURCE_LOCAL">
    <properties>
        <property name="hibernate.show_sql" value="false"/>
        <property name="hibernate.connection.pool_size" value="10"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"/>
        <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
        <property name="javax.persistence.validation.mode" value="none"/>
    </properties>
</persistence-unit>

Creating a Junit5 extension

Now that we have all the pieces, let’s integrate it with junit5 using an extension. We want that @BeforeEach test the H2 DB is like a new one. The runner will expect that the test class have an EntityManager em attribute. We will access it throw refelction and a assign it a new created_EntityManager_ before each test.

public class H2Extension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {

  private static EntityManagerFactory emf;

  @Override
  public void beforeEach(ExtensionContext context) throws NoSuchFieldException, IllegalAccessException {
    Object testInstance = context.getTestInstance().get();
    EntityManager em = emf.createEntityManager();
    testInstance.getClass().getField("em").set(testInstance, em);
  }

  @Override
  public void afterEach(ExtensionContext context) throws NoSuchFieldException, IllegalAccessException {

    Object testInstance = context.getTestInstance().get();
    EntityManager em = (EntityManager) testInstance.getClass().getField("em").get(testInstance);
    PersistenceContextHelper.deleteAllEntities(em);
  }

  @Override
  public void beforeAll(ExtensionContext context) {
    if (emf == null) {
      emf = Persistence.createEntityManagerFactory("SCHEMA_NAME");
    }
  }
}

This extension will assign a new EntiyManager to the test instance each time anew test run and will clear the database each timea test is finsihed. The static EntityManagerFactory allow to reuse it for all the test clases using this extension and not only for one.

Useful commands with less

Use less to read logs efficiently

Albert Lacambra BasilAlbert Lacambra Basil

Move through the document

shift g: goes to end.

shift f: tails the file.

Search through the document

– ctrl+c: switch to normal mode (no tails)
- ?: search upwards
-/: search downward
– n: search next match in current direction
– shift n: search next match in counter direction

Also useful to find 2 or more words in one line is the regex:

– Word1.+Word2 finds both words in the same line

As an extra to review logs, it is also useful the command grep with options after and before:

grep -A: lines after match

grep -B: lines before match

CDI event with JTA Transactions

CDI events can we triggered in different faces of a transaction

Albert Lacambra BasilAlbert Lacambra Basil

CDI Events

We can send events using CDI.

Declaration of an event.
@Inject
Event<EventObject> event;

...
event.fire(new EventObject("Some event's payload"));
...
Declarition of an event handler
public void inProgress(@Observes EventObject eventObject) {
    System.out.println("IN_PROGRESS: " + eventObject.getMsg());
}

Events on transaction phases

A transaction have the following phases:

  • TransactionPhase.IN_PROGRESS: Identifies a regular observer method, called when the event is fired.

  • TransactionPhase.BEFORE_COMPLETION: Identifies a before completion observer method, called during the before completion phase of the transaction.

  • TransactionPhase.AFTER_SUCCESS: Identifies an after success observer method, called during the after completion phase of the transaction, only when the

  • TransactionPhase.AFTER_COMPLETION: Identifies an after completion observer method, called during the after completion phase of the transaction.

  • TransactionPhase.AFTER_FAILURE: Identifies an after failure observer method, called during the after completion phase of the transaction, only when the transaction fails.

Execution order

Given a transactional method, I will trigger an event before to persist and after persist a model:

@Transactional
public class Service {

  @PersistenceContext
  EntityManager em;

  @Inject
  Event<EventObject> event;

  public Model anotherModel(){

    event.fire(new EventObject("before save a model"));

    Model m = new Model();

    m.setName("name" + System.currentTimeMillis());
    em.persist(m);

    event.fire(new EventObject("after persist the model"));
    return m;
  }
}
Observers
package tech.lacambra;

import javax.enterprise.event.Observes;
import javax.enterprise.event.TransactionPhase;

public class Observers {

  public void inProgress(@Observes(during = TransactionPhase.IN_PROGRESS) EventObject eventObject) {
    System.out.println("IN_PROGRESS: " + eventObject.getMsg());
  }

  public void after(@Observes(during = TransactionPhase.AFTER_COMPLETION) EventObject eventObject) {
    System.out.println("AFTER_COMPLETION: " + eventObject.getMsg());
  }

  public void before(@Observes(during = TransactionPhase.BEFORE_COMPLETION) EventObject eventObject) {
    System.out.println("BEFORE_COMPLETION: " + eventObject.getMsg());
  }

  public void afterTx(@Observes(during = TransactionPhase.AFTER_SUCCESS) EventObject eventObject) {
    System.out.println("AFTER_SUCCESS: " + eventObject.getMsg());
  }

  public void afterFailure(@Observes(during = TransactionPhase.AFTER_FAILURE) EventObject eventObject) {
    System.out.println("AFTER_FAILURE: " + eventObject.getMsg());
  }
}

The above code will produce the following output:

IN_PROGRESS: before save a model: Event 1
Hibernate: insert into Model (name) values (?)
IN_PROGRESS: after persist the model: Event 2
BEFORE_COMPLETION: before save a model: Event 1
BEFORE_COMPLETION: after persist the model: Event 2
AFTER_COMPLETION: after persist the model: Event 2
AFTER_SUCCESS: after persist the model: Event 2
AFTER_COMPLETION: before save a model: Event 1
AFTER_SUCCESS: before save a model: Event 1

And using directly UserTransactions:

public class ManualService {

  @PersistenceContext
  EntityManager em;

  @Resource
  UserTransaction userTransaction;

  @Inject
  Event<EventObject> event;

  public Model anotherModel() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException {

    event.fire(new EventObject("Before TX: Event 1"));
    userTransaction.begin();
    event.fire(new EventObject("TX begins: Event 2"));
    Model m = new Model();

    m.setName("name" + System.currentTimeMillis());
    em.persist(m);

    event.fire(new EventObject("After persist. Before TX commit: Event 3"));
    userTransaction.commit();
    event.fire(new EventObject("After TX commit: Event 4"));

    return m;
  }
}

The above code will produces:

IN_PROGRESS: Before TX: Event 1
AFTER_COMPLETION: Before TX: Event 1
AFTER_SUCCESS: Before TX: Event 1
BEFORE_COMPLETION: Before TX: Event 1
AFTER_FAILURE: Before TX: Event 1
IN_PROGRESS: TX begins: Event 2
Hibernate: insert into Model (name) values (?)
IN_PROGRESS: After persist. Before TX commit: Event 3
BEFORE_COMPLETION: TX begins: Event 2
BEFORE_COMPLETION: After persist. Before TX commit: Event 3
AFTER_COMPLETION: After persist. Before TX commit: Event 3
AFTER_SUCCESS: After persist. Before TX commit: Event 3
AFTER_COMPLETION: TX begins: Event 2
AFTER_SUCCESS: TX begins: Event 2
IN_PROGRESS: After TX commit: Event 4
AFTER_COMPLETION: After TX commit: Event 4
AFTER_SUCCESS: After TX commit: Event 4
BEFORE_COMPLETION: After TX commit: Event 4
AFTER_FAILURE: After TX commit: Event 4

Interestingly, if you use any TransactionPhase when not inside a TX context, all observers will be triggered (Look events 1 and 4).˚

Create openshift jenkins slave images

Per default you can only use a couple of images for your slaves. Sometimes is enough to get java and maven but most of the time you will want to have your own images. That allows to add new executables, cache dependencies, ...

Albert Lacambra BasilAlbert Lacambra Basil

Create an image file

First you need to create your dockerfile. It must be created from _FROM quay.io/openshift/origin-jenkins-agent-base:v4.0 _

To build this blog, I am using the foillowing Dockerfile:

FROM quay.io/openshift/origin-jenkins-agent-base:v4.0
# This is a base image that install and configures JBake.
# Child Dockerfiles can run command `jbake` to bake or anything else.

# Define environment variables.
ENV BUILD_DATE=05252019
ENV JBAKE_HOME=/opt/jbake
ENV JBAKE_USER=jbake
ENV JBAKE_VERSION=2.6.4
ENV PATH ${JBAKE_HOME}/bin:$PATH

# SSL Cert for downloading mule zip

RUN adduser ${JBAKE_USER}

RUN mkdir -p /opt/jbake-${JBAKE_VERSION} && \
    ln -s /opt/jbake-${JBAKE_VERSION} ${JBAKE_HOME} && \
    chown ${JBAKE_USER}:${JBAKE_USER} -R /opt/jbake*

RUN mkdir /opt/jbake-structure && \
    chown ${JBAKE_USER}:${JBAKE_USER} -R /opt/jbake*

# For checksum, alpine linux needs two spaces between checksum and file name
RUN cd ~ && wget https://dl.bintray.com/jbake/binary/jbake-${JBAKE_VERSION}-bin.zip && \
    unzip ~/jbake-${JBAKE_VERSION}-bin.zip && \
    cd /opt && cp -R ~/jbake-${JBAKE_VERSION}-bin/* ${JBAKE_HOME}/ && \
    rm ~/jbake-${JBAKE_VERSION}-bin.zip && \
    rm -rf ~/jbake-${JBAKE_VERSION}-bin

RUN cd /opt

USER ${JBAKE_USER}

CMD jbake -b /opt/jbake-structure

Once is created, push the image into a repository that is accessible by openshift.

Create an imagestream

Second, create a new imagestream. You can import it and then edit the required labels or create it from a template. That is the template of my JBake imagestream:

apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  labels:
    app: jbake
    role: jenkins-slave
    slave-label: jbake
  name: jbake
  namespace: blog
spec:
  tags:
    - annotations:
        openshift.io/generated-by: OpenShiftWebConsole
        openshift.io/imported-from: alacambra/jbake
      from:
        kind: DockerImage
        name: alacambra/jbake
      name: latest
      referencePolicy:
        type: Source

Pay special attention to the keys metadata.labels.role and metadata.labels.slave-label.

  • role: jenkins-slave indicates that this image is to be used by a jenkins slave

  • slave-label: jbake is the value to be used into the jenkins pipelins to use this slave image.

Reference the image into the Jenkinsfile

Finally you just need to tell your pipeline to use the created image. Do that giving to label the value of slave-label:

pipeline{
    agent {
        label 'jbake'
    }
....

Done.