Sustainable peace with database changes into a Java environment

12/08/2013 Leave a comment

Sustainable peace for us is remove uncertainty. In this case over database changes the idea Active Record Migrations of Ruby was welcomed.

And what does migration means for us?. Well, it is a convenient way to alter our database schema overtime in a consistent and easy way that removes a lot of uncertainty about database changes in our software development process.

Goal

Our goal will be maintaining the lifecycle of the database according to the development and evolution of the project with an absolute control over the changes.

For this we have to look for a simple tool with a basic group of characteristics as the following ones:

  • Works with any database although now our database is MySQL.
  • Enable concurrent developers to work independently.
  • Enable different development environments.
  • Able to integrate with any version control system.
  • Able to integrate easily migration tasks into Apache Ant.
  • Allow forward and backward migrations and conflicts easily manageable.

We select MyBatis Migrations tool as the best solution for us and a GitHub repository Ant Script to run MyBatis Migrations’ commands as a start line.

Let’s go to the point: How we work with migrations

With these tools we think that a lifecycle of migration may be like this one

The first time
  • Create a migrations directory into our project directory.
  • Download MyBatis Schema migrations file mybatis-migrations-3.1.1-bundle.zip.
  • Create a lib directory and copy mybatis-3.2.3.jar and mybatis-migrations-3.1.1.jar files.
  • Download Ant tasks build.properties and build.xml files from mybatis-migrations-anttasks-master.zip and rename it as migrations.properties/xml for clearer goals.
  • Obviously, this files define ant tasks and basic properties for migrations tool while migrations.properties (comments are included for clearly) defines
    
    # Default environment
    mybatis.default.environment=development
    
    mybatis.dir=migrations
    mybatis.lib.dir=${mybatis.dir}/lib
    
    mybatis.repository.dir=${mybatis.dir}/db
    
    # This directory contains your migration SQL files. These are the files 
    # that contain your DDL to both upgrade and downgrade your database 
    # structure. By default, the directory will contain the script to 
    # create the changelog table, plus one empty example migration script. 
    mybatis.scripts.dir=${mybatis.repository.dir}/scripts
    
    # Place your JDBC driver .jar or .zip files in this directory.
    # Upon running a migration, the drivers will be dynamically loaded.
    mybatis.drivers.dir=${mybatis.repository.dir}/drivers
    
    # In the environments folder you will find .properties files that 
    # represent your database instances. By default a development.properties 
    # file is created for you to configure your development time database 
    # properties.
    # You can also create test.properties and production.properties 
    # files. The properties file is self documented.
    mybatis.env.dir=${mybatis.repository.dir}/environments
    
    

    and migrations.xml defines ant tasks as you can see in the original documentation. Of course, you must rename it as xml file descriptor property to load it

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="MyBatis Migrations" basedir="." 
             default="db:migrate:status">
    
    	<property file="migrations/migrations.properties" />
    
    .....
    </project>
    
  • But, how to install it … It’s easy, basically we have to execute:
    $ ant -f migrations.xml db:migrate:init
    

    It creates directories and the initial files as they were defined in migrations.properties as you can see in this output log

    Buildfile: /wpr/myproject/migrations/migrations.xml
    
    db:migrate:init:
         [echo] ** Executing "migrate init" on "development" environment **
         [java] ------------------------------------------------------------
         [java] -- MyBatis Migrations - init
         [java] ------------------------------------------------------------
         [java] Initializing: db
         [java] Creating: environments
         [java] Creating: scripts
         [java] Creating: drivers
         [java] Creating: README
         [java] Creating: development.properties
         [java] Creating: bootstrap.sql
         [java] Creating: 20131123174059_create_changelog.sql
         [java] Creating: 20131123174100_first_migration.sql
         [java] Done!
         [java] 
         [java] ------------------------------------------------------------
         [java] -- MyBatis Migrations SUCCESS
         [java] -- Total time: 2s
         [java] -- Finished at: Sat Nov 23 18:41:00 CET 2013
         [java] -- Final Memory: 1M/117M
         [java] ------------------------------------------------------------.
    
    BUILD SUCCESSFUL
    Total time: 3 seconds
    

    while

    • environments, scripts and drivers are directories (as seen before).
    • README, that explains directories contents as the name suggests.
    • bootstral.sql, in which you have to include the database actual schema. You need to start from a known state.
    • 20131123174059_create_changelog.sql contains a default control table for migration tool. It’s a price that you have to pay.
    • 20131123174100_first_migration.sql will be your first SQL migration file. You can delete it or rename it for clearly although you must keep the format as yyyymmddHHMMss_.
  • Keep migrations/db/environment/development.properties database properties for development environment
    ## JDBC connection properties. 
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/<databaseName>
    username=root
    password=root
    
  • Add others environment properties files to each migrations/db/environment/<environment>.properties if you need.
  • As last step, put your actual database schema into bootstrap.sql file.
