/********************************************************************
 * 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;
	}
}
