@Transactional y @Rollback en Spring JDBC

Estoy en un proyecto en el que he abandonado Hibernate. Prefiero tener el control de las queries. Realmente no van a ser unas queries poderosas, pero tambien es cierto que realmente no entiendo la ventaja de Hibernate. Y para colmo no tengo acceso a ningun curso, con el cual pueda abrir los ojos.

He decidido probar Spring JDBC. Uso NamedParameterJdbcTemplate para todas las operaciones. Esto implica no tener que estar inyectando el DataSource por todos los lados. Simplificación y legibilidad.

Con el paso de los días, y en progresión a la experiencia. Decidí hacer test, pero sin grabar en bbdd. Haciendo rollback. Así me queda un proyecto tan completo como los que he realizado con Hibernate. Añadí a mi fichero de configuración de Spring:


    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

y en los métodos en los que se hace insert, update ó delete, indico la anotación @Transactional

	@Transactional
	protected void update(String query, T bean) {
	      // ...
	}

Ahora bien, en los test. He estado probando y realizando pruebas para saber concretamente el uso que tiene Transactional y Rollback.

  • Si pones solo Transacctional , hara rollback, cuando se lance alguna excepción no cacheadas (derivadas de RuntimeException). Dentro de la anotación puedes indicarle otras opciones.
  • Si acompañas la anotacion @Transacctional con @Rollback (por defecto es true), estas indicando que siempre se va a hacer Rollback. Mediante la anotacion Rollback, estas sobreescribiendo el comportamiento de Transacctional, referente a los rollback.
  • 	@Test
    	@Transactional
    	@Rollback
    	public void testSave() {
    	      // ...
    	}
    

    Indicar @Transacctional en el metodo de test, es sobreescribir el comportamiento de @Transacctional en el metodo a testear.

El Javadoc de Rollback dice:

Test annotation to indicate whether or not the transaction for the annotated
test method should be rolled back after the test method has
completed. If {@code true}, the transaction will be rolled back;
otherwise, the transaction will be committed.

@Transactional y @Rollback en Spring JDBC

Get spring context

	    @Autowired  
	    ApplicationContext applicationContext; 

	    // ...
	    ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	    // ...
	    ApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
	    // ...
	    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-webservices.xml");//, "spring/spring-ddbb.xml", "spring/spring-properties.xml" );
	    // ...
	    ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-webservices.xml");//, "spring/spring-ddbb.xml", "spring/spring-properties.xml" );	
	    // ...
Get spring context

Change connection pool at runtime

Retrieve pool data source, from spring context.

	com.mchange.v2.c3p0.ComboPooledDataSource pool = (com.mchange.v2.c3p0.ComboPooledDataSource) context.getBean("idDataSource");
	pool.setJdbcUrl("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=pepe.host.es) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)))");
	try {
		Connection con = pool.getConnection();
		if(con != null){
 			// TODO check database connection
		}else{
			throw new ConnectionException("Can't load new URL database connection");
		}
		
	} catch (SQLException e) {
		e.printStackTrace();
	}

The method setJdbcUrl(), do resetPoolManager. The true atribute means that not broke the actual connections.

    public void setJdbcUrl( String jdbcUrl )
    { 
        dmds.setJdbcUrl( jdbcUrl ); 
        this.resetPoolManager( false );
//      System.err.println("setting jdbcUrl: " + jdbcUrl + " [dmds@" + C3P0ImplUtils.identityToken( dmds ) + "]"); 
//      if (jdbcUrl == null)
//      new Exception("*** NULL SETTER ***").printStackTrace();
    }
Change connection pool at runtime

