Configure JPA project for Multiple Transacction Manager

Configuracion del proyecto de persistencia A

Configuracion de los ficheros de configuracion

applicationContext_ddbb-A.xml. Personalizamos el nombre del application context pues deberemos llamarlo en otro proyecto para que spring carge esta configuracion.

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

	<context:component-scan base-package="es.tecnocom.auditoria">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
	</context:component-scan>
	
	<tx:annotation-driven />

<!-- 	<context:property-placeholder location="classpath*:ddbba_database.properties" /> -->
<!--  Por motivos de implementacion es obligatorio que quien me llame, me ponga en el contexto este properties -->

	<bean id="aDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		p:driverClass="${ddbba.database.driver}" p:jdbcUrl="${ddbba.database.url}" p:user="${ddbba.database.user}"
		p:password="${ddbba.database.password}" p:acquireIncrement="${ddbba.database.c3p0.acquireIncrement}"
		p:idleConnectionTestPeriod="${ddbba.database.c3p0.idleConnectionTestPeriod}"
		p:maxPoolSize="${ddbba.database.c3p0.maxPoolSize}" p:maxStatements="${ddbba.database.c3p0.maxStatements}"
		p:minPoolSize="${ddbba.database.c3p0.minPoolSize}" />

	<bean id="aEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="aDataSource" />
		<property name="persistenceUnitName" value="ddbbAPersist" />
		<property name="jpaVendorAdapter">
	      	<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
		        <property name="showSql" value="${ddbba.jpa.show_sql}" />
		        <property name="generateDdl" value="${ddbba.jpa.generateDdl}" />
		        <property name="databasePlatform" value="${ddbba.jpa.dialect}" />
	      	</bean>
  		 </property>
  		 <property name="jpaProperties">
            <props>
            	<prop key="hibernate.hbm2ddl.auto">${ddbba.hibernate.hbm2ddl.auto}</prop>
            	<prop key="hibernate.archive.autodetection">${ddbba.hibernate.archive.autodetection}</prop>
<!--             	<prop key="hibernate.transaction.flush_before_completion">${ddbba.hibernate.transaction.flush_before_completion}</prop> -->
            	<prop key="hibernate.connection.driver_class">${ddbba.hibernate.connection.driver_class}</prop>
            </props>
        </property>
	</bean>

	<bean id="aTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="aEntityManagerFactory" />
		<qualifier value="ddbbA"/>
	</bean>

</beans>

Como se puede observar la etiqueta va sin mas configuración, a pesar que habría que indicar que transacción es la que se esta lanzando. Pero como vamos a lanzar mas de una, no ahora en este proyecto, sino que el proyecto que llame a éste, también llamará a otro que se conectará a otra base de datos para persistir o consultar. Por tanto en el entorno de spring tendremos mas de un Transaction Manager. Eso es lo que vamos a configurar.

El fichero de propiedades ddbba_database.properties, esta personalizado para que las propiedades no sean las mismas para los distintos proyectos que vamos a configurar. La personalizacion en este proyecto viene dada por la etiqueta: {ddbba.}

################################################################
# database properties
################################################################
# MySQL
ddbba.database.driver				= com.mysql.jdbc.Driver
ddbba.database.url				= jdbc:mysql://localhost/ddbba
# ORACLE
#ddbba.database.driver			= oracle.jdbc.driver.OracleDriver
#ddbba.database.url				= jdbc:oracle:thin:@127.0.0.1:1521:Oracle

ddbba.database.user				= root
ddbba.database.password			= root

# for apache commons-dbcp. SIN POOL
#ddbba.database.initialsize								= 5
#ddbba.database.maxactive								= 10

# for c3p0
ddbba.database.c3p0.acquireIncrement				         	= 5
ddbba.database.c3p0.idleConnectionTestPeriod		                = 60
ddbba.database.c3p0.maxPoolSize							= 10
ddbba.database.c3p0.maxStatements						= 5
ddbba.database.c3p0.minPoolSize							= 5

