/********************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ********************************************************************/


package gov.va.med.fw.service.transaction;

import java.util.Properties;
import java.util.Set;

import org.aopalliance.aop.Advice;
import org.apache.commons.lang.Validate;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;

/**
 * Specialization of Spring TransactionProxyFactoryBean that "lazily" adds a
 * postInterceptor for methods that can start their own Transactions.  An alternate
 * way of doing this could have been to proxy the setTransactionAttributes call and
 * perform after advice to do this task.
 * 
 * Created Nov 3, 2005 9:35:23 AM
 * @author DNS   BOHMEG
 */
public class SynchronizableTransactionProxyFactoryBean extends
	ConfigurableTransactionProxyFactoryBean {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5276223151452991790L;


	/** Overriden to lazily created PostInterecptor for TX Synchronization if needed */
	public void setTransactionAttributes(Properties transactionAttributes) {
		super.setTransactionAttributes(transactionAttributes);
		
		// see if any are starting TX, if so use
		Set transactionalMethods = getMethodNamesThatInitiateTransactions();		
		if(transactionalMethods.size() > 0) {
			synchronizationAdvisor = new NameMatchMethodPointcutAdvisor();
			synchronizationAdvisor.setAdvice(transactionSynchronizationInitializer);
			synchronizationAdvisor.setMappedNames((String[]) transactionalMethods.toArray(new String[transactionalMethods.size()]));
			initSynchronizationAdvisorAsPostInterceptor();
		}	
	}
	
	private void initSynchronizationAdvisorAsPostInterceptor() {
		Object[] modifiedPostInterceptors = null;
		if(configuredPostInterceptors != null) {
			modifiedPostInterceptors = new Object[configuredPostInterceptors.length + 1];
			for(int i=0; i<configuredPostInterceptors.length; i++) {
				modifiedPostInterceptors[i] = configuredPostInterceptors[i]; 
			}
			modifiedPostInterceptors[modifiedPostInterceptors.length - 1] = synchronizationAdvisor;
		} else {
			modifiedPostInterceptors = new Object[1];
			modifiedPostInterceptors[0] = synchronizationAdvisor;
		}
		
		super.setPostInterceptors(modifiedPostInterceptors);
	}
	
	public void setPostInterceptors(Object[] postInterceptors) {
		configuredPostInterceptors = postInterceptors;
		Object[] modifiedPostInterceptors = postInterceptors;
		// always add
		if(synchronizationAdvisor != null) {
			modifiedPostInterceptors = new Object[postInterceptors.length + 1];
			for(int i=0; i<postInterceptors.length; i++) {
				modifiedPostInterceptors[i] = postInterceptors[i]; 
			}
			modifiedPostInterceptors[modifiedPostInterceptors.length - 1] = synchronizationAdvisor;
		}
		super.setPostInterceptors(modifiedPostInterceptors);			
	}
	
	private Object[] configuredPostInterceptors;
	private NameMatchMethodPointcutAdvisor synchronizationAdvisor;
	private Advice transactionSynchronizationInitializer;

	
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		Validate.notNull(transactionSynchronizationInitializer);
	}

	/**
	 * @return Returns the transactionSynchronizationInitializer.
	 */
	public Advice getTransactionSynchronizationInitializer() {
		return transactionSynchronizationInitializer;
	}

	/**
	 * @param transactionSynchronizationInitializer The transactionSynchronizationInitializer to set.
	 */
	public void setTransactionSynchronizationInitializer(
			Advice transactionSynchronizationInitializer) {
		this.transactionSynchronizationInitializer = transactionSynchronizationInitializer;
	}
}
