/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <axis2_phase_resolver.h>
#include <axutil_property.h>
#include <axis2_addr.h>
/*
 * It is important to understand the following relationships between the
 * functions defined here and else where.
 * axis2_phase_resolver_engage_module_globally->axis2_svc_add_module_ops->
 * ->axis2_phase_resolver_build_execution_chains_for_module_op->axis2_phase_resolver_build_execution_chains_for_op
 *  and
 * axis2_phase_resolver_engage_module_to_svc->axis2_svc_add_module_ops->
 * ->axis2_phase_resolver_build_execution_chains_for_module_op->axis2_phase_resolver_build_execution_chains_for_op
 */
struct axis2_phase_resolver
{

    /** axis2 configuration */
    axis2_conf_t *axis2_config;

    /** service */
    axis2_svc_t *svc;
};

static axis2_status_t
axis2_phase_resolver_build_execution_chains_for_op(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    int type,
    axis2_op_t * op);

static axis2_status_t
axis2_phase_resolver_add_module_handlers_to_system_defined_phases(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_module_desc_t * module_desc);

static axis2_status_t
axis2_phase_resolver_add_module_handlers_to_user_defined_phases(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    struct axis2_svc *svc,
    struct axis2_module_desc *module_desc);

/* Deprecated and no longer used */
static axis2_status_t
axis2_phase_resolver_build_in_transport_chains(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_transport_in_desc_t * transport);

/* Deprecated and no longer used */
static axis2_status_t
axis2_phase_resolver_build_out_transport_chains(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_transport_out_desc_t * transport);

static axis2_status_t
axis2_phase_resolver_add_to_handler_list(
    const axutil_env_t * env,
    axutil_array_list_t *handler_list,
    axis2_op_t *op,
    axis2_module_desc_t * module_desc,
    int type);

AXIS2_EXTERN axis2_phase_resolver_t *AXIS2_CALL
axis2_phase_resolver_create(
    const axutil_env_t * env)
{
    axis2_phase_resolver_t *phase_resolver = NULL;

    phase_resolver = (axis2_phase_resolver_t *)AXIS2_MALLOC(env->allocator,
        sizeof(axis2_phase_resolver_t));

    if(!phase_resolver)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory.");
        return NULL;
    }

    phase_resolver->axis2_config = NULL;
    phase_resolver->svc = NULL;

    return phase_resolver;
}

AXIS2_EXTERN axis2_phase_resolver_t *AXIS2_CALL
axis2_phase_resolver_create_with_config(
    const axutil_env_t * env,
    axis2_conf_t * axis2_config)
{
    axis2_phase_resolver_t *phase_resolver = NULL;

    AXIS2_PARAM_CHECK(env->error, axis2_config, NULL);

    phase_resolver = (axis2_phase_resolver_t *)axis2_phase_resolver_create(env);

    phase_resolver->axis2_config = axis2_config;

    return phase_resolver;
}

AXIS2_EXTERN axis2_phase_resolver_t *AXIS2_CALL
axis2_phase_resolver_create_with_config_and_svc(
    const axutil_env_t * env,
    axis2_conf_t * axis2_config,
    axis2_svc_t * svc)
{
    axis2_phase_resolver_t *phase_resolver = NULL;

    AXIS2_PARAM_CHECK(env->error, axis2_config, NULL);

    phase_resolver = (axis2_phase_resolver_t *)axis2_phase_resolver_create(env);

    if(!phase_resolver)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No Memory.");
        return NULL;
    }
    phase_resolver->axis2_config = axis2_config;

    phase_resolver->svc = svc;
    AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Service name is : %s", axis2_svc_get_name(
        phase_resolver->svc, env));

    return phase_resolver;
}

AXIS2_EXTERN void AXIS2_CALL
axis2_phase_resolver_free(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env)
{
    if(phase_resolver)
    {
        AXIS2_FREE(env->allocator, phase_resolver);
    }

    return;
}

/**
 * This is in general called to engage a module to the axis2 engine. In other words modules handlers 
 * are added into all global and operation specific phases appropriately. Where these handlers 
 * should go is determined by the module handler specific descriptions in module.xml file. Also 
 * module operations are added to service and built exeuction chains for those operations as well.
 * First add all the handlers defined for system phases are added into system phases. Then module
 * operations are added into each service. At the same time execution chains for these module
 * operations are built as well. Then handlers defined for user phases are added into user defined
 * pahses.
 */

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_engage_module_globally(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_module_desc_t * module_desc)
{
    axis2_status_t status = AXIS2_FAILURE;
    axutil_qname_t *qname_addressing = NULL;
    axutil_hash_t *svcs = NULL;

    const axutil_qname_t *mod_qname = NULL;
    axis2_char_t *mod_name = NULL;
    axutil_hash_t *all_ops = NULL;
    axutil_hash_index_t *index_j = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_engage_module_globally");

    AXIS2_PARAM_CHECK(env->error, module_desc, AXIS2_FAILURE);

    mod_qname = axis2_module_desc_get_qname(module_desc, env);
    mod_name = axutil_qname_get_localpart(mod_qname, env);

    /* Add module handlers into global phases */
    status = axis2_phase_resolver_add_module_handlers_to_system_defined_phases(phase_resolver, env,
        module_desc);

    if(AXIS2_SUCCESS != status)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Engaging module %s to global chain failed",
            mod_name);
        return status;
    }

    /* Module is engaged to all the services */
    svcs = axis2_conf_get_all_svcs(phase_resolver->axis2_config, env);
    if(!svcs)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "There are no services in the axis2 configuration");
        return AXIS2_FAILURE;
    }

    qname_addressing = axutil_qname_create(env, AXIS2_MODULE_ADDRESSING, NULL, NULL);

    for(index_j = axutil_hash_first(svcs, env); index_j; index_j = axutil_hash_next(env, index_j))
    {
        axis2_svc_t *svc = NULL;
        void *w = NULL;
        axis2_svc_grp_t *parent = NULL;
        const axis2_char_t *svc_name = NULL;
        const axis2_char_t *svc_grp_name = NULL;

        axutil_hash_this(index_j, NULL, NULL, &w);
        svc = (axis2_svc_t *)w;
        svc_name = axis2_svc_get_name(svc, env);

        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "svc name is:%s", svc_name);

        /* Module operations are added to service and built execution chains for operations. */
        status = axis2_svc_add_module_ops(svc, env, module_desc, phase_resolver->axis2_config);
        if(AXIS2_SUCCESS != status)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Adding module operations for module %s to service %s failed", mod_name, svc_name);
            axutil_qname_free(qname_addressing, env);
            return status;
        }

        /* Call this function to add module handlers into service operation phases */
        status = axis2_phase_resolver_add_module_handlers_to_user_defined_phases(phase_resolver,
            env, svc, module_desc);

        if(AXIS2_SUCCESS != status)
        {
            axutil_qname_free(qname_addressing, env);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Engaging module %s to service %s failed",
                mod_name, svc_name);

            return status;
        }

        if(axutil_qname_equals(mod_qname, env, qname_addressing))
        {
            /* If addressing module then all operations which are not module 
             * operations with a wsa mapping parameter is added to the 
             * service's wsa-mapping list*/
            all_ops = axis2_svc_get_all_ops(svc, env);
            if(all_ops)
            {
                axutil_hash_index_t *hi = NULL;
                void *val = NULL;

                for(hi = axutil_hash_first(all_ops, env); hi; hi = axutil_hash_next(env, hi))
                {
                    axutil_hash_this(hi, NULL, NULL, &val);

                    if(val)
                    {
                        if(!axis2_op_is_from_module((axis2_op_t *)val, env))
                        {
                            axis2_op_t *op_desc = NULL;
                            axutil_array_list_t *params = NULL;
                            int j = 0;
                            int sizej = 0;

                            op_desc = (axis2_op_t *)val;
                            params = axis2_op_get_all_params(op_desc, env);
                            /* Adding wsa-mapping into service */
                            sizej = axutil_array_list_size(params, env);
                            for(j = 0; j < sizej; j++)
                            {
                                axutil_param_t *param = NULL;
                                axis2_char_t *param_name = NULL;

                                param = axutil_array_list_get(params, env, j);
                                param_name = axutil_param_get_name(param, env);
                                if(!axutil_strcmp(param_name, AXIS2_WSA_MAPPING))
                                {
                                    axis2_char_t *key = NULL;
                                    key = (axis2_char_t *)axutil_param_get_value(param, env);
                                    axis2_svc_add_mapping(svc, env, key, op_desc);
                                }
                            }
                        }
                        val = NULL;
                    }
                }
            }
        }

        parent = axis2_svc_get_parent(svc, env);
        if(parent)
        {
            axutil_array_list_t *modules = NULL;
            int j = 0;
            int sizej = 0;
            axis2_bool_t add_to_group = AXIS2_TRUE;
            svc_grp_name = axis2_svc_grp_get_name(parent, env);

            modules = axis2_svc_grp_get_all_module_qnames(parent, env);
            sizej = axutil_array_list_size(modules, env);
            for(j = 0; j < sizej; j++)
            {
                axutil_qname_t *module = NULL;

                module = (axutil_qname_t *)axutil_array_list_get(modules, env, j);
                if(axutil_qname_equals(mod_qname, env, module))
                {
                    add_to_group = AXIS2_FALSE;
                    break;
                }
            }

            if(add_to_group)
            {
                status = axis2_svc_grp_add_module_qname(parent, env, mod_qname);
            }
        }

        if(AXIS2_SUCCESS != status)
        {
            axutil_qname_free(qname_addressing, env);

            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Adding module %s to service group %s failed",
                mod_name, svc_grp_name);

            return status;
        }
    }

    axutil_qname_free(qname_addressing, env);

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_engage_module_globally");

    return status;
}

