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>
Anuncios
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

List springframework classpath

in applicationContext-XX.xml

<bean id="pathMatchingResourcePatternResolver" class="org.springframework.core.io.support.PathMatchingResourcePatternResolver" />

in your class:


	@Autowired
	@Qualifier("pathMatchingResourcePatternResolver")
	private PathMatchingResourcePatternResolver path;

	public void method(...) throws Exception {

		URLClassLoader loader = (URLClassLoader) path.getClassLoader();
		for(URL url: loader.getURLs()){
			System.out.println(url.toString());
		}
		// ...

List springframework classpath

Bootstrap listener for custom Log4J initialization in a web environment

Para aplicaciones web, Spring tiene un configurardor de log. Este se configura en el fichero web.xml. En el cual le dices:

  1. que fichero tiene la configuracion de log4j.
  2. Y cual es la clase de Spring que levanta la configuracion.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:web="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<!-- ... -->
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>classpath:log4j_pepito.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener
		</listener-class>
	</listener>
	<!-- ... -->
</web-app>
Bootstrap listener for custom Log4J initialization in a web environment

Graba en bbdd la entrada y salida en el log del Web Service con Apache CXF

En referencia al articulo anterior: Mostrar la entrada y salida en el log del Web Service con Apache CXF.

Ahora lo que quiero es grabarlo en la bbdd.

Apache CXF en su modulo cxf-rt-management tiene un paquete denominado: org.apache.cxf.management.persistence. Vamos utilizar esto, que ya esta casi hecho para grabar en nuestra bbdd.

En el paquete vemos las siguientes clases:
ExchangeData: es un bean que contiene toda la informacion sobre la peticion y la respuesta. ¡Ya podian haber implementado un toString para cuando debugeas!.
ExchangeDataDAO: la interfaz que se llama para persistir.
ExchangeDataFilter: la interfaz que se llama para saber si se puede persistir. Es un filtro.
ExchangeDataProperty: el bean para las propiedades de los mensajes de entrada-salida. Esta clase se utiliza en el bean ExchangeData.
FilesystemExchangeDataDAO: implementación de la interfaz del DAO que graba en un fichero. Si no se le indica la ruta, lo hace en una carpeta temporal. En mi caso, en la carpeta /temp de $CATALINA_HOME.
StatusExchangeDataFilter: implementación de la interfaz del filtro. Esta implementación es solo para errores.

¿Como se usa?

A) usando las clases que hemos visto.

Es decir, vamos a grabar a fichero en la carpeta /temp de $CATALINA_HOME, solo los mensajes que den error (excepciones) en nuestro Web Service.
En nuestro fichero de configuración de CXF (un aplicattionContext.xml de spring), ponemos lo siguiente:

<?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:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core"
	xsi:schemaLocation="
	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:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
	<cxf:bus>
		<cxf:inInterceptors> <!-- logueamos entrada-salida y la persistimos -->
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
			<ref bean="persistInInterceptor" />
		</cxf:inInterceptors>

		<cxf:inFaultInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
			<ref bean="persistInInterceptor" />
		</cxf:inFaultInterceptors>

		<cxf:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
			<ref bean="persistOutInterceptor" />
		</cxf:outInterceptors>

		<cxf:outFaultInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
			<ref bean="persistOutInterceptor" />
		</cxf:outFaultInterceptors>
	</cxf:bus>

	<jaxws:endpoint id="busquedaService" implementor="#busqueda" address="/busqueda" />

	<bean id="exchangeDAOImpl" class="org.apache.cxf.management.persistence.FilesystemExchangeDataDAO" />

	<bean id="statusExchangeDataFilter" class="org.apache.cxf.management.persistence.StatusExchangeDataFilter" >
		<property name="statusFilter" value="ERROR" /> <!-- Registramos solo los errores, opcion por defecto que no hace falta poner -->
		<!-- <property name="statusFilter" value="OK" /> --> <!-- Tb podemos registrar los aciertos -->
	</bean>
	
	<bean id="persistInInterceptor" class="org.apache.cxf.management.interceptor.PersistInInterceptor" />
	<bean id="persistOutInterceptor" class="org.apache.cxf.management.interceptor.PersistOutInterceptor">
		<property name="exchangeDataDAO" ref="exchangeDAOImpl" />
		<property name="exchangeDataFilter" ref="statusExchangeDataFilter" />
	</bean>
	
</beans>

b) Personalizando el dao y el filtro.

Exactamente lo anterior pero Con nuestra propia implementación de las interfaces.

