/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
 *  bonobo-activation-server: CORBA activation dameon.
 *
 *  Copyright (C) 1999, 2000 Red Hat, Inc.
 *  Copyright (C) 1999, 2000 Eazel, Inc.
 *  Copyright (C) 1999, 2003 Ximian, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this library; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Authors: Elliot Lee <sopwith@redhat.com>,
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <time.h>
#include <string.h>

#include <glib/gi18n.h>

#include "server.h"

#include "activation-context.h"
#include "bonobo-activation-id.h"
#include "activation-context-query.h"
#include "activation-server-corba-extensions.h"
#include <libbonobo.h>

#undef LOCALE_DEBUG

#define Bonobo_LINK_TIME_TO_LIVE 256

static GObjectClass *parent_class = NULL;

static void
directory_info_free (ActivationContext *actx, CORBA_Environment *ev)
{
	CORBA_Object_release (actx->obj, ev);
        actx->obj = CORBA_OBJECT_NIL;

        if (actx->by_iid) {
                g_hash_table_destroy (actx->by_iid);
                actx->by_iid = NULL;
        }

        if (actx->list) {
                CORBA_sequence_set_release (actx->list, CORBA_TRUE);
                CORBA_free (actx->list);
                actx->list = NULL;
        }

        if (actx->active_servers)
                g_hash_table_destroy (actx->active_servers);
        CORBA_free (actx->active_server_list);
        actx->active_server_list = NULL;
}

static void
ac_update_active (ActivationContext *actx, CORBA_Environment *ev)
{
	int i;
	Bonobo_ServerStateCache *cache;

	cache = Bonobo_ObjectDirectory_get_active_servers (
                actx->obj, actx->time_active_pulled, ev);

	if (ev->_major != CORBA_NO_EXCEPTION) {
                CORBA_Object_release (actx->obj, ev);
                actx->obj = CORBA_OBJECT_NIL;
	}

	if (cache->_d) {
		if (actx->active_servers) {
			g_hash_table_destroy (actx->active_servers);
			CORBA_free (actx->active_server_list);
		}

		actx->active_server_list = cache;

		actx->time_active_pulled = time (NULL);
		actx->active_servers =
			g_hash_table_new (g_str_hash, g_str_equal);
		for (i = 0; i < cache->_u.active_servers._length; i++)
			g_hash_table_insert (actx->active_servers,
					     cache->_u.
					     active_servers._buffer[i],
					     GINT_TO_POINTER (1));
	} else
		CORBA_free (cache);
}

static char *
ac_CORBA_Context_get_value (CORBA_Context         ctx, 
                            const char           *propname,
                            CORBA_Environment    *ev)
{
        return activation_server_CORBA_Context_get_value (
                ctx, propname,
                ex_Bonobo_Activation_IncompleteContext, ev);
}

static void
ac_update_list (ActivationContext *actx, CORBA_Environment *ev)
{
	int i;
	Bonobo_ServerInfoListCache *cache;

	cache = Bonobo_ObjectDirectory_get_servers (
                actx->obj, actx->time_list_pulled, ev);

	if (ev->_major != CORBA_NO_EXCEPTION) {
		actx->list = NULL;
                CORBA_Object_release (actx->obj, ev);
                actx->obj = CORBA_OBJECT_NIL;
		return;
	}

	if (cache->_d) {
		if (actx->by_iid)
			g_hash_table_destroy (actx->by_iid);
		if (actx->list) {
			CORBA_sequence_set_release (actx->list, CORBA_TRUE);
			CORBA_free (actx->list);
			actx->list = NULL;
		}

                actx->list = ORBit_copy_value (&cache->_u.server_list,
                                               TC_Bonobo_ServerInfoList);

		actx->time_list_pulled = time (NULL);
		actx->by_iid = g_hash_table_new (g_str_hash, g_str_equal);
		for (i = 0; i < actx->list->_length; i++)
			g_hash_table_insert (actx->by_iid,
					     actx->list->_buffer[i].iid,
					     &(actx->list->_buffer[i]));
	}

	CORBA_free (cache);
}

