Archive

Author Archive

Cheap Virtual Reality option

Probably you have eared about the Virtual Reality, and how companies like Oculus Rift are creating devices for that.

I am very interested on it, because it could improve a lot your gaming experience. However, I was quite disappointed about the prize (around 700 euros in Europe) and the minimum requirements to use Oculus Rift. In their web page you can download one software that will check if your computer is acceptable or not.

I started to investigate if there is a possible option to use your smartphone as a screen, I even though to develop something, but finally it was not necessary. The main issue I found is that there are people comments from the middle of 2015 and I didn’t find almost nothing from that date to nowadays (it is 23-01-2016 in the moment that I’m writing these lines).

I’m going to show you how can we play Project Cars game using our smartphone as a Virtual Reality screen and head tracker.

What software do you need

  • Trinus VR. There is one server application for desktop and other for the client. Our client will be a Nexus 5 with Android 6.0.1. This application is going to redirect the image from your computer to your smartphone and it will be listening your inputs to do the head tracking. It also split the screen in two parts to provide the Virtual Reality effect when you insert your smartphone in a VR glasses.
  • Opentrack 2.3. As I said, Trinus VR will be listening your inputs… but wait, what inputs?. It could be a mouse move, UDP packages incoming, etc. Theoretically, we don’t need this application, but I realized that the head tracking in Project Cars doesn’t work with Trinus VR. So I will use Opentrack to fix this problem.
  • FreePIE IMU. There is no direct link to download. You will find this Android application inside the Opentrack 2.3. Instructions here. It is an APK that you will need to copy in your Android device and install it.

Physical devices

  • Android smartphone that can run Trinus VR client and FreePIE IMU.
  • PC that can run Project Cars and all the software mentioned with Windows 7 or higher.
  • USB cable to connect your smartphone with your computer.
  • (Optional) VR glasses to insert your smartphone.

Lets start

  • Install all the required software.
  • Plug your smartphone with USB to your PC.
  • Run Project Cars in windowed mode, because it is mandatory for the Trinus VR display. Start any circuit in free practice for example and set the camera to the helmet view.
  • PC: Run as an administrator, Trinus VR and Opentrack.
  • Android: Run Trinus VR and FreePIE IMU, use the IP of your computer.
  • Load the profile .ini (you will find it at the end of the post) in Opentrack and check the screenshots I attach.
  • Make sure that the port value is the same as FreePIE IMU is using. Click start and if everything is fine, the octopus will move with the movement of your smartphone.
  • Run Trinus VR with the configuration that you can see in the screenshots. It is important to set the Sensor output: Free Track.

At this point, you should see the game screen in your smartphone and the movements of the smartphone should be captured in the game.

One more thing. In my case, somehow Trinus VR starts to capture the inputs after clicking start in Opentrack and there is a mess because we have 2 programs setting the head track. There are two options:

  • Stop the Opentrack.
  • Set the sensor output in TrinusVR to no sensor.

Attachments

Screenshot_20160123-113157opentrack-port opentracktrinusVR-sensors trinusVR-net trinusVR-main

Profile .ini (Save this in a file with any name .ini and load it in the Opentrack as a profile)

[Curves-tx]
point-count=1
point-0-x=100
point-0-y=100

[opentrack-ui]
camera-pitch=0
camera-yaw=0
compensate-translation=true
compensate-translation-disable-z-axis=false
filter-dll=Accela
pitch-alt-axis-sign=false
pitch-invert-sign=false
pitch-source-index=4
pitch-zero-pos=0
protocol-dll=freetrack 2.0 Enhanced
roll-alt-axis-sign=false
roll-invert-sign=true
roll-source-index=5
roll-zero-pos=0
tracker-dll=FreePIE UDP receiver
tracker2-dll=
use-system-tray=false
x-alt-axis-sign=false
x-invert-sign=false
x-source-index=0
x-zero-pos=0
y-alt-axis-sign=false
y-invert-sign=false
y-source-index=1
y-zero-pos=0
yaw-alt-axis-sign=false
yaw-invert-sign=false
yaw-source-index=3
yaw-zero-pos=0
z-alt-axis-sign=false
z-invert-sign=false
z-source-index=2
z-zero-pos=0
button-center=-1
button-start-tracking=-1
button-stop-tracking=-1
button-toggle=-1
button-toggle-tracking=-1
button-zero=-1
camera-roll=0
center-at-startup=true
centering-method=1
guid-center=
guid-start-tracking=
guid-stop-tracking=
guid-toggle=
guid-toggle-tracking=
guid-zero=
keycode-center=F12
keycode-start-tracking=
keycode-stop-tracking=
keycode-toggle=
keycode-toggle-tracking=
keycode-zero=