/**
 * This function is called to engage a module to a service specifically. In other words all module 
 * handlers defined for user phases are added into user defined phases and all module handlers 
 * defined for system defined phases are added into system defined phases. Note that user defined
 * phases are in the flows taken from operation and system defined phases are in the flows taken
 * from conf. Where each module handler should go is determined by module handler descriptions in 
 * module.xml file.
 * First we add the operations defined in the module into the service and built execution chains for 
 * them. Then for all the operations of the service we check whether the module 
 * already engaged to operation. If not engage it to service operation. Also if the module is newly 
 * engaged to operation add the module qnname to the engaged module list of the operation.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_engage_module_to_svc(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_svc_t * svc,
    axis2_module_desc_t * module_desc)
{
    axutil_hash_t *ops = NULL;
    axutil_hash_index_t *index_i = NULL;
    axis2_status_t status = AXIS2_FAILURE;
    const axutil_qname_t *module_d_qname = NULL;
    axis2_char_t *modname_d = NULL;
    const axis2_char_t *svcname = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_engage_module_to_svc");

    module_d_qname = axis2_module_desc_get_qname(module_desc, env);
    modname_d = axutil_qname_get_localpart(module_d_qname, env);
    svcname = axis2_svc_get_name(svc, env);

    AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Module %s will be engaged to %s", modname_d, svcname);

    ops = axis2_svc_get_all_ops(svc, env);
    if(!ops)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Service %s has no operation", svcname);

        return AXIS2_FAILURE;
    }

    /* Module operations are added to service and built execution chains */
    status = axis2_svc_add_module_ops(svc, env, module_desc, phase_resolver->axis2_config);

    if(AXIS2_SUCCESS != status)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
            "Adding module operations from module %s into service %s failed", modname_d, svcname);

        return status;
    }

    for(index_i = axutil_hash_first(ops, env); index_i; index_i = axutil_hash_next(env, index_i))
    {
        axutil_array_list_t *modules = NULL;
        axis2_op_t *op_desc = NULL;
        int size = 0;
        int j = 0;
        void *v = NULL;
        axis2_bool_t engaged = AXIS2_FALSE;
        axis2_char_t *opname = NULL;

        axutil_hash_this(index_i, NULL, NULL, &v);
        op_desc = (axis2_op_t *)v;

        opname = axutil_qname_get_localpart(axis2_op_get_qname(op_desc, env), env);

        modules = axis2_op_get_all_modules(op_desc, env);
        if(modules)
        {
            size = axutil_array_list_size(modules, env);
        }

        for(j = 0; j < size; j++)
        {
            axis2_module_desc_t *module_desc_l = NULL;
            const axutil_qname_t *module_d_qname_l = NULL;

            module_desc_l = axutil_array_list_get(modules, env, j);
            module_d_qname_l = axis2_module_desc_get_qname(module_desc_l, env);
            if(axutil_qname_equals(module_d_qname, env, module_d_qname_l))
            {
                engaged = AXIS2_TRUE;
                status = AXIS2_SUCCESS;
                AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI,
                    "Module %s already engaged to operation %s of service %s", modname_d, opname,
                    svcname);

                break;
            }
        }

        if(!engaged)
        {
            status = axis2_phase_resolver_engage_module_to_op(phase_resolver, env, op_desc,
                module_desc);

            if(AXIS2_SUCCESS != status)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                    "Engaging module %s to operation %s failed.", modname_d, opname);

                return status;
            }

            status = axis2_op_add_to_engaged_module_list(op_desc, env, module_desc);
        }

    }

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_engage_module_to_svc");

    return status;
}