static QueryExprConst
ac_query_get_var (Bonobo_ServerInfo *si, const char *id, QueryContext *qctx)
{
	QueryExprConst retval;
        ActivationContext *actx = qctx->user_data;

	retval.value_known = FALSE;
	retval.needs_free = FALSE;

	if (!strcasecmp (id, "_active")) {
		CORBA_Environment ev;

		CORBA_exception_init (&ev);
		ac_update_active (actx, &ev);
		CORBA_exception_free (&ev);

		retval.value_known = TRUE;
		retval.type = CONST_BOOLEAN;
		retval.u.v_boolean =
			g_hash_table_lookup (actx->active_servers,
					     si->iid) ? TRUE : FALSE;
	}

	return retval;
}

/* This function should only be called by
 * impl_Bonobo_ActivationContext_query and
 * impl_Bonobo_ActivationContext_activateMatching - hairy implicit preconditions
 * exist. */
static void
ac_query_run (ActivationContext        *actx,
	      const CORBA_char        *requirements,
	      const Bonobo_StringList *selection_order,
	      CORBA_Context            ctx,
	      Bonobo_ServerInfo      **items,
              CORBA_Environment       *ev)
{
	int total, i;
	QueryContext qctx;

	Bonobo_ServerInfo **orig_items;
	int item_count, orig_item_count;
	char *errstr;
	Bonobo_Activation_ParseFailed *ex;

	QueryExpr *qexp_requirements;
	QueryExpr **qexp_sort_items;

	/* First, parse the query */
	errstr = (char *) qexp_parse (requirements, &qexp_requirements);
	if (errstr) {
		puts (errstr);

		g_strstrip (errstr);
		ex = Bonobo_Activation_ParseFailed__alloc ();
		ex->description = CORBA_string_dup (errstr);
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Activation_ParseFailed,
				     ex);
		return;
	}

	qexp_sort_items =
		g_alloca (selection_order->_length * sizeof (QueryExpr *));
	for (i = 0; i < selection_order->_length; i++) {
		errstr =
			(char *) qexp_parse (selection_order->_buffer[i],
					     &qexp_sort_items[i]);

		if (errstr) {
			qexp_free (qexp_requirements);
			for (i--; i >= 0; i--)
				qexp_free (qexp_sort_items[i]);

			g_strstrip (errstr);
			ex = Bonobo_Activation_ParseFailed__alloc ();
			ex->description = CORBA_string_dup (errstr);

			CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
					     ex_Bonobo_Activation_ParseFailed,
					     ex);
			return;
		}
	}

	total = actx->total_servers;
	orig_items = g_alloca (total * sizeof (Bonobo_ServerInfo *));

        {
		int i;

                item_count = 0;

		if (actx->obj != CORBA_OBJECT_NIL)
                        for (i = 0; i < actx->list->_length; i++, item_count++)
                                items[item_count] = &actx->list->_buffer[i];
	}

	memcpy (orig_items, items, item_count * sizeof (Bonobo_ServerInfo *));
	orig_item_count = item_count;

	qctx.sil = orig_items;
	qctx.nservers = orig_item_count;
	qctx.cctx = ctx;
	qctx.id_evaluator = ac_query_get_var;
	qctx.user_data = actx;

	for (i = 0; i < item_count; i++) {
		if (!qexp_matches (items[i], qexp_requirements, &qctx))
			items[i] = NULL;
	}

	qexp_sort (items, item_count, qexp_sort_items,
		   selection_order->_length, &qctx);

        qexp_free (qexp_requirements);
        for (i = 0; i < selection_order->_length; i++)
                qexp_free (qexp_sort_items[i]);
}

static void
ac_update_lists (ActivationContext *actx,
		 CORBA_Environment *ev)
{
	int prev, new;

	if (actx->refs > 0) {
                /* FIXME: what happens on re-enterency here ?
                 * looks like this could get seriously out of date */
		return;
        }

	if (actx->list)
		prev = actx->list->_length;
	else
		prev = 0;

	ac_update_list (actx, ev);

	if (actx->list)
		new = actx->list->_length;
	else
		new = 0;

	actx->total_servers += (new - prev);
}

static GList *clients = NULL;