# hibernate JPA properties
#jpa.dialect									= org.hibernate.dialect.Oracle10gDialect
ddbba.jpa.dialect									= org.hibernate.dialect.MySQLDialect
ddbba.jpa.generateDdl								= false
ddbba.jpa.show_sql								= true
ddbba.hibernate.archive.autodetection					= class
ddbba.hibernate.hbm2ddl.auto						= validate
ddbba.hibernate.transaction.flush_before_completion		= true
#ddbba.hibernate.connection.driver_class				= oracle.jdbc.driver.OracleDriver
ddbba.hibernate.connection.driver_class					= com.mysql.jdbc.Driver
ddbba.hibernate.cache.provider_class					= org.hibernate.cache.HashtableCacheProvider

En /src/main/resources/META-INF/persistence.xml. Solo el nombre de la unidad de persistencia, ya que el resto se lo configuramos a traves del applicationContext de spring.

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://java.sun.com/xml/ns/persistence
		http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="ddbbAPersist" />

</persistence>

Configuracion de las clases

La interfaz del DAO.

package mi.paquete..dao;

import java.util.List;

import org.springframework.dao.DataAccessException;

import mi.paquete.domain.Audit;

public interface DdbbADao {
	public List<DdbbA> getAllXXX() throws DataAccessException;
	public DdbbA getXXX(Long aId) throws DataAccessException;
	public Long save(final String description1, final String description2) throws DataAccessException;
	public void delete(Long id) throws DataAccessException;
	public void updateD2(final Long idA, final String description2) throws DataAccessException;
}

La implementacion del DAO. Fijate que la anotacion @DdbbATx es propia de este proyecto que esta a nivel de clase. Esto ya lo he discutido en este articulo.

package es.tecnocom.persistence.auditoria.dao.spi;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;

import es.tecnocom.persistence.auditoria.dao.AuditDao;
import es.tecnocom.persistence.auditoria.dao.MessageDao;
import es.tecnocom.persistence.auditoria.domain.Audit;
import es.tecnocom.persistence.auditoria.domain.Message;

@DdbbATx
@Repository
public class DdbbADaoImpl extends GenericJPAController<DdbbA> implements DdbbADao {

	@Autowired
	private OtraTablaDao otroDao;
	
	public DdbbADaoImpl(){
		setClazz(DdbbA.class);
	}
	
	public List<DdbbA> getAllXXX() throws DataAccessException {
		return findAll();
	}
	
	public Audit getXXX(Long aId) throws DataAccessException {
		return findOne(aId);
	}
	
	public Long save(String description1, String description1) throws DataAccessException {
		otroDao.save(description1);
		
		DdbbA a = new DdbbA();
		a.setUser(user);
		a.setOperation(operation);
		a.setRequest(messageRequest);
		
		save(a);
		return a.getId();
	}

	public void delete(final Long id) throws DataAccessException{
		delete(getXXX(id));
	}
	
	public void updateD2(final Long idA, final String description2) throws DataAccessException {
		DdbbA a = getXXX(idA);
		a.setDescription2(description2);
		
		updateD2(a);
	}
}

Ahora toca mostrar el

package mi.paquete.auditoria.dao.spi;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;

import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import mi.paquete.dao.ConstDbbADao;

public abstract class GenericJPAController <T extends Serializable>{

	private Class<T> clazz;
	
	@PersistenceContext(unitName =ConstDbbADao.PERSISTENCE_UNIT_NAME)
	protected EntityManager entityManager;
	
	@PersistenceUnit(unitName =ConstDbbADao.ENTITY_MANAGER_FACTORY)
	private EntityManagerFactory entityManagerFactory;

	public EntityManagerFactory getEntityManagerFactory() {
		return entityManagerFactory;
	}

	public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
		this.entityManagerFactory = entityManagerFactory;
	}
	
	protected EntityManager getEntityManager() {
		return entityManager;
	}

	protected void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	public void setClazz(final Class<T> clazzToSet){
		this.clazz = clazzToSet;
	}

	@DdbbATx
	public T findOne(final Long id) throws DataAccessException{
		return getEntityManager().find(clazz, id);
	}
	
	@SuppressWarnings("unchecked")
	@DdbbATx
	public List<T> findAll() throws DataAccessException{
		return getEntityManager().createQuery("from " + clazz.getName()).getResultList();
	}

	@Transactional(value=ConstDbbADao.TRANSACTION_MANAGER, propagation = Propagation.REQUIRED, readOnly = false)
	public void save(T entity) throws DataAccessException{
		getEntityManager().persist(entity);
	}

	@DdbbATx
	public void actualize(final T entity) throws DataAccessException{
		getEntityManager().merge(entity);
	}

	@DdbbATx
	public void delete(final T entity) throws DataAccessException{
		getEntityManager().remove(entity);
	}

}