[Curves-tx_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-ty]
point-count=1
point-0-x=100
point-0-y=100

[Curves-ty_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-tz]
point-count=1
point-0-x=100
point-0-y=100

[Curves-tz_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-rx]
point-count=3
point-0-x=7.1999998092651403
point-0-y=8.7804880142211896
point-1-x=30.034286499023398
point-1-y=90.439025878906193
point-2-x=50.194286346435497
point-2-y=180

[Curves-rx_alt]
point-count=1
point-0-x=180
point-0-y=180

[Curves-ry]
point-count=3
point-0-x=6.9942855834960902
point-0-y=8.7804880142211896
point-1-x=30.034286499023398
point-1-y=91.317070007324205
point-2-x=49.7828559875488
point-2-y=180

[Curves-ry_alt]
point-count=1
point-0-x=180
point-0-y=180

[Curves-rz]
point-count=1
point-0-x=180
point-0-y=180

[Curves-rz_alt]
point-count=1
point-0-x=180
point-0-y=33.365852355957003

[Accela]
ewma=2
rotation-threshold=25
translation-threshold=25

[freepie-udp-tracker]
add-pitch-degrees=0
add-roll-degrees=0
add-yaw-degrees=0
axis-index-x=0
axis-index-y=1
axis-index-z=2
port=5555

[keyboard-shortcuts]
key-alt-center=false
key-alt-toggle=false
key-alt-zero=false
key-ctrl-center=false
key-ctrl-toggle=false
key-ctrl-zero=false
key-index-center=19
key-index-toggle=0
key-index-zero=0
key-shift-center=false
key-shift-toggle=false
key-shift-zero=false

[proto-freetrack]
use-memory-hacks=false
used-interfaces=0

[udp-proto]
ip1=127
ip2=0
ip3=0
ip4=0
port=5556

This other profile works much better for me:

[Curves-tx]
point-count=1
point-0-x=100
point-0-y=100

[opentrack-ui]
camera-pitch=0
camera-yaw=0
compensate-translation=true
compensate-translation-disable-z-axis=true
filter-dll=Accela
pitch-alt-axis-sign=false
pitch-invert-sign=true
pitch-source-index=4
pitch-zero-pos=0
protocol-dll=freetrack 2.0 Enhanced
roll-alt-axis-sign=false
roll-invert-sign=true
roll-source-index=5
roll-zero-pos=0
tracker-dll=FreePIE UDP receiver
tracker2-dll=
use-system-tray=false
x-alt-axis-sign=false
x-invert-sign=true
x-source-index=0
x-zero-pos=0
y-alt-axis-sign=false
y-invert-sign=true
y-source-index=1
y-zero-pos=0
yaw-alt-axis-sign=false
yaw-invert-sign=false
yaw-source-index=3
yaw-zero-pos=0
z-alt-axis-sign=false
z-invert-sign=true
z-source-index=2
z-zero-pos=0
button-center=-1
button-start-tracking=-1
button-stop-tracking=-1
button-toggle=-1
button-toggle-tracking=-1
button-zero=-1
camera-roll=0
center-at-startup=true
centering-method=1
guid-center=
guid-start-tracking=
guid-stop-tracking=
guid-toggle=
guid-toggle-tracking=
guid-zero=
keycode-center=F11
keycode-start-tracking=
keycode-stop-tracking=
keycode-toggle=
keycode-toggle-tracking=
keycode-zero=

[Curves-tx_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-ty]
point-count=1
point-0-x=100
point-0-y=100

[Curves-ty_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-tz]
point-count=1
point-0-x=100
point-0-y=100

[Curves-tz_alt]
point-count=1
point-0-x=100
point-0-y=100

[Curves-rx]
point-count=3
point-0-x=7.1999998092651403
point-0-y=8.7804880142211896
point-1-x=76
point-1-y=80
point-2-x=180
point-2-y=180

