/*
 * Created on Jul 31, 2006
 * 
 * (C) Copyright TANDBERG Television Ltd.
 */

package com.tandbergtv.workflow.webservice.filesubsystem.messagehandler;

import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.log4j.Logger;

import com.tandbergtv.workflow.message.IMessageUID;
import com.tandbergtv.workflow.message.WorkflowMessage;
import com.tandbergtv.workflow.message.WorkflowPayload;
import com.tandbergtv.workflow.message.WorkflowMessage.MessageType;

/**
 * Abstract Implementation of the MessageHandler that contains helper methods to create the message.
 * 
 * @author Vijay Silva
 */
public abstract class AbstractMessageHandler implements MessageHandler
{
	private static final Logger logger = Logger.getLogger(AbstractMessageHandler.class);
	
	public static final String ASYNC_MESSAGE_UID = "070107";

	/**
	 * Default Constructor
	 */
	public AbstractMessageHandler()
	{
	}

	/**
	 * Default implementation to handle the input message.
	 * 
	 * @see com.tandbergtv.workflow.webservice.filesubsystem.messagehandler.MessageHandler#handleMessage(com.tandbergtv.workflow.message.WorkflowMessage)
	 */
	public WorkflowMessage handleMessage(WorkflowMessage message)
	{
		WorkflowMessage response = createEmptyResponseMessage(message);

		try
		{
			this.performOperation(message, response);
			response.setType(MessageType.ack);
		}
		catch (Exception ex)
		{
			response.setType(MessageType.nack);
			this.handleOperationException(ex, response);
		}

		return response;
	}

	// ========================================================================
	// ====================== ABSTRACT METHODS
	// ========================================================================

	/**
	 * Method to perform the Operation given the input workflow message and the output response
	 * message. If the operation fails, an exception must be thrown.
	 * 
	 * @param message
	 *            The Input workflow message
	 * @param response
	 *            the Output response workflow message
	 * 
	 * @throws Exception
	 *             Exception performing the operation.
	 */
	protected abstract void performOperation(WorkflowMessage message, WorkflowMessage response)
			throws Exception;

	// ========================================================================
	// ====================== HELPER METHODS
	// ========================================================================

	/**
	 * Method to handle the Exception thrown when the Operation fails.
	 * 
	 * @param ex
	 *            The Exception thrown when the operation is performed
	 * @param response
	 *            The Workflow Response message
	 */
	protected void handleOperationException(Throwable ex, WorkflowMessage response)
	{
		this.writeErrorProperties(response, ex);
		logger.error("Failed to perform File Subsystem Operation: " + ex.getMessage(), ex);
	}

	/*
	 * Method to copy the meaningful message properties from the source message to the response.
	 */
	protected WorkflowMessage createEmptyResponseMessage(WorkflowMessage message)
	{
		IMessageUID messageUID = message.getMessageUID();
		return new WorkflowMessage(messageUID, message.getKey());
	}

	/*
	 * Helper method that writes the Error Message as well as the Error Stack from the exception to
	 * the workflow response message payload.
	 */
	protected void writeErrorProperties(WorkflowMessage response, Throwable ex)
	{
		WorkflowPayload payload = (WorkflowPayload) response.getPayload();

		StringWriter writer = new StringWriter();
		ex.printStackTrace(new PrintWriter(writer));
		payload.putValue(MessageParameters.ERROR_MESSAGE, ex.getMessage());
		payload.putValue(MessageParameters.ERROR_STACK, writer.toString());
	}
	
	/**
	 * Method to Serialize the Object into an XML String.
	 * 
	 * @param obj
	 *            the object to serialize
	 * @return The String containing the XML Serialization of the object
	 */
	protected String serializeObject(Object obj)
	{
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		BufferedOutputStream bufStream = new BufferedOutputStream(outStream);
		XMLEncoder encoder = new XMLEncoder(bufStream);
		encoder.writeObject(obj);
		encoder.close();

		return outStream.toString();
	}	
}