<?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:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core"
	xsi:schemaLocation="
	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:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
	<cxf:bus>
		<cxf:inInterceptors> <!-- logueamos entrada-salida y la persistimos -->
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
			<ref bean="persistInInterceptor" />
		</cxf:inInterceptors>

		<cxf:inFaultInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
			<ref bean="persistInInterceptor" />
		</cxf:inFaultInterceptors>

		<cxf:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
			<ref bean="persistOutInterceptor" />
		</cxf:outInterceptors>

		<cxf:outFaultInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
			<ref bean="persistOutInterceptor" />
		</cxf:outFaultInterceptors>
	</cxf:bus>

	<jaxws:endpoint id="busquedaService" implementor="#busqueda" address="/busqueda" />

	<bean id="exchangeDAOImpl" class="mi.paquete.cxf.interceptor.persistence.PersistExchangeDataDAO" />

	<bean id="exchangeDataFilter" class="mi.paquete.cxf.interceptor.persistence.NoFiltroNada" />
	
	<bean id="persistInInterceptor" class="org.apache.cxf.management.interceptor.PersistInInterceptor" />
	<bean id="persistOutInterceptor" class="org.apache.cxf.management.interceptor.PersistOutInterceptor">
		<property name="exchangeDataDAO" ref="exchangeDAOImpl" />
		<property name="exchangeDataFilter" ref="exchangeDataFilter" />
	</bean>
	
</beans>

El filtro, el cual queremos que no filtre nada, que persista siempre:

package mi.paquete.cxf.interceptor.persistence;

import org.apache.cxf.management.persistence.ExchangeData;
import org.apache.cxf.management.persistence.ExchangeDataFilter;

public class NoFiltroNada  implements ExchangeDataFilter {

	public boolean shouldPersist(ExchangeData data) {
		if(data == null){
			return false;
		}else{
			return true;
		}
	}

}

Nuestra implementación de como vamos a graba en la base de datos:

package mi.paquete.cxf.interceptor.persistence;

import org.apache.cxf.management.persistence.ExchangeData;
import org.apache.cxf.management.persistence.ExchangeDataDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import mi.paquete.cxf.interceptor.persistence.AuditoriaWsDao ;

public class ExchangeDataDaoImpl implements ExchangeDataDAO{

	@Autowired
	@Qualifier("auditoriaWsDao")
	private AuditoriaWsDao auditDao;
	
	public void save(ExchangeData exchangeData) throws Exception {
		if(exchangeData == null) return;
		
		/* 
		* GUARDAR:
		* (
		*             --descripcion del servicio--
		*   NombreDelServicio, NombreDeLaOperacionLLamada,
		*             --descripcion de la peticion--
		*   fechaPeticion, xmlPeticion, tipoDePeticion, pilaError,
		*             --descripcion de la respuesta--
		*   fechaRespuesta, xmlRespuesta, tipoDeRespuesta, pilaError,
		*  )
		*/
		auditDao.save(exchangeData.getUri(), exchangeData.getOperation(), 
			exchangeData.getInDate(), exchangeData.getRequest(), AuditDao.REQUEST, null,
			exchangeData.getOutDate(), exchangeData.getResponse(), AuditDao.RESPONSE, exchangeData.getStackTrace());
	}

}

la implementación del DAO, de la configurarión de la bbdd, de Hibernate, de Spring Transacctional, etc. no corresponden a este articulo.

Graba en bbdd la entrada y salida en el log del Web Service con Apache CXF

Mostrar la entrada y salida en el log del Web Service con Apache CXF

Tambien te puede interesar: Graba en bbdd la entrada y salida en el log del Web Service con Apache CXF.

Muchas veces te gustaria (deberias) saber cuales son los mensajes de entrada y salida del web service. Tan facil como configurar unos interceptors que vienen en CXF por defecto. En el fichero de configuracion de cxf, que es un applicationContext.xml 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:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core"
	xsi:schemaLocation="
	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:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
	
	<context:annotation-config />
	<context:component-scan base-package="es.depontevedra.ws.cxf.cividas.search" />

	<cxf:bus>
	        <cxf:inInterceptors>
	            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
	        </cxf:inInterceptors>
	        
	  	<cxf:inFaultInterceptors>
	            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
	        </cxf:inFaultInterceptors>
	        
	        <cxf:outInterceptors>            
	            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
	        </cxf:outInterceptors>
	        
	  	<cxf:outFaultInterceptors>           
	             <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
	        </cxf:outFaultInterceptors>
	    </cxf:bus>

	<bean id="busqueda" class="mi.paquete.spi.BusquedaImpl" />
	<jaxws:endpoint id="busquedaService" implementor="#busqueda" address="/busqueda" />
	
</beans>

¿Por que cuatro?. Dos de la entrada-salida normal y dos de la entrada-salida de error. Aunque me pica en la nariz que el cxf:inFaultInterceptors para el log no es necesario. Todavia no he conseguido hacerlo fallar… se me estan ocurriendo nuevos test malignos… Nop, no hay manera no consigo que el mensaje de entrada consiga ir por aqui cxf:inFaultInterceptors.

Mostrar la entrada y salida en el log del Web Service con Apache CXF