[Curves-rx_alt]
point-count=1
point-0-x=180
point-0-y=180

[Curves-ry]
point-count=3
point-0-x=6.9942855834960902
point-0-y=8.7804880142211896
point-1-x=100
point-1-y=100
point-2-x=180
point-2-y=180

[Curves-ry_alt]
point-count=1
point-0-x=180
point-0-y=180

[Curves-rz]
point-count=1
point-0-x=180
point-0-y=180

[Curves-rz_alt]
point-count=1
point-0-x=180
point-0-y=33.365852355957003

[Accela]
ewma=2
rotation-threshold=25
translation-threshold=25

[freepie-udp-tracker]
add-pitch-degrees=0
add-roll-degrees=0
add-yaw-degrees=0
axis-index-x=0
axis-index-y=1
axis-index-z=2
port=5555

[keyboard-shortcuts]
key-alt-center=false
key-alt-toggle=false
key-alt-zero=false
key-ctrl-center=false
key-ctrl-toggle=false
key-ctrl-zero=false
key-index-center=19
key-index-toggle=0
key-index-zero=0
key-shift-center=false
key-shift-toggle=false
key-shift-zero=false

[proto-freetrack]
use-memory-hacks=false
used-interfaces=0

[udp-proto]
ip1=127
ip2=0
ip3=0
ip4=0
port=5556

Is your code thread safe?

The last weeks I started to considerate about concurrency testing. I wrote an utility that tries to test if your code is thread safe or not.

When you repeat an experiment many times, the result tends to be the expected

This means that if you throw a die one time, we can not know what result is going to happen, but if we do it thousands of times, we will know that each result is going to happens 1/6 of the times.

In less words, lets repeat this many times at the same time and then check if it is fine or not. The problem here is, how to do it at the same time?.

The utility has 2 classes, ConcurrenceHelper and Task. Task is an interface that you need to implement to provide ConcurrenceHelper the job to do. ConcurrenceHelper will trigger all the tasks “at the same time”.

This is the interface:

public interface Task {

	void execute();
	
}

And this class will trigger all the tasks in different threads. Onces all the threads have executed the tasks, the main thread will leave the method (it means that this method is synchronous).

import java.util.concurrent.CountDownLatch;

public class ConcurrenceHelper {

	public void triggerTasks(final int nThreadsPerTask, final Task ... tasks) throws InterruptedException{
		final CountDownLatch waitForThreadsEnd = new CountDownLatch(nThreadsPerTask*tasks.length);
		final CountDownLatch waitToStart = new CountDownLatch(nThreadsPerTask*tasks.length);
		for(final Task task : tasks){
			for(int i=0;i<nThreadsPerTask;i++){
				final String THREAD_NAME = task+"-"+i;
				new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							waitToStart.countDown();
							// Threads are waiting until countDown=nThreadsPerTask*tasks.length
							waitToStart.await();
							task.execute();
						} catch (InterruptedException e) {
							throw new RuntimeException(THREAD_NAME+" can not wait any more o_O", e);
						}finally{
                        	waitForThreadsEnd.countDown();
                        }
						
					}
				}, THREAD_NAME).start();
			}
		}
		// The main thread is waiting until countDown=nThreadsPerTask*tasks.length
		waitForThreadsEnd.await();
		// At this point all threads have executed the task
	}
	
}

And now lets do some testing:


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

import java.util.List;
import java.util.Vector;

import org.junit.Test;

public class SamplesTest {

	private final ConcurrenceHelper concurrence = new ConcurrenceHelper();
	private final int CONCURRENCE_TASKS = 10000;
	
	@Test
	public void vectorIsThreadSafe() throws InterruptedException{
		final List<Long> list = new Vector<>();
		concurrence.triggerTasks(CONCURRENCE_TASKS, new Task() {
			@Override
			public void execute() {
				list.add(System.currentTimeMillis());
			}
		});
		System.out.println("Value "+list.size());
		assertEquals(CONCURRENCE_TASKS, list.size());
	}
	