No perder la conexion RMI

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
		http://www.springframework.org/schema/util 
		http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop  
		http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<context:annotation-config />
	<context:component-scan base-package="es.depontevedra.cividas.rmi.connect" />

	<bean id="cividasE" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://${cividas.server.host}:${cividas.server.port}/${cividas.server.name}" />
		<property name="serviceInterface" value="com.ontimize.locator.EntityReferenceLocator" />
		<property name="refreshStubOnConnectFailure" value="true" />
	</bean>
	
	<bean id="cividasU" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://${cividas.server.host}:${cividas.server.port}/${cividas.server.name}" />
		<property name="serviceInterface" value="com.ontimize.locator.UtilReferenceLocator" />
		<property name="refreshStubOnConnectFailure" value="true" />
	</bean>
	
	<bean id="cividasRemoteEntity" class="es.depontevedra.cividas.rmi.connect.CividasRemoteEntity">
		<property name="user" value="${cividas.server.user}" />
		<property name="pass" value="${cividas.server.pass}" />
		<property name="host" value="${cividas.server.host}" />
		<property name="port" value="${cividas.server.port}" />
		<property name="name" value="${cividas.server.name}" />
	</bean>

	<bean id="cividasEntity" class="es.depontevedra.cividas.rmi.connect.CividasEntity">
		<property name="user" value="${cividas.server.user}" />
		<property name="pass" value="${cividas.server.pass}" />
		<property name="host" value="${cividas.server.host}" />
		<property name="port" value="${cividas.server.port}" />
		<property name="name" value="${cividas.server.name}" />
	</bean>

</beans>

depues en la clase que implemntes capturas:

public class CividasConnection {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(CividasConnection.class);
	
	@Autowired
	private EntityReferenceLocator cividas;
	
	@Autowired
	private UtilReferenceLocator cividasUtil;

No perder la conexion RMI

TestNG test parametrizados

Hay dos maneras, parametrizando en la clase(@DataProvider) o ejecutando un suite(testng.xml)

Parametrizando en la clase @DataProvider

@ContextConfiguration(locations = { "classpath:spring-test.xml", "classpath:spring/spring-client.xml" })
public class ConsultaIdentidadEnPaxaseTest extends AbstractTestNGSpringContextTests {

	@DataProvider(name = "ficheros")
	public Object[][] consultarIdentidad_peticionProvider() {
		return new Object[][]{
				{String.class, new String("src/test/resources/peticiones/Peticion1.xml")}, 
				{String.class, new String("src/test/resources/peticiones/Peticion2.xml")}, 
		};
	}

	@Test(dataProvider = "ficheros")
	public void consultarIdentidad_peticion(Class<?> clzz, String fichero) {
		consultarIdentidad_peticion(fichero);
	}
}

Ejecutando un fichero

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="cliente_consultaIdentidad" preserve-order="true">

	<test name="cliente_Peticion1">
		<parameter name="fichero" value="src/test/resources/peticiones/Peticion1.xml" />
		<classes>
			<class name="es.una.ruta.services.spi.ConsultaIdentidadEnPaxaseTest"></class>
		</classes>
	</test>
	
	<test name="cliente_Peticion2">
		<parameter name="fichero" value="src/test/resources/peticiones/Peticion2.xml" />
		<classes>
			<class name="es.una.ruta.services.spi.ConsultaIdentidadEnPaxaseTest"></class>
		</classes>
	</test>

</suite>
@ContextConfiguration(locations = { "classpath:spring-test.xml", "classpath:spring/spring-client.xml" })
public class ConsultaIdentidadEnPaxaseTest extends AbstractTestNGSpringContextTests {

	@Autowired  
	@Qualifier("clientCI")
	private es.una.ruta.services.ConsultaIdentidad clientCI;

	@Parameters({ "fichero" })
	@Test
	public void consultarIdentidad_peticion(String fichero){
		try {
			String peticion = getPeticion(fichero);
			String respuesta = clientCI.consultar(peticion);
			System.out.println(respuesta);
			assertNotNull("Respuesta null", respuesta);
		} catch (Exception e) {
			assertTrue(false, fichero + ": " +e.getMessage());
		}
	}

