// $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. Used with permission of // Addison-Wesley. // // Modified for TAO by Mike Moran // // ============================================================================ #include #include #include #include "server.h" #include #include "icp.h" #include "orbsvcs/orbsvcs/CosNamingC.h" const char * Controller_oid = "Controller"; const unsigned int DeviceLocator_impl::MAX_EQ_SIZE = 100; //---------------------------------------------------------------- template 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 &) { throw; } catch (const CORBA::Exception & e) { std::cerr << "Cannot get initial reference for " << id << ": " //<< e << std::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) { std::cerr << "Cannot narrow reference for " << id << ": " //<< e << std::endl; throw 0; } if (CORBA::is_nil (ref.in ())) { std::cerr << "Incorrect type of reference for " << id << std::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 is not needed for TAO. 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) { std::ifstream afile (m_asset_file.in (), std::ios::in|std::ios::out);//, 0666); if (!afile) { std::cerr << "Cannot open " << m_asset_file.in () << std::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. std::ofstream afile (m_asset_file.in ()); if (!afile) { std::cerr << "Cannot open " << m_asset_file.in () << std::endl; assert (0); } AssetMap::iterator i; for (i = m_assets.begin (); i != m_assets.end (); i++) { afile << i->first << std::endl; if (!afile) { std::cerr << "Cannot update " << m_asset_file.in () << std::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].in (); 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 */ ) 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 ( 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. PortableServer::ServantManager_var locator = new DeviceLocator_impl (&ctrl_servant); // 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) { std::cerr << "Uncaught CORBA exception: " //<< e << std::endl; return 1; } catch (...) { assert (0); // Uncaught exception, dump core } return 0; }