	@Test
	public void notThreadSafeClass() throws InterruptedException{
		final NotThreadSafeClass obj = new NotThreadSafeClass();
		concurrence.triggerTasks(CONCURRENCE_TASKS, new Task() {	
			@Override
			public void execute() {
				obj.i++;
			}
		});
		System.out.println("Value "+obj.i);
		assertNotEquals(CONCURRENCE_TASKS, obj.i);
	}
	
	private class NotThreadSafeClass{
		protected int i;
	}
	
}

We are testing one class that we know that is thread safe (Vector) and other one that is not thread safe (NotThreadSafeClass). It is easier to test that one class is thread safe than checking that is not thread safe. The test vectorIsThreadSafe() will be successfull the 100% of the times, but notThreadSafeClass() will not be successfull the 100%. For that reason, to prevent you to have randomly testing errors, make the testing oriented to check that is thread safe (by the way, is what we really want to test).

To prevent concurrency problems, try to make the classes as much inmutables as possible.

Playing with JerseyTest (Jersey 2.5.1 and DI)

I’m going to try explaining a trivial REST example. The idea is building a basic schema to start playing with Jersey. When I begin to use some framework, I usually develop a test enviroment for failing fast, and that is what I’m going to do.

The next example has these features:

  • Jersey 2.5.1
  • Dependency Injection
  • JUnit for testing

Classes:

  • Resource: it will attend the HTTP calls.
  • Service: it’s an interface with two implementations, Impl1 and Impl2.
  • ServiceProvider: it will give the apropiate implementation of Service per each request call in runtime.
  • TestBinder: it set the bindings into the Resource.

 


import static org.junit.Assert.assertEquals;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;

import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

public class JerseyInjectionTest extends JerseyTest {

	private static final String EXPECTED_CONTENT = "any string :P";

	/**
	 * Checks that the Resource uses Impl1.class
	 */
	@Test
	public void invokeImpl1(){
		invoke(Impl1.class);
	}
	
	/**
	 * Checks that the Resource uses Impl2.class
	 */
	@Test
	public void invokeImpl2(){
		invoke(Impl2.class);
	}
	
	/**
	 * Checks that Resource.anyContent has always the value of EXPECTED_CONTENT
	 */
	@Test
	public void checkContent(){
		Response response = target("example/content").request().get();
		assertEquals(EXPECTED_CONTENT, response.readEntity(String.class));
	}
	
	private <T extends Service> void invoke(Class<T> service){
		final String serviceName = service.getName();
		Response response = target("example/"+serviceName).request().get();
		assertEquals(service.getName(), response.readEntity(String.class));
	}
	
	/**
	 * Register the Resource and TestBinder in the Application
	 */
	@Override
	protected Application configure() {
		return new ResourceConfig() {
			{
				register(new TestBinder());
				register(Resource.class);
			}
		};
	}

	@Path("/example")
	public static class Resource {

		@Inject
		Service service;
		@Inject
		String anyContent;

		/**
		 * Returns the name of the Service's implementation
		 */
		@GET
		@Path("/{serviceClass}")
		public Response getDynamicInvokedService() {
			return Response.ok(service.getClass().getName()).build();
		}

		/**
		 * Returns always the value of anyContent
		 */
		@GET
		@Path("/content")
		public Response getStaticContent() {
			return Response.ok(anyContent).build();
		}

	}
	
	/**
	 * This class will help Resource to set the @Inject fields.
	 */
	public static class TestBinder extends AbstractBinder{

		@Override
		protected void configure() {
			bindFactory(ServiceProvider.class).to(Service.class);
			bind(EXPECTED_CONTENT).to(String.class);
		}
		
	}

	/**
	 * This class will instance a Services's implementation
	 * per each time that the Resource is called.
	 */
	@RequestScoped
	public static class ServiceProvider implements Factory<Service> {

		private final String serviceName;

		public ServiceProvider(@PathParam("serviceClass") String serviceName) {
			this.serviceName = serviceName;
		}

		@Override
		public void dispose(Service arg0) {}

		@Override
		public Service provide() {
			try {
				return (Service) Class.forName(serviceName).newInstance();
			} catch (Exception e) {
				return null;
			}
		}

	}

	/**
	 * Dummy services
	 */
	public static interface Service {}
	public static class Impl1 implements Service {}
	public static class Impl2 implements Service {}

}

Now we can try new features easily.

I hope that helps.

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

%d bloggers like this: