// Copyright (c) 2005-2007 Andre Merzky (andre@merzky.net) // Copyright (c) 2005-2007 Hartmut Kaiser (hartmut.kaiser@gmail.com) // Copyright (c) 2005 Michel Zandstra (michelzandstra@gmail.com) // // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; /////////////////////////////////////////////////////////////////////////////// namespace saga { namespace impl { ///////////////////////////////////////////////////////////////////////////// inline void handle_ini_file (saga::ini::ini & ini, std::string const & loc) { try { ini.read (loc); } catch ( saga::exception const & /*e*/ ) { ; } } inline void handle_ini_file_env (saga::ini::ini & ini, char const * env_var, char const * file_suffix = NULL) { const char *env = getenv (env_var); if ( NULL != env ) { namespace fs = boost::filesystem; fs::path inipath (env, fs::native); if ( NULL != file_suffix ) { inipath /= fs::path (file_suffix, fs::native); } SAGA_VERBOSE(SAGA_VERBOSE_LEVEL_INFO) { std::cout << "handle_ini_file_env (" << env << ", " << file_suffix << ") = " << inipath.string () << "\n"; } handle_ini_file (ini, inipath.string ()); } } ///////////////////////////////////////////////////////////////////////////// engine::engine () { init (); } engine::~engine (void) { // make sure the adaptor list get's cleared last adaptor_infos.clear(); adaptors.clear(); modules.clear(); } bool engine::load_adaptor (saga::impl::session *current_session, boost::filesystem::path const& lib) { namespace fs = boost::filesystem; if ( fs::extension (lib) == SAGA_SHARED_LIB_EXTENSION ) { try { SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_INFO) { std::cerr << "loading " << lib.string () << std::endl; } /* get the handle of the library */ boost::plugin::dll d (lib.string ()); boost::plugin::plugin_factory pf (d); /* invoke the loader function of the library, so that the * maker function and * description table get registered * with the registry */ // create the adaptor registration object TR1::shared_ptr adap (pf.create ("adaptor")); // call the register function, and store the adaptors it returns adaptor_selector::adaptor_info_list_type new_infos = adap->adaptor_register (current_session); // store adaptor description info std::copy (new_infos.begin(), new_infos.end(), std::back_inserter(adaptor_infos)); // store adaptor instance if (!new_infos.empty()) { // some adaptors do not register any functions (yes, this happens!) saga::uuid id (new_infos.front().get_adaptor_id()); if (adaptors.find(id) == adaptors.end()) { adaptors.insert (std::make_pair (id, adap)); } } // hold on on this shared library modules.push_back (d); SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_INFO) { std::cerr << "loaded adaptor: " << adap->get_name() << " from file: " << lib.string() << std::endl; } } catch ( std::logic_error const & e ) { SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_WARNING) { /* report error, and skip the library */ std::cerr << "Could not load adaptor: " << e.what () << std::endl; } return false; } return true; // adaptor got loaded } return false; } /** * Reads all available ini files, in following order: * /etc/saga.ini * $SAGA_LOCATION/share/SAGA/saga.ini * $HOME/.saga.ini * $PWD/.saga.ini * $SAGA_INI */ void engine::init (void) { // look in the current directory first namespace fs = boost::filesystem; std::string cwd = fs::current_path().string() + "/.saga.ini"; handle_ini_file (ini, cwd); // afterwards in the standard locations #if !defined(BOOST_WINDOWS) // /etc/saga.ini doesn't make sense for Windows handle_ini_file (ini, std::string ("/etc/saga.ini")); #endif handle_ini_file_env (ini, "SAGA_LOCATION", "/share/saga/saga.ini"); handle_ini_file_env (ini, "HOME", "/.saga.ini"); handle_ini_file_env (ini, "PWD", "/.saga.ini"); handle_ini_file_env (ini, "SAGA_INI"); } /** * Reads all adaptor info files (*.ini) ini path, and tries * to open the shared libraries specified in there. It uses * boost::plugin to handle the shared libraries. If it can * open a library, it invokes the loader function of this * library to register it's maker function and description * table with the registry. * * @throws saga::exception is thrown if file "adaptorlist" * could not be opened, or does not exist */ void engine::load (saga::impl::session *current_session) { std::string ini_path; std::vector ini_paths; try { ini_path = ini.get_entry ("info.ini_path"); } catch ( saga::exception const & ) { std::cerr << "Cannot obtain ini path - trying default /usr/share/saga/\n"; ini_path = "/usr/share/saga/"; } SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_BLURB) { cout << "merging " << ini_path << endl; } // split of the separate paths from the given path list typedef boost::tokenizer > tokenizer; boost::char_separator sep (SAGA_INI_PATH_DELIMITER); tokenizer tok (ini_path, sep); tokenizer::iterator tok_end = tok.end (); for ( tokenizer::iterator tit = tok.begin (); tit != tok_end; ++tit ) { ini_paths.push_back (*tit); } // have all path elements, now find ini files in there... std::vector ::iterator ini_end = ini_paths.end(); for (std::vector ::iterator it = ini_paths.begin (); it != ini_end; ++it) { namespace fs = boost::filesystem; try { fs::directory_iterator nodir; for ( fs::directory_iterator dir (fs::path (*it, fs::native)); dir != nodir; ++dir ) { if ( fs::extension (*dir) == ".ini" ) { // read and merge the ini file into the main ini hierarchy try { SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_BLURB) { cout << "merging " << (*dir).string () << endl; } ini.merge ((*dir).string ()); } catch ( saga::exception const & e ) { std::cerr << "Cannot read ini file " << (*dir).string () << " - skipping (" << e.what () << ")" << std::endl; } } } } catch ( fs::filesystem_error const & e ) { std::cerr << e.what () << std::endl; } } if ( ! ini.has_section_full ("saga.adaptors") ) { SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_ERROR) { std::cerr << "Did not find any [saga.adaptors] section in any of the " << "SAGA configuration files." << std::endl; } return; } // now load all known adaptors saga::ini::section a_sec = ini.get_section ("saga.adaptors"); saga::ini::section_map s = a_sec.get_sections (); saga::ini::section_map::iterator end = s.end (); SAGA_VERBOSE(SAGA_VERBOSE_LEVEL_BLURB) { ini.dump (); } for ( saga::ini::section_map::iterator i = s.begin (); i != end; ++i ) { namespace fs = boost::filesystem; fs::path a_path; std::string a_name = i->second.get_name (); try { a_path = fs::path(i->second.get_entry ("path"), fs::native); if ( ! load_adaptor (current_session, a_path) ) { // build path to adaptor to load #if !defined(BOOST_WINDOWS) std::string libname ("libsaga_adaptor_" + a_name + SAGA_SHARED_LIB_EXTENSION); #else #if defined(_DEBUG) std::string libname (a_name + "d" + SAGA_SHARED_LIB_EXTENSION); #else std::string libname (a_name + SAGA_SHARED_LIB_EXTENSION); #endif #endif a_path /= fs::path (libname, fs::native); if ( ! load_adaptor (current_session, a_path) ) { continue; // next please :-P } } SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_INFO) { std::string name = i->first; std::cout << "loading ok " << a_path.string () << " - " << name << std::endl; } } catch (saga::exception const& e) { SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_WARNING) { std::string name = i->first; std::cout << "loading failed " << a_path.string () << " - " << name << ": " << e.what() << std::endl; } // FIXME: use default adaptor location } } } ///////////////////////////////////////////////////////////////////////////// // This is a helper struct, which during destruction pushes the // adaptor_info 'info' into the given list 'no_no_list'. The bool // 'update' will be set (from the outside) to false if everything goes // fine. struct update_no_no_list { update_no_no_list (v1_0::cpi_info const & info_, adaptor_selector::adaptor_info_list_type & no_adaptor_infos) : update(true), info(info_), no_no_list(no_adaptor_infos) {} ~update_no_no_list() { if ( update ) no_no_list.push_back (info); } bool update; v1_0::cpi_info const & info; adaptor_selector::adaptor_info_list_type &no_no_list; }; // Creates an adaptor object implementing the interface v1_0::cpi* engine::get_adaptor(std::string const& ops_cpi_name, std::string const& op_name, preference_type const& prefs, adaptor_selector::adaptor_info_list_type& no_adaptor_infos, proxy* proxy_ ) const { // we try to select an adaptor. If we find one, we try to init it. If // that fails, we add it to the no_adaptor list, and select anew (a // different one) etc. If we don't find any new adaptor, we finish. try { // select_adaptor throws on error cpi_info const& info = selector.select_adaptor (adaptor_infos, no_adaptor_infos, proxy_->get_cpi_name(), ops_cpi_name, op_name, prefs); BOOST_ASSERT (info.get_cpi_name () != "NONE"); // update no_no_list on exit, if appropriate (on exception) update_no_no_list on_exit (info, no_adaptor_infos); cpi::maker_type maker = info.get_maker (); if (NULL == maker) { // could not get a maker: ignore this adaptor, select another SAGA_THROW("saga::engine::get_adaptor: could not get a " "factory function for CPI: " + ops_cpi_name , saga::Unexpected); } else { // find corresponding adaptor instance adaptor_instance_map_type::const_iterator it = adaptors.find (info.get_adaptor_id ()); if (it == adaptors.end()) { SAGA_THROW("saga::engine::get_adaptor: could not (re-)" "find adaptor instance for CPI: " + ops_cpi_name, saga::Unexpected); } // call maker to create adaptor instance, // and return adaptor on success saga::ini::ini glob_ini; if (ini.has_section ("preferences")) { glob_ini = ini.get_section ("preferences"); } saga::ini::ini adap_ini; std::string adaptor_section("saga.adaptors." + (*it).second->get_name()); if (ini.has_section_full(adaptor_section)) { adap_ini = ini.get_section (adaptor_section); } SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_BLURB) { std::cout << "Trying to create CPI: " << ops_cpi_name << " for operation: " << op_name << std:: endl; std::cout << "from proxy for CPI: " << proxy_->get_cpi_name() << " using adaptor: " << (*it).second->get_name() << " (" << info.get_adaptor_id().string() << ")" << std::endl; } v1_0::cpi* result = maker (proxy_, info, glob_ini, adap_ini, (*it).second); if (NULL != result) { on_exit.update = false; // don't push info into no_no_list } return result; } } catch (saga::exception const & e) { if ( e.get_error () != saga::NoAdaptor ) { throw; // some unexpected error happened } } return NULL; } bool engine::test_adaptor( v1_0::cpi const* adaptor, std::string const& cpi_name, std::string const& op_name, preference_type const& prefs) const { bool result = selector.test_adaptor (adaptor_infos, adaptor, cpi_name, op_name, prefs); SAGA_VERBOSE (SAGA_VERBOSE_LEVEL_BLURB) { std::cout << "Testing adaptor " << adaptor->get_adaptor ()->get_name () << " for " << cpi_name << " -> " << op_name << " : " << result << std::endl; } return result; } } } // namespace saga::impl ///////////////////////////////////////////////////////////////////////////////