	private String getPeticion(String path){
		String peticion = null;
		try {
			File file = new File(path);
			peticion = FileUtils.readFileToString(file, "UTF-8");
			System.out.println(peticion);
		} catch (Exception e) {
			e.printStackTrace();
		} 

		return peticion;
	}

}
	<build>
		<plugins>
			<plugin>
				<!-- mvn test -->
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<suiteXmlFiles>
						<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
					</suiteXmlFiles>
				</configuration>
			</plugin>
		</plugins>
	</build>
TestNG test parametrizados

Cliente del «Servicio de Verificación y Consulta de Datos: Plataforma de Intermediación» con CXF

Me ha tocado realizar un cliente para este Servicio Web del Estado (Español).
En los documentos adjuntos, hay mucha informacion relativa a lo que hace y como quiere los datos.
Sobre seguridad no indica nada mas que permite: WS-Security, XMLDSig y Xades. Que se tiene que firmar, pero no indica, que algoritmos hay que usar, como firmar las cabeceras, que partes hay que firmar, etc. Cosa sorprendente cuando ves que hay varias maneras de firmar y de hacer las cosas. Pero te dan una peticion y una respuesta tipo. En base a esa peticion he ido realizando pruebas y puedo afirmar que ya lo tengo.

En CXF recomiendan el uso de WS-Policy siempre y cuando el WSDL contenga estos apartados, pero en este caso tenemos que usar los interceptors de CXF. En el cliente antes de enviar la peticion SOAP, tenemos que firmar el soap:body. Esto se hace en el soap:header, con WS-Security y los Interceptors.

Por lo que he deducido las caracteristicas de WS-Security son:

Dejo mi fichero de configuración de spring para CXF y mi fichero de propiedades:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:jaxws="http://cxf.apache.org/jaxws"
	xmlns:cxf="http://cxf.apache.org/core" xmlns:http="http://cxf.apache.org/transports/http/configuration"
	xmlns:p="http://cxf.apache.org/policy" xmlns:wsa="http://cxf.apache.org/ws/addressing"
	xmlns:wsp="http://www.w3.org/2006/07/ws-policy" xmlns:sec="http://cxf.apache.org/configuration/security"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
	xsi:schemaLocation="
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
	http://cxf.apache.org/transports/http/configuration
	http://cxf.apache.org/schemas/configuration/http-conf.xsd
	http://cxf.apache.org/configuration/security
	http://cxf.apache.org/schemas/configuration/security.xsd
	http://cxf.apache.org/ws/addressing 
	http://cxf.apache.org/schema/ws/addressing.xs
	http://cxf.apache.org/transports/http/configuration 
	http://cxf.apache.org/schemas/configuration/http-conf.xsd
	http://www.w3.org/2006/07/ws-policy	
	http://www.w3.org/2006/07/ws-policy.xsd
	http://cxf.apache.org/policy 
	http://cxf.apache.org/schemas/policy.xsd
	http://cxf.apache.org/core 
	http://cxf.apache.org/schemas/core.xsd
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://cxf.apache.org/jaxws 
	http://cxf.apache.org/schemas/jaxws.xsd">

	<import resource="classpath*:spring-xsd.xml" />
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

	<http:conduit name="https://localhost:.*">
		<http:tlsClientParameters disableCNCheck="true">
			<!-- <sec:trustManagers> -->
			<!-- <sec:keyStore type="jks" password="cspass" resource="clientKeystore.jks" 
				/> -->
			<!-- </sec:trustManagers> -->
		</http:tlsClientParameters>
	</http:conduit>

	<cxf:bus>
		<cxf:features>
			<cxf:logging />
		</cxf:features>
		<cxf:inInterceptors>
			<ref bean="SignResponse" />
			<bean class="org.apache.cxf.ws.security.wss4j.DefaultCryptoCoverageChecker" />
		</cxf:inInterceptors>
		<cxf:outInterceptors>
			<ref bean="SignRequest" />
		</cxf:outInterceptors>
	</cxf:bus>

	<util:map id="mapKeystorePasswords">
		<entry key="${org.apache.ws.security.crypto.merlin.keystore.alias}" value="${org.apache.ws.security.crypto.merlin.keystore.password}" />
	</util:map>
	
	<bean id="clientPasswordCallback"
		class="es.una.ruta.callback.ClientPasswordCallback" >
		</bean>

	<bean id="url" class="java.lang.String">
		<constructor-arg
			value="https://${paxase.host}:${paxase.port}/${paxase.wsname}" />
	</bean>
	<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
		<property name="serviceClass" value="es.map.xml_schemas.PeticionPortType" />
		<property name="address" value="#{url}/consultarIdentidad" />
	</bean>
	<bean id="clientConsultaIdentidad" class="es.map.xml_schemas.PeticionPortType"
		factory-bean="clientFactory" factory-method="create" />