void
activation_clients_cache_notify (void)
{
        GList *l;
        GSList *notify = NULL, *l2;
        CORBA_Environment ev;

        CORBA_exception_init (&ev);

        for (l = clients; l; l = l->next)
                notify = g_slist_prepend (notify, CORBA_Object_duplicate (l->data, &ev));

        for (l2 = notify; l2; l2 = l2->next) {
                Bonobo_ActivationClient_resetCache (l2->data, &ev);
                if (ev._major != CORBA_NO_EXCEPTION)
                        clients = g_list_remove (clients, l2->data);

                CORBA_Object_release (l2->data, &ev);
                CORBA_exception_free (&ev);
        }

	g_slist_free (notify);
}

gboolean
activation_clients_is_empty_scan (void)
{
        GList *l, *check = NULL;

        for (l = clients; l; l = l->next)
                check = g_list_prepend (check, CORBA_Object_duplicate (l->data, NULL));

        for (l = check; l; l = l->next) {
                if (ORBit_small_get_connection_status (l->data) ==
                    ORBIT_CONNECTION_DISCONNECTED) {
                        GList *remove;
                        if ((remove = g_list_find (clients, l->data))) {
                                CORBA_Object_release (remove->data, NULL);
                                clients = g_list_delete_link (clients, remove);
                        }
                }
                CORBA_Object_release (l->data, NULL);
        }

        g_list_free (check);

        return clients == NULL;
}

static gboolean
ac_rescan (gpointer data)
{
        static gboolean in_rescan = FALSE;
        static guint idle_id = 0;

        server_lock ();

        /* We tend to get a lot of 'broken' callbacks at once */
        if (in_rescan) {
                if (!idle_id)
                        idle_id = g_idle_add (ac_rescan, NULL);
                server_unlock ();
                return FALSE;
        }
        in_rescan = TRUE;

        if (activation_clients_is_empty_scan ()) {
#ifdef BONOBO_ACTIVATION_DEBUG
                g_warning ("All clients dead");
#endif
                check_quit ();
        }

        in_rescan = FALSE;
        server_unlock ();

        return FALSE;
}

static void
active_client_cnx_broken (ORBitConnection *cnx,
                          gpointer         dummy)
{
        ac_rescan (NULL);
}

static void
impl_Bonobo_ActivationContext_addClient (PortableServer_Servant        servant,
                                         const Bonobo_ActivationClient client,
                                         const CORBA_char             *locales,
                                         CORBA_Environment            *ev)
{
        GList *l;
        gboolean new_locale;
        ORBitConnection *cnx;

        server_lock ();

        new_locale = register_interest_in_locales (locales);

        cnx = ORBit_small_get_connection (client);
        for (l = clients; l; l = l->next)
                if (cnx == ORBit_small_get_connection (l->data))
                        break;
        
        clients = g_list_prepend (
                clients, CORBA_Object_duplicate (client, ev));

        if (!l) {
                ORBit_small_listen_for_broken
                        (client, G_CALLBACK (active_client_cnx_broken), NULL);
                check_quit ();
        }

        if (new_locale)
                bonobo_object_directory_reload ();

        server_unlock ();
}

static Bonobo_ObjectDirectoryList *
impl_Bonobo_ActivationContext__get_directories (PortableServer_Servant servant,
                                                CORBA_Environment     *ev)
{
        ActivationContext *actx;
	Bonobo_ObjectDirectoryList *retval;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

	retval = Bonobo_ObjectDirectoryList__alloc ();
        if (actx->obj != CORBA_OBJECT_NIL) {
                retval->_length = 1;
                retval->_buffer =
                        CORBA_sequence_Bonobo_ObjectDirectory_allocbuf (1);
		retval->_buffer[0] = CORBA_Object_duplicate (actx->obj, ev);
	} else {
                retval->_length = 0;
        }

	CORBA_sequence_set_release (retval, CORBA_TRUE);

        server_unlock ();

	return retval;
}

static void
impl_Bonobo_ActivationContext_addDirectory (PortableServer_Servant servant,
                                            Bonobo_ObjectDirectory dir,
                                            CORBA_Environment     *ev)
{
        ActivationContext *actx;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

        if (actx->obj == dir)
                CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
                                     ex_Bonobo_Activation_AlreadyListed,
                                     NULL);
	else {
                CORBA_Object_release (actx->obj, ev);
                actx->obj = CORBA_Object_duplicate (dir, ev);
        }

        server_unlock ();
}

