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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Constants;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * 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),
			DefaultTransactionDefinition.PREFIX_PROPAGATION);

	private static final String TX_REQUIRES_NEW_CONSTANT = TX_CONSTANTS.toCode(new Integer(
			TransactionDefinition.PROPAGATION_REQUIRES_NEW),
			DefaultTransactionDefinition.PREFIX_PROPAGATION);

	private int transactionTimeout = TransactionDefinition.TIMEOUT_DEFAULT;

	private Set methodNamesThatInitiateTransactions = new HashSet();

	protected final Log logger = LogFactory.getLog(getClass());

	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.PREFIX_TIMEOUT
						+ 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.PREFIX_TIMEOUT) == -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;
	}
}
