/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.service.transaction;

// Java classes
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

// Spring classes
import org.springframework.core.Constants;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;

/**
 * Allows for insertion of global transaction attributes
 * 
 * Created Mar 10, 2006 10:32:21 PM
 * 
 * @author DNS   BOHMEG
 */
public class ConfigurableTransactionProxyFactoryBean extends TransactionProxyFactoryBean {
   
	/**
    * An instance of serialVersionUID 
    */
   private static final long serialVersionUID = -1945956638456105718L;

   private static final Constants TX_CONSTANTS = new Constants(TransactionDefinition.class);
	
   private static final String TX_REQUIRED_CONSTANT = 
      TX_CONSTANTS.toCode( new Integer(TransactionDefinition.PROPAGATION_REQUIRED),
                           TransactionDefinition.PROPAGATION_CONSTANT_PREFIX);
   
	private static final String TX_REQUIRES_NEW_CONSTANT = 
      TX_CONSTANTS.toCode( new Integer(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
                           TransactionDefinition.PROPAGATION_CONSTANT_PREFIX);
	
	private int transactionTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
   
	private Set methodNamesThatInitiateTransactions = new HashSet();
	
	public void setTransactionAttributes(Properties transactionAttributes) {
		preProcessTransactionMetadata(transactionAttributes);
		super.setTransactionAttributes(transactionAttributes);
	}

	protected Properties preProcessTransactionMetadata(Properties props) {
		Set methods = props.keySet();
		String methodName = null;
		String metadata = null;
		for (Iterator itr = methods.iterator(); itr.hasNext();) {
			methodName = (String) itr.next();
			metadata = (String) props.get(methodName);
			if (shouldUpdateMetadataForTimeout(methodName, metadata)) {
				if (logger.isDebugEnabled())
					logger.debug("### Bean method ["
							+ methodName
							+ "] - applying tx timeout default of "
							+ transactionTimeout);

				metadata = metadata + ","
						+ DefaultTransactionDefinition.TIMEOUT_PREFIX
						+ transactionTimeout;
				props.put(methodName, metadata);
			}
		}
		return props;
	}

	protected boolean shouldUpdateMetadataForTimeout(String methodName, String metadata) {
		// only update if does not have a timeout already and will start a TX
		boolean shouldUpdate = false;
		if (metadata != null) {
			if (metadata.indexOf(TX_REQUIRED_CONSTANT) >= 0
					|| metadata.indexOf(TX_REQUIRES_NEW_CONSTANT) >= 0) {
				methodNamesThatInitiateTransactions.add(methodName);
				// ok, it starts a TX, now see if there is already a timeout
				if (metadata.indexOf(DefaultTransactionDefinition.TIMEOUT_PREFIX) == -1)
					shouldUpdate = true;
			}
		}
		return transactionTimeout != TransactionDefinition.TIMEOUT_DEFAULT ? shouldUpdate : false;
	}

	/**
	 * @return Returns the transactionTimeout.
	 */
	public int getTransactionTimeout() {
		return transactionTimeout;
	}

	/**
	 * @param transactionTimeout
	 *            The transactionTimeout to set.
	 */
	public void setTransactionTimeout(int transactionTimeout) {
		this.transactionTimeout = transactionTimeout;
	}

	/**
	 * @return Returns the methodNamesThatInitiateTransactions.
	 */
	protected Set getMethodNamesThatInitiateTransactions() {
		return methodNamesThatInitiateTransactions;
	}
}
