blob: 311e057c152aaea020ef9ff3e0532a0f5e7d7317 [file] [log] [blame]
/* factory.cpp
Remote object factory
The remote object factory uses a lease-based system. Remote objects
are purged from the pool when the lease expires. Supports inheritance.
Compile:
soapcpp2 factory.h
c++ -o factory factory.cpp stdsoap2.cpp soapC.cpp soapServer.cpp
Run (e.g. in the background)
factory <port>
where <port> is a available port number, e.g. 18085
Copyright (C) 2000-2002 Robert A. van Engelen. All Rights Reserved.
*/
#include <sys/stat.h> // for open()
#include "soapH.h"
#include "factory.nsmap"
#define POOLSIZE 1000 // max number of remote objects that can be alive at any instance
#define LEASETERM 60 // lease term (in seconds). Also the rate at which objects are purged
////////////////////////////////////////////////////////////////////////////////
//
// Factory class maintains pool of objects and can act as a simple ORB
//
////////////////////////////////////////////////////////////////////////////////
class Factory
{ public:
t__root *ref[POOLSIZE]; // pool of objects (t__root is base class)
unsigned int handle; // handle generation counter (is allowed to wrap around 32 bits)
Factory();
~Factory();
unsigned int create(struct soap *soap, enum t__object object, char *name);
unsigned int lookup(enum t__object object, char *name);
unsigned int rename(unsigned int handle, char *name);
void release(unsigned int handle);
void purge(struct soap* soap);
t__root *get(unsigned int handle);
t__root *root(unsigned int handle);
t__adder *adder(unsigned int handle);
t__counter *counter(unsigned int handle);
int save(const char *file);
int load(const char *file);
};
// Initialize empty pool and set handle generation counter to 0
Factory::Factory()
{ for (int i = 0; i < POOLSIZE; i++)
ref[i] = NULL;
handle = 0;
}
// Remove all objects from pool
Factory::~Factory()
{ for (int i = 0; i < POOLSIZE; i++)
if (ref[i])
delete ref[i];
}
// Create a new object, place it in the pool, and return handle
unsigned int Factory::create(struct soap *soap, enum t__object object, char *name)
{ for (int i = 0; i < POOLSIZE; i++)
if (!ref[++handle % POOLSIZE]) // succeeds if this slot is available
{ t__root *r = NULL;
if (!handle)
handle += POOLSIZE; // make sure handle is never 0 (0 indicates invalid handle)
switch (object) // type of object to instantiate
{ case ROOT:
r = new t__root();
break;
case ADDER:
r = new t__adder();
break;
case COUNTER:
r = new t__counter();
break;
}
if (r)
{ ref[handle % POOLSIZE] = r; // add object to the pool
r->object = object; // save type
if (name) // save name (if assigned)
{ r->name = (char*)malloc(strlen(name+1));
strcpy(r->name, name);
}
else
r->name = NULL;
r->handle = handle; // keep handle for verification
r->renew(); // renew its lease
return handle;
}
return 0;
}
return 0;
}
// Lookup the name of an object and return handle
unsigned int Factory::lookup(enum t__object object, char *name)
{ for (int i = 0; i < POOLSIZE; i++)
if (ref[i] && ref[i]->object == object && ref[i]->name && !strcmp(ref[i]->name, name))
{ ref[i]->renew();
return ref[i]->handle;
}
return 0;
}
// Rename object and return handle if successful
unsigned int Factory::rename(unsigned int handle, char *name)
{ t__root *r = get(handle);
if (r)
{ if (r->name)
free(r->name);
r->name = (char*)malloc(strlen(name)+1);
strcpy(r->name, name);
r->renew();
return handle;
}
return 0;
}
// get ref to object from handle
t__root *Factory::get(unsigned int handle)
{ t__root *r = ref[handle % POOLSIZE];
if (r && r->handle == handle)
return r;
return NULL;
}
// get ref to root object from handle and renew lease
t__root *Factory::root(unsigned int handle)
{ t__root *r = get(handle);
if (r)
r->renew();
return r;
}
// get ref to adder object from handle and renew lease
t__adder *Factory::adder(unsigned int handle)
{ t__adder *a = (t__adder*)get(handle);
if (a)
{ if (a->object == ADDER || a->object == COUNTER)
a->renew();
else
a = NULL;
}
return a;
}
// get ref to counter object from handle and renew lease
t__counter *Factory::counter(unsigned int handle)
{ t__counter *c = (t__counter*)get(handle);
if (c)
{ if (c->object == COUNTER)
c->renew();
else
c = NULL;
}
return c;
}
// remove all objects from pool whose lease has expired
void Factory::purge(struct soap *soap)
{ time_t t = time(NULL); // current time
int flag = 1;
for (int i = 0; i < POOLSIZE; i++)
{ t__root *r = ref[i];
if (r && r->lease < t) // expired?
{ if (flag)
fprintf(stderr, "\nPurging objects:");
if (r->name)
fprintf(stderr, "%s(%u)\n", r->name, r->handle);
else
fprintf(stderr, "(%u)\n", r->handle);
soap_delete(soap, r);
ref[i] = NULL;
flag = 0;
}
}
}
// remove object from pool and release slot
void Factory::release(unsigned int handle)
{ t__root *r = get(handle);
if (r)
ref[handle % POOLSIZE] = NULL;
}
// save object pool to file (or stdout)
int Factory::save(const char *file)
{ struct soap soap; // use a new local gSOAP environment
soap_init(&soap);
soap_begin(&soap);
if (file)
soap.sendfd = open(file, O_CREAT|O_TRUNC|O_WRONLY, S_IREAD|S_IWRITE);
if (soap.sendfd < 0)
return -1;
soap_begin_send(&soap);
for (int i = 0; i < POOLSIZE; i++)
if (ref[i])
{ ref[i]->soap_serialize(&soap);
soap_begin_send(&soap);
ref[i]->soap_put(&soap, "item", NULL);
soap_end_send(&soap);
}
if (file)
close(soap.sendfd);
soap_end(&soap);
soap_done(&soap);
return 0;
}
// load object pool from file (or stdin)
int Factory::load(const char *file)
{ struct soap soap;
t__root *r;
soap_init(&soap);
if (file)
soap.recvfd = open(file, O_RDONLY);
if (soap.recvfd < 0)
return -1;
soap_begin_recv(&soap);
for (int i = 0; i < POOLSIZE; i++)
{ if (ref[i])
{ delete ref[i];
ref[i] = NULL;
}
}
for (;;)
{ r = soap_in_t__root(&soap, "item", NULL, NULL); // use the 'in' routine ('get' will also attempt to parse the remaining XML)
if (r)
ref[r->handle % POOLSIZE] = r;
else
break;
}
if (file)
close(soap.recvfd);
if (soap.error != SOAP_OK && soap.error != SOAP_EOF)
{ soap_print_fault(&soap, stderr);
soap_print_fault_location(&soap, stderr);
}
soap_free(&soap); // do not call soap_end: this would remove all deserialized data
soap_done(&soap);
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//
// Main server program
//
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{ int m, s;
struct soap soap;
Factory factory; // create factory and simple ORB
soap_init(&soap);
soap.user = (void*)&factory; // associate factory with run-time
soap.accept_timeout = 1; // check every second, if not too busy for purging objects
if (argc < 2)
{ factory.load("factory.dat"); // if CGI is used, load the entire pool (not very efficient and there may be a competition for access to this file! This is just to demonstrate load/save of the entire pool)
factory.purge(&soap);
soap_serve(&soap);
factory.save("factory.dat"); // ... and save afterwards
}
else
{ m = soap_bind(&soap, NULL, atoi(argv[1]), 100); // use command line argument as port number
if (m < 0)
{ soap_print_fault(&soap, stderr);
exit(1);
}
fprintf(stderr, "Socket connection successful %d\n", m);
for (int i = 1; ; i++)
{ s = soap_accept(&soap);
if (s < 0)
{ if (soap.errnum)
soap_print_fault(&soap, stderr);
else // errnum is 0, which means a timeout has occurred
{ factory.purge(&soap); // purge objects whose lease has ran out
continue;
}
exit(1);
}
fprintf(stderr, "%d: accepted %d IP=%d.%d.%d.%d ... ", i, s, (int)(soap.ip>>24)&0xFF, (int)(soap.ip>>16)&0xFF, (int)(soap.ip>>8)&0xFF, (int)soap.ip&0xFF);
soap_serve(&soap);
fprintf(stderr, "served\n");
soap_end(&soap); // clean up: this will remove deserialized data
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//
// Remote factory method implementations
//
////////////////////////////////////////////////////////////////////////////////
int ns__create(struct soap *soap, enum t__object object, char *name, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (!soap->header)
soap->header = (struct SOAP_ENV__Header*)soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
if (soap->header)
{ soap->header->h__handle = factory->create(soap, object, name);
if (soap->header->h__handle)
status = FACTORY_OK;
else
status = FACTORY_INVALID;
}
else
status = FACTORY_RETRY;
return SOAP_OK;
}
int ns__lookup(struct soap *soap, enum t__object object, char *name, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (!soap->header)
soap->header = (struct SOAP_ENV__Header*)soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
if (soap->header)
{ soap->header->h__handle = factory->lookup(object, name);
if (soap->header->h__handle)
status = FACTORY_OK;
else
status = FACTORY_NOTFOUND;
}
else
status = FACTORY_RETRY;
return SOAP_OK;
}
int ns__rename(struct soap *soap, char *name, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (soap->header)
{ soap->header->h__handle = factory->rename(soap->header->h__handle, name);
if (soap->header->h__handle)
status = FACTORY_OK;
else
status = FACTORY_INVALID;
}
else
status = FACTORY_INVALID;
return SOAP_OK;
}
int ns__release(struct soap *soap, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (soap->header && soap->header->h__handle)
{ factory->release(soap->header->h__handle);
status = FACTORY_OK;
}
else
status = FACTORY_INVALID;
return SOAP_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// Remote adder method implementations
//
////////////////////////////////////////////////////////////////////////////////
int ns__set(struct soap *soap, double val, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (soap->header)
{ t__adder *adder = factory->adder(soap->header->h__handle);
if (adder)
{ adder->set(val);
status = FACTORY_OK;
}
else
status = FACTORY_INVALID;
}
else
status = FACTORY_INVALID;
return SOAP_OK;
}
int ns__get(struct soap *soap, double &val)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
val = DBL_NAN;
if (soap->header)
{ t__adder *adder = factory->adder(soap->header->h__handle);
if (adder)
val = adder->get();
}
return SOAP_OK;
}
int ns__add(struct soap *soap, double val, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (soap->header)
{ t__adder *adder = factory->adder(soap->header->h__handle);
if (adder)
{ adder->add(val);
status = FACTORY_OK;
}
else
status = FACTORY_INVALID;
}
else
status = FACTORY_INVALID;
return SOAP_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// Remote counter method implementations
//
////////////////////////////////////////////////////////////////////////////////
int ns__inc(struct soap *soap, enum t__status &status)
{ Factory *factory = (Factory*)soap->user; // get factory from gSOAP environment
if (soap->header)
{ t__counter *counter = factory->counter(soap->header->h__handle);
if (counter)
{ counter->inc();
status = FACTORY_OK;
}
else
status = FACTORY_INVALID;
}
else
status = FACTORY_INVALID;
return SOAP_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// Server-side base factory class methods
//
////////////////////////////////////////////////////////////////////////////////
t__root::t__root()
{ }
t__root::~t__root()
{ if (name)
free(name);
}
void t__root::renew()
{ lease = time(NULL) + LEASETERM; // can adopt a leasing policy per class
}
////////////////////////////////////////////////////////////////////////////////
//
// Server-side adder class methods
//
////////////////////////////////////////////////////////////////////////////////
void t__adder::set(double val)
{ this->val = val; // copy data to update state
}
double t__adder::get()
{ return val;
}
void t__adder::add(double val)
{ this->val += val;
}
////////////////////////////////////////////////////////////////////////////////
//
// Server-side counter class methods
//
////////////////////////////////////////////////////////////////////////////////
void t__counter::inc()
{ add(1.0);
}