Mostrando entradas con la etiqueta Axis. Mostrar todas las entradas
Mostrando entradas con la etiqueta Axis. Mostrar todas las entradas

viernes, 26 de diciembre de 2008

Lessons learned using GWT, Axis and JPA simultaneously

Note: This article is solely focused on some issues related to the EntityManager that may appear working with GWT, Axis and JPA. If you are interested in the performance of JPA, this other post about a comparison among different JPA implementations (OpenJPA, Hibernate, OpenJPA and Eclipselink) may be of some interest for you.

In my experience with the three technologies which appear in the title of this article, I had to face a number of problems, actually quite easy to solve once you know what all is about.
So I decided to publish this, for others not to have to face these same problems.
Here you have some lessons learned that should be taken into account when working with the Google Web Toolkit, Axis and any of the JPA implementations out there.
You have this same article in Spanish here.

These are the facts:
  • EntityManager can not be shared among threads. This is specified in JPA, but you don't give it enough importance until you start using JPA from the web (which will probably be the usual case).
  • Second, Axis creates a new object (and thread) for each call to the server.
  • Third, contrary to Axis, when you use one of the so-called GWT server remote services, only one object and thread are created and this object is reused everytime the remote service is called. That is to say, GWT does not create a new object for every new call.
As a result of these facts, we have these consequences:
  • If we use a static EntityManager in a call to Axis, the code will fail as soon as two threads cross with each other (quite usual in a web application), given that they will be using the same EntityManager object.
  • Contrary to Axis, in the case of GWT it is not necessary that EntityManager is static to start generating problems; it will be enough that it is a global attribute belonging to the object that implements the GWT remote service. Being one only object that answers all petitions, two calls to the server will use the same EntityManager and this will make the code fail.
So, we can conclude two rules:
  • An Axis service must therefore create a new EntityManager and this can not be static.
  • Each method of a GWT remote service must create a new EntityManager for individual use (it is not enough that it creates it at a global level).
If any of these two rules are not followed, 2 simultaneous calls will use the same EntityManager at the same time, producing exceptions.
Unfortunately, JPA exceptions are not especially explicit. This is common to almost every implementation of JPA: they give almost no concrete information (including Hibernate).

The EntityManagerFactory can and should be shared among different threads, given that it is a quite expensive object to create.

In short, if you don't want to have problems and you would like to have a piece of code that can be used both by GWT and Axis, you could write something like this:
public class JPAManager {
private final static EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPAImplementationTest");
private EntityManager em = emf.createEntityManager();
public EntityManager getEntityManager () {
return em;
}
}
Each method called in an invocation to a web service or to a GWT server remote service should create its own JPAManager and get the EntityManager with the public method. This way you will avoid having problems among the different threads created in the invocations.

You could also use more sophisticated solutions like implementing a singleton (with the problems associated to them) or using the ServletContextListener, as someone suggested in the comments. But this simple solution worked for me and resisted some load tests.

martes, 23 de diciembre de 2008

Lecciones aprendidas en el uso simultáneo de GWT, Axis y JPA

En mi experiencia con las tres tecnologías que dan nombre al artículo, me he encontrado con algunos problemas bastante fáciles de resolver una vez sabes de qué se trata (como siempre). Por eso, y para evitar que otros se peguen con lo mismo, aquí van una serie de lecciones aprendidas que deben ser tenidas en cuenta a la hora de trabajar con el Google Web Toolkit, Axis y cualquiera de las implementaciones de JPA. Para una comparativa detallada entre diferentes implementaciones de JPA (Hibernate, Toplink, Eclipselink y Openjpa), tienes este otro artículo disponible.
Si quieres ver este artículo en inglés, lo tienes disponible aquí.

Pues bien, los hechos:
  • En primer lugar, así como el EntityManagerFactory puede ser compartido por diferentes objetos (e hilos), el EntityManager no puede ser compartido entre diferentes threads. Esto está especificado en JPA, pero no le das importancia hasta que empiezas a usar JPA desde la web (que será el caso más habitual).
  • En segundo lugar, Axis crea un nuevo objeto (e hilo) por cada petición que entra al servidor.
  • En tercero, y al contrario que con Axis, al utilizar uno de los llamados servicio remotos de servidor del GWT se crea un objeto (y thread) que es posteriormente reutilizado cada vez que se llama a dicho servicio. Es decir, no se crea uno nuevo por cada uno que entra.
Las consecuencias a las que dan lugar los hechos:
  1. Si utilizamos un objeto EntityManager estático en una llamada a Axis, el código fallará en el momento en que dos hilos se crucen (bastante normal en la web), al utilizar el mismo objeto los 2.
  2. En el caso de GWT, al contrario que en Axis, no es necesario que el EntityManager sea estático para que de problemas (que también los dará, claro); bastará con que sea un atributo global dentro del objeto que implementa el servicio remoto. Al ser un objeto único el que responde a las peticiones, 2 llamadas al servidor utilizarán el mismo EntityManager y esto hará que código falle.
  3. Un servicio de Axis deberá, por tanto, crear un nuevo EntityManager, y éste no podrá ser estático.
  4. Cada método de un servicio remoto de GWT tiene que crear un nuevo EntityManager para uso individual (no vale que lo cree el objeto a nivel global, ver punto 2. Si no lo hace, 2 llamadas simultáneas al mismo servicio remoto utilizarán a la vez el mismo EntityManager, dando inmediatamente lugar a excepciones.
Por desgracia, las excepciones de JPA son muy poco explícitas. Esto es común para casi todas las implementaciones de JPA: dan poca información concreta de errores (incluyendo a Hibernate).

El EntityManagerFactory sí puede ser compartido por los diferentes hilos.

En definitiva, si no queréis tener problemas y queréis usar un código que puedan utilizar tanto GWT como las llamadas a servicios web a través de Axis, podríais usar un código como el siguiente:

public class JPAManager {
private final static EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPAImplementationTest");
private EntityManager em = emf.createEntityManager();
public EntityManager getEntityManager () {
return em;
}
}

Cada método llamado en la invocación del web service o del servicio remoto de servidor de GWT deberá crear su propio JPAManager. De esta forma, os evitáis tener problemas entre los diferentes hilos creados en las invocaciones.

Se pueden usar métodos más sofisticados, como implementar un singleton o usar el ServletContextListener, como ha puesto alguno en los comentarios. Pero lo cierto es que este sencillo método funcionó y resistió algunas pruebas de carga.