/**
 * In this function all the handlers in each flow of the module description are added to the phases 
 * of the operation(user define phases) and phases of the conf(system defined phases). First handlers 
 * for system defined phases are added. Then handlers for operation specific phases are added.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_engage_module_to_op(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_op_t * axis_op,
    axis2_module_desc_t * module_desc)
{
    int type = 0;
    axis2_phase_holder_t *phase_holder = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_engage_module_to_op");
    AXIS2_PARAM_CHECK(env->error, axis_op, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, module_desc, AXIS2_FAILURE);

    for(type = 1; type < 5; type++)
    {
        axis2_flow_t *flow = NULL;
        axis2_char_t *flowname = NULL;
        axutil_array_list_t *phases = NULL;

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                phases = axis2_op_get_in_flow(axis_op, env);
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                phases = axis2_op_get_out_flow(axis_op, env);
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                phases = axis2_op_get_fault_in_flow(axis_op, env);
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                phases = axis2_op_get_fault_out_flow(axis_op, env);
                break;
            }
        }

        if(phases)
        {
            phase_holder = axis2_phase_holder_create_with_phases(env, phases);
        }

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                flow = axis2_module_desc_get_in_flow(module_desc, env);
                flowname = "in flow";
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                flow = axis2_module_desc_get_out_flow(module_desc, env);
                flowname = "out flow";
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                flow = axis2_module_desc_get_fault_in_flow(module_desc, env);
                flowname = "fault in flow";
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                flow = axis2_module_desc_get_fault_out_flow(module_desc, env);
                flowname = "fault out flow";
                break;
            }
        }

        if(flow && phase_holder)
        {
            int j = 0;
            int handler_count = 0;

            handler_count = axis2_flow_get_handler_count(flow, env);
            for(j = 0; j < handler_count; j++)
            {
                /* For all handlers in the flow from the module description */
                axis2_handler_desc_t *metadata = NULL;
                const axis2_char_t *phase_name = NULL;
                axis2_phase_rule_t *phase_rule = NULL;
                const axutil_string_t *handlersname = NULL;
                const axis2_char_t *handlername = NULL;
                axis2_status_t status = AXIS2_FAILURE;

                metadata = axis2_flow_get_handler(flow, env, j);
                handlersname = axis2_handler_desc_get_name(metadata, env);
                handlername = axutil_string_get_buffer(handlersname, env);
                phase_rule = axis2_handler_desc_get_rules(metadata, env);
                phase_name = axis2_phase_rule_get_name(phase_rule, env);

                /* For user/operation specific phases */
                if((axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) && (axutil_strcmp(
                    AXIS2_PHASE_DISPATCH, phase_name)) && (axutil_strcmp(AXIS2_PHASE_POST_DISPATCH,
                    phase_name)) && (axutil_strcmp(AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                {
                    status = axis2_phase_holder_add_handler(phase_holder, env, metadata);
                    if(AXIS2_SUCCESS != status)
                    {
                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                            "Handler %s inclusion failed for %s phase within flow %s. Phase might"
                                "not available in axis2.xml", handlername, phase_name, phase_name,
                            flowname);

                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "");
                        axis2_phase_holder_free(phase_holder, env);
                        return status;
                    }

                }

                /* For System defined phases */
                if((!axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_POST_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                {
                    axutil_array_list_t *phase_list = NULL;
                    axis2_phase_holder_t *phase_holder = NULL;

                    phase_list = axis2_conf_get_in_phases_upto_and_including_post_dispatch(
                        phase_resolver->axis2_config, env);

                    if(phase_holder)
                    {
                        axis2_phase_holder_free(phase_holder, env);
                        phase_holder = NULL;
                    }

                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);

                    status = axis2_phase_holder_add_handler(phase_holder, env, metadata);
                    axis2_phase_holder_free(phase_holder, env);
                    phase_holder = NULL;

                    if(AXIS2_SUCCESS != status)
                    {
                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                            "Adding handler %s to phase %s within flow %s failed", handlername,
                            phase_name, flowname);

                        return status;
                    }
                }
            }
        }

        if(phase_holder)
        {
            axis2_phase_holder_free(phase_holder, env);
            phase_holder = NULL;
        }
    }

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_engage_module_to_op");

    return AXIS2_SUCCESS;
}

/**
 * The caller function first set the service into the phase resolver. Then call this function to 
 * build execution chains for that services operations. Within this function it just call
 * axis2_phase_resolver_build_execution_chains_for_op() function to build exection chains for
 * each operation.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_build_execution_chains_for_svc(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env)
{
    axutil_hash_index_t *index_i = 0;
    axis2_status_t status = AXIS2_FAILURE;
    axis2_op_t *op = NULL;
    axutil_hash_t *ops = NULL;

    if(!(phase_resolver->svc))
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No service set to phase resolver");
        return AXIS2_FAILURE;
    }

    ops = axis2_svc_get_all_ops(phase_resolver->svc, env);

    for(index_i = axutil_hash_first(ops, env); index_i; index_i = axutil_hash_next(env, index_i))
    {
        void *v = NULL;
        int j = 0;

        axutil_hash_this(index_i, NULL, NULL, &v);
        op = (axis2_op_t *)v;
        for(j = 1; j < 5; j++)
        {
            status = axis2_phase_resolver_build_execution_chains_for_op(phase_resolver, env, j, op);
        }
    }

    return status;
}

/**
 * For operation passed as parameter, build execution chains. To do this get all engaged modules
 * from the axis2 configuration and for each module get the all handlers to be add to the operation 
 * specific phases. Then for each operation specific phases add those handlers. It should be noted 
 * that by the time this function is called the module handlers are already added to system specific 
 * phases. This function is called from axis2_phase_resolver_build_execution_chains_for_svc() 
 * function and axis2_phase_resolver_build_execution_chains_for_module_op() function.
 */