Day by day

Among all migrate commands we normally use

Optional steps included:

  • Revert migrations if necessary to solve conflicts. Any mistake has an easy solution with db:migrate:down .. but remember that it is by single steps.
  • Apply pending migrations out of order if it’s safe to do so with db:migrate:pending or db:migrate:version. Actually, if you want to execute those tasks you will have to add the code belong into migrations.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project name="MyBatis Migrations" basedir="." default="db:migrate:status">
    ....
    
    	<!-- $ migrate pending -->
    	<target name="db:migrate:pending" description="Runs all pending migrations regardless of their order or position in the status log">
    		<migrate command="pending" environment="${environment}" />
    	</target>
    
    	<!-- $ migrate version -->
    	<target name="db:migrate:version" description="Migrate the schema to any specific version">
    		<input addproperty="specific.version" message="Specific version to migrate:" />
    		<migrate command="version" environment="${environment}">
    			<extraarguments>
    				<arg value="${specific.version}" />
    			</extraarguments>
    		</migrate>
    	</target>
    
    </project>
    
  • Generate migration scripts to be run “offline” in environments that are beyond your control.
  • Get the status of the system at any time doing db:migrate:status.

We hope you find useful our solution, all comments are welcomed and apologies for my english.

Hacer un proxy de un objeto

Estaba ojeando la parte de reflexión de Guava y he descubierto lo facil que es hacer un Proxy de un Objeto. El inconveniente es que solo se pueden hacer proxys de una interfaz, pero hay forma de poderlo apañar.

Por ejemplo, Date no es ninguna interfaz, y quiero hacer fechas que tengan los milisegundos a cero. Para ello me hago una interfaz Proxy que voy a rellenar con mi nuevo codigo.


import static org.junit.Assert.assertEquals;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

import org.junit.Test;

import com.google.common.reflect.Reflection;

public class ProxyTest {

	@Test
	public void dateZeroMillis() {
		Date withMillis = new Date();
		@SuppressWarnings("unchecked")
		Proxy<Date> proxyObject = Reflection.newProxy(Proxy.class,
				new RemoveMillis(withMillis));
		Date date = proxyObject.get();
		System.out.println("Date was: " + withMillis.getTime()
				+ " but now is: " + date.getTime());
		assertEquals(0, date.getTime() % 1000);
	}

	public static class RemoveMillis implements InvocationHandler {

		private Date date;

		public RemoveMillis(Date date) {
			this.date = date;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			return new Date((date.getTime() / 1000) * 1000);
		}

	}

	public static interface Proxy<T> {
		T get();
	}
}

La salida del test es esta:
Date was: 1383914151227 but now is: 1383914151000

Categories: Uncategorized

Chain of responsibility eventbus

Hoy vengo a hablar de una nueva librería java que resuelve una limitación del patrón Cadena de responsabilidad:

http://es.wikipedia.org/wiki/Chain_of_Responsibility_%28patr%C3%B3n_de_dise%C3%B1o%29

Haciendo un breve resumen, con este patrón podemos modularizar nuestro código. Esto es bueno, porque obligamos a desarrollar el contenido en pequeñas partes, para luego ser reutilizado, eliminado y añadido mas facilmente. Estas pequeñas partes se ejecutarán en un orden, una detrás de otra, hasta que la cadena termine o un eslabón decida que no quiere continuar con la cadena.