Vamos por metodos:
– Todos los que deben tener la anotacion @Transactional llevan la anotacion personalizada @DdbbATx
– El metodo save(…). Como quiero personalizarlo poniendo el tipo de propagacion lo pongo con la anotacion @Transactional. Es decir mi anotacion personalizada no permite modificaciones.
– Fijate que tanto EntityManager, como EntityManagerFactory las tengo con anotaciones, indicando cuales debe instanciar.

La anotacion Transactional personalizada:

package mi.paquete.dao.spi;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.transaction.annotation.Transactional;

import mi.paquete.dao.ConstDbbADao;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(ConstDbbADao.TRANSACTION_MANAGER)
public @interface DdbbATx{

}

Fijate que ya indico la transaccion que voy a usar: @Transactional(ConstDbbADao.TRANSACTION_MANAGER)

las constantes usadas. Se corresponden con los nombres que estan en persistence.xml, y applicationContext….xml

package mi.paquete.dao;

public class ConstDbbADao{

	public static final String TRANSACTION_MANAGER 		= "aTransactionManager";
	public static final String ENTITY_MANAGER_FACTORY 	= "aEntityManagerFactory";
	public static final String PERSISTENCE_UNIT_NAME 	        = "aPersist";
}

La clase DdbbA que esta en el paquete domain la deduces tu mismo.

Configuracion de los test

Para los test vamos a necesitar indicar al contexto spring donde estan las propiedades. applicationContext_ddbb-A-test.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="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">

	<!-- El test llama, entonces tiene que indicar donde estan las propiedades -->
	<bean id="properties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="locations">
			<list>
				<value>classpath*:ddbba_database.properties</value>
			</list>
		</property>
	</bean>
	<context:property-placeholder properties-ref="properties" />
</beans>

Ejemplo de la clase test.

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import javax.sql.rowset.serial.SerialException;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import mi.paquete.dao.DdbbADao;
import mi.paquete.domain.DdbbA;

@Transactional
@TransactionConfiguration(transactionManager=ConstTest.TRANSACTION_MANAGER, defaultRollback=false)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {ConstTest.APPLICATION_CONTEXT, ConstTest.APPLICATION_CONTEXT_TEST})
public class AuditTest {

	@Autowired
	private DdbbADao dao;

	@Test
	public void getAuditTest() throws SQLException, IOException {
		DdbbA a= dao.getXXX(1L);
		Assert.assertTrue(1 == a.getId());
		Assert.assertEquals("description1", a.getDescription1());
		Assert.assertEquals("description2", a.getDescription2());
	}

	@Test
	public void getAuditsTest(){
		List<DdbbA > list = dao.getAllXXX();
		Assert.assertEquals(5, list.size());
	}
	
	@Test
	@Rollback(true)
	public void saveUpdateDeleteTest() throws DataAccessException {
		List<DdbbA > list = dao.getAllXXX();
		int tot = list.size();
		
		//SAVE
		Long id = dao.save("description11", "description22");
		
		list = dao.getAllXXX();
		Assert.assertTrue((tot + 1) == list.size());
		
		//UPDATE
		dao.updateD2(id, "description2222");
		
		DdbbA a = dao.getXXX(id);
		Assert.assertEquals(a.getDescription2(), "description2222");
		
		//DELETE
		dao.delete(id);
		
		list = dao.getAllXXX();
		Assert.assertEquals(tot, list.size());
	}

la clase de constantes.

public class ConstTest {

	public static final String APPLICATION_CONTEXT = "/applicationContext_ddbb-A.xml";
	public static final String APPLICATION_CONTEXT_TEST = "/applicationContext_ddbb-A-test.xml";
	public static final String TRANSACTION_MANAGER = "aTransactionManager";
}

Configuracion del proyecto de persistencia B

Lo mismo que el A pero siendo B. No se si me explico…

Configuracion del proyecto que llama a persistencia A y B

applicationContext-proyectoAB.xml. Fijate que llamamos a los applicationContext de los proyectos de persistencia que se encuentran dentro de los jar de los proyectos. Cargamos los ficheros de propiedades que se encuentran en /src/main/resources.

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
        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">