static axis2_status_t
axis2_phase_resolver_build_execution_chains_for_op(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    int type,
    axis2_op_t *op)
{
    axutil_array_list_t *handler_list = NULL;
    axutil_array_list_t *moduleqnames = NULL;
    int i = 0;
    int size = 0;
    int status = AXIS2_FAILURE;
    axis2_char_t *flowname = NULL;
    axis2_phase_holder_t *phase_holder = NULL;
    axutil_array_list_t *engaged_module_list_for_parent_svc = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Entry:axis2_phase_resolver_build_execution_chains_for_op");

    handler_list = axutil_array_list_create(env, 0);
    if(!handler_list)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory");
        return AXIS2_FAILURE;
    }

    /* Engage handlers from axis2.xml and from modules */
    moduleqnames = axis2_conf_get_all_engaged_modules(phase_resolver->axis2_config, env);

    size = axutil_array_list_size(moduleqnames, env);

    for(i = 0; i < size; i++)
    {
        axis2_char_t *modulename = NULL;
        axutil_qname_t *moduleqname = NULL;
        axis2_module_desc_t *module_desc = NULL;

        moduleqname = (axutil_qname_t *)axutil_array_list_get(moduleqnames, env, i);
        modulename = axutil_qname_get_localpart(moduleqname, env);

        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Module name is:%s", modulename);

        module_desc = axis2_conf_get_module(phase_resolver->axis2_config, env, moduleqname);
        if(!module_desc)
        {
            AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_MODULE_REF, AXIS2_FAILURE);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Module description not found in axis2 configuration for name %s", modulename);

            if(handler_list)
            {
                axutil_array_list_free(handler_list, env);
            }

            return AXIS2_FAILURE;
        }

        status = axis2_phase_resolver_add_to_handler_list(env, handler_list, op, module_desc, type);
        if(AXIS2_SUCCESS != status)
        {
            if(handler_list)
            {
                axutil_array_list_free(handler_list, env);
            }
            return AXIS2_FAILURE;
        }

        axis2_op_add_to_engaged_module_list(op, env, module_desc);
    }

    engaged_module_list_for_parent_svc
        = axis2_svc_get_engaged_module_list(phase_resolver->svc, env);
    size = axutil_array_list_size(engaged_module_list_for_parent_svc, env);

    for(i = 0; i < size; i++)
    {
        axis2_char_t *modulename = NULL;
        axutil_qname_t *moduleqname = NULL;
        axis2_module_desc_t *module_desc = NULL;

        module_desc = axutil_array_list_get(engaged_module_list_for_parent_svc, env, i);
        if(!module_desc)
        {
            AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_MODULE_REF, AXIS2_FAILURE);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Module description not found in engaged module list for service %s",
                axis2_svc_get_name(phase_resolver->svc, env));

            if(handler_list)
            {
                axutil_array_list_free(handler_list, env);
            }

            return AXIS2_FAILURE;
        }

        moduleqname = (axutil_qname_t *)axis2_module_desc_get_qname(module_desc, env);
        modulename = axutil_qname_get_localpart(moduleqname, env);
        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Module name is:%s", modulename);

        status = axis2_phase_resolver_add_to_handler_list(env, handler_list, op, module_desc, type);
        if(AXIS2_SUCCESS != status)
        {
            if(handler_list)
            {
                axutil_array_list_free(handler_list, env);
            }
            return AXIS2_FAILURE;
        }

        axis2_op_add_to_engaged_module_list(op, env, module_desc);
    }

    if(0 == axutil_array_list_size(handler_list, env))
    {
        /* No flows configured */
        if(handler_list)
        {
            axutil_array_list_free(handler_list, env);
        }

        return AXIS2_SUCCESS;
    }

    switch(type)
    {
        case AXIS2_IN_FLOW:
        {
            axutil_array_list_t *phase_list = NULL;

            phase_list = axis2_op_get_in_flow(op, env);
            phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
            flowname = "in flow";
            break;
        }
        case AXIS2_OUT_FLOW:
        {
            axutil_array_list_t *phase_list = NULL;

            phase_list = axis2_op_get_out_flow(op, env);
            phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
            flowname = "out flow";
            break;
        }
        case AXIS2_FAULT_IN_FLOW:
        {
            axutil_array_list_t *phase_list = NULL;

            phase_list = axis2_op_get_fault_in_flow(op, env);
            phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
            flowname = "fault in flow";
            break;
        }
        case AXIS2_FAULT_OUT_FLOW:
        {
            axutil_array_list_t *phase_list = NULL;

            phase_list = axis2_op_get_fault_out_flow(op, env);
            phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
            flowname = "fault out flow";
            break;
        }
    }

    size = axutil_array_list_size(handler_list, env);
    for(i = 0; i < size; i++)
    {
        axis2_handler_desc_t *metadata = NULL;

        metadata = (axis2_handler_desc_t *)axutil_array_list_get(handler_list, env, i);
        if(phase_holder)
        {
            status = axis2_phase_holder_add_handler(phase_holder, env, metadata);
            if(!status)
            {
                break;
            }
        }
    }

    /* Free the locally created handler_list*/
    if(handler_list)
    {
        axutil_array_list_free(handler_list, env);
    }

    if(phase_holder)
    {
        axis2_phase_holder_free(phase_holder, env);
    }

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Exit:axis2_phase_resolver_build_execution_chains_for_op");

    return status;
}

/**
 * For module operation build execution chains. This is called by axis2_svc_add_module_ops() function.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_build_execution_chains_for_module_op(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_op_t * op)
{
    int i = 0;
    axis2_status_t status = AXIS2_FAILURE;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Entry:axis2_phase_resolver_build_execution_chains_for_module_op");
    AXIS2_PARAM_CHECK(env->error, op, AXIS2_FAILURE);

    for(i = 1; i < 5; i++)
    {
        status = axis2_phase_resolver_build_execution_chains_for_op(phase_resolver, env, i, op);
        if(!status)
        {
            break;
        }
    }

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Exit:axis2_phase_resolver_build_execution_chains_for_module_op");
    return status;
}

/**
 * Take the phases for each flow from the axis2 configuration, take all the handlers of each flow 
 * from the module description and then each handler is added into the corresponding global phase. 
 * This function is called from  function axis2_phase_resolver_engage_module_globally() to add 
 * module handlers into global phases.
 */
static axis2_status_t
axis2_phase_resolver_add_module_handlers_to_system_defined_phases(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_module_desc_t * module_desc)
{
    int type = 0;
    axis2_status_t status = AXIS2_FAILURE;
    axis2_phase_holder_t *phase_holder = NULL;
    const axutil_qname_t *modqname = NULL;
    axis2_char_t *modname = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Entry:axis2_phase_resolver_add_module_handlers_to_system_defined_phases");

    modqname = axis2_module_desc_get_qname(module_desc, env);
    modname = axutil_qname_get_localpart(modqname, env);
    for(type = 1; type < 5; type++)
    {
        axis2_flow_t *flow = NULL;
        axis2_char_t *flow_name = NULL;

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                axutil_array_list_t *phase_list = NULL;

                phase_list = axis2_conf_get_in_phases_upto_and_including_post_dispatch(
                    phase_resolver->axis2_config, env);
                phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                if(!phase_holder)
                    continue;
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                axutil_array_list_t *phase_list = NULL;

                phase_list = axis2_conf_get_out_flow(phase_resolver->axis2_config, env);
                phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                if(!phase_holder)
                    continue;
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                axutil_array_list_t *phase_list = NULL;

                phase_list = axis2_conf_get_in_fault_flow(phase_resolver-> axis2_config, env);
                phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                if(!phase_holder)
                    continue;
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                axutil_array_list_t *phase_list = NULL;

                phase_list = axis2_conf_get_out_fault_flow(phase_resolver-> axis2_config, env);
                phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                if(!phase_holder)
                    continue;
                break;
            }
        }

        /* Modules referred by axis2.xml */
        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                flow = axis2_module_desc_get_in_flow(module_desc, env);
                flow_name = "in flow";
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                flow = axis2_module_desc_get_out_flow(module_desc, env);
                flow_name = "out flow";
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                flow = axis2_module_desc_get_fault_in_flow(module_desc, env);
                flow_name = "fault in flow";
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                flow = axis2_module_desc_get_fault_out_flow(module_desc, env);
                flow_name = "fault out flow";
                break;
            }
        }
        if(flow)
        {
            int j = 0;
            for(j = 0; j < axis2_flow_get_handler_count(flow, env); j++)
            {
                axis2_handler_desc_t *metadata = NULL;
                const axis2_char_t *phase_name = NULL;
                axis2_phase_rule_t *phase_rule = NULL;
                const axutil_string_t *handlersname = NULL;
                const axis2_char_t *handlername = NULL;

                metadata = axis2_flow_get_handler(flow, env, j);
                handlersname = axis2_handler_desc_get_name(metadata, env);
                handlername = axutil_string_get_buffer(handlersname, env);
                phase_rule = axis2_handler_desc_get_rules(metadata, env);
                if(phase_rule)
                {
                    phase_name = axis2_phase_rule_get_name(phase_rule, env);
                }
                if(!phase_name)
                {
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Phase rules for handler %s has no name", handlername);
                    return AXIS2_FAILURE;
                }
                if((!axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_POST_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                {
                    /* If a global phase add the module handler*/
                    status = axis2_phase_holder_add_handler(phase_holder, env, metadata);
                    if(!status)
                    {
                        axis2_phase_holder_free(phase_holder, env);
                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                            "Adding handler %s of module %s to phase %s of "
                                "flow %s failed", handlername, modname, phase_name, flow_name);
                        return status;
                    }
                }
            }
        }
        if(phase_holder)
        {
            axis2_phase_holder_free(phase_holder, env);
        }
    }
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Exit:axis2_phase_resolver_add_module_handlers_to_system_defined_phases");
    return AXIS2_SUCCESS;
}

