static void NativeModuleHost_Destroy(MODULE_HANDLE moduleHandle)
{
    if (moduleHandle != NULL)
    {
        MODULE_HOST* module_host = (MODULE_HOST*)moduleHandle;
        if (module_host->module != NULL)
        {
            const MODULE_LOADER* module_loader = module_host->module_loader;
            MODULE_LIBRARY_HANDLE module_library = module_host->module_library_handle;

            if (module_loader != NULL)
            {
                MODULE_LOADER_API * loader_api = module_loader->api;
                if ((module_library != NULL) && (loader_api != NULL))
                {
                    const MODULE_API* module_apis = loader_api->GetApi(module_loader, module_library);
                    if (MODULE_DESTROY(module_apis) != NULL)
                    {
                        /*Codes_SRS_NATIVEMODULEHOST_17_028: [ NativeModuleHost_Destroy shall free all remaining allocated resources if moduleHandle is not NULL. ]*/
                        MODULE_DESTROY(module_apis)(module_host->module);
                    }

                    loader_api->Unload(module_loader, module_library);
                }
            }
            module_host->module = NULL;
            module_host->module_library_handle = NULL;
            module_host->module_host_broker = NULL;
            free(module_host);
        }
    }
    /*Codes_SRS_NATIVEMODULEHOST_17_027: [ NativeModuleHost_Destroy shall always destroy the module loader. ]*/
    ModuleLoader_Destroy();
}
static MODULE_HANDLE NativeModuleHost_Create(BROKER_HANDLE broker, const void* configuration)
{
    MODULE_HOST * result;
    if (broker == NULL || configuration == NULL)
    {
        /*Codes_SRS_NATIVEMODULEHOST_17_008: [ NativeModuleHost_Create shall return NULL if broker is NULL. ]*/
        /*Codes_SRS_NATIVEMODULEHOST_17_009: [ NativeModuleHost_Create shall return NULL if configuration does not contain valid JSON. ]*/
        LogError("broker [%p] or configuration [%p] is NULL, both are required", broker, configuration);
        result = NULL;
    }
    else
    {
        /*Codes_SRS_NATIVEMODULEHOST_17_010: [ NativeModuleHost_Create shall intialize the Module_Loader. ]*/
        if (ModuleLoader_Initialize() != MODULE_LOADER_SUCCESS)
        {
            /*Codes_SRS_NATIVEMODULEHOST_17_026: [ If any step above fails, then NativeModuleHost_Create shall free all resources allocated and return NULL. ]*/
            LogError("ModuleLoader_Initialize failed");
            result = NULL;
        }
        else
        {
            /*Codes_SRS_NATIVEMODULEHOST_17_011: [ NativeModuleHost_Create shall parse the configuration JSON. ]*/
            char * outprocess_module_args = (char *)configuration;
            JSON_Value *root_value = json_parse_string(outprocess_module_args);
            JSON_Object * module_host_args;
            if ((root_value == NULL) ||
                ((module_host_args = json_value_get_object(root_value)) == NULL))
            {
                if (root_value != NULL)
                {
                    json_value_free(root_value);
                }
                LogError("NativeModuleHost_Create could not parse arguments as JSON");
                result = NULL;
            }
            else
            {
                /*Codes_SRS_NATIVEMODULEHOST_17_035: [ If the "outprocess.loaders" array exists in the configuration JSON, NativeModuleHost_Create shall initialize the Module_Loader from this array. ]*/
                JSON_Value * loaders_array = json_object_get_value(module_host_args, OOP_MODULE_LOADERS_ARRAY_KEY);
                if ((loaders_array != NULL) &&
                    (ModuleLoader_InitializeFromJson(loaders_array) != MODULE_LOADER_SUCCESS))
                {
                    LogError("NativeModuleHost_Create could not extract loaders array from module arguments.");
                    result = NULL;
                }
                else
                {
                    /*Codes_SRS_NATIVEMODULEHOST_17_012: [ NativeModuleHost_Create shall get the "outprocess.loader" object from the configuration JSON. ]*/
                    JSON_Object * loader_args = json_object_get_object(module_host_args, OOP_MODULE_LOADER_KEY);
                    if (loader_args == NULL)
                    {
                        LogError("NativeModuleHost_Create could not get loader arguments.");
                        result = NULL;
                    }
                    else
                    {
                        GATEWAY_MODULE_LOADER_INFO loader_info;
                        if (parse_loader(loader_args, &loader_info) != 0)
                        {
                            /*Codes_SRS_NATIVEMODULEHOST_17_026: [ If any step above fails, then NativeModuleHost_Create shall free all resources allocated and return NULL. ]*/
                            LogError("NativeModuleHost_Create could not extract loader information from loader arguments.");
                            result = NULL;
                        }
                        else
                        {
                            // Have loader and entrypoint now, get module.
                            result = (MODULE_HOST*)malloc(sizeof(MODULE_HOST));
							if (result == NULL)
							{
								LogError("NativeModuleHost_Create could not allocate module.");
								result = NULL;
							}
							else
							{
								/*Codes_SRS_NATIVEMODULEHOST_17_018: [ NativeModuleHost_Create shall get the "module.args" object from the configuration JSON. ]*/
								JSON_Value * module_args = json_object_get_value(module_host_args, OOP_MODULE_ARGS_KEY);
								char * module_args_string = json_serialize_to_string(module_args);
								if (module_create(result, broker, &loader_info, module_args_string) != 0)
								{
									/*Codes_SRS_NATIVEMODULEHOST_17_026: [ If any step above fails, then NativeModuleHost_Create shall free all resources allocated and return NULL. ]*/
									LogError("NativeModuleHost_Create could not load module.");
									free(result);
									result = NULL;
								}
								/*Codes_SRS_NATIVEMODULEHOST_17_024: [ NativeModuleHost_Create shall free all resources used during module loading. ]*/
								json_free_serialized_string(module_args_string);
							}
                            loader_info.loader->api->FreeEntrypoint(loader_info.loader, loader_info.entrypoint);
                        }
                    }
                }
                /*Codes_SRS_NATIVEMODULEHOST_17_024: [ NativeModuleHost_Create shall free all resources used during module loading. ]*/
                json_value_free(root_value);
            }
            if (result == NULL)
            {
                // failed to create a module, give up entirely.
                /*Codes_SRS_NATIVEMODULEHOST_17_026: [ If any step above fails, then NativeModuleHost_Create shall free all resources allocated and return NULL. ]*/
                ModuleLoader_Destroy();
            }
        }
    }
    return result;
}
MODULE_LOADER_RESULT ModuleLoader_Initialize(void)
{
    MODULE_LOADER_RESULT result;

    /*Codes_SRS_MODULE_LOADER_13_001: [ ModuleLoader_Initialize shall initialize g_module_loader.lock. ]*/
    g_module_loaders.lock = Lock_Init();
    if (g_module_loaders.lock == NULL)
    {
        LogError("Lock_Init failed");

        /*Codes_SRS_MODULE_LOADER_13_002: [ ModuleLoader_Initialize shall return MODULE_LOADER_ERROR if an underlying platform call fails. ]*/
        result = MODULE_LOADER_ERROR;
    }
    else
    {
        /*Codes_SRS_MODULE_LOADER_13_003: [ ModuleLoader_Initialize shall acquire the lock on g_module_loader.lock. ]*/
        if (Lock(g_module_loaders.lock) != LOCK_OK)
        {
            LogError("Lock failed");
            Lock_Deinit(g_module_loaders.lock);
            g_module_loaders.lock = NULL;

            /*Codes_SRS_MODULE_LOADER_13_002: [ ModuleLoader_Initialize shall return MODULE_LOADER_ERROR if an underlying platform call fails. ]*/
            result = MODULE_LOADER_ERROR;
        }
        else
        {
            /*Codes_SRS_MODULE_LOADER_13_004: [ ModuleLoader_Initialize shall initialize g_module.module_loaders by calling VECTOR_create. ]*/
            g_module_loaders.module_loaders = VECTOR_create(sizeof(MODULE_LOADER*));
            if (g_module_loaders.module_loaders == NULL)
            {
                LogError("VECTOR_create failed");
                Unlock(g_module_loaders.lock);
                Lock_Deinit(g_module_loaders.lock);
                g_module_loaders.lock = NULL;

                /*Codes_SRS_MODULE_LOADER_13_002: [ ModuleLoader_Initialize shall return MODULE_LOADER_ERROR if an underlying platform call fails. ]*/
                result = MODULE_LOADER_ERROR;
            }
            else
            {
                // add all supported module loaders
                const MODULE_LOADER* supported_loaders[] =
                {
                    DynamicLoader_Get()
#ifdef NODE_BINDING_ENABLED
                    , NodeLoader_Get()
#endif
#ifdef JAVA_BINDING_ENABLED
                    , JavaLoader_Get()
#endif
#ifdef DOTNET_BINDING_ENABLED
                    , DotnetLoader_Get()
#endif
#ifdef DOTNET_CORE_BINDING_ENABLED
                    , DotnetCoreLoader_Get()
#endif
                };

                size_t loaders_count = sizeof(supported_loaders) / sizeof(supported_loaders[0]);
                size_t i;
                for (i = 0; i < loaders_count; i++)
                {
                    /*Codes_SRS_MODULE_LOADER_13_005: [ ModuleLoader_Initialize shall add the default support module loaders to g_module.module_loaders. ]*/
                    if (add_module_loader(supported_loaders[i]) != MODULE_LOADER_SUCCESS)
                    {
                        LogError("Could not add loader - %s", supported_loaders[i]->name);
                        break;
                    }
                }

                /*Codes_SRS_MODULE_LOADER_13_007: [ ModuleLoader_Initialize shall unlock g_module.lock. ]*/
                Unlock(g_module_loaders.lock);

                // adding loaders failed if we bailed early from the loop above
                if (i < loaders_count)
                {
                    ModuleLoader_Destroy();

                    /*Codes_SRS_MODULE_LOADER_13_002: [ ModuleLoader_Initialize shall return MODULE_LOADER_ERROR if an underlying platform call fails. ]*/
                    result = MODULE_LOADER_ERROR;
                }
                else
                {
                    /*Codes_SRS_MODULE_LOADER_13_006: [ ModuleLoader_Initialize shall return MODULE_LOADER_SUCCESS once all the default loaders have been added successfully. ]*/
                    result = MODULE_LOADER_SUCCESS;
                }
            }
        }
    }

    return result;
}