	<import resource="classpath*:applicationContext_ddbb-A.xml" />
	<import resource="classpath*:applicationContext_ddbb-B.xml" />

	<!-- Se cargan las propiedades de las bases de datos -->
	<bean id="properties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="locations">
			<list>
				<value>classpath:ddbba_database.properties</value>
				<value>classpath:ddbbb_database.properties</value>
			</list>
		</property>
	</bean>
	<context:property-placeholder properties-ref="properties" />    

	<context:annotation-config />
	<context:component-scan base-package="mi.paquete.ab" />

        <!-- ... definiciones varias -->
	
</beans>

Todo ok. ¿Te ha funcionado?.

Ahora bien, y si quiero que el proyecto A sea con transacciones dinamicas, pues va a grabar en la base de datos A1 y A2… entonces mira estos enlaces:
http://stackoverflow.com/questions/4106078/dynamic-jpa-connection
http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/
http://blog.springsource.org/2011/08/15/configuring-spring-and-jta-without-full-java-ee/

Basicamente lo que recomienda mucha gente es cambiar a JTA y

Anuncios
Configure JPA project for Multiple Transacction Manager

Crear un Handler para Web Services en Apache CXF

Los handler que son propios de los Web Services, no son la forma mas adecuada para Apache CXF. La mejor forma, por que te da muchas mas opciones, son los interceptors. Pero como vamos a ver a continuación, no hay ningún problema en añadir un handler a un Web Service creado con Apache CXF.

Lo primero de todo es crear el Handler. Queremos mostrar por el log, los mensajes de entrada y salida.

package mi.paquete.ws.cxf.handler;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

@SuppressWarnings("restriction")
public class LogHandler implements SOAPHandler<SOAPMessageContext> {

	private static final Logger LOGGER = LoggerFactory.getLogger(LogHandler.class);
	
	public boolean handleMessage(SOAPMessageContext mc) {
		logMessage(mc);
		return true;
	}

	public boolean handleFault(SOAPMessageContext context) {
		return true;
	}

	public void close(MessageContext context) {
	}

	public Set<QName> getHeaders() {
		return null;
	}

	private Boolean isResponse(SOAPMessageContext mc) {
		Boolean isReponse = (Boolean) mc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
		return isReponse;
	}

	private void logMessage(SOAPMessageContext mc){
		boolean isResponse = isResponse(mc);
		
		String type = isResponse ? "XML RESPONSE : " : "XML REQUEST: ";
		String strMessage = "";
		
		try {
			strMessage = messageToString(mc.getMessage());
		} catch (SOAPException e) {
			LOGGER.warn("SOAPException: ", e);
		} catch (IOException e) {
			LOGGER.warn("IOException: ", e);
		}
				
		LOGGER.info("WS "+ type + strMessage);
	}
	
	private String  messageToString(final SOAPMessage message)
			throws SOAPException, IOException {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		message.writeTo(out);
		return out.toString();
	}

}

Ahora simplemente configuramos cxf para que ejecute el handle a su momento.
Hay varias maneras de hacerlo:
1 – con un fichero handler.xml ubicado:
1.1 – en la siguiente ruta: /src/main/resources/mi/paquete/ws/cxf/spi/handler.xml, asi en la creacion del jar el fichero handler.xml estará en la misma carpeta que la clase que lo llama. La anotacion en la clase que implementa el web service (la clase que lo llama) seria @HandlerChain(handler.xml)
1.2 – o en una ruta cualquiera ya que la anotacion tiene una ruta absoluta: @HandlerChain(file=”../../common/handlers/myhandlers.xml”).
2 – en la configuracion de cxf (fichero applicationContext.xml de spring), que a todas luces no es nada intrusiva y por configuracion indicas si pones un handler dos, tres o ninguno.

<?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="mi.paquete.ws.cxf" />

	<bean id="busqueda" class="mi.paquete.ws.cxf.spi.WebServiceImpl" />