/**
 * For each operation of the service first check whether module is already engaged to operation. 
 * If not take each operations flows and add the module handlers into them appropriately. This 
 * function is called from function axis2_phase_resolver_engage_module_globally() to add handlers 
 * from module into each services all operations.
 */
static axis2_status_t
axis2_phase_resolver_add_module_handlers_to_user_defined_phases(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_svc_t * svc,
    axis2_module_desc_t * module_desc)
{
    axutil_hash_t *ops = NULL;
    axis2_bool_t engaged = AXIS2_FALSE;
    axutil_hash_index_t *index_i = NULL;
    int type = 0;
    axis2_status_t status = AXIS2_FAILURE;
    axis2_phase_holder_t *phase_holder = NULL;
    const axis2_char_t *svc_name = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Entry:axis2_phase_resolver_add_module_handlers_to_user_defined_phases");

    AXIS2_PARAM_CHECK(env->error, svc, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, module_desc, AXIS2_FAILURE);
    svc_name = axis2_svc_get_name(svc, env);
    ops = axis2_svc_get_all_ops(svc, env);
    if(!ops)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No operations for the service %s", svc_name);
        return AXIS2_FAILURE;
    }

    for(index_i = axutil_hash_first(ops, env); index_i; index_i = axutil_hash_next(env, index_i))
    {
        void *v = NULL;
        axis2_op_t *op_desc = NULL;
        int j = 0;
        axutil_array_list_t *modules = NULL;
        axis2_flow_t *flow = NULL;
        axis2_char_t *flowname = NULL;
        const axutil_qname_t *module_desc_qname = NULL;
        axis2_char_t *module_desc_name = NULL;
        int size = 0;
        axis2_char_t *op_name = NULL;

        axutil_hash_this(index_i, NULL, NULL, &v);
        op_desc = (axis2_op_t *)v;
        op_name = axutil_qname_get_localpart(axis2_op_get_qname(op_desc, env), env);
        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Operation name is : %s", op_name);

        /* Get all modules engaged to the operation */
        modules = axis2_op_get_all_modules(op_desc, env);
        module_desc_qname = axis2_module_desc_get_qname(module_desc, env);
        module_desc_name = axutil_qname_get_localpart(module_desc_qname, env);
        if(modules)
        {
            size = axutil_array_list_size(modules, env);
        }

        /* Checking whether module is already engaged to operation */
        for(j = 0; j < size; j++)
        {
            axis2_module_desc_t *module_desc_l = NULL;
            const axutil_qname_t *module_desc_qname_l = NULL;

            module_desc_l = (axis2_module_desc_t *)axutil_array_list_get(modules, env, j);

            module_desc_qname_l = axis2_module_desc_get_qname(module_desc_l, env);
            if(axutil_qname_equals(module_desc_qname_l, env, module_desc_qname))
            {
                engaged = AXIS2_TRUE;
                break;
            }
        }

        if(engaged)
        {
            continue;
        }

        for(type = 1; type < 5; type++)
        {
            switch(type)
            {
                case AXIS2_IN_FLOW:
                {
                    axutil_array_list_t *phase_list = NULL;

                    phase_list = axis2_op_get_in_flow(op_desc, env);
                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                    break;
                }
                case AXIS2_OUT_FLOW:
                {
                    axutil_array_list_t *phase_list = NULL;

                    phase_list = axis2_op_get_out_flow(op_desc, env);
                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                    break;
                }
                case AXIS2_FAULT_IN_FLOW:
                {
                    axutil_array_list_t *phase_list = NULL;

                    phase_list = axis2_op_get_fault_in_flow(op_desc, env);
                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                    break;
                }
                case AXIS2_FAULT_OUT_FLOW:
                {
                    axutil_array_list_t *phase_list = NULL;

                    phase_list = axis2_op_get_fault_out_flow(op_desc, env);
                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);
                    break;
                }
            }

            /* Process modules referred by axis2.xml */

            switch(type)
            {
                case AXIS2_IN_FLOW:
                {
                    flow = axis2_module_desc_get_in_flow(module_desc, env);
                    flowname = "in flow";
                    break;
                }
                case AXIS2_OUT_FLOW:
                {
                    flow = axis2_module_desc_get_out_flow(module_desc, env);
                    flowname = "out flow";
                    break;
                }
                case AXIS2_FAULT_IN_FLOW:
                {
                    flow = axis2_module_desc_get_fault_in_flow(module_desc, env);
                    flowname = "fault in flow";
                    break;
                }
                case AXIS2_FAULT_OUT_FLOW:
                {
                    flow = axis2_module_desc_get_fault_out_flow(module_desc, env);
                    flowname = "fault out flow";
                    break;
                }
            }

            if(flow)
            {
                int handler_count = 0;

                handler_count = axis2_flow_get_handler_count(flow, env);
                for(j = 0; j < handler_count; j++)
                {
                    axis2_handler_desc_t *metadata = NULL;
                    const axis2_char_t *phase_name = NULL;
                    axis2_phase_rule_t *phase_rule = NULL;
                    const axutil_string_t *handlersname = NULL;
                    const axis2_char_t *handlername = NULL;

                    metadata = axis2_flow_get_handler(flow, env, j);
                    handlersname = axis2_handler_desc_get_name(metadata, env);
                    handlername = axutil_string_get_buffer(handlersname, env);
                    phase_rule = axis2_handler_desc_get_rules(metadata, env);
                    if(phase_rule)
                    {
                        phase_name = axis2_phase_rule_get_name(phase_rule, env);
                    }
                    if(!phase_name)
                    {
                        AXIS2_LOG_ERROR(
                            env->log,
                            AXIS2_LOG_SI,
                            "Handler rules for the handler description %s within flow %s has no name",
                            handlername, flowname);

                        return AXIS2_FAILURE;
                    }

                    /* If phase is not a system defined phase, add module handler to it */
                    if((axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) && (axutil_strcmp(
                        AXIS2_PHASE_DISPATCH, phase_name)) && (axutil_strcmp(
                        AXIS2_PHASE_POST_DISPATCH, phase_name)) && (axutil_strcmp(
                        AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                    {
                        if(phase_holder)
                        {
                            status = axis2_phase_holder_add_handler(phase_holder, env, metadata);
                            if(!status)
                            {
                                axis2_phase_holder_free(phase_holder, env);
                                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Adding handler desc %s to"
                                    "phase %s within flow %s failed", handlername, phase_name,
                                    flowname);

                                return status;
                            }
                        }
                    }
                }
            }

            if(phase_holder)
            {
                axis2_phase_holder_free(phase_holder, env);
            }
        }
        status = axis2_op_add_to_engaged_module_list(op_desc, env, module_desc);
        if(AXIS2_SUCCESS != status)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Adding module description %s to engaged "
                "module list of operation %s failed", module_desc_name, op_name);

            return status;
        }
    }

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI,
        "Exit:axis2_phase_resolver_add_module_handlers_to_user_defined_phases");

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_disengage_module_from_svc(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_svc_t * svc,
    axis2_module_desc_t * module_desc)
{
    axutil_hash_t *ops = NULL;
    axutil_hash_index_t *index_i = NULL;
    axis2_status_t status = AXIS2_FAILURE;
    const axutil_qname_t *module_d_qname = NULL;
    const axis2_char_t *svc_name = axis2_svc_get_name(svc, env);
    axis2_char_t *modname_d = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_disengage_module_from_svc");

    ops = axis2_svc_get_all_ops(svc, env);
    if(!ops)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Service %s has no operation", svc_name);
        return AXIS2_FAILURE;
    }

    module_d_qname = axis2_module_desc_get_qname(module_desc, env);
    modname_d = axutil_qname_get_localpart(module_d_qname, env);
    for(index_i = axutil_hash_first(ops, env); index_i; index_i = axutil_hash_next(env, index_i))
    {
        axutil_array_list_t *modules = NULL;
        axis2_op_t *op_desc = NULL;
        int size = 0;
        int j = 0;
        void *v = NULL;
        axis2_bool_t engaged = AXIS2_FALSE;
        const axutil_qname_t *opqname = NULL;
        axis2_char_t *opname = NULL;

        axutil_hash_this(index_i, NULL, NULL, &v);
        op_desc = (axis2_op_t *)v;
        opqname = axis2_op_get_qname(op_desc, env);
        opname = axutil_qname_get_localpart(opqname, env);
        modules = axis2_op_get_all_modules(op_desc, env);
        if(modules)
        {
            size = axutil_array_list_size(modules, env);
        }
        for(j = 0; j < size; j++)
        {
            axis2_module_desc_t *module_desc_l = NULL;
            const axutil_qname_t *module_d_qname_l = NULL;

            module_desc_l = axutil_array_list_get(modules, env, j);
            module_d_qname_l = axis2_module_desc_get_qname(module_desc_l, env);
            if(axutil_qname_equals(module_d_qname, env, module_d_qname_l))
            {
                engaged = AXIS2_TRUE;
                AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Module %s already engaged.", modname_d);
                break;
            }
        }

        if(engaged)
        {
            status = axis2_phase_resolver_disengage_module_from_op(phase_resolver, env, op_desc,
                module_desc);
            if(AXIS2_SUCCESS != status)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                    "Disengaging module %s from operation %s failed", modname_d, opname);
                return status;
            }

            status = axis2_op_remove_from_engaged_module_list(op_desc, env, module_desc);
        }

    }
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_disengage_module_from_svc");
    return status;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_disengage_module_from_op(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_op_t * axis_op,
    axis2_module_desc_t * module_desc)
{
    int type = 0;
    axis2_phase_holder_t *phase_holder = NULL;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_disengage_module_from_op");
    AXIS2_PARAM_CHECK(env->error, axis_op, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, module_desc, AXIS2_FAILURE);

    for(type = 1; type < 5; type++)
    {
        axis2_flow_t *flow = NULL;
        axis2_char_t *flowname = NULL;
        axutil_array_list_t *phases = NULL;

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                phases = axis2_op_get_in_flow(axis_op, env);
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                phases = axis2_op_get_out_flow(axis_op, env);
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                phases = axis2_op_get_fault_in_flow(axis_op, env);
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                phases = axis2_op_get_fault_out_flow(axis_op, env);
                break;
            }
        }

        if(phases)
        {
            phase_holder = axis2_phase_holder_create_with_phases(env, phases);
        }

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                flow = axis2_module_desc_get_in_flow(module_desc, env);
                flowname = "in flow";
                break;
            }
            case AXIS2_OUT_FLOW:
            {
                flow = axis2_module_desc_get_out_flow(module_desc, env);
                flowname = "out flow";
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                flow = axis2_module_desc_get_fault_in_flow(module_desc, env);
                flowname = "fault in flow";
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                flow = axis2_module_desc_get_fault_out_flow(module_desc, env);
                flowname = "fault out flow";
                break;
            }
        }

        if(flow && phase_holder)
        {
            int j = 0;
            int handler_count = 0;

            handler_count = axis2_flow_get_handler_count(flow, env);
            for(j = 0; j < handler_count; j++)
            {
                axis2_handler_desc_t *metadata = NULL;
                const axis2_char_t *phase_name = NULL;
                axis2_phase_rule_t *phase_rule = NULL;
                const axutil_string_t *handlersname = NULL;
                const axis2_char_t *handlername = NULL;
                axis2_status_t status = AXIS2_FAILURE;

                metadata = axis2_flow_get_handler(flow, env, j);
                handlersname = axis2_handler_desc_get_name(metadata, env);
                handlername = axutil_string_get_buffer(handlersname, env);
                phase_rule = axis2_handler_desc_get_rules(metadata, env);
                phase_name = axis2_phase_rule_get_name(phase_rule, env);
                if((axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) && (axutil_strcmp(
                    AXIS2_PHASE_DISPATCH, phase_name)) && (axutil_strcmp(AXIS2_PHASE_POST_DISPATCH,
                    phase_name)) && (axutil_strcmp(AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                {
                    status = axis2_phase_holder_remove_handler(phase_holder, env, metadata);
                    if(AXIS2_SUCCESS != status)
                    {
                        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI,
                            "Handler %s Removal failed for %s phase within flow %s", handlername,
                            phase_name, flowname);
                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "");
                        axis2_phase_holder_free(phase_holder, env);
                        return status;
                    }

                }
                if((!axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_POST_DISPATCH, phase_name)) || (!axutil_strcmp(
                    AXIS2_PHASE_PRE_DISPATCH, phase_name)))
                {
                    axutil_array_list_t *phase_list = NULL;
                    axis2_phase_holder_t *phase_holder = NULL;

                    phase_list = axis2_conf_get_in_phases_upto_and_including_post_dispatch(
                        phase_resolver->axis2_config, env);
                    if(phase_holder)
                    {
                        axis2_phase_holder_free(phase_holder, env);
                        phase_holder = NULL;
                    }
                    phase_holder = axis2_phase_holder_create_with_phases(env, phase_list);

                    status = axis2_phase_holder_remove_handler(phase_holder, env, metadata);
                    axis2_phase_holder_free(phase_holder, env);
                    phase_holder = NULL;
                    if(AXIS2_SUCCESS != status)
                    {
                        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                            "Removing handler %s from phase %s within flow %s failed", handlername,
                            phase_name, flowname);
                        return status;
                    }
                }
            }
        }

        if(phase_holder)
        {
            axis2_phase_holder_free(phase_holder, env);
            phase_holder = NULL;
        }
    }
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_disengage_module_from_op");
    return AXIS2_SUCCESS;
}

