/**
 * @copyright
 * ====================================================================
 * Copyright (c) 2003 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 * @endcopyright
 *
 * @file JNIThreadData.cpp
 * @brief Implementation of the class JNIThreadData
 */

#include "JNIThreadData.h"
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_thread_proc.h>
#include "JNIUtil.h"

apr_threadkey_t *JNIThreadData::g_key;

/**
 * create and initialize a new object
 */
JNIThreadData::JNIThreadData()
{
    m_env = NULL;
    m_exceptionThrown = false;
    m_requestPool = NULL;
	m_previous = NULL;
}
/**
 * destroy an object
 */
JNIThreadData::~JNIThreadData()
{

}
/**
 * initialize the thread local storage
 * @return success or failure
 */
bool JNIThreadData::initThreadData()
{
    // if already initialized -> nothing to do
    if(g_key != NULL)
    {
        return false;
    }

    // request a key for the thread local storage from the global pool and
    // register a callback function called, when the thread is deleted
    apr_status_t apr_err = apr_threadkey_private_create  ( &g_key, del, 
        JNIUtil::getPool());
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_create");
        return false;
    }

    return true;
}
/**
 * Get the thread local storage for this thread
 * @return thread local storage
 */
JNIThreadData * JNIThreadData::getThreadData()
{
    // we should never be called before initThreadData
    if(g_key == NULL)
    {
        return NULL;
    }

    // retrieve the thread local storage from apr
    JNIThreadData *data = NULL;
    apr_status_t apr_err = apr_threadkey_private_get 
        (reinterpret_cast<void**>(&data), g_key);
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
        return NULL;
    }

    // not already allocated
    if(data == NULL)
    {
        // allocate and store to apr
        data = new JNIThreadData;
        apr_err = apr_threadkey_private_set (data, g_key);
        if(apr_err)
        {
            JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
            return NULL;
        }
    }
    return data;
}
/**
 * Allocate a new ThreadData for the current call from java and push it on the
 * stack
 */
void JNIThreadData::pushNewThreadData()
{
    JNIThreadData *data = NULL;
    apr_status_t apr_err = apr_threadkey_private_get 
        (reinterpret_cast<void**>(&data), g_key);
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
        return;
    }
	JNIThreadData *newData = new JNIThreadData();
	newData->m_previous =data;
    apr_err = apr_threadkey_private_set (newData, g_key);
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
        return;
    }
}
/**
 * Pop the current ThreadData from the stack, because the call is completed
 */
void JNIThreadData::popThreadData()
{
    JNIThreadData *data = NULL;
    apr_status_t apr_err = apr_threadkey_private_get 
        (reinterpret_cast<void**>(&data), g_key);
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
        return;
    }
	if(data == NULL)
	{
		return;
	}
	JNIThreadData *oldData = data->m_previous;
	delete data;
    apr_err = apr_threadkey_private_set (oldData, g_key);
    if(apr_err)
    {
        JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
        return;
    }
}
/**
 * callback called by apr, when the thread dies. Deletes the thread local 
 * storage
 */
void JNIThreadData::del(void *p)
{
    delete reinterpret_cast<JNIThreadData*>(p);
}