	<jaxws:endpoint id="busquedaService" implementor="#busqueda" address="/busqueda">
		<jaxws:handlers>
			<bean class="mi.paquete.ws.cxf.handler.LogHandle" />
		</jaxws:handlers>
	</jaxws:endpoint>
	
</beans>

LIMITACIONES: si hay un error, una excepcion, el mensaje de salida no se loguea, es null. Supongo que para los handler hay fases de cuando quieres que se ejecute. Pero encontre los interceptors de CXF y no busque mas informacion sobre los handlers.

VENTAJAS: Los interceptors tienen la limitacion que solo se pueden usar con CXF, no se pueden llevar a jax-ws. Mientras que los handlers son extrapolables a todos los webservices.

Crear un Handler para Web Services en Apache CXF

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

@Transactional

¿Porque escribir un post sobre esta anotación?, porque me ha tenido 16h bloqueado. En un principio parece que hay que usarla a nivel de clase.Posteriormente, leyendo en stackoverflow, te dicen que a nivel de método. Oki. Tampoco va. Porque tiene que ser a nivel de clase y de método. Olé.¿Porqué?
Ejemplo en el manual de Spring. ¿Alguien ve anotaciones a nivel de método?

// the service class that we want to make transactional

@Transactional
public class DefaultFooService implements FooService {

 Foo getFoo(String fooName);
 Foo getFoo(String fooName, String barName);
 void insertFoo(Foo foo);
 void updateFoo(Foo foo);

}

Su applicationContext.xml


<!-- from the file 'context.xml' -->
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- other <bean/> definitions here -->

</beans>

Del manual de Spring:

Si  <bean id=”txManager”> se llamase transactionManager, entonces no haría falta ponerlo en: <tx:annotation-driven transaction-manager=”txManager”/>. Se quedaria: <tx:annotation-driven />.

“You can place the @Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class. However, the mere presence of the @Transactional annotation is not enough to activate the transactional behavior.” Es decir se puede poner conjuntamente, pero no aclara que debe ser asi.

“Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Transactional annotation, as opposed to annotating interfaces.” Si no es una interfaz… si es una clase… la anotación va en la clase y en el método… puff.

¿Porque no usarlo en interfaces?. Porque si utilizas <tx:annotation-driven mode=”aspectj”/> ó <tx:annotation-driven proxy-target-class=”true”/>. Spring no va a poder hacer su magia.

Cuidadin con poner @Transactional y @Service juntas…

“The most derived location takes precedence when evaluating the transactional settings for a method”. Es decir si tienes la anotacion a nivel de clase y de metodo, prevalece la del metodo. La configuracion que introduzcas en el metodo tienen prioridad sobre la configuracion de la anotacion en la clase. Ej: si en la clase, la transaccion es de solo lectura, y en el metodo de escritura, ese metodo podra escribir.

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

Cuando usas proxy, la anotación solo es viable en métodos públicos. No se lanzara ningún error, pero el proxy no los podrá ver. Si por alguna razón, necesitas métodos de otro tipo, protected ó private, utiliza mejor aspectos.

Bien, esto ya ha terminado, ¿donde pone que es obligatorio que el método y la clase deban tener la anotacion @Transaction?. Si no lo haces así, no funciona.

@Transactional

Subversion en consola

descargar un proyecto a la carpeta local:
svn checkout https://(projectname).(domain)/svn/(projectname)/(DIR) (projectname) –username [type-user-name-here]

svn add: añadir un fichero.
svn revert: si te equivocas.
svn blame: viendo el contenido de archivos específicos con la información de revisión y autor.
svn cat: ver el contenido de un archivo específico.
svn cleanup: destrabando una copia de trabajo.
svn copy: copiar un archivo o directorio en la copia de trabajo o en el repositorio.
svn delete/del/remove/rm: borrar un archivo o directorio desde tu copia local.
svn diff file1 file2: ver las diferencias entre archivos.
para comparar el archivo modificado localmente “index.php” contra el que se encuentra en el repositorio, debes tipear:
svn diff $SRC/…../index.html https://(projectname).(domain)/svn/(projectname)/trunk (projectname) –username [type-user-name-here]
svn diff -r 456:459 subfn.cpp
svn export: exportar un directorio limpio de archivos .svn a tu máquina local.
Con este comando puedes extraer una copia sin versionar (sin los archivos .svn) del proyecto, para obtener el directorio sin versionar de cualquier revisión debes escribir:
svn export [-r REV] [PATH]
svn help: ver la ayuda de subversion.
svn commit: subiendo tus cambios.
svn commit -m “Type your comment here” [files]
svn import: importando un archivo o directorio sin versionar al repositorio.
svn info: mostrar información acerca de paths en tu copia de trabajo.
svn list: listar los directorios en el repositorio.
svn log: mostrando los mensajes de log de los commits.
svn log -v .
svn log -r RevisionNumber http://URL/path/file
svn log -l 4 (solo las ultimas 4 entradas)
svn log -v –limit 4 (verbose)
svn log -r 1:HEAD (To list everything in ascending order)
svn log -r HEAD:1 (To list everything in descending order)
svn log -r 13:BASE (To list everything from the thirteenth to the base of the currently checked-out revision in ascending order)
svn log -r {2011-02-02}:{2011-02-03} (To get everything between the given dates)

svn merge: fusionado cambios.
svn mkdir: creando un nuevo directorio.
svn move: moviendo un archivo o directorio.
svn resolved: resolviendo conflictos.
svn status: obteniendo el estado de un archivo o directorio.
svn update: actualizando tu copia de trabajo.

Explicacion de las columnas:
The first column
‘ ‘: No modifications.
‘A’: Item is scheduled for Addition.
‘D’: Item is scheduled for Deletion.
‘M’: Item has been modified.
‘R’: Item has been replaced in your working copy. This means the file was scheduled for deletion, and then a new file with the same name was scheduled for addition in its place.
‘C’: The contents (as opposed to the properties) of the item conflict with updates received from the repository.
‘X’: Item is related to an externals definition.
‘I’: Item is being ignored (e.g. with the svn:ignore property).
‘?’: Item is not under version control.
‘!’: Item is missing (e.g. you moved or deleted it without using svn). This also indicates that a directory is incomplete (a checkout or update was interrupted).
‘~’: Item is versioned as one kind of object (file, directory, link), but has been replaced by different kind of object.

The second column tells the status of a file’s or directory’s properties.
‘ ‘: No modifications.
‘M’: Properties for this item have been modified.
‘C’: Properties for this item are in conflict with property updates received from the repository.

The third column is populated only if the working copy directory is locked. (See the section called “svn cleanup”.)
‘ ‘: Item is not locked.
‘L’:Item is locked.

The fourth column is populated only if the item is scheduled for addition-with-history.
‘ ‘:No history scheduled with commit.
‘+’: History scheduled with commit.

The fifth column is populated only if the item is switched relative to its parent (see the section called “Switching a Working Copy”).
‘ ‘: Item is a child of its parent directory.
‘S’: Item is switched.

The sixth column is populated with lock information.
‘ ‘: When –show-updates is used, the file is not locked. If –show-updates is not used, this merely means that the file is not locked in this working copy.
K: File is locked in this working copy.
O: File is locked either by another user or in another working copy. This only appears when –show-updates is used.
T: File was locked in this working copy, but the lock has been “stolen”and is invalid. The file is currently locked in the repository. This only appears when –show-updates is used.
B: File was locked in this working copy, but the lock has been “broken”and is invalid. The file is no longer locked This only appears when –show-updates is used.

The out-of-date information appears in the seventh column (only if you pass the –show-updates switch).
‘ ‘: The item in your working copy is up-to-date.
‘*’: A newer revision of the item exists on the server.

Realizar un tag:
svn copy http://host/repos/project/trunk http://host/repos/project/tags/TagName-1.4.5 -m “Tag Release 1.4.5”

Para darle un toque de color:
https://github.com/jmlacroix/svn-color/blob/master/svn-color.sh

Subversion en consola

SIGLAS para Negocios y correos electronicos

TGIF: Thank God it’s Friday, gracias a Dios es viernes.
WTF: What the fuck, Pero qué coño ó qué cojones.

Siglas que ya forman parte del Diccionario Oxford
FYI: For your information, para tu informacion.
LOL: Laughing out loud, riendo alto.
OMG: Oh, my god!, ¡Oh, Dios mío!.
TMI: Too much information, Demasiada informacion.

En español:
NPI: no poseo informacion ó ni puta idea

SIGLAS para Negocios y correos electronicos