| /* 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); |
| } |
| |