Archivo de la categoría: Quality development

Arquillian y GlassFish: portar ‘Hola Mundo’ desde TomEE

Por ahora hemos visto cómo efectuar un test Hola Mundo con Arquillian y WildFly, y cómo portar dicho test a Arquillian + TomEE.

Hoy veremos cómo ejecutar ese test contra GlassFish, usando su adaptador managed.

1.Configuración de dependencias

En teoría, lo único que debería cambiar a nivel de dependencias son las librerías de arquillian para GlassFish (cambio #1).

Modificaremos build.gradle para utilizar org.jboss.arquillian.container:arquillian-glassfish-managed-3.1:1.0.0.Final-SNAPSHOT, como sigue:

   // *****************************************************************************
   // GlassFish managed
   testRuntime 'org.jboss.arquillian.container:arquillian-glassfish-managed-3.1:1.0.0.Final-SNAPSHOT'

2. Código

El mismo que para WildFly y para TomEE.

3. Configuración de Runtime

Aunque tocaría ahora, ya hemos cambiado las dependencias de tiempo de ejecución para tests más arriba.

Debemos cambiar el archivo arquillian.xml para configurar el servidor managed de GlassFish (cambio #2), que quedará como sigue:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
   <container qualifier="glassfish-managed" default="true">
      <configuration>
         <property name="glassFishHome">/home/pagdev/pagde/tool/glassfish-4.1/</property>
      </configuration>
   </container>
</arquillian>

La única propiedad obligatoria de esta configuración es glassFishHome, que debe apuntar al directorio de instalación de GlassFish.

4. Ejecutar tests

Se ejecutan exactamente igual:

gradle test --tests *Test

Notas

Tan solo hemos tenido que hacer dos cambios para poder lanzar el test más simple posible usando un bean EJB.

Gradle tardó en ejecutar el test contra GlassFish managed alrededor de 18 segundos, y contra WildFly managed sobre los 11 segundos, y a lo largo de numerosas ejecuciones los tiempor variaron no más de 2 segundos. Contra Tomcat embedded, los tests se ejecutaron en torno a 6 o 7 segundos.

Software utilizado

Esto ha sido probado con GlassFish 4.1 y Arquillian 1.1.8, usando Gradle 2.3.

Arquillian y TomEE: portar ‘Hola Mundo’ de WildFly a TomEE

En teoría, Arquillian permite escribir tests de integración para varios servidores utilizando el mismo código y con unos cambios mínimos de configuración.

En este blog mi objetivo es usar Arquillian con TomEE en lugar de WildFly, y ver qué cambios debemos realizar para ejecutar el mismo test Hola Mundo que ya vimos en WildFly.

El objetivo es detallar todos y cada uno los cambios de configuración entre TomEE embedded y WildFly embedded.

1.Configuración de dependencias

Dado que no usamos ninguna funcionalidad específica de ningún servidor de aplicaciones, las dependencias de compilación y de test son las mismas que en el proyecto para WildFly.

En teoría, lo único que debería cambiar a nivel de dependencias son las librerías del runtime de Tomee (cambio #1), necesarias para arrancar y ejecutar TomEE en lugar de WildFly.

Modificaremos build.gradle para utilizar org.apache.openejb:arquillian-tomee-embedded:1.7.1, como sigue:

   // *****************************************************************************
   // TomeePlume: embedded
   testRuntime 'org.apache.openejb:arquillian-tomee-embedded:1.7.1'

Sin embargo, en la práctica, al ejecutar los test, nos encontramos con un error, java.lang.ClassFormatError, que se soluciona cambiando la dependencia de la API de JEE6 a javax:javaee-api:6.0 (cambio #2) para TomEE.

Como WildFly funciona perfectamente contra esta dependencia, este es más bien un cambio a hacer a la configuración de WildFly que a la de TomEE.

2. Código

El mismo que para WildFly.

3. Configuración de Runtime

Aunque tocaría ahora, ya hemos cambiado las dependencias para ejecutar TomEE en lugar de WildFly más arriba.

Por supuesto, debemos cambiar el archivo arquillian.xml para configurar el servidor embedded de Tomee (cambio #3), que quedará como sigue:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
   <container qualifier="tomee" default="true">
      <configuration>
      </configuration>
   </container>
</arquillian>

Y, sí, no hace falta configurar ningún parámetro para usar el TomEE embedded.

Otra cosa a tener en cuenta es que en la versión de WildFly con el adaptador embedded era obligatorio pasar a la máquina virtual del servidor el parámetro java.util.logging.manager, seleccionando el log manager de WildFly. Pero dicho gestor no está disponible en TomEE, por lo que no arrancará.

Solución: no pasar el parámetro java.util.logging.manager al runtime de TomEE (cambio #4).

4. Ejecutar tests

Se ejecutan exactamente igual.

En resumen

Hemos tenido que hacer cuatro cambios para poder lanzar el test más simple posible usando un bean EJB.

En realidad, si actualizamos la versión de WildFly para que use la dependencia org.apache.openejb:javaee-api:6.0-4 como API de JEE 6, con la que funciona perfectamente, el número de cambios es de 3, todos ellos triviales.

No está nada mal, ¿no?

Notas

Como nota aparte, comentar que Gradle tardó en ejecutar el test contra TomEE 5.856 segundos. El mismo test, lanzado contra WildFly con la configuración standalone.xml tardó 9.443 segundos.

Los tiempos en numerosas ejecuciones se mantuvieron siempre en torno a estos valores, con variaciones de no más de 1 segundo. Es una diferencia notable y que vale la pena mencionar, pero tampoco le daría mucha importancia, ya que el nuestro no es un escenario realista.

Software utilizado

Esto ha sido probado con TomeePlume 1.7.1 y Arquillian 1.1.8, usando Gradle 2.3.

Arquillian y WildFly: test ‘Hola Mundo’

Arquillian permite crear tests de integración con servidores/contenedores web y JEE como WildFly (o Tomcat), de forma bastante sencilla.

Para trabajar con un servidor, Arquillian usa adaptadores de contenedor, que permiten arrancar y parar los contenedores. Estos pueden ser de tres tipos: embebidos, gestionados, y remotos (embedded, managed y remote, a partir de ahora).

Para este Hola mundo usaremos el adaptador embedded, y dejaremos para otro artículo la discusión de qué ventajas e inconvenientes tiene cada tipo de adaptador y cuándo usar cada uno.

1. Configuración de dependencias

Para compilar los tests necesitaremos JUnit y Arquillian: con tan solo incluir junit:junit:4.11 y org.jboss.arquillian.junit:arquillian-junit-container:1.1.8.Final en build.gradle se incluirán todos los demás jars necesarios, especialment las dependencias de Arquillian.

Para ejecutar los tests, debemos incluir un adaptador de contenedor de WildFly, que Arquillian usará para arrancar y parar el contenedor contra el que se ejecutará el test. Como usaremos el contenedor embedded de WildFly, debemos incluir org.wildfly:wildfly-arquillian-container-embedded:8.2.0.Final en la lista de dependencias.

Por último, para que Gradle pueda encontrar algunas de estas dependencias deberemos añadir un repositorio de JBoss a nuestro build.gradle, que una vez hecho todo esto quedará como sigue:

apply plugin: 'java'

sourceCompatibility = 1.6

repositories {
  mavenCentral()
  maven {
    url "http://repository.jboss.org/nexus/content/groups/public-jboss"
  }
}

dependencies {
  compile 'javax:javaee-api:6.0'
  testCompile 'junit:junit:4.11',
    'org.jboss.arquillian.junit:arquillian-junit-container:1.1.8.Final'
  testRuntime 'org.wildfly:wildfly-arquillian-container-embedded:8.2.0.Final'
}

test {
  // Si no se especifica el log manager con el adaptador embedded de WildFly 8.2,
  // no arrancara correctamente
  systemProperties 'java.util.logging.manager': 'org.jboss.logmanager.LogManager'
}

2. Código

Para demostrar cómo implementar y ejecutar tests sencillos crearemos un stateless session bean, MyStatelessEjb, que simplemente suma dos números:

import javax.ejb.Stateless;

@Stateless
public class MyStatelessEjb {
   public int sum(int a, int b) {
      return a + b;
   }
}

Para hacer un test JUnit con Arquillian necesitaremos un runner de JUnit especial, incluido con Arquillian, para lo que anotaremos la clase de test con @RunWith(Arquillian.class).

También debemos especificar la configuración básica de la aplicación, para que el test se pueda ejecutar en el contexto adecuado. Debemos indicar dónde están los .class necesarios, si vamos a crear un war u otra cosa, quizá incluir un archivo de configuración como persistence.xml, etc.

Para ello se debe crear un método anotado con @Deployment, que utilizará la clase ShrinkWrap para llevar a cabo toda esta tarea de configuración.

El código resultante para hacer el test será como sigue:

@RunWith(Arquillian.class)
public class MyStatelessEjbTest {
   @EJB
   private MyStatelessEjb ejb;

   @Deployment
   public static Archive<?> createTestArchive() {
       return ShrinkWrap.create(WebArchive.class)
               .addPackage(MyStatelessEjb.class.getPackage());
   }

   @Test
   public void testSum() {
      Assert.assertEquals(5, ejb.sum(6,-1));
   }
}

Por lo demás, los tests son tests JUnit normales y corrientes, como se puede ver con testSum. Nótese que este test usa nuestro bean, que se ha inyectado en el test con @EJB, y que el propio contenedor WildFly gestiona.

3. Configuración de Runtime

Llegados a este punto, la aplicación compilará, pero aún es necesario dar unos pasos extra para que la aplicación arranque y podamos ejecutar los tests.

Las librerías adicionales necesarias en tiempo de ejecución ya las hemos incluido en un paso anterior, pero aún debemos configurar Arquillian. Esto se hace mediante arquillian.xml, que ubicaremos en el directorio src/test/resources del proyecto. Quedará como sigue:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://jboss.org/schema/arquillian
      http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
  <container qualifier="jboss-embedded" default="true">
    <configuration>
      <property name="jbossHome">/home/pagdev/pagde/tool/wildfly-8.2.0.Final</property>
      <property name="modulePath">/home/pagdev/pagde/tool/wildfly-8.2.0.Final/modules</property>
      <property name="serverConfig">standalone-full.xml</property>
    </configuration>
  </container>
</arquillian>

Aquí usamos la propiedad jbossHome, requerida, para indicar cuál es el directorio donde se halla JBoss (como JBOSS_HOME).

Esta versión del adaptador también requiere que se le pase el parámetro modulePath, que es donde se encuentran los módulos utilizables de JBoss. Por defecto están en el subdirectorio modules en JBoss. Es curioso que este parámetro sea obligatorio, dado que podrían utilizar un valor por defecto sin problemas.

También he añadido el parámetro serverConfig, que nos permite especificar qué configuración de servidor vamos a utilizar, y que no es requerido.

4. Ejecutar tests

Desafortunadamente necesitamos un pequeño workaround para un bug del adaptador embedded. Es necesario pasarle al contenedor WildFly arrancado por Arquillian el parámetro java.util.logging.manager, para el que simplemente utilizaremos el LogManager por defecto de WildFly. Esto es lo que hace el siguiente fragmento de build.gradle:


test {
  systemProperties 'java.util.logging.manager':
         'org.jboss.logmanager.LogManager'
}

Una alternativa a esto sería llamar a gradle desde la línea de comando pasándole ese parámetro. Para lanzar todos los tests del proyecto, por ejemplo, tendríamos la siguiente línea de comandos:

gradle test -Djava.util.logging.manager=org.jboss.logmanager.LogManager --tests *Test

Con la configuración actual de build.gradle, podemos lanzar el test con

gradle test --tests *Test

Por otro lado, si ejecutamos los tests desde Eclipse y no usando Gradle desde la línea de comandos, habrá que indicarle al launcher correspondiente que pase a la VM el parámetro -Djava.util.logging.manager=org.jboss.logmanager.LogManager.

Si queremos ejecutar estos tests desde la línea de comandos, y depurarlos con Eclipse, vale la pena consultar este otro artículo.

Software utilizado

Esto ha sido probado con WildFly 8.2 y Arquillian 1.1.8, usando Gradle 2.3.

Bean Validator: a custom validator that checks against other field validations

Sometimes I just want to apply to a property exactly the same Bean Validator validations I apply to another property.

Point in case: I have a password property in a User class, and there is a ChangePassword data transfer object somewhere else that happens to have currentPassword, newPassword and repeatPassword fields, used precisely to change User.password.

It is obvious that I will want to apply the validations for User.property to all these fields. And, of course, I want changes to validation rules in the original property to be taken into account, automatically.

Copying them is a no-no, because we will violate the DRY (Don’ Repeat Yourself) rule, and creating a custom validation annotation, say @Password, might be overkill. And it would not convey the right meaning, or that’s how I see it.

Creating some kind of DelegateValidation validator that replicates other validations makes a lot of sense, as it clearly conveys the intention and keeps code DRY.

Well, here is the code for the validation annotation itself:


@Target( { ElementType.METHOD, ElementType.FIELD, 
           ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DelegateValidationValidator.class)
@Documented
public @interface DelegateValidation {  
   Class<?> clazz();
   String   property();

   Class<? extends Payload>[] payload() default {};
   String message() default "";
   Class<?>[] groups() default {};
}

Here clazz and property are the class and property whose validations we will replicate to validate our own property.

For example, if we have a ValidatedClass like this one,

public static class ValidatedClass {
   public static final String OTHER_PROPERTY= "otherProperty";
      
   @Length( min = 3)
   @NotNull
   @Size(min = 4)
   public String otherProperty;
      
   @NotNull
   public String getSecondProperty() {
      return "hi";
   }
}

we can apply the validations in that class otherProperty and secondProperty (corresponding to the getSecondProperty getter) to other properties in our MyClass class as follows:

public static class MyClass {
   @DelegateValidation(clazz=ValidatedClass.class, 
                       property=ValidatedClass.OTHER_PROPERTY)
   public String value;

   @DelegateValidation( clazz=ValidatedClass.class, 
                        property="secondProperty")
   public String value;
}

Here is the validator implementation:

public class DelegateValidationValidator implements ConstraintValidator<DelegateValidation, Object> {

   private static transient Validator globalValidator;
   
   public static void setGlobalValidator(Validator v ) {
      assert v != null;
      assert globalValidator == null;
      
      globalValidator = v;
   }
   
   @Inject
   private transient javax.validation.Validator validator;
   
   private Validator getValidator() {
      if( this.validator == null ) {
         assert globalValidator != null: 
            "A validator has not been injected: please, supply one via " +
            "setGlobalValidator";
         return globalValidator;
      }
      return this.validator;
   }
   
   
   private Class<Object> referencedClass;
   
   private String property;

   // To make happy very demanding compiler with regard to generic parameters
   @SuppressWarnings("unchecked") 
   @Override
   public void initialize(DelegateValidation constraintAnnotation) {
      // To make happy very demanding compiler with regard to generic parameters
      this.referencedClass = (Class<Object>)constraintAnnotation.clazz();
      this.property = constraintAnnotation.property();
   }

   @Override
   public boolean isValid(Object value, ConstraintValidatorContext context) {
      Set<ConstraintViolation<Object>> constraints = 
          getValidator().validateValue(this.referencedClass,
                                       this.property, value);
      if( constraints.isEmpty() ) {
         return true;
      }

      context.disableDefaultConstraintViolation();
      for( ConstraintViolation<Object> c : constraints) {
         context.buildConstraintViolationWithTemplate(
            c.getMessage()).addConstraintViolation();
      }
      return false;
   }
}

Here we must get access to the Validator we are using to peform validations to ask it to perform validations against the tracked property. Since I use CDI, as well as the Seam validtor module, I rely on them to inject the right Validator instance in my validator implementation.

However, if you are not using CDI/Seam, or just to make testing easier, I am providing a way to supply a validator via the setGlobalValidator method, that you should call before any validations are performed.

Last but not least, let me tellyou that, if you provide a wrong property name for the validator, the validation will fail with a ValidationException, as attested by some of the following tests.

The tests

For the sake of completeness, here are my tests, written against TestNg:

public class DelegateValidationTest {

   public static class ValidatedClass {
      public static final String OTHER_PROPERTY= "otherProperty";
      
      @Length( min = 3)
      @NotNull
      @Size(min = 4)
      public String otherProperty;
      
      @NotNull
      public String getSecondProperty() {
         return "hi";
      }
      
      private Integer propertyWithNoValidations;
   }

   private Validator validator;
   
   @BeforeClass
   void classSetUp() {
      if( this.validator == null ) {
        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
        DelegateValidationValidator.setGlobalValidator(this.validator);
      }
   }
   
   public static class MyClass {
      @DelegateValidation(clazz=ValidatedClass.class, property=ValidatedClass.OTHER_PROPERTY)
      public String value;
   }
   
   @Test
   public void testValueValidation() {
      Assert.assertEquals( this.validator.validateValue(
         MyClass.class, "value", null).size(), 1);
      Assert.assertEquals( this.validator.validateValue(
         MyClass.class, "value", "ab").size(), 2);
      Assert.assertEquals( this.validator.validateValue(
         MyClass.class, "value", "abc").size(), 1);
      Assert.assertTrue( this.validator.validateValue(
         MyClass.class, "value", "abcd").isEmpty());
   }

   @Test
   public void testWholeBeanValidation() {
      ConstraintViolation<MyClass> firstV;
      ConstraintViolation<MyClass> secondV;
      Iterator<ConstraintViolation<MyClass>> it;
      
      MyClass mc = new MyClass();
      mc.value = "ab";
      Set<ConstraintViolation<MyClass>> violations = 
         this.validator.validate(mc);
      Assert.assertEquals( violations.size(), 2);
      it = violations.iterator();
      firstV = it.next();
      secondV = it.next();
      
      // We expect MyClass, even if validations are run 
      // against ValidatedClass
      Assert.assertEquals( firstV.getRootBeanClass(), 
         MyClass.class);
      // We expect "value", even if validations are run 
      // against "otherProperty"
      Assert.assertEquals( firstV.getPropertyPath().toString(), 
         "value");
      // We expect MyClass, even if validations are run 
      // against ValidatedClass
      Assert.assertEquals( secondV.getRootBeanClass(), 
         MyClass.class);
      // We expect "value", even if validations are run 
      // against "otherProperty"
      Assert.assertEquals( secondV.getPropertyPath().toString(), 
         "value");
   }
   
   public static class MyClassWithWrongDelegationProperty {
      @DelegateValidation(clazz=ValidatedClass.class, 
                          property="wrongPropertyName")
      public String value;
   }
   
   @Test(expectedExceptions={ValidationException.class})
   public void testReferencePropertyDoesNotExist() {
      this.validator.validateValue(
         MyClassWithWrongDelegationProperty.class, "value", "ab");      
   }
   
   public static class MyClassTrackingGetter {
      @DelegateValidation( clazz=ValidatedClass.class, 
                           property="secondProperty")
      public String value;
   }
   
   @Test
   public void testWorksWithGetters() {
      Assert.assertEquals( this.validator.validateValue(
         MyClassTrackingGetter.class, "value", null).size(), 1);
      Assert.assertTrue( this.validator.validateValue(
         MyClassTrackingGetter.class, "value", "a").isEmpty());
   }
   
   public static class MyClassTrackingPropertyWithNoValidations {
      @DelegateValidation( clazz=ValidatedClass.class, 
                           property="propertyWithNoValidations")
      public Integer value;
   }
   
   @Test
   public void testWorksWithPropertyHavingNoValidations() {
      Assert.assertTrue( this.validator.validateValue(
         MyClassTrackingPropertyWithNoValidations.class, "value", null).
            isEmpty());
   }
}

Against foggy money and the “Cheaper is Good but Expensive can Be Better” Law

Two days ago I wrote about some ugly problems with Glassfish/Weld and I ended up talking about how hard is it to get a good rythm for writing and testing code in a JEE environment.

Time is money. If your train of thought is broken due to delays, then you are wasting even more money than you might think.

Why not spend money in tools to avoid throwing it away in low quality work time? Tools like JRebel. It reloads classes at runtime when you modify them, without requiring that you pack them in jars/wars/ears again and restart the application(s). Almost zero code to deployment time in many cases. Nice!

But there is a problem: money spent in tools or libraries tends to be too solid, and therefore the ones making the purchase decision become a bit too noticeable.

Developers do not use JRebel not because it is not reasonable to do so, but because somebody has to pay with solid money for it. Solid money is money that’s very clearly seen as it dissapears, because there is an invoice backing the expenditure. You can be caught spending solid money.

If we spend 1.000 euros in a tool, we might be seen doing the expenditure. However, if we spend 10.000 euros in wasted time, who the hell is gonna “see” that? This money is foggy money, money that evaporates without a trace.

Not investing in high quality development tools and environments or training is a clear case of cheap is good but expensive is better. It is death by a thousand cuts.

Unfortunately, using bad free but expensive tools & techniques is preferred all too often to using non free but cheap tools & techniques.

Refusing to do what we think is right for fear of finger pointing, all the time, is a proof of lack of maturity, and I doubt great software can be created if worrying and politics take the breath out of you.

It is time for the industry to mature and make a quantum leap towards responsibility, accountability and quality. Time to stop diverting energies away from finger pointing and penny pinching, and to start thinking big & deep.

Because there is no other way to be great!

Just thinking…

Findbugs 2.0: an update

I’m a big advocate of FindBugs, to which have devoted several posts.

My experience in “porting” a project to use FindBugs 1.x to the maximum extent possible was pretty good, and I decided to define a set of recommendations for using FindBugs in all new projects.

These days I have decided to take a look at FindBugs 2.0, and I have to say that the experience was good. It felt unobtrusive, worked well, and found some new potential issues. These were not real issues for my project, but would have been in most other projects, and it felt good to have them pinpointed.

FindBugs 2.0 works like 1.x did: in fact, the recommendations I laid out for using 1.x remain unchanged nd are fully applicable. Nothing new under the sun, just small improvements well worth it.

By the way, I decided to add FindBugs plugins, that help check additional potential issues. I’m using fb-contrib. I have to say that I did not find additional potential issues in my code, but this might be just my project. Since this did no harm, I’m keeping these extra checks.

Kudos to the FindBugs guys for their 2.0 release. I hope they keep the good work, FindBugs is a nice and helpful tool that everybody should use if they are concerned with code quality.

Making sure Hibernate does not leak sessions/entity managers

Resource cleanup is a must for all applications, especially long-running ones.

The most precious Hibernate resource requiring cleanup is the Session. If you are using JPA, as I frequently do, a session is wrapped by an EntityManager, but is still there.

In order for your apps to behave, you will need to make sure you are not forgetting to release sessions, or you’ll end up having trouble sooner than later.

To ensure that, you’ll need a way to check whether, after finishing some self contained operation, you have released all sessions you allocated -but not more.

To do that, I created a utility class, called SessionAllocationChecker. How do you use it? The typical usage is as follows:

SessionAllocatorChecker checker = new SessionAllocatorChecker( 
   myHibernateSessionFactory );
try {
  // ... code that opens and closes hibernate sessions/entity managers
}
finally {
  checker.dispose();
}

Here, if there are 5 sessions open when checker is created, and 8 sessions open when dispose is called, you will get an AssertionError, telling you you’ve got 3 extra sessions open you probably forgot to close.

How does it do that?

Now, how does it work? When an instance of SessionAllocationChecker is allocated, it checks the statistics maintained by Hibernate and notes the number of open sessions vs. the number of closed sessions at the moment: the difference will be the number of effectively open sessions at creation time.

If Hibernate statistics are not enabled, it will enable them -and quietly disable them when you call dispose.

Next, when you call getOutstandingSessions or hasOutstandingSessions, it performs the same operation, to check the number of currently open sessions. getOutstandingSessions will return the number of sessions currently open minus the number of session open at creation time, that is, the number of additional sessions you have open and not closed yet.

When you call the dispose method, it ensures Hibernate statistics enabled state is restored to its old status. Besides, it will check whether you have session leakage, by calling getOutstandingSessions , but only if you are executing you app with the JVM -enableassertions option. If that’s the case, an AssertionError exception will be thrown.

If the -enableassertions flag is not set, you will have to check the number of outstanding sessions by calling getOutstandingSessions on you own.

While rough, I found this trick extremely useful: in fact, I use this class to check that I never forget to release sessions/entity managers in my own tests.

Another place where you can put this to good use is in web app filters, to ensure that your requests do not leak sessions.

Here is the full source code:

package com.softwarementors.bzngine.engines.hibernate;

import javax.persistence.EntityManagerFactory;

import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.stat.Statistics;

public class SessionAllocationChecker {

  private boolean statisticsEnabledOnEntry;

  private long outstandingSessionsOnEntry;

  private SessionFactory factory;

  private boolean disposed;

  public SessionAllocationChecker( SessionFactory factory ) {
    assert factory != null;
    
    this.factory = factory;
    Statistics statistics = factory.getStatistics();
    this.statisticsEnabledOnEntry = statistics.isStatisticsEnabled();
    statistics.setStatisticsEnabled(true);
    this.outstandingSessionsOnEntry = 
       statistics.getSessionOpenCount() 
       - statistics.getSessionCloseCount();
  }
  
  public SessionAllocationChecker(EntityManagerFactory factory) {
    this( ((HibernateEntityManagerFactory) factory).getSessionFactory() );
  }
  
/*  
  public SessionAllocationChecker( EntityManager entityManager ) {
    this( entityManager.getEntityManagerFactory());
  }
  
  public SessionAllocationChecker( Session session ) {
    this(session.getSessionFactory());
  }
*/  

  public boolean isDisposed() {
    return this.disposed;
  }
  
  public long getOutstandingSessions() {
    assert !isDisposed();

    Statistics statistics = this.factory.getStatistics();
    assert statistics.isStatisticsEnabled() : 
       "Somebody else disabled the statistics: avoid that";
    long outstandingSessions = statistics.getSessionOpenCount() 
       - statistics.getSessionCloseCount();
    long sessionsNotReleasedAfterCreation = outstandingSessions 
       - this.outstandingSessionsOnEntry;

    assert sessionsNotReleasedAfterCreation >= 0 : 
       "Somebody else played with the statistics " + 
       "(maybe cleared/disabled them?): avoid that";
    return sessionsNotReleasedAfterCreation;
  }

  public boolean hasOutstandingSessions() {
    assert !isDisposed();
    
    return getOutstandingSessions() > 0;
  }

  public void dispose() {
    if (!isDisposed()) {     
      assert !hasOutstandingSessions() : "There are " 
         + getOutstandingSessions() + 
         " additional sessions open and not closed";

      this.disposed = true;
      
      // Reset statistics to the state they were before we started to
      // be interested in open/closed session count
      Statistics statistics = this.factory.getStatistics();
      statistics.setStatisticsEnabled(this.statisticsEnabledOnEntry);
    }
  }
}

If you take a look at the code, you will find I have added lots of sanity checks via assertions: using the Hibernate statistics as I do is not completely foolproof, if you clear them or disable them between the moment you created the checker and you disposed it, you will get incoherent readings that will fool SessionAllocationChecker.

While I have provided lots of checks, they are not completely safe -not could they be, given the way statistics are implemented by Hibernate. Keep an open eye, and all will be well.

You’ll have noticed that I use assertions all around: you can read this article if you are interested in knowing why I’m such a fan of assertions.

By the way, if you don’t have at hand a SessionFactory or a EntityManagerFactory to pass it to the SessionAllocationChecker constructor, all is not lost: you can get them from a Session or an EntityManager. How to do it is part of the code: just look at the commented code.

Believe it or not, the code is commented because I just have no tests for it in the library where SessionAllocationChecker belongs.

No test, no public exposure -at least, as part of a high quality library.