	<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"
		id="SignRequest">
		<constructor-arg>
			<map>
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).IS_BSP_COMPLIANT}"
					value="#{T(java.lang.Boolean).FALSE.toString()}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).MUST_UNDERSTAND}"
					value="#{T(java.lang.Boolean).FALSE.toString()}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}"
					value="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).USER}"
					value="${org.apache.ws.security.crypto.merlin.keystore.alias}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_PROP_FILE}"
					value="client_sign.properties" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_KEY_ID}"
					value="${signature.key.identifier}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_ALGO}"
					value="#{T(org.apache.ws.security.WSConstants).RSA_SHA1}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_DIGEST_ALGO}"
					value="#{T(org.apache.ws.security.WSConstants).SHA1}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_C14N_ALGO}"
					value="#{T(org.apache.ws.security.WSConstants).C14N_EXCL_OMIT_COMMENTS}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE_PARTS}"
					value="#{'{}{' + T(org.apache.ws.security.WSConstants).URI_SOAP11_ENV + '}' + T(org.apache.ws.security.WSConstants).ELEM_BODY + ';'}" />
               	<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).PW_CALLBACK_REF}" 
					value-ref="clientPasswordCallback" />
			</map>
		</constructor-arg>
	</bean>

	<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"
		id="SignResponse">
		<constructor-arg>
			<map>
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}"
					value="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE}" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_PROP_FILE}"
					value="client_sign.properties" />
				<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).PW_CALLBACK_REF}" 
					value-ref="clientPasswordCallback" />
				<entry
					key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_ALGO}"
					value="#{T(org.apache.ws.security.WSConstants).RSA_SHA1}" />
			</map>
		</constructor-arg>
	</bean>

</beans>

En mi fichero de propiedades del keystore tengo puesto el tipo de firma:

# Defines which key identifier type to use for signature.
# Valid values are: - "IssuerSerial" - "DirectReference" - "X509KeyIdentifier" - "Thumbprint" - "SKIKeyIdentifier" - "KeyValue" - "EmbeddedKeyName" - "EncryptedKeySHA1"
signature.key.identifier =DirectReference
#
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientKeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
Cliente del «Servicio de Verificación y Consulta de Datos: Plataforma de Intermediación» con CXF

Constantes en Spring description file

El fichero de spring:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd>

	<util:constant id="constant1" static-field="org.apache.ws.security.handler.WSHandlerConstants.ACTION"/>
	<bean id="constant2" class="java.lang.String">
		<constructor-arg value="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}" />
	</bean>
	<util:map id="mapProperties">
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).IS_BSP_COMPLIANT}" value="#{T(java.lang.Boolean).FALSE.toString()}"/>
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}" value="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE}"/>
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).USER}" value="myclientkey" />
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_PROP_FILE}" value="client_sign.properties" />
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_KEY_ID}" value="DirectReference"/>
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_ALGO}" value="#{T(org.apache.ws.security.WSConstants).RSA_SHA1}" />
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_DIGEST_ALGO}" value="#{T(org.apache.ws.security.WSConstants).SHA1}" />
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_C14N_ALGO}" value="#{T(org.apache.ws.security.WSConstants).C14N_OMIT_COMMENTS}" />
		<entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE_PARTS}" 
			value="#{'{}{' + T(org.apache.ws.security.WSConstants).URI_SOAP11_ENV + '}' + T(org.apache.ws.security.WSConstants).ELEM_BODY + ';'}" />
	</util:map>

La clase test:

package es.depontevedra.soap.interoperabilidad.identidad.paxase;

import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import static org.testng.Assert.assertTrue;

@ContextConfiguration(locations = { "classpath:spring-properties.xml" })
public class Testing extends AbstractTestNGSpringContextTests {

	@Autowired
	private String constant1;
	
	@Autowired
	private String constant2;
	
	@Resource(name="mapProperties")
	private Map<String, String> mapProperties;
	