Los eslabones no deberian tener referencias a otros eslabones concretos, porque estariamos rompiendo la modularidad. Generalmente, tienen una referencia al siguiente eslabón, pero se maneja en forma de interfaz.

El problema es el siguiente

Este patrón funciona muy bien cuando hay que desarrollar una serie de acciones aisladas, que poco tienen que ver unas con otras, ¿pero que pasa si algun eslabón necesita de un objeto que ya ha sido recuperado previamente por otro eslabón?. Con la habitual implementación habría que volverlo a recuperar. Esto puede ser lento y se puede repetir en muchas ocasiones a lo largo de toda la cadena.

Esta nueva librería aporta una solución sencilla a este problema. Consiste en la posibilidad de que las diferentes cadenas puedan postear cualquier objeto a un bus de eventos. El propio bus de eventos se encargará de asignar esos objetos al resto de eslabones que esten suscritos a objetos de ese tipo.

Os dejo el link:

https://code.google.com/p/chain-of-responsibility-eventbus/

Categories: Uncategorized

Programación orientada a aspectos con Guice

Me gusta la programación orientada a aspectos por la cantidad de ifs que te quita. Ademas son ifs que tienes que ir repitiendo a lo largo del código.
Se me ha ocurrido hacer un pequeño ejemplo con JUnit. Vamos a hacer las tipicas comprobaciones de:

Las vamos a hacer de las dos formas, con aspectos y sin aspectos, demostrando que el resultado es el mismo, pero con aspectos queda mas limpio.

Antes de empezar con el Test, hay que configurar Shiro. Explicandolo por encima, tenemos un fichero que se llama shiro.ini, donde he definido los usuarios con su rol.

[users]
root = secret, admin
guest = guest, guest

[roles]
admin = *

También hay que configurar el modulo de Shiro para Guice, donde se le dice que se configure en base al contenido de shiro.ini:

package com.tododev.shiro;

import org.apache.shiro.config.Ini;
import org.apache.shiro.guice.ShiroModule;
import org.apache.shiro.realm.text.IniRealm;

import com.google.inject.Provides;

public class MyShiroModule extends ShiroModule{

	protected void configureShiro() {
        try {
            bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
        } catch (NoSuchMethodException e) {
            addError(e);
        }
    }

    @Provides
    Ini loadShiroIni() {
        return Ini.fromResourcePath("classpath:shiro.ini");
    }

}

Ahora vamos ya con el test. Este test contiene 4 metodos de test, que comprueban lo siguiente sobre el método de la interfaz Interface.doSomething:

  • Si no estas autenticado lanza una UnauthenticatedException.
  • Si no tienes el rol de “admin” lanza una UnauthorizedException.
  • Si le pasas el parámetro a nulo lanza una ConstraintViolationException.
  • Si estas autenticado, con el rol de “admin” y no pasas el parámetro a nulo, no lanza excepciones.

La gracia es que el test lo vamos a hacer con dos implementaciones diferentes de Interface.class:

  • Aop.class
  • NoAop.class

Es decir, se van a hacer 2 x 4 = 8 tests. El resultado tiene que se el mismo.


package com.tododev.shiro;

import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.constraints.NotNull;

import org.apache.bval.guice.Validate;
import org.apache.bval.guice.ValidationModule;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.guice.aop.ShiroAopModule;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

/**
 * Vamos a hacer que el test se ejecute con la clase Parameterized. Esta clase
 * nos ejecutara 2 veces todos los tests, insertando en el constructor los
 * valores del metodo data().
 * 
 * @author jorge
 * 
 */
@RunWith(Parameterized.class)
public class GuiceAopTest {

	private final static String ADMIN_ROLE = "admin";
	private final UsernamePasswordToken ADMIN = new UsernamePasswordToken(
			"root", "secret");
	private final UsernamePasswordToken GUEST = new UsernamePasswordToken(
			"guest", "guest");
	private final Injector injector;

	/**
	 * Cuando se crea la instancia de GuiceAopTest.class, se le inserta uno de
	 * los valores del metodo data().
	 * 
	 * @param injector
	 */
	public GuiceAopTest(Injector injector) {
		this.injector = injector;
	}