/* This function is deprecated and no longer used */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_phase_resolver_build_transport_chains(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env)
{
    axis2_transport_in_desc_t **transports_in = NULL;
    axis2_transport_out_desc_t **transports_out = NULL;
    int index_i = 0;
    axis2_status_t status = AXIS2_FAILURE;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_build_transport_chains");

    transports_in = axis2_conf_get_all_in_transports(phase_resolver->axis2_config, env);
    if(!transports_in)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_TRANSPORT_IN_CONFIGURED, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No transport in descriptions configured");
        return AXIS2_SUCCESS;
    }

    transports_out = axis2_conf_get_all_out_transports(phase_resolver->axis2_config, env);
    if(!transports_out)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_TRANSPORT_OUT_CONFIGURED, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No transport out descriptions configured");
        return AXIS2_SUCCESS;
    }

    for(index_i = 0; index_i < AXIS2_TRANSPORT_ENUM_MAX; index_i++)
    {
        if(transports_in[index_i])
        {
            status = axis2_phase_resolver_build_in_transport_chains(phase_resolver, env,
                transports_in[index_i]);
            if(AXIS2_SUCCESS != status)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Building transport in chains failed");
                return status;
            }
        }
    }

    for(index_i = 0; index_i < AXIS2_TRANSPORT_ENUM_MAX; index_i++)
    {
        if(transports_out[index_i])
        {
            status = axis2_phase_resolver_build_out_transport_chains(phase_resolver, env,
                transports_out[index_i]);
            if(AXIS2_SUCCESS != status)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Building transport out chains failed");
                return status;
            }
        }
    }

    /* If transport in or transport out maps are not null but still they don't
     * have chains configured then we return success, because there are no
     * chains to process.
     */
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_build_transport_chains");
    return AXIS2_SUCCESS;
}