	@Test
	public void test(){
		System.out.println("Constante 1: " + constant1);
		System.out.println("Constante 2: " + constant2);
		for(Entry<String, String> entry: mapProperties.entrySet()){
			System.out.println("Clave: " + entry.getKey());
			System.out.println("Valor: " + entry.getValue());
		}
		
		assertTrue(true);
	}
	
}

el resultado:

Constante 1: action
Constante 2: action
Clave: isBSPCompliant
Valor: false
Clave: action
Valor: Signature
Clave: user
Valor: myclientkey
Clave: signaturePropFile
Valor: client_sign.properties
Clave: signatureKeyIdentifier
Valor: DirectReference
Clave: signatureAlgorithm
Valor: http://www.w3.org/2000/09/xmldsig#rsa-sha1
Clave: signatureDigestAlgorithm
Valor: http://www.w3.org/2000/09/xmldsig#sha1
Clave: signatureC14nAlgorithm
Valor: http://www.w3.org/TR/2001/REC-xml-c14n-20010315
Clave: signatureParts
Valor: {}{http://schemas.xmlsoap.org/soap/envelope/}Body;
PASSED: test

librerias que vas a necesitar, en el fichero pom de Maven:

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.ws.security</groupId>
			<artifactId>wss4j</artifactId>
			<version>1.6.13</version>
		</dependency>

		<!-- TEST -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<scope>test</scope>
		</dependency>
Constantes en Spring description file

Spring AOP con XML

Tengo una clase que genera «id» de conexiones. Es necesario, para operar con la plataforma Cividas. Estoy intentando que el programador se abstraiga de abrir y cerrar conexiones. De esta manera en la parte aop:pointcut indico que clases y metodos se veran afectados.

	<bean id="cividasConnection" class="es.depontevedra.cividas.rmi.connect.CividasConnection">
		<property name="user" value="${cividas.server.user}" />
		<property name="pass" value="${cividas.server.pass}" />
		<property name="host" value="${cividas.server.host}" />
		<property name="port" value="${cividas.server.port}" />
		<property name="name" value="${cividas.server.name}" />
	</bean>
	<!-- AOP configuration -->
	<aop:config>
		<aop:aspect ref="cividasConnection">
			<aop:pointcut id="serviceMethod" expression="execution(* es.depontevedra.cividas.rmi.connect.service.*.*(..))" />
			<aop:before method="startSession" pointcut-ref="serviceMethod" />
			<aop:after method="stopSession" pointcut-ref="serviceMethod" />
			<aop:after-throwing method="stopSession" pointcut-ref="serviceMethod" />
		</aop:aspect>
	</aop:config>
Spring AOP con XML

testNG

Del ultimo post, hay un test con JUnit.
El mismo test pero con TestNG:

		<!-- en el pom.xml quitar la dependencia con JUnit y añadir-->
		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>6.8.5</version>
			<scope>test</scope>
		</dependency>
package net.prietopalacios.josemanuel.aop.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import static org.testng.Assert.*;

@ContextConfiguration("file:src/main/resources/spring.xml")
public class ServiceImplTest extends AbstractTestNGSpringContextTests {
	
	@Autowired
	Service service;

	@Test(threadPoolSize = 2, invocationCount = 2,  timeOut = 10000)
//	@Test(threadPoolSize = 2, invocationCount = 4)
	public void testDoSomething() {
		try {
			service.sendSomething();
			assertTrue(true);
		} catch (Exception e) {
			assertException(e);
		}
		
	}
	
	@Test(threadPoolSize = 2, invocationCount = 2,  timeOut = 10000)
	public void testThrowEsception() {
		try {
			service.sendEsception();
			assertTrue(true);
		} catch (Exception e) {
			assertException(e);
		}
	}

	private void assertException(Exception e) {
		if(e.getMessage() != null){
			assertTrue(false, e.getMessage());
		}else{
			assertTrue(false, e.getCause().getMessage());
		}
	}

}
testNG

Spring AOP start close connection con anotaciones

package net.prietopalacios.josemanuel.aop.service;

public interface Service {

	public void sendSomething();
	public void sendEsception() throws Exception;
	
}
package net.prietopalacios.josemanuel.aop.service;


public class ServiceImpl implements Service {

	public void sendSomething() {
		System.out.println("--------------------");
		System.out.println("I'm doing something.");
		System.out.println("--------------------");

	}

	public void sendEsception() throws Exception {
		throw new Exception("Exception");
	}

	@Override
	public String toString() {
		return "ServiceImpl []";
	}

}
package net.prietopalacios.josemanuel.aop;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AopService {

	private Map<Integer, Integer> sessions;
	private Map<Integer, Integer> history;

	public AopService() {
		this.sessions = new HashMap<Integer, Integer>();
		this.history = new HashMap<Integer, Integer>();
	}
	
	@Before(value="(execution(* net.prietopalacios.josemanuel.aop.service.*.*(..)))")
	public void startsession(JoinPoint joinPoint) throws Exception {  
		int hashCode = joinPoint.hashCode();
		int session = startSession();
		startSession(hashCode, session);

		System.out.println("==================");
		System.out.println("BEFORE: ");
		System.out.println("hashCode: " + hashCode);
		System.out.println("session: " + session);
	}

	@AfterThrowing(value="(execution(* net.prietopalacios.josemanuel.aop.service.*.*(..)))", throwing="exception")
	public void afterThrowingStopSession(JoinPoint joinPoint, Exception exception) throws Exception {  
		int hashCode = joinPoint.hashCode();
		int session = sessions.get(hashCode);

		System.out.println("AFTERTHROWING: ");
		System.out.println("hashCode: " + hashCode);
		System.out.println("session: " + session);

		stopSession(hashCode, session);
	}

	@After(value="(execution(* net.prietopalacios.josemanuel.aop.service.*.*(..)))")  
	public void afterStopSession(JoinPoint joinPoint) throws Exception {  
		int hashCode = joinPoint.hashCode();
		if(!sessions.containsKey(hashCode)) return;
		int session = sessions.get(hashCode);

		System.out.println("AFTER: ");
		System.out.println("hashCode: " + hashCode);
		System.out.println("session: " + session);

		stopSession(hashCode, session);
	}

	private int startSession(){
		Random randomGenerator = new Random();
		return randomGenerator.nextInt(100);
	}

	private void startSession(int hashCode, int session) throws Exception{
		if(sessions.containsKey(hashCode)) {
			throw new Exception ("Ya existe la clave: " + hashCode);
		}
		sessions.put(hashCode, session);
		history.put(hashCode, session);
	}

	private void stopSession(int hashCode, int session) throws Exception{
		printMap();
		int remove = sessions.remove(hashCode);
		if(remove != session) throw new Exception("Imposible cerrar conexion");
	}
	
	private void printMap(){
		int n=1;
		for(Entry<Integer, Integer> entry: history.entrySet()){
			System.out.println(n++ + ".- (h)clave: " + entry.getKey() + ", valor:" + entry.getValue());
		}
		n=0;
		for(Entry<Integer, Integer> entry: sessions.entrySet()){
			System.out.println(n++ + ".- (s)clave: " + entry.getKey() + ", valor:" + entry.getValue());
		}
	}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<aop:aspectj-autoproxy />

	<bean id="service" class="net.prietopalacios.josemanuel.aop.service.ServiceImpl" />
	<bean id="aop" class="net.prietopalacios.josemanuel.aop.AopService" />

</beans>  
package net.prietopalacios.josemanuel.aop.service;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class ServiceImplTest {
	
	@Autowired
	Service service;

	@Test
	public void testDoSomething() {
		try {
			service.sendSomething();
			service.sendSomething();
			assertTrue(true);
		} catch (Exception e) {
			if(e.getMessage() != null){
				assertTrue(e.getMessage(), false);	
			}else{
				assertTrue(e.getCause().getMessage(), false);
			}
		}
		
	}
	
	@Test
	public void testThrowEsception() {
		try {
			service.sendEsception();
			assertTrue(true);
		} catch (Exception e) {
			if(e.getMessage() != null){
				assertTrue(e.getMessage(), false);	
			}else{
				assertTrue(e.getCause().getMessage(), false);
			}
		}
	}

}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>net.prietopalacios.josemanuel</groupId>
	<artifactId>aop</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>aop_example</name>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>3.0.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>1.7.3</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>3.0.5.RELEASE</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

</project>
Spring AOP start close connection con anotaciones