	/**
	 * Devuelve una coleccion que contiene un inyector con AOP y otro que no. La
	 * idea es demostrar que los tests pasan satisfactoriamente de las dos
	 * formas posibles.
	 * 
	 * @return
	 */
	@Parameters
	public static Collection<Object[]> data() {
		Injector withAop = Guice.createInjector(new ShiroAopModule(),
				new MyShiroModule(), new ValidationModule(),
				new AbstractModule() {
					@Override
					protected void configure() {
						bind(Interface.class).to(Aop.class);
					}
				});
		Injector noAop = Guice.createInjector(new MyShiroModule(),
				new AbstractModule() {
					@Override
					protected void configure() {
						bind(Interface.class).to(NoAop.class);
					}
				});

		Object[][] data = { { noAop }, { withAop } };
		return Arrays.asList(data);
	}

	public static interface Interface {
		void doSomething(String value);
	}

	/**
	 * Implementacion preparada para tener aspectos.
	 * 
	 * @author jorge
	 * 
	 */
	public static class Aop implements Interface {

		@Validate
		@RequiresRoles(ADMIN_ROLE)
		@Override
		public void doSomething(@NotNull String value) {
			System.out.println(getClass() + ": " + value);
		}

	}

	/**
	 * Hace lo mismo que hace Aop, pero esta no usara aspectos.
	 * 
	 * @author jorge
	 * 
	 */
	public static class NoAop implements Interface {

		@Override
		public void doSomething(String value) {
			if (value != null) {
				Subject currentUser = SecurityUtils.getSubject();
				if (currentUser.isAuthenticated()) {
					if (currentUser.isPermitted(ADMIN_ROLE)) {
						System.out.println(getClass() + ": " + value);
					} else {
						throw new UnauthorizedException();
					}
				} else {
					throw new UnauthenticatedException();
				}
			} else {
				throw new ConstraintViolationException(
						new HashSet<ConstraintViolation<?>>());
			}
		}

	}

	/**
	 * Preparamos Shiro para ser usado.
	 */
	@Before
	public void before() {
		SecurityManager securityManager = injector
				.getInstance(SecurityManager.class);
		SecurityUtils.setSecurityManager(securityManager);
	}

	/**
	 * Deslogueamos al usuario si estuviera logueado.
	 */
	@After
	public void after() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.logout();
	}

	/**
	 * Vefirica que si no estas logueado salta una UnauthenticatedException
	 */
	@Test(expected = UnauthenticatedException.class)
	public void unauthenticated() {
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
		fail("Unreachable code");
	}

	/**
	 * Verifica que si no tienes los permisos adecuados salta una
	 * UnauthorizedException
	 */
	@Test(expected = UnauthorizedException.class)
	public void unauthorized() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(GUEST);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
		fail("Unreachable code");
	}

	/**
	 * Verifica que si llamas al metodo con el String nulo, salta una
	 * ConstraintViolationException
	 */
	@Test(expected = ConstraintViolationException.class)
	public void constraintViolation() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(ADMIN);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething(null);
		fail("Unreachable code");
	}

	/**
	 * Verifica el caso de que todo es correcto y no hay excepciones
	 */
	@Test
	public void allowed() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(ADMIN);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
	}

}


El resultado de los test es este:


Los 8 tests han pasado bien.

Este es el resultado que se muestra en la consola:

class com.tododev.shiro.GuiceAopTest$NoAop: any value
class com.tododev.shiro.GuiceAopTest$Aop$$EnhancerByGuice$$4ee6b586: any value

Os podeis fijar que la clase Aop “ha sido mejorada” por Guice, añadiendole los aspectos de Shiro y BVal. Lo curioso es que en las clases compiladas vemos lo siguiente:


La clase que aparece señalada debería llamarse GuiceAopTest$Aop$$EnhancerByGuice$$4ee6b586, pero se llama GuiceAopTest$Aop. Esto es porque Guice no hace los aspectos en tiempo de compilación, sino en ejecución. Esto tiene algunas limitaciones, pero en muchos casos resulta ser suficiente.

En conclusión:

Se puede comprobar lo limpio que queda el código usando aspectos. Así es mas fácil ver qué es lo que hace el método doSomething(). Además, esas anotaciones las podemos aprovechar y llevar a otros métodos que requieran esas comprobaciones.