/**
 * This function is called from function 
 * axis2_phase_resolver_build_transport_chains().
 * This function is deprecated and no longer used.
 */
static axis2_status_t
axis2_phase_resolver_build_in_transport_chains(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_transport_in_desc_t * transport)
{
    int type = 0;
    int j = 0;
    axis2_status_t status = AXIS2_FAILURE;
    axutil_array_list_t *handlers = NULL;
    AXIS2_TRANSPORT_ENUMS transport_enum = axis2_transport_in_desc_get_enum(transport, env);
    ;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_build_in_transport_chains");
    AXIS2_PARAM_CHECK(env->error, transport, AXIS2_FAILURE);

    for(type = 1; type < 4; type++)
    {
        axis2_flow_t *flow = NULL;
        axis2_char_t *flowname = NULL;
        axis2_phase_t *phase = NULL;

        switch(type)
        {
            case AXIS2_IN_FLOW:
            {
                flow = axis2_transport_in_desc_get_in_flow(transport, env);
                phase = axis2_transport_in_desc_get_in_phase(transport, env);
                flowname = "in flow";
                break;
            }
            case AXIS2_FAULT_IN_FLOW:
            {
                flow = axis2_transport_in_desc_get_fault_in_flow(transport, env);
                phase = axis2_transport_in_desc_get_fault_phase(transport, env);
                flowname = "fault in flow";
                break;
            }
        }
        if(flow)
        {
            axis2_phase_holder_t *phase_holder = NULL;
            int size = 0;

            size = axis2_flow_get_handler_count(flow, env);
            handlers = axutil_array_list_create(env, 0);
            for(j = 0; j < size; j++)
            {
                axis2_handler_desc_t *metadata = NULL;
                axis2_phase_rule_t *rule = NULL;
                const axis2_char_t *handlername = NULL;
                const axutil_string_t *handlersname = NULL;

                metadata = axis2_flow_get_handler(flow, env, j);
                handlersname = axis2_handler_desc_get_name(metadata, env);
                handlername = axutil_string_get_buffer(handlersname, env);
                rule = axis2_handler_desc_get_rules(metadata, env);
                if(rule)
                {
                    status = axis2_phase_rule_set_name(rule, env, AXIS2_TRANSPORT_PHASE);
                }
                if(AXIS2_SUCCESS != status)
                {
                    if(handlers)
                    {
                        axis2_handler_desc_t *handler_d = NULL;
                        int i = 0;
                        int size = 0;

                        size = axutil_array_list_size(handlers, env);
                        for(i = 0; i < size; i++)
                        {
                            handler_d = axutil_array_list_get(handlers, env, i);
                            axis2_handler_desc_free(handler_d, env);
                        }
                        axutil_array_list_free(handlers, env);
                    }
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Setting name %s to phase rules for handler %s failed"
                            "for in transport %d within flow %s", AXIS2_TRANSPORT_PHASE,
                        handlername, transport_enum, flowname);
                    return status;
                }
                status = axutil_array_list_add(handlers, env, metadata);
                if(AXIS2_SUCCESS != status)
                {
                    if(handlers)
                    {
                        axis2_handler_desc_t *handler_d = NULL;
                        int i = 0;
                        int size = 0;

                        size = axutil_array_list_size(handlers, env);
                        for(i = 0; i < size; i++)
                        {
                            handler_d = axutil_array_list_get(handlers, env, i);
                            axis2_handler_desc_free(handler_d, env);
                        }
                        axutil_array_list_free(handlers, env);
                    }
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Adding handler %s from in transport %d to handler "
                            "list failed within flow %s", handlername, transport_enum, flowname);
                    return status;
                }
            }
            phase_holder = axis2_phase_holder_create(env);
            if(!phase_holder)
            {
                if(handlers)
                {
                    axis2_handler_desc_t *handler_d = NULL;
                    int i = 0;
                    int size = 0;

                    size = axutil_array_list_size(handlers, env);
                    for(i = 0; i < size; i++)
                    {
                        handler_d = axutil_array_list_get(handlers, env, i);
                        axis2_handler_desc_free(handler_d, env);
                    }
                    axutil_array_list_free(handlers, env);
                }
                AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory");
                return AXIS2_FAILURE;
            }

            status = axis2_phase_holder_build_transport_handler_chain(phase_holder, env, phase,
                handlers);
            if(phase_holder)
            {
                axis2_phase_holder_free(phase_holder, env);
            }
        }
        else
        {
            /* Do nothing */
        }
    }
    if(handlers)
    {
        axutil_array_list_free(handlers, env);
    }
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_build_in_transport_chains");
    return status;
}

/**
 * This function is called from function
 * axis2_phase_resolver_build_transport_chains().
 * This is deprecated and no longer used.
 */
