diff options
Diffstat (limited to 'TAO/examples/Advanced/ch_18/server.cpp')
-rw-r--r-- | TAO/examples/Advanced/ch_18/server.cpp | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/TAO/examples/Advanced/ch_18/server.cpp b/TAO/examples/Advanced/ch_18/server.cpp new file mode 100644 index 00000000000..8cf4fac044a --- /dev/null +++ b/TAO/examples/Advanced/ch_18/server.cpp @@ -0,0 +1,791 @@ +// $Id$ +// ============================================================================ +// +// = LIBRARY +// TAO/examples/Advanced/ch_18 +// +// = FILENAME +// server.cpp +// +// = AUTHORS +// Source code used in TAO has been modified and adapted from the code +// provided in the book, "Advanced CORBA Programming with C++" by Michi +// Henning and Steve Vinoski. Copyright 1999. Addison-Wesley, Reading, +// MA. +// +// Modified for TAO by Mike Moran <mm4@cs.wustl.edu> +// +// ============================================================================ + + + + +#include "server.h" +#include <algorithm> +#include "icp.h" +#include <orbsvcs/CosNamingC.h> +#include <strstream.h> + +// #include <iostream.h> +// #include <fstream.h> + +const char * Controller_oid = "Controller"; + +//---------------------------------------------------------------- + +template<class T> +typename T::_ptr_type +resolve_init(CORBA::ORB_ptr orb, const char * id) +{ + CORBA::Object_var obj; + try { + obj = orb->resolve_initial_references(id); + } + catch (const CORBA::ORB::InvalidName & e) { + throw; + } + catch (const CORBA::Exception & e) { + cerr << "Cannot get initial reference for " + << id << ": " << e << endl; + throw 0; + } + assert(!CORBA::is_nil(obj.in())); + + typename T::_var_type ref; + try { + ref = T::_narrow(obj.in()); + } + catch (const CORBA::Exception & e) { + cerr << "Cannot narrow reference for " + << id << ": " << e << endl; + throw 0; + } + if (CORBA::is_nil(ref.in())) { + cerr << "Incorrect type of reference for " + << id << endl; + throw 0; + } + return ref._retn(); +} + +//---------------------------------------------------------------- + +// Generic ostream inserter for exceptions. Inserts the exception +// name, if available, and the repository ID otherwise. + +//#if 0 // This inserter may or may not be needed for your ORB. + +static ostream & +operator<<(ostream & os, const CORBA::Exception & e) +{ + CORBA::Any tmp; + tmp <<= e; + + CORBA::TypeCode_var tc = tmp.type(); + const char * p = tc->name(); + if (*p != '\0') + os << p; + else + os << tc->id(); + return os; +} + +//#endif + +//---------------------------------------------------------------- + +// Helper function to create object references. + +static CCS::Thermometer_ptr +make_dref(PortableServer::POA_ptr poa, CCS::AssetType anum) +{ + // Convert asset number to OID. + ostrstream ostr; + ostr << anum << ends; + char * anum_str = ostr.str(); + PortableServer::ObjectId_var oid + = PortableServer::string_to_ObjectId(anum_str); + delete[] anum_str; + + // Look at the model via the network to determine + // the repository ID. + char buf[32]; + assert(ICP_get(anum, "model", buf, sizeof(buf)) == 0); + const char * rep_id = strcmp(buf, "Sens-A-Temp") == 0 + ? "IDL:acme.com/CCS/Thermometer:1.0" + : "IDL:acme.com/CCS/Thermostat:1.0"; + + // Make a new reference. + CORBA::Object_var obj + = poa->create_reference_with_id(oid.in(), rep_id); + return CCS::Thermometer::_narrow(obj.in()); +} + +//---------------------------------------------------------------- + +Controller_impl * Thermometer_impl::m_ctrl; // static member + +// Helper function to read the model string from a device. + +CCS::ModelType +Thermometer_impl:: +get_model() +{ + char buf[32]; + assert(ICP_get(m_anum, "model", buf, sizeof(buf)) == 0); + return CORBA::string_dup(buf); +} + +// Helper function to read the temperature from a device. + +CCS::TempType +Thermometer_impl:: +get_temp() +{ + short temp; + assert(ICP_get(m_anum, "temperature", &temp, sizeof(temp)) == 0); + return temp; +} + +// Helper function to read the location from a device. + +CCS::LocType +Thermometer_impl:: +get_loc() +{ + char buf[32]; + assert(ICP_get(m_anum, "location", buf, sizeof(buf)) == 0); + return CORBA::string_dup(buf); +} + +// Helper function to set the location of a device. + +void +Thermometer_impl:: +set_loc(const char * loc) +{ + assert(ICP_set(m_anum, "location", loc) == 0); +} + +// Constructor. + +Thermometer_impl:: +Thermometer_impl(CCS::AssetType anum) : m_anum(anum) +{ + m_ctrl->add_impl(anum, this); // Add self to controller's set +} + +// Destructor. + +Thermometer_impl:: +~Thermometer_impl() +{ + if (m_ctrl->exists(m_anum)) + m_ctrl->add_impl(m_anum, 0); // Clear servant pointer +} + +// IDL model attribute. + +CCS::ModelType +Thermometer_impl:: +model() throw(CORBA::SystemException) +{ + return get_model(); +} + +// IDL asset_num attribute. + +CCS::AssetType +Thermometer_impl:: +asset_num() throw(CORBA::SystemException) +{ + return m_anum; +} + +// IDL temperature attribute. + +CCS::TempType +Thermometer_impl:: +temperature() throw(CORBA::SystemException) +{ + return get_temp(); +} + +// IDL location attribute accessor. + +CCS::LocType +Thermometer_impl:: +location() throw(CORBA::SystemException) +{ + return get_loc(); +} + +// IDL remove operation. + +void +Thermometer_impl:: +remove() throw(CORBA::SystemException) +{ + m_ctrl->remove_impl(m_anum); + assert(ICP_offline(m_anum) == 0); + //delete this; +} + +// IDL location attribute modifier. + +void +Thermometer_impl:: +location(const char *loc) throw(CORBA::SystemException) +{ + set_loc(loc); +} + +//---------------------------------------------------------------- + +// Helper function to get a thermostat's nominal temperature. + +CCS::TempType +Thermostat_impl:: +get_nominal_temp() +{ + short temp; + assert(ICP_get(m_anum, "nominal_temp", &temp, sizeof(temp)) == 0); + return temp; +} + +// Helper function to set a thermostat's nominal temperature. + +CCS::TempType +Thermostat_impl:: +set_nominal_temp(CCS::TempType new_temp) +throw(CCS::Thermostat::BadTemp) +{ + short old_temp; + + // We need to return the previous nominal temperature, + // so we first read the current nominal temperature before + // changing it. + assert( + ICP_get( + m_anum, "nominal_temp", &old_temp, sizeof(old_temp) + ) == 0 + ); + + // Now set the nominal temperature to the new value. + if (ICP_set(m_anum, "nominal_temp", &new_temp) != 0) { + + // If ICP_set() failed, read this thermostat's minimum + // and maximum so we can initialize the BadTemp exception. + CCS::Thermostat::BtData btd; + ICP_get( + m_anum, "MIN_TEMP", + &btd.min_permitted, sizeof(btd.min_permitted) + ); + ICP_get( + m_anum, "MAX_TEMP", + &btd.max_permitted, sizeof(btd.max_permitted) + ); + btd.requested = new_temp; + btd.error_msg = CORBA::string_dup( + new_temp > btd.max_permitted ? "Too hot" : "Too cold" + ); + throw CCS::Thermostat::BadTemp(btd); + } + return old_temp; +} + +// Constructor. + +Thermostat_impl:: +Thermostat_impl(CCS::AssetType anum) : Thermometer_impl(anum) +{ + // Intentionally empty. +} + +// Destructor. + +Thermostat_impl:: +~Thermostat_impl() +{ + // Intentionally empty. +} + +// IDL get_nominal operation. + +CCS::TempType +Thermostat_impl:: +get_nominal() throw(CORBA::SystemException) +{ + return get_nominal_temp(); +} + +// IDL set_nominal operation. + +CCS::TempType +Thermostat_impl:: +set_nominal(CCS::TempType new_temp) +throw(CORBA::SystemException, CCS::Thermostat::BadTemp) +{ + return set_nominal_temp(new_temp); +} + +//---------------------------------------------------------------- + +// Helper function to add an entry to the asset map. + +void +Controller_impl:: +add_impl(CCS::AssetType anum, Thermometer_impl * tip) +{ + m_assets[anum] = tip; +} + +// Helper function to remove an entry from the asset map. + +void +Controller_impl:: +remove_impl(CCS::AssetType anum) +{ + m_assets.erase(anum); +} + +// Helper function to locate a servant in the asset map. + +bool +Controller_impl:: +exists(CCS::AssetType anum) +{ + return m_assets.find(anum) != m_assets.end(); +} + +// Constructor + +Controller_impl:: +Controller_impl( + PortableServer::POA_ptr poa, + const char * asset_file +) throw(int) : m_poa(PortableServer::POA::_duplicate(poa)), + m_asset_file(asset_file) +{ + fstream afile(m_asset_file.in(), ios::in|ios::out, 0666); + if (!afile) { + cerr << "Cannot open " << m_asset_file.in() << endl; + throw 0; + } + CCS::AssetType anum; + while (afile >> anum) + m_assets[anum] = 0; + //afile.close(); + //if (!afile) { + // cerr << "Cannot close " << m_asset_file << endl; + // throw 0; + //} +} + +// Destructor + +Controller_impl:: +~Controller_impl() +{ + // Write out the current set of asset numbers + // and clean up all servant instances. + ofstream afile(m_asset_file.in()); + if (!afile) { + cerr << "Cannot open " << m_asset_file.in() << endl; + assert(0); + } + AssetMap::iterator i; + for (i = m_assets.begin(); i != m_assets.end(); i++) { + afile << i->first << endl; + if (!afile) { + cerr << "Cannot update " << m_asset_file.in() << endl; + assert(0); + } + delete i->second; + } + //afile.close(); + //if (!afile) { + // cerr << "Cannot close " << m_asset_file << endl; + // assert(0); + //} +} + +CCS::Thermometer_ptr +Controller_impl:: +create_thermometer(CCS::AssetType anum, const char * loc) +throw(CORBA::SystemException, CCS::Controller::DuplicateAsset) +{ + if (anum % 2 == 0) + throw CORBA::BAD_PARAM(); // Thermometers have odd numbers + if (exists(anum)) + throw CCS::Controller::DuplicateAsset(); + + assert(ICP_online(anum) == 0); + assert(ICP_set(anum, "location", loc) == 0); + add_impl(anum, 0); + return make_dref(m_poa.in(), anum); +} + +CCS::Thermostat_ptr +Controller_impl:: +create_thermostat( + CCS::AssetType anum, + const char* loc, + CCS::TempType temp) +throw( + CORBA::SystemException, + CCS::Controller::DuplicateAsset, + CCS::Thermostat::BadTemp) +{ + if (anum % 2 != 0) + throw CORBA::BAD_PARAM(); // Thermostats have even numbers + if (exists(anum)) + throw CCS::Controller::DuplicateAsset(); + + assert(ICP_online(anum) == 0); + assert(ICP_set(anum, "location", loc) == 0); + // Set the nominal temperature. + if (ICP_set(anum, "nominal_temp", &temp) != 0) { + + // If ICP_set() failed, read this thermostat's minimum + // and maximum so we can initialize the BadTemp exception. + CCS::Thermostat::BtData btd; + ICP_get( + anum, "MIN_TEMP", + &btd.min_permitted, sizeof(btd.min_permitted) + ); + ICP_get( + anum, "MAX_TEMP", + &btd.max_permitted, sizeof(btd.max_permitted) + ); + btd.requested = temp; + btd.error_msg = CORBA::string_dup( + temp > btd.max_permitted ? "Too hot" : "Too cold" + ); + ICP_offline(anum); + throw CCS::Thermostat::BadTemp(btd); + } + + add_impl(anum, 0); + CORBA::Object_var obj = make_dref(m_poa.in(), anum); + return CCS::Thermostat::_narrow(obj.in()); +} + +// IDL list operation. + +CCS::Controller::ThermometerSeq * +Controller_impl:: +list() throw(CORBA::SystemException) +{ + // Create a new thermometer sequence. Because we know + // the number of elements we will put onto the sequence, + // we use the maximum constructor. + CCS::Controller::ThermometerSeq_var listv + = new CCS::Controller::ThermometerSeq(m_assets.size()); + listv->length(m_assets.size()); + + // Loop over the m_assets set and create a + // reference for each device. + CORBA::ULong count = 0; + AssetMap::iterator i; + for (i = m_assets.begin(); i != m_assets.end(); i++) + listv[count++] = make_dref(m_poa.in(), i->first); + return listv._retn(); +} + +// IDL change operation. + +void +Controller_impl:: +change( + const CCS::Controller::ThermostatSeq & tlist, + CORBA::Short delta +) throw(CORBA::SystemException, CCS::Controller::EChange) +{ + CCS::Controller::EChange ec; // Just in case we need it + + // We cannot add a delta value to a thermostat's temperature + // directly, so for each thermostat, we read the nominal + // temperature, add the delta value to it, and write + // it back again. + for (CORBA::ULong i = 0; i < tlist.length(); i++) { + if (CORBA::is_nil(tlist[i])) + continue; // Skip nil references + + // Read nominal temp and update it. + CCS::TempType tnom = tlist[i]->get_nominal(); + tnom += delta; + try { + tlist[i]->set_nominal(tnom); + } + catch (const CCS::Thermostat::BadTemp &bt) { + // If the update failed because the temperature + // is out of range, we add the thermostat's info + // to the errors sequence. + CORBA::ULong len = ec.errors.length(); + ec.errors.length(len + 1); + ec.errors[len].tmstat_ref = tlist[i]; + ec.errors[len].info = bt.details; + } + } + + // If we encountered errors in the above loop, + // we will have added elements to the errors sequence. + if (ec.errors.length() != 0) + throw ec; +} + +// IDL find operation + +void +Controller_impl:: +find(CCS::Controller::SearchSeq & slist) +throw(CORBA::SystemException) +{ + // Loop over input list and lookup each device. + CORBA::ULong listlen = slist.length(); + for (CORBA::ULong i = 0; i < listlen; i++) { + + AssetMap::iterator where; // Iterator for asset set + int num_found = 0; // Num matched per iteration + + // Assume we will not find a matching device. + slist[i].device = CCS::Thermometer::_nil(); + + // Work out whether we are searching by asset, + // model, or location. + CCS::Controller::SearchCriterion sc = slist[i].key._d(); + if (sc == CCS::Controller::ASSET) { + // Search for matching asset number. + where = m_assets.find(slist[i].key.asset_num()); + if (where != m_assets.end()) + slist[i].device = make_dref(m_poa.in(), where->first); + } else { + // Search for model or location string. + const char *search_str; + if (sc == CCS::Controller::LOCATION) + search_str = slist[i].key.loc(); + else + search_str = slist[i].key.model_desc(); + + // Find first matching device (if any). + where = find_if( + m_assets.begin(), m_assets.end(), + StrFinder(sc, search_str) + ); + + // While there are matches... + while (where != m_assets.end()) { + if (num_found == 0) { + // First match overwrites reference + // in search record. + slist[i].device = make_dref(m_poa.in(), where->first); + } else { + // Further matches each append a new + // element to the search sequence. + CORBA::ULong len = slist.length(); + slist.length(len + 1); + slist[len].key = slist[i].key; + slist[len].device = make_dref(m_poa.in(), where->first); + } + num_found++; + + // Find next matching device with this key. + where = find_if( + ++where, m_assets.end(), + StrFinder(sc, search_str) + ); + } + } + } +} + +//---------------------------------------------------------------- + +DeviceLocator_impl:: +DeviceLocator_impl(Controller_impl * ctrl) : m_ctrl(ctrl) +{ + // Intentionally empty +} + +PortableServer::Servant +DeviceLocator_impl:: +preinvoke( + const PortableServer::ObjectId & oid, + PortableServer::POA_ptr /* poa */, + const char * operation, + void * & /* cookie */, + CORBA_Environment & +) throw(CORBA::SystemException, PortableServer::ForwardRequest) +{ + // Convert object id into asset number. + CORBA::String_var oid_string; + try { + oid_string = PortableServer::ObjectId_to_string(oid); + } catch (const CORBA::BAD_PARAM &) { + throw CORBA::OBJECT_NOT_EXIST(); + } + + if (strcmp(oid_string.in(), Controller_oid) == 0) + return m_ctrl; + + istrstream istr(oid_string.in()); + CCS::AssetType anum; + istr >> anum; + if (istr.fail()) + throw CORBA::OBJECT_NOT_EXIST(); + + // Check whether the device is known. + if (!m_ctrl->exists(anum)) + throw CORBA::OBJECT_NOT_EXIST(); + + // Look at the object map to find out whether + // we have a servant in memory. + Thermometer_impl * servant; + ActiveObjectMap::iterator servant_pos = m_aom.find(anum); + if (servant_pos == m_aom.end()) { + // No servant in memory. If evictor queue is full, + // evict servant at head of queue. + if (m_eq.size() == MAX_EQ_SIZE) { + servant = m_eq.back(); + m_aom.erase(servant->m_anum); + m_eq.pop_back(); + delete servant; + } + // Instantiate correct type of servant. + char buf[32]; + assert(ICP_get(anum, "model", buf, sizeof(buf)) == 0); + if (strcmp(buf, "Sens-A-Temp") == 0) + servant = new Thermometer_impl(anum); + else + servant = new Thermostat_impl(anum); + } else { + // Servant already in memory. + servant = *(servant_pos->second); // Remember servant + m_eq.erase(servant_pos->second); // Remove from queue + + // If operation is "remove", also remove entry from + // active object map -- the object is about to be deleted. + if (strcmp(operation, "remove") == 0) + m_aom.erase(servant_pos); + } + + // We found a servant, or just instantiated it. + // If the operation is not a remove, move + // the servant to the tail of the evictor queue + // and update its queue position in the map. + if (strcmp(operation, "remove") != 0) { + m_eq.push_front(servant); + m_aom[anum] = m_eq.begin(); + } + + return servant; +} + +//---------------------------------------------------------------- + +int +main(int argc, char * argv[]) +{ + CORBA::ORB_var orb; + + try { + // Initialize orb + orb = CORBA::ORB_init(argc, argv); + + // Get reference to Root POA. + CORBA::Object_var obj + = orb->resolve_initial_references("RootPOA"); + PortableServer::POA_var poa + = PortableServer::POA::_narrow(obj.in()); + + // Get POA manager + PortableServer::POAManager_var poa_mgr = poa->the_POAManager(); + + // Create a policy list. We use persistent objects with + // user-assigned IDs, and explicit activation. + CORBA::PolicyList policy_list; + policy_list.length(6); + policy_list[0] = poa->create_lifespan_policy( + PortableServer::TRANSIENT // REVISIT + ); + policy_list[1] = poa->create_id_assignment_policy( + PortableServer::USER_ID + ); + policy_list[2] = poa->create_implicit_activation_policy( + PortableServer::NO_IMPLICIT_ACTIVATION + ); + policy_list[3] = poa->create_request_processing_policy( + PortableServer::USE_SERVANT_MANAGER + ); + policy_list[4] = poa->create_servant_retention_policy( + PortableServer::NON_RETAIN + ); + policy_list[5] = poa->create_thread_policy( + PortableServer::SINGLE_THREAD_MODEL + ); + + // Create a POA for all CCS elements. + PortableServer::POA_var ccs_poa + = poa->create_POA("CCS_POA", poa_mgr.in(), policy_list); + + // Create a controller and set static m_ctrl member + // for thermostats and thermometers. + Controller_impl ctrl_servant(ccs_poa.in(), "/tmp/CCS_assets"); + Thermometer_impl::m_ctrl = &ctrl_servant; + + // Create a reference for the controller and + // create the corresponding CORBA object. + PortableServer::ObjectId_var oid + = PortableServer::string_to_ObjectId(Controller_oid); + CORBA::Object_var ctrl + = ccs_poa->create_reference_with_id( + oid.in(), "IDL:acme.com/CCS/Controller:1.0" + ); + + // Get reference to initial naming context. + CosNaming::NamingContext_var inc + = resolve_init<CosNaming::NamingContext>( + orb.in(), "NameService" + ); + + // Attempt to create CCS context. + CosNaming::Name n; + n.length(1); + n[0].id = CORBA::string_dup("CCS"); + try { + CosNaming::NamingContext_var nc + = inc->bind_new_context(n); + } catch (const CosNaming::NamingContext::AlreadyBound &) { + // Fine, CCS context already exists. + } + + // Force binding of controller reference to make + // sure it is always up-to-date. + n.length(2); + n[1].id = CORBA::string_dup("Controller"); + inc->rebind(n, ctrl.in()); + + // Instantiate the servant locator for devices. + DeviceLocator_impl my_locator(&ctrl_servant); + PortableServer::ServantManager_var locator + = my_locator._this(); + + // Set servant locator. + ccs_poa->set_servant_manager(locator.in()); + + // Activate the POA manager. + poa_mgr->activate(); + + // Accept requests + orb->run(); + } + catch (const CORBA::Exception & e) { + cerr << "Uncaught CORBA exception: " << e << endl; + return 1; + } + catch (...) { + assert(0); // Uncaught exception, dump core + } + return 0; +} |