Problema con Singletons y patron estrategia

El patron estrategia es uno de los que mas “ifs” te pueden quitar en el codigo. Un ejemplo sencillo de como funciona seria imaginar una partida de ajedrez. Sin el patron estrategia, cada vez que un jugador moviese una ficha, el programa tendria que comprobar de que ficha se trata para calcular sus posibles movimientos. Usando el patron estrategia, es la propia ficha la que sabe como moverse y asi nos ahorramos hacer las comprobaciones (ifs).

El problema es que hay veces que las diferentes estrategias cuestan mucho instanciarlas. Por ejemplo porque hace alguna llamada HTTP, o tiene que ir a base de datos a recuperar informacion al levantarse, etc. Este tipo de clases tan costosas suele ser buena idea hacerlas Singleton. El problema de los singleton, es que tienen que ser inmutables (no deberiamos modificar su contenido) o podriamos tener problemas de concurrencia. En nuestro ejemplo del ajedrez, esto supondria que tendriamos que pasar por parametros de metodo las coordenadas a donde queremos mover, en vez de por constructor. Esto seria la solucion definitiva en una partida de ajedrez, ya que solo se necesitan 2 coordenadas para posicionar una figura en el espacio, pero si se tratase del parchis necesitariamos solo 1, ya que es unidireccional. Este detalle no parece importante, pero imagina que estas en un proyecto bastante grande y tienes que estar ignorando parametros del metodo en funcion de si se trata de ajedrez o parchis.

Estaba interesado en dar una solucion flexible al problema del patron estrategia con singletons. La idea del siguiente ejemplo, es hacer que las estrategias sean lo mas limpias posibles, sin exceso de parametros y que sean singletons.

El ejemplo consiste en una interfaz, con dos implementaciones. Cada implementacion es singleton y maneja un objeto de datos diferente (DTO). He usado Guice para ello:

package com.tododev.guice;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.logging.Logger;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;

import org.junit.Test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Provides;

public class StrategySingletonTest {

	private static final Logger log = Logger.getLogger(StrategySingletonTest.class.getName());
	private final Injector injector = Guice.createInjector(new MyModule());
	private final static String PARAM_1 = "paramDto1";
	private final static String PARAM_2 = "paramDto2";
	
	@Test
	public void test(){
		Logic<?> logicDto1 = injector.getInstance(LogicDto1.class);
		logicDto1.watchDto();
		
		// Comprobamos que aunque son singletons, los DTO's que instancian son diferentes
		assertNotSame(logicDto1.getInstance(), logicDto1.getInstance());
		
		Logic<?> logicDto1SameInstance = injector.getInstance(LogicDto1.class);
		
		// Comprobamos que son la misma instancia
		assertEquals(logicDto1, logicDto1SameInstance);
		
		
		Logic<?> logicDto2 = injector.getInstance(LogicDto2.class);
		logicDto2.watchDto();
		
	}
	
	
	// Dto que manejara la implementacion LogicDto1
	private static class Dto1{
		private final String field;
		public Dto1(String field) {
			this.field = field;
		}
	}
	
	// Dto que manejara la implementacion LogicDto2
	private static class Dto2{
		private final int integer;
		public Dto2(int integer) {
			this.integer = integer;
		}
	}
	
	// Definimos la forma de instanciarse Dto1
	private static class Dto1Provider implements Provider<Dto1>{

		private final HttpServletRequest request;
		
		@Inject
		public Dto1Provider(HttpServletRequest request){
			this.request=request;
		}
		
		@Override
		public Dto1 get() {
			return new Dto1(request.getParameter(PARAM_1));
		}
		
	}
	
	// Definimos la forma de instanciarse Dto2
	private static class Dto2Provider implements Provider<Dto2>{

		private final HttpServletRequest request;
		
		@Inject
		public Dto2Provider(HttpServletRequest request){
			this.request=request;
		}
		
		@Override
		public Dto2 get() {
			return new Dto2(Integer.parseInt(request.getParameter(PARAM_2)));
		}
		
	}
	
//	Decimos que los DTOs se consiguen a traves de los providers y
//	 introducimos las implementaciones de las interfaces
//	 para que guice sepa que existen
	private static class MyModule extends AbstractModule{