static axis2_status_t
axis2_phase_resolver_build_out_transport_chains(
    axis2_phase_resolver_t * phase_resolver,
    const axutil_env_t * env,
    axis2_transport_out_desc_t * transport)
{
    int type = 0;
    axis2_status_t status = AXIS2_FAILURE;
    axutil_array_list_t *handlers = NULL;
    AXIS2_TRANSPORT_ENUMS transport_enum = axis2_transport_out_desc_get_enum(transport, env);
    ;

    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_phase_resolver_build_out_transport_chains");
    AXIS2_PARAM_CHECK(env->error, transport, AXIS2_FAILURE);
    for(type = 1; type < 5; type++)
    {
        axis2_flow_t *flow = NULL;
        axis2_char_t *flowname = NULL;
        axis2_phase_t *phase = NULL;

        switch(type)
        {
            case AXIS2_OUT_FLOW:
            {
                flow = axis2_transport_out_desc_get_out_flow(transport, env);
                phase = axis2_transport_out_desc_get_out_phase(transport, env);
                flowname = "out flow";
                break;
            }
            case AXIS2_FAULT_OUT_FLOW:
            {
                flow = axis2_transport_out_desc_get_fault_out_flow(transport, env);
                phase = axis2_transport_out_desc_get_fault_phase(transport, env);
                flowname = "fault out flow";
                break;
            }
        }

        if(flow)
        {
            axis2_phase_holder_t *phase_holder = NULL;
            int hndlr_count = 0;
            int j = 0;
            hndlr_count = axis2_flow_get_handler_count(flow, env);
            if(AXIS2_SUCCESS != AXIS2_ERROR_GET_STATUS_CODE(env->error))
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "");
                return AXIS2_ERROR_GET_STATUS_CODE(env->error);
            }
            handlers = axutil_array_list_create(env, 0);

            for(j = 0; j < hndlr_count; j++)
            {
                axis2_handler_desc_t *metadata = NULL;
                axis2_phase_rule_t *rule = NULL;
                const axis2_char_t *handlername = NULL;
                const axutil_string_t *handlersname = NULL;

                metadata = axis2_flow_get_handler(flow, env, j);
                handlersname = axis2_handler_desc_get_name(metadata, env);
                handlername = axutil_string_get_buffer(handlersname, env);

                rule = axis2_handler_desc_get_rules(metadata, env);
                if(rule)
                {
                    status = axis2_phase_rule_set_name(rule, env, AXIS2_TRANSPORT_PHASE);
                }
                if(AXIS2_SUCCESS != status)
                {
                    if(handlers)
                    {
                        axis2_handler_desc_t *handler_d = NULL;
                        int i = 0;
                        int size = 0;

                        size = axutil_array_list_size(handlers, env);
                        for(i = 0; i < size; i++)
                        {
                            handler_d = axutil_array_list_get(handlers, env, i);
                            axis2_handler_desc_free(handler_d, env);
                        }
                        axutil_array_list_free(handlers, env);
                    }
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Setting name %s to phase rules for handler %s failed"
                            "for out transport %d within flow %s", AXIS2_TRANSPORT_PHASE,
                        handlername, transport_enum, flowname);
                    return status;
                }

                status = axutil_array_list_add(handlers, env, metadata);
                if(AXIS2_FAILURE == status)
                {
                    if(handlers)
                    {
                        axis2_handler_desc_t *handler_d = NULL;
                        int i = 0;
                        int size = 0;

                        size = axutil_array_list_size(handlers, env);
                        for(i = 0; i < size; i++)
                        {
                            handler_d = axutil_array_list_get(handlers, env, i);
                            axis2_handler_desc_free(handler_d, env);
                        }
                        axutil_array_list_free(handlers, env);
                    }
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Adding handler %s from out transport %d to handler "
                            "list failed within flow %s", handlername, transport_enum, flowname);
                    return status;
                }
            }
            phase_holder = axis2_phase_holder_create(env);
            if(!phase_holder)
            {
                if(handlers)
                {
                    axis2_handler_desc_t *handler_d = NULL;
                    int i = 0;
                    int size = 0;

                    size = axutil_array_list_size(handlers, env);
                    for(i = 0; i < size; i++)
                    {
                        handler_d = axutil_array_list_get(handlers, env, i);
                        axis2_handler_desc_free(handler_d, env);
                    }
                    axutil_array_list_free(handlers, env);
                }
                AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory");
                return AXIS2_FAILURE;
            }

            status = axis2_phase_holder_build_transport_handler_chain(phase_holder, env, phase,
                handlers);
            if(phase_holder)
            {
                axis2_phase_holder_free(phase_holder, env);
            }
        }
        else
        {
            /* Do nothing */
        }
    }
    if(handlers)
    {
        axutil_array_list_free(handlers, env);
    }
    AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Exit:axis2_phase_resolver_build_out_transport_chains");
    return status;
}

/**
 * This function is called from axis2_phase_resolver_build_execution_chains_for_op() function.
 */
static axis2_status_t
axis2_phase_resolver_add_to_handler_list(
    const axutil_env_t * env,
    axutil_array_list_t *handler_list,
    axis2_op_t *op,
    axis2_module_desc_t * module_desc,
    int type)
{
    axis2_flow_t *flow = NULL;
    axis2_char_t *flowname = NULL;
    const axutil_qname_t *opqname = NULL;
    axis2_char_t *opname = NULL;
    axis2_status_t status = AXIS2_FAILURE;

    opqname = axis2_op_get_qname(op, env);
    opname = axutil_qname_get_localpart(opqname, env);

    switch(type)
    {
        case AXIS2_IN_FLOW:
        {
            flow = axis2_module_desc_get_in_flow(module_desc, env);
            flowname = "in flow";
            break;
        }
        case AXIS2_OUT_FLOW:
        {
            flow = axis2_module_desc_get_out_flow(module_desc, env);
            flowname = "out flow";
            break;
        }
        case AXIS2_FAULT_IN_FLOW:
        {
            flow = axis2_module_desc_get_fault_in_flow(module_desc, env);
            flowname = "fault in flow";
            break;
        }
        case AXIS2_FAULT_OUT_FLOW:
        {
            flow = axis2_module_desc_get_fault_out_flow(module_desc, env);
            flowname = "fault out flow";
            break;
        }
    }

    if(flow)
    {
        int j = 0;
        int count = 0;

        /* Ignore all the errors upto now */
        AXIS2_ERROR_SET_STATUS_CODE(env->error, AXIS2_SUCCESS);

        count = axis2_flow_get_handler_count(flow, env);
        /*if(AXIS2_SUCCESS != AXIS2_ERROR_GET_STATUS_CODE(env->error))
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Getting hanlder count for the flow %s failed",
                flowname);

            return AXIS2_ERROR_GET_STATUS_CODE(env->error);
        }*/

        for(j = 0; j < count; j++)
        {
            axis2_handler_desc_t *metadata = NULL;
            const axis2_char_t *phase_name = NULL;
            axis2_phase_rule_t *phase_rule = NULL;
            const axutil_string_t *handlername = NULL;
            const axis2_char_t *handlername_buff = NULL;

            metadata = axis2_flow_get_handler(flow, env, j);
            handlername = axis2_handler_desc_get_name(metadata, env);
            handlername_buff = axutil_string_get_buffer(handlername, env);
            phase_rule = axis2_handler_desc_get_rules(metadata, env);
            phase_name = axis2_phase_rule_get_name(phase_rule, env);
            if(!phase_name)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                    "Phase rules name null for the handler description %s within flow %s",
                    handlername_buff, flowname);

                return AXIS2_FAILURE;
            }

            /* If user defined phases */
            if((axutil_strcmp(AXIS2_PHASE_TRANSPORT_IN, phase_name)) && (axutil_strcmp(
                AXIS2_PHASE_DISPATCH, phase_name)) && (axutil_strcmp(AXIS2_PHASE_POST_DISPATCH,
                phase_name)) && (axutil_strcmp(AXIS2_PHASE_PRE_DISPATCH, phase_name)))
            {
                status = axutil_array_list_add(handler_list, env, metadata);
                if(AXIS2_SUCCESS != status)
                {
                    AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Adding handler description %s failed for phase %s within flow %s",
                        handlername_buff, phase_name, flowname);

                    return status;
                }
            }
            else
            {
                AXIS2_LOG_DEBUG(
                    env->log,
                    AXIS2_LOG_SI,
                    "Trying to add this handler %s to system pre defined phases , but those "
                        "handlers are already added to global chain which run irrespective of the service",
                    handlername_buff);
            }
        }
    }

    return AXIS2_SUCCESS;
}