static void
impl_Bonobo_ActivationContext_removeDirectory (PortableServer_Servant servant,
                                               Bonobo_ObjectDirectory dir,
                                               CORBA_Environment     *ev)
{
        ActivationContext *actx;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

        if (dir != actx->obj)
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Activation_NotListed,
				     NULL);
        else {
                if (actx->refs) {
                        CORBA_Object_release (actx->obj, ev);
                        actx->obj = CORBA_OBJECT_NIL;
                } else
                        directory_info_free (actx, ev);
        }

        server_unlock ();
}

static void
ac_do_activation (ActivationContext                  *actx,
		  Bonobo_ServerInfo                  *server,
		  const Bonobo_ActivationEnvironment *environment,
		  Bonobo_ActivationResult            *out,
		  Bonobo_ActivationFlags              flags,
		  const char                         *hostname,
                  Bonobo_ActivationClient             client,
		  CORBA_Context                       ctx,
                  CORBA_Environment                  *ev)
{
	int num_layers;
	Bonobo_ServerInfo *activatable;
        gchar *aid = NULL;

	/* When doing checks for shlib loadability, we 
         * have to find the info on the factory object in case
	 * a factory is inside a shlib 
         */
	if (!actx->obj || ev->_major != CORBA_NO_EXCEPTION) {
		Bonobo_GeneralError *errval = Bonobo_GeneralError__alloc ();
		errval->description =
			CORBA_string_dup
			(_("Couldn't find which child the server was listed in"));
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_GeneralError, errval);
		return;
	}

	for (num_layers = 0, activatable = server;
             activatable && activatable->server_type &&
                     !strcmp (activatable->server_type, "factory") &&
             num_layers < Bonobo_LINK_TIME_TO_LIVE; num_layers++) {

		activatable = g_hash_table_lookup (actx->by_iid, activatable->location_info);
	}

	if (activatable == NULL) {		
		Bonobo_GeneralError *errval = Bonobo_GeneralError__alloc ();
		errval->description = CORBA_string_dup ("Couldn't find the factory server");
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION, 
				     ex_Bonobo_GeneralError, errval);
		return;
	} 
	else if (num_layers == Bonobo_LINK_TIME_TO_LIVE) {
		Bonobo_GeneralError *errval = Bonobo_GeneralError__alloc ();
		errval->description = CORBA_string_dup ("Location loop");
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_GeneralError, errval);
		return;
        }

	/* A shared library must be on the same host as the activator in
	 * order for loading to work properly (no, we're not going to
	 * bother with loading a remote shlib into a process - it gets far too complicated
	 * far too quickly :-) */

        if (activatable)
                aid = g_strdup_printf ("OAFAID:[%s,%s,%s]", activatable->iid,
                                       activatable->username, activatable->hostname);
	
	if (activatable && activatable->server_type && !strcmp (activatable->server_type, "shlib")
	    && !(flags & Bonobo_ACTIVATION_FLAG_NO_LOCAL)
	    && (hostname && !strcmp (activatable->hostname, hostname))) {
		int j;
		
		out->res._d = Bonobo_ACTIVATION_RESULT_SHLIB;		

		/* Here is an explanation as to why we add 2 to num_layers.
		 * At the end of the string list, after all the factory iids are added
		 * to the string list, we then add the iid of the shaed library and the 
		 * location info.  This data is later used in oaf_server_activate_shlib
		 * to activate the component
		 */		 
		out->res._u.res_shlib._length = num_layers + 2;
		out->res._u.res_shlib._buffer = CORBA_sequence_CORBA_string_allocbuf (num_layers + 2);

		/* Copy over factory info */
		for (j = 0, activatable = server; activatable
		     && !strcmp (activatable->server_type, "factory"); j++) {
			out->res._u.res_shlib._buffer[j] = CORBA_string_dup (activatable->iid);
			activatable = g_hash_table_lookup (actx->by_iid,
						     	   activatable->location_info);
		}

		/* Copy shlib iid into buffer */
		out->res._u.res_shlib._buffer[j] = CORBA_string_dup (activatable->iid);

		/* Copy location into last buffer slot for use in later activation */
		out->res._u.res_shlib._buffer[j+1] = CORBA_string_dup (activatable->location_info);

		out->aid = CORBA_string_dup (aid);
	} else {
		CORBA_Object retval;
                char *iid = g_strdup (server->iid);

		retval = Bonobo_ObjectDirectory_activate (
                        actx->obj, iid, BONOBO_OBJREF (actx),
                        environment, flags, client, ctx, ev);
                g_free (iid);

		if (ev->_major == CORBA_NO_EXCEPTION) {
			out->res._d = Bonobo_ACTIVATION_RESULT_OBJECT;
			out->res._u.res_object = retval;
			out->aid = CORBA_string_dup (aid);
		}
#ifdef BONOBO_ACTIVATION_DEBUG
                else
                        g_warning ("Activation of '%s' failed with exception '%s'",
                                   aid, ev->_id);
#endif
	}
        g_free (aid);
}


static Bonobo_ActivationResult *
impl_Bonobo_ActivationContext_activateMatchingFull (
        PortableServer_Servant              servant,
        const CORBA_char                   *requirements,
        const Bonobo_StringList            *selection_order,
	const Bonobo_ActivationEnvironment *environment,
        const Bonobo_ActivationFlags        flags,
        Bonobo_ActivationClient             client,
        CORBA_Context                       ctx,
        CORBA_Environment                  *ev)
{
        ActivationContext *actx;
	Bonobo_ActivationResult *retval = NULL;
	Bonobo_ServerInfo **items, *curitem;
	int i;
	char *hostname;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

	ac_update_lists (actx, ev);

	actx->refs++;

	items = g_alloca (actx->total_servers *
                          sizeof (Bonobo_ServerInfo *));
	ac_query_run (actx, requirements, selection_order, ctx, items, ev);

	if (ev->_major != CORBA_NO_EXCEPTION)
		goto out;

	hostname = ac_CORBA_Context_get_value (ctx, "hostname", ev);

	retval = Bonobo_ActivationResult__alloc ();
	retval->res._d = Bonobo_ACTIVATION_RESULT_NONE;

        bonobo_object_ref (actx);
	for (i = 0; (retval->res._d == Bonobo_ACTIVATION_RESULT_NONE) && items[i]
	     && (i < actx->total_servers); i++) {
		curitem = items[i];

		ac_do_activation (actx, curitem, environment,
				  retval, flags, hostname, client, ctx, ev);
	}
        bonobo_object_unref (actx);

	if (retval->res._d == Bonobo_ACTIVATION_RESULT_NONE)
		retval->aid = CORBA_string_dup ("");

	g_free (hostname);

 out:
	if (ev->_major != CORBA_NO_EXCEPTION) {
                CORBA_free (retval);
                retval = NULL;
        }

	actx->refs--;

        server_unlock ();

	return retval;
}

static Bonobo_ActivationResult *
impl_Bonobo_ActivationContext_activateMatching (
        PortableServer_Servant              servant,
        const CORBA_char                   *requirements,
        const Bonobo_StringList            *selection_order,
	const Bonobo_ActivationEnvironment *environment,
        const Bonobo_ActivationFlags        flags,
        CORBA_Context                       ctx,
        CORBA_Environment                  *ev)
{
        return impl_Bonobo_ActivationContext_activateMatchingFull
                (servant, requirements, selection_order,
                 environment, flags, CORBA_OBJECT_NIL, ctx, ev);
}

static Bonobo_ServerInfoList *
impl_Bonobo_ActivationContext_query (PortableServer_Servant servant,
                                     const CORBA_char * requirements,
                                     const Bonobo_StringList * selection_order,
                                     CORBA_Context ctx, CORBA_Environment * ev)
{
        ActivationContext *actx;
	Bonobo_ServerInfoList *retval;
	Bonobo_ServerInfo **items;
	int item_count;
	int i, j, total;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

	retval = Bonobo_ServerInfoList__alloc ();
	retval->_length = 0;
	retval->_buffer = NULL;
	CORBA_sequence_set_release (retval, CORBA_TRUE);

	/* Pull in new lists from OD servers */
	ac_update_lists (actx, ev);
	actx->refs++;

	items = g_alloca (actx->total_servers *
                          sizeof (Bonobo_ServerInfo *));
	item_count = actx->total_servers;

	ac_query_run (actx, requirements, selection_order, ctx, items, ev);

	if (ev->_major == CORBA_NO_EXCEPTION) {
		for (total = i = 0; i < item_count; i++) {
			if (items[i])
				total++;
		}

		retval->_length = total;
		retval->_buffer =
			CORBA_sequence_Bonobo_ServerInfo_allocbuf (total);

		for (i = j = 0; i < item_count; i++) {
			if (!items[i])
				continue;

			Bonobo_ServerInfo_copy (&retval->_buffer[j], items[i]);

			j++;
		}
	}

	actx->refs--;

        server_unlock ();

	return retval;
}

static char *
ac_aid_to_query_string (const CORBA_char *aid)
{
        char *requirements;
        char *iid_requirement;
        char *username_requirement;
        char *hostname_requirement;
	BonoboActivationInfo *ainfo;

	ainfo = bonobo_activation_id_parse (aid);
	if (!ainfo)
                return NULL;

        iid_requirement = g_strconcat ("iid == \'", ainfo->iid, "\' ", NULL);

        if (ainfo->user) {
                username_requirement = g_strconcat ("AND username == \'", ainfo->user, "\'", NULL);
        } else {
                username_requirement = g_strdup ("");
        }
        
        if (ainfo->host) {
                hostname_requirement = g_strconcat ("AND hostname == \'", ainfo->host, "\'", NULL);
        } else {
                hostname_requirement = g_strdup ("");
        }
        
        requirements = g_strconcat (iid_requirement, username_requirement, 
                                    hostname_requirement, NULL);

        g_free (iid_requirement);
        g_free (username_requirement);
        g_free (hostname_requirement);
        bonobo_activation_info_free (ainfo);

        return requirements;
}

static void
ac_context_to_string_array (CORBA_Context context, char **sort_criteria,
                            CORBA_Environment *ev)
{
	char *context_username;
	char *context_hostname;

        context_username = ac_CORBA_Context_get_value (context, "username", ev);
        context_hostname = ac_CORBA_Context_get_value (context, "hostname", ev);
	if (ev->_major != CORBA_NO_EXCEPTION) {
                g_free (context_username);
                g_free (context_hostname);
                return;
        }

        sort_criteria[0] = g_strconcat ("username == \'", context_username, "\'", NULL);
        sort_criteria[1] = g_strconcat ("hostname == \'", context_hostname, "\'", NULL);
        sort_criteria[2] = NULL;

        g_free (context_username);
        g_free (context_hostname);
}

#define PARSE_ERROR_NOT_AN_AID (_("Not a valid Activation ID"))

static Bonobo_ActivationResult *
impl_Bonobo_ActivationContext_activateFromAidFull (PortableServer_Servant  servant,
                                                   const CORBA_char       *aid,
                                                   Bonobo_ActivationFlags  flags,
                                                   Bonobo_ActivationClient client,
                                                   CORBA_Context           ctx,
                                                   CORBA_Environment      *ev)
{
        ActivationContext *actx;
	Bonobo_ActivationResult *retval = NULL;
        char *requirements;
        char *sort_criteria[3];
        Bonobo_StringList selection_order;
	Bonobo_ActivationEnvironment environment;

        server_lock ();

        actx = ACTIVATION_CONTEXT (servant);

	if (strncmp ("OAFAID:", aid, 7) != 0) {
		Bonobo_Activation_ParseFailed *ex;

		ex = Bonobo_Activation_ParseFailed__alloc ();
		ex->description = CORBA_string_dup (PARSE_ERROR_NOT_AN_AID);

		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Activation_ParseFailed,
				     ex);
                goto act_out;
	}

	ac_update_lists (actx, ev);
        if (ev->_major != CORBA_NO_EXCEPTION)
                goto act_out;

	actx->refs++;

        requirements = ac_aid_to_query_string (aid);
        if (requirements == NULL) {
                actx->refs--;
                goto act_out;
        }

        ac_context_to_string_array (ctx, sort_criteria, ev);
        if (ev->_major != CORBA_NO_EXCEPTION) {
                actx->refs--;
                g_free (requirements);
                goto act_out;
        }

        selection_order._length = 2;
        selection_order._buffer = sort_criteria;
        CORBA_sequence_set_release (&selection_order, CORBA_FALSE);

	memset (&environment, 0, sizeof (Bonobo_ActivationEnvironment));

        retval = impl_Bonobo_ActivationContext_activateMatchingFull (
                actx, requirements, &selection_order, &environment,
                flags, client, ctx, ev);

        g_free (sort_criteria[0]);
        g_free (sort_criteria[1]);
        g_free (requirements);

        actx->refs--;

 act_out:
        server_unlock ();

        return retval;
}