		@Override
		protected void configure() {
			bind(Dto1.class).toProvider(Dto1Provider.class);
			bind(Dto2.class).toProvider(Dto2Provider.class);
			bind(LogicDto1.class);
			bind(LogicDto2.class);
		}
		
//		Devuelve un mock de HttpServletRequest, que es con lo que
//		 formaremos los DTOs
		@Provides
		public HttpServletRequest getRequest(){
			HttpServletRequest request = mock(HttpServletRequest.class);
			when(request.getParameter(PARAM_1)).thenReturn("Jorge");
			when(request.getParameter(PARAM_2)).thenReturn("28");
			return request;
		}
		
	}
	
	private static interface Logic<DTO> {
		void watchDto();
		DTO getInstance();
	}
	
	@Singleton
	private static class LogicDto1 implements Logic<Dto1>{

		@Inject
		private Provider<Dto1> provider;
		
		@Override
		public void watchDto() {
			Dto1 dto = getInstance();
			log.info("I'm "+dto+" and my name is: "+dto.field);
		}

		@Override
		public Dto1 getInstance() {
			return provider.get();
		}
		
	}
	
	@Singleton
	private static class LogicDto2 implements Logic<Dto2>{

		@Inject
		private Provider<Dto2> provider;
		
		@Override
		public void watchDto() {
			Dto2 dto = getInstance();
			log.info("I'm "+dto+" and I am: "+dto.integer);
		}

		@Override
		public Dto2 getInstance() {
			return provider.get();
		}
		
	}
	
}

Esta es la salida que obtendremos:
I’m com.tododev.guice.StrategySingletonTest$Dto1@6ae441ee and my name is: Jorge
I’m com.tododev.guice.StrategySingletonTest$Dto2@5ef4f91d and I am: 28

Este es el grafo de las dependencias que tiene el inyector:

example

Testear la salida de un servlet

A veces me ha interesado saber que es lo que se escribe en la respuesta de un servlet, por ejemplo cuando quieres comprobar JSON. Con este pequeño ejemplo podemos analizar el objeto “buffer” para comprobar que se ha escrito lo que esperabamos a cada test:

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class MyTest {

   @Mock
   private HttpServletRequest request;
   @Mock
   private HttpServletResponse response;

   private StringBuffer buffer;

   @Before
   public void before() throws IOException{
      MockitoAnnotations.initMocks(this);
      PrintWriter writer = mock(PrintWriter.class);
      when(response.getWriter()).thenReturn(writer);
      buffer=new StringBuffer();
      doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
               char[] chars=(char[])invocation.getArguments()[0];
               int init=(Integer)invocation.getArguments()[1];
               int end=(Integer)invocation.getArguments()[2];
               for(int i=init;i<end;i++){
                  buffer.append(chars[i]);
               }
               return null;
            }
      }).when(writer).write(any(char[].class), anyInt(), anyInt());
   }

}

El objeto buffer tendra el valor de lo que se haya escrito en el response.

Categories: Uncategorized

Creación de consultas dinámicas en JPA sin SQL (ni JPQL): Usando el metamodelo

En la entrada anterior vimos cómo realizar consultas JPA sin escribir código SQL. Sin embargo,  echemos un vistazo a una porción concreta del código que usamos en aquel momento:

  final CriteriaQuery<User> q = cb.createQuery(User.class);
  final Root<User> users = q.from(User.class);
  final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
  q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

Puede observarse como, en ciertos lugares, se obtienen valores de atributos del objeto User, en concreto, privilegeLevel y userId, haciendo referencia al nombre del dicho atributo. Este enfoque tiene el problema de que no se realizan comprobaciones en tiempo de ejecución. Dicho de otro modo, si, por ejemplo,  se cambiase el nombre del atributo userId a user_id, el código seguiría siendo correcto desde el punto de vista del compilador. Sin embargo, al ejecutarlo, se producirían errores al no existir tal atributo.
Una posible solución consiste en usar el metamodelo de Java. Una introducción, con ejemplos no muy afortunados, desde mi punto de vista, puede encontrarse en The Java EE6 Tutorial.

