blob: b8c62dc09ddf82908d800f47a4bf9e5bde606c3f [file] [log] [blame]
/* $Id$
*
* 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.
*/
/*
* xmpl_perf_helper.c
* transport helper for perf service
*/
#include "etch_apr.h"
#include "xmpl_perf_helper.h"
#include "xmpl_perf_valufact.h"
#include "xmpl_perf_client_impl.h"
#include "etch_svcobj_masks.h"
#include "etch_global.h"
#include "etch_url.h"
#include "etchlog.h"
#include <tchar.h>
#include <stdio.h>
#include <conio.h>
char* ETCHETCH = "ETCH";
char* ETCHTHIS = "PERF";
char* ETCHCVER = "etch C v0.5.000";
/**
* new_remote_server()
* this is java binding's helper.newServer(uri, rex, implfactory);
* constructs a client side remote server per specs in the supplied uri.
* generally invoked from a run thread in the client main .exe.
* RemotePerfServer server = PerfHelper.newServer( uri, null, implFactory );
* @param uri
* @param p client parameters including callback to client ctor,
* caller reliquishes this memory.
* @return the new remote server object.
*/
perf_remote_server* new_remote_server (wchar_t* uri, etch_client_factory* p)
{
i_perf_client* myclient = NULL;
perf_client_stub* client_stub = NULL;
i_delivery_service* deliverysvc = NULL;
perf_remote_server* remote_server = NULL;
perf_value_factory* vf = new_perf_value_factory();
ETCH_ASSERT(vf && p);
p->in_valufact = (etch_value_factory*) vf;
p->in_resx = etch_transport_resources_init(p->in_resx);
ETCH_ASSERT(p->in_resx);
etch_resources_add (p->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) vf);
/* instantiate a delivery service */
deliverysvc = new_etch_transport (uri, (etch_factory_params*) p, NULL);
ETCH_ASSERT(is_etch_ideliverysvc(deliverysvc)); /* TODO exception not assert */
p->dsvc = deliverysvc;
/* instantiate the remote server */
remote_server = new_perf_remote_server (NULL, deliverysvc, (etch_value_factory*) vf);
ETCH_ASSERT(is_etch_remote_server(remote_server)); /* TODO return exception or return null */
p->server_id = remote_server->server_base->server_id;
p->server = remote_server;
remote_server->client_factory = p; /* assume ownership */
/* here we call back to the client constructor in [main]. the purpose of the
* callback is to isolate the editable xxxx_client_impl constructor from the
* private constructor pieces. the callback instantiates a client implenentation
* and returns an interface to it.
*/
myclient = p->new_client? p->new_client (remote_server): NULL;
ETCH_ASSERT(is_etch_client_base(myclient)); /* TODO return exception or return null */
p->iclient = myclient;
p->fpool = (etch_threadpool*) etch_resources_get (p->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
p->qpool = (etch_threadpool*) etch_resources_get (p->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
ETCH_ASSERT(p->fpool && p->qpool);
/* construct stub */
client_stub = new_perf_client_stub (p);
ETCH_ASSERT(is_etch_client_stub(client_stub));
p->stub = client_stub;
return remote_server;
}
/**
* new_perf_listener()
* invoked from [main] to construct the session listener.
* this function is simply a proxy providing additional parameters
* to new_etch_listener, in order to simplify the call in [main].
* @param main_new_server_callback pointer to [main]'s new_xxx_server()
* @return i_sessionlistener interface to listener. note that the returned
* listener.thisx is the specific server object, e.g. etch_tcp_server.
*/
i_sessionlistener* new_perf_listener (wchar_t* uri, main_new_server_funcptr main_new_server)
{
i_sessionlistener* listener = NULL;
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating main listener ...\n");
listener = new_etch_listener (uri, NULL, new_helper_accepted_server,
main_new_server, new_perf_resources);
if (listener)
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "main listener instantiated\n");
else
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not instantiate main listener\n");
return listener;
}
/**
* start_perf_client()
* start the remote server, blocking until it comes up or errors out.
* @param uri the etch-formatted URI.
* @param new_client a pointer to a callback in the main client module
* which constructs the client implementation object and returns an interface
* to it.
* @param waitupms how long to wait for a connection to server to complete.
* @return the remote server, or null indicating error.
* todo return exception in remote server rather than null.
*/
perf_remote_server* start_perf_client (wchar_t* uri,
new_client_funcptr new_client, const int waitupms)
{
int result = 0;
perf_remote* remotebase = NULL;
perf_remote_server* remote = NULL;
etch_client_factory* impl_factory = NULL;
ETCH_ASSERT(new_client);
etchlog_open_client(); /* force logger to use client filename */
etchlog (ETCHTHIS, ETCHLOG_INFO, "creating perf client ...\n");
impl_factory = new_client_factory (NULL, NULL, new_client);
ETCH_ASSERT(impl_factory);
/* instantiate a remote server, which invokes client constructor */
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating remote server ...\n");
remote = new_remote_server (uri, impl_factory);
ETCH_ASSERT(remote); /* todo exception rather than assert */
etchlog (ETCHTHIS, ETCHLOG_DEBUG,"remote server instantiated\n");
etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client created\n");
remotebase = remote->remote_base;
etchlog (ETCHTHIS, ETCHLOG_INFO, "starting perf client ...\n");
/* fyi call sequence here is as follows:
* etchremote_start_waitup()
* etchremote_transport_control()
* tcpdelsvc_transport_control()
* pmboxmgr_transport_control()
* msgzr_transport_control()
* pktzr_transport_control()
* tcpconx_transport_control()
* tcpconx_start()
* tcpconx_open()
*/
/* BLOCK here until the connection to server comes up or times out */
result = remotebase->start_waitup (remotebase, waitupms);
if (0 != result)
{ /* todo return exception with actual error in remote, rather than null */
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not start perf client\n");
remote->destroy(remote);
remote = NULL;
}
else etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client started\n");
return remote;
}
/**
* stop_perf_client()
* stop the remote server, blocking until it comes down or errors out.
* @return 0 success, -1 failure
* TODO return exception rather than result code.
*/
int stop_perf_client (perf_remote_server* remote, const int waitdownms)
{
int result = 0;
etchlog (ETCHTHIS, ETCHLOG_INFO, "stopping perf client ...\n");
/* fyi call sequence here is as follows:
* etchremote_stop_waitdown()
* etchremote_transport_control()
* tcpdelsvc_transport_control()
* pmboxmgr_transport_control()
* msgizer_transport_control()
* pktizer_transport_control()
* tcpconx_transport_control()
* tcpclient_stop_listener()
* tcpconx_closex()
*/
result = remote->remote_base->stop_waitdown (remote->remote_base, waitdownms);
if (0 == result)
etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client stopped\n");
else etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not stop perf client\n");
return result;
}
/**
* run_perf_listener()
* start the main (accept) listener, block until listener exit,
* and finally tear down this server's outstanding client sessions.
* @return 0 success, -1 failure.
*/
int run_perf_listener (i_sessionlistener* listener, const int waitupms)
{
int result = 0;
if (NULL == listener) return -1;
etchlog (ETCHTHIS, ETCHLOG_INFO, "starting main listener ...\n");
/* call through to e.g. etch_tcpsvr_transport_control() to start listener */
result = listener->transport_control (listener->thisx,
new_etch_event(CLASSID_CONTROL_START_WAITUP, waitupms), NULL);
if (0 == result)
{ etchlog (ETCHTHIS, ETCHLOG_INFO, "main listener started on thread %d\n",
transport_thread_id (listener));
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "listening for connect requests ...\n");
/* BLOCK here until listener thread exits (etch_listener_waitfor_exit) */
result = listener->wait_exit (listener);
etchlog (ETCHTHIS, ETCHLOG_INFO, "main listener ended\n");
if (transport_session_count (listener) > 0)
{
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "begin client sessions teardown\n");
result = transport_teardown_client_sessions (listener);
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "end client sessions teardown\n");
}
}
else
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not start main listener\n");
return result;
}
/**
* new_perf_resources()
* callback from new_etch_listener to initialize service-specific resources.
* @param p the etch_server_factory to be initialized
* @return 0 success, -1 failure.
*/
int new_perf_resources (etch_server_factory* p)
{
int result = 0;
ETCH_ASSERT((NULL != p) && (NULL == p->in_valufact));
ETCH_ASSERT (p->in_resx && is_etch_hashtable(p->in_resx));
p->in_valufact = (etch_value_factory*) new_perf_value_factory ();
ETCH_ASSERT(p->in_valufact);
if (NULL == p->in_valufact) return -1;
result = etch_resources_add (p->in_resx,
ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) p->in_valufact);
ETCH_ASSERT(0 == result);
return result;
}
/**
* new_helper_accepted_server()
* this is java binding's newServer().
* constructs the server side listener component of a remote client.
* invoked from the server's accepted connection handler, such as
* tcpxfact_session_accepted(), to create the remote client and stub
* with which to communicate with the individual client.
* we see then that each client has its own listener thread on the server,
* as opposed to a client ID scheme where all clients would filter through
* a single socket on the server.
* @param p parameter bundle, including the callback to the to the service-
* specific new server constructors in [main]. caller relinquishes.
* @return the client's service-specific server stub.
*/
void* new_helper_accepted_server (etch_server_factory* p, etch_session* session)
{
i_perf_server* iserver;
perf_server_stub* stub;
perf_remote_client* client;
ETCH_ASSERT(p && p->helper_new_accepted && p->main_new_server);
ETCH_ASSERT(p->in_resx && p->in_valufact); // TODO assert delivery service
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating accepted client listener ...\n");
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating remote client...\n");
client = new_remote_client (NULL, session, p->in_valufact);
client->session_id = session->session_id;
session->client = client;
/* here we CALL BACK to the constructor in [main], the purpose of the callback
* being to isolate the editable constructor from the private constructor.
* the callback instantiates a client's server implementation and returns
* an interface to it.
*/
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating server implementation ...\n");
iserver = p->main_new_server (p, session);
iserver->session_id = session->session_id;
session->server = iserver;
/* note that the main listener will use p->mainpool as a thread manager, not these */
p->qpool = (etch_threadpool*) etch_resources_get(p->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
p->fpool = (etch_threadpool*) etch_resources_get(p->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
/* eventually new_perf_server_stub() gets to stub_base constructor, which sets
* the delivery service's session to this, the server stub. so, in the java binding,
* the server stub is referenced as delivery service.session. we should perhaps also
* store the stub opaquely in both the client and listener objects.
*/
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating server stub ...\n");
stub = new_perf_server_stub (p, session);
stub->session_id = session->session_id;
session->server_stub = stub;
if (iserver && stub)
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "accepted client listener instantiated\n");
else
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not instantiate accepted client listener\n");
return stub;
}
/**
* get_perf_client_impl()
* convenience to extract the client impl from the remote server object.
*/
perf_client_impl* get_perf_client_impl (perf_remote_server* rs)
{
i_perf_client* iclient = rs->client_factory->iclient;
perf_client_impl* pci = iclient? iclient->thisx: NULL;
return pci;
}
/**
* get_perf_client_stub()
* convenience to extract the client stub from the remote server object.
*/
perf_client_stub* get_perf_client_stub (perf_remote_server* rs)
{
perf_client_stub* stub = (perf_client_stub*) rs->client_factory->stub;
ETCH_ASSERT(is_perf_client_stub(stub));
return stub;
}
/**
* get_perf_client_stubbase()
* convenience to extract the client stub base from the remote server object.
*/
etch_stub* get_perf_client_stubbase (perf_remote_server* rs)
{
perf_client_stub* stub = get_perf_client_stub (rs);
etch_stub* base = stub->stub_base;
ETCH_ASSERT(is_etch_stub(base));
return base;
}
/**
* perf_client_cleanup()
* destroy instantiations from [main]
*/
void perf_client_cleanup (perf_remote_server* remote)
{
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "destroying remote server ...\n");
ETCHOBJ_DESTROY(remote);
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "remote server destroyed\n");
}
/**
* etch_init()
* do client-side etch runtime initialization and service-specific initialization.
* this is intended as a convenience function to be invoked only by user client code.
*/
int etch_init()
{
return perf_runtime_init(INIT_ETCH_CLIENT);
}
/**
* etch_exit()
* etch runtime teardown plus service-specific teardown
* this is intended as a convenience function to be invoked only by user client code.
*/
int etch_exit()
{
return perf_runtime_exit();
}
/**
* perf_runtime_exit()
* do etch runtime teardown plus service-specific teardown.
*/
int perf_runtime_exit ()
{
etchvf_free_builtins(); /* TODO move this somewhere else */
exitparams.is_show_leak_detail = config.is_log_memory_leak_detail;
etch_runtime_exit (&exitparams);
printf("\n%s exit\n", ETCHCVER);
return 0;
}
/**
* perf_runtime_init()
* do etch runtime initialization plus service-specific initialization.
*/
int perf_runtime_init (const int is_client)
{
printf("%s %s start\n", ETCHCVER, is_client? "client": "server");
return etch_runtime_init_all (is_client);
}