static Bonobo_ActivationResult *
impl_Bonobo_ActivationContext_activateFromAid (PortableServer_Servant  servant,
					       const CORBA_char       *aid,
					       Bonobo_ActivationFlags  flags,
					       CORBA_Context           ctx,
					       CORBA_Environment      *ev)
{
        return impl_Bonobo_ActivationContext_activateFromAidFull
                (servant, aid, flags, CORBA_OBJECT_NIL, ctx, ev);
}

static CORBA_long
impl_Bonobo_ActivationContext_getVersion (PortableServer_Servant  servant,
                                          CORBA_Environment      *ev)
{
        return (BONOBO_ACTIVATION_MAJOR_VERSION*10000 + 
                BONOBO_ACTIVATION_MINOR_VERSION*100 +
                BONOBO_ACTIVATION_MICRO_VERSION);
}

static ActivationContext *main_ac = NULL;

void
activation_context_setup (PortableServer_POA     poa,
                          Bonobo_ObjectDirectory dir,
                          CORBA_Environment     *ev)
{
        main_ac = g_object_new (activation_context_get_type (),
                                "poa", poa, NULL);

        impl_Bonobo_ActivationContext_addDirectory
                (BONOBO_OBJECT (main_ac), dir, ev);
}

void
activation_context_shutdown (void)
{
        if (main_ac) {
                bonobo_object_set_immortal (BONOBO_OBJECT (main_ac), FALSE);
                bonobo_object_unref (BONOBO_OBJECT (main_ac));
                main_ac = NULL;
        }        
}

Bonobo_ActivationContext
activation_context_get (void)
{
        if (!main_ac)
                return CORBA_OBJECT_NIL;
        else
                return BONOBO_OBJREF (main_ac);
}

static void 
activation_context_finalize (GObject *object)
{
        CORBA_Environment ev[1];
        ActivationContext *actx = (ActivationContext *) object;

        CORBA_exception_init (ev);

        directory_info_free (actx, ev);

        CORBA_exception_free (ev);

        parent_class->finalize (object);
}

static void
activation_context_class_init (ActivationContextClass *klass)
{
        GObjectClass *object_class = (GObjectClass *) klass;
	POA_Bonobo_ActivationContext__epv *epv = &klass->epv;

        parent_class = g_type_class_peek_parent (klass);
        object_class->finalize = activation_context_finalize;

        epv->_get_directories = impl_Bonobo_ActivationContext__get_directories;
	epv->addClient         = impl_Bonobo_ActivationContext_addClient;
	epv->addDirectory      = impl_Bonobo_ActivationContext_addDirectory;
	epv->removeDirectory   = impl_Bonobo_ActivationContext_removeDirectory;
	epv->query             = impl_Bonobo_ActivationContext_query;
	epv->activateMatching  = impl_Bonobo_ActivationContext_activateMatching;
	epv->activateFromAid   = impl_Bonobo_ActivationContext_activateFromAid;
	epv->getVersion        = impl_Bonobo_ActivationContext_getVersion;
	epv->activateMatchingFull = impl_Bonobo_ActivationContext_activateMatchingFull;
	epv->activateFromAidFull  = impl_Bonobo_ActivationContext_activateFromAidFull;
}

static void
activation_context_init (ActivationContext *actx)
{
        bonobo_object_set_immortal (BONOBO_OBJECT (actx), TRUE);
}

BONOBO_TYPE_FUNC_FULL (ActivationContext,
                       Bonobo_ActivationContext,
                       BONOBO_TYPE_OBJECT,
                       activation_context)


syntax highlighted by Code2HTML, v. 0.9.1