El truco consiste en acceder a los atributos del objeto usando el metamodelo, de manera que un cambio en cualquiera de los atributos del objeto sería detectado mediante un fallo de compilación. Lo primero que hay que hacer es generar la clase del metamodelo correspondiente, la EntityType. Hay varias formas de hacerlo, aunque quizás la mas sencilla sea indicarlo con el flag de compilación correspondiente. Por ejemplo, para openJPA, el flag es  -Aopenjpa.metamodel=true. 

Se genera la clase User_, que no es mas que la clase de  metamodelo de User, siguiendo el estándar de nomenclatura .

* Generated by OpenJPA MetaModel Generator Tool. **/
package com.wordpress.tododev.criteria.entities;
import javax.persistence.metamodel.SingularAttribute;
@javax.persistence.metamodel.StaticMetamodel
(value=com.wordpress.tododev.criteria.entities.User.class)
@javax.annotation.Generated
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Mon Mar 04 16:47:46 CET 2013")
public class User_ {
 public static volatile SingularAttribute<User,Boolean> active;
 public static volatile SingularAttribute<User,String> email;
 public static volatile SingularAttribute<User,String> name;
 public static volatile SingularAttribute<User,Integer> privilegeLevel;
 public static volatile SingularAttribute<User,String> userDigestedPasswd;
 public static volatile SingularAttribute<User,Integer> userId;
}

Quizás la primera opción que se viene a la cabeza sea añadir este código autogenerado al sistema de control de versiones. Pero no suele ser buena idea añadir código autogenerado y, sobre todo, una modificación posterior en los atributos de User pasaría desapercibida por el compilador a menos que se generase de nuevo la clase User_.  En el caso de usar ant, podría solucionarse añadiendo un target específico que genere las clase del metamodelo. Dicho target debería ejecutarse cada vez que se realizara alguna modificación en cualquiera de las Entities JPA de la aplicación.

Otra opción consiste en instruir al IDE para generar código de acuerdo con las anotaciones. En Eclipse, hay que añadir el flag de compilación ya mencionado en  Properties->Java Compiler->Annotation Processor y, a continuación, añadir el jar que contiene el Annotation Processor de la implementación JPA elegida en la sección Factory Path (dentro de Annotations Processor). El problema es que, en este caso, la clase de metamodelo User_ se genera al compilar, pero para compilar se necesita la clase del metamodelo, y llegamos al dilema del huevo o la gallina.

En cualquier caso, una vez se disponga de la clase de metamodelo, se puede añadir otro test a la suite anterior que proporciona el mismo resultado sin necesidad de pasar una cadena con el nombre del atributo:

@Test
 public void testUserCriteriaMetaModel(){
 EntityManagerFactory emf = null;
 EntityManager em = null;
 try {
 emf = Persistence.createEntityManagerFactory("criteria");
 em = emf.createEntityManager();
 final CriteriaBuilder cb = em.getCriteriaBuilder();
 final CriteriaQuery<User> q = cb.createQuery(User.class);
 final Metamodel m = em.getMetamodel();
 final Root<User> user = q.from(m.entity(User.class));
 final Predicate condition = cb.equal(user.get(User_.privilegeLevel), 5);
 q.select(user).where(condition).orderBy(cb.asc(user.get(User_.userId)));

 em.getTransaction().begin();
 List<User> result = em.createQuery(q).getResultList();
 em.getTransaction().commit();

 assertNotNull(result);
 assertEquals(2, result.size());

 assertEquals(1, (int)result.get(0).getUserId());
 assertEquals("Pepe", result.get(0).getName());

 assertEquals(3, (int)result.get(1).getUserId());
 assertEquals("Dolores", result.get(1).getName());
} catch (Exception e) {
 fail("Unexpected Exception " + e.getMessage());
 } finally {
 if (em != null)
 em.close();
 if (emf != null)
 emf.close();
 }
 }

Los cambios importantes están en las líneas en negrita. Sobre todo,  user.get(User_.privilegeLevel), en lugar de users.get(“privilegeLevel”) y user.get(User_.userId) en lugar de users.get(“userId”).

Podéis encontrar el código fuente actualizado en GitHub.

%d bloggers like this: