// DatasetRepository.cxx #include "dataset_base/DatasetRepository.h" #include #include #include #include #include "dataset_util/getcwd.h" #include "dataset_util/XmlElement.h" #include "dataset_xml/XmlParser.h" #include "dataset_catalog/SqlGenericRepository.h" #include "dataset_catalog/ConnectionResolver.h" #include "dataset_base/DatasetCreator.h" using std::string; using std::cerr; using std::endl; using std::ostream; using std::istringstream; using std::ostringstream; using std::auto_ptr; using dset::SqlTable; using dset::GenericRepository; using dset::SqlGenericRepository; using dset::ConnectionResolver; using dset::CatalogError; using dset::Dataset; using dset::DatasetRepository; typedef DatasetRepository::size_type size_type; //********************************************************************** // Local definitions. //********************************************************************** namespace { //********************************************************************** // Default repository. // First pass, use set_default_instance or define an invalid repository // if this fails. DatasetRepository* defrep(DatasetRepository* pnewrep =0) { static bool first = true; static DatasetRepository* prep = 0; if ( first ) { first = false; DatasetRepository::set_default_instance(""); if ( prep == 0 ) { prep = new DatasetRepository; } } if ( pnewrep != 0 ) { delete prep; prep = pnewrep; } return prep; } //********************************************************************** } // end unnamed namespace //********************************************************************** // Static methods. //********************************************************************** // Return the default instance. DatasetRepository& DatasetRepository::default_instance() { return *defrep(); } //********************************************************************** // Set the default instance. int DatasetRepository::set_default_instance(string name) { DatasetRepository *prep = new DatasetRepository(name); if ( prep->is_valid() ) { defrep(prep); } else { delete prep; return 1; } return 0; } //********************************************************************** // Create default instance. int DatasetRepository::create_default_instance() { // Define connection resolver. ConnectionResolver::set_configuration_file("resolver.dat"); // Error if DatasetRepository is already present. ConnectionResolver res; if ( res.resolve("DatasetRepository", "").size() ) return 2; // Append to resolver. Text txt("resolver.dat"); txt.append("DatasetRepository"); txt.append("SQLRESULT:" + getcwd() + "/dr.dat"); txt.write(); // Construct file description of repository. system("rm -f dr.dat"); Text tdsc("dr.dat"); tdsc.append("SQLRESULT"); tdsc.append("idhi,idlo,sxml"); tdsc.append("0,0,DatasetRepository"); tdsc.write(); return 0; } //********************************************************************** // Private member functions. //********************************************************************** // Copy constructor. DatasetRepository::DatasetRepository(const DatasetRepository&) { assert(false); } //********************************************************************** // Assignment. DatasetRepository& DatasetRepository::operator=(const DatasetRepository&) { assert(false); return *this; } //********************************************************************** // Non-static methods. //********************************************************************** // Default Constructor. DatasetRepository::DatasetRepository() : m_prep(0) { } //********************************************************************** // Constructor from a generic repository. DatasetRepository::DatasetRepository(GenericRepository *prep) : m_prep(prep) { } //********************************************************************** // Constructor from connection string. DatasetRepository::DatasetRepository(string conn) : m_prep(GenericRepository::create("DatasetRepository", conn)), m_manage(true) { } //********************************************************************** // Destructor. // Manage the GenericRepository only if m_manage=true DatasetRepository::~DatasetRepository() { if ( m_manage == true ) delete m_prep; } //********************************************************************** // Validity. bool DatasetRepository::is_valid() const { m_error = CatalogError::no_error(); if ( m_prep == 0 || ! m_prep->is_valid() ) { m_error = CatalogError::generic_access_error(); return false; } return true; } //********************************************************************** // Error code. int DatasetRepository::error() const { return m_error; } //********************************************************************** // Error message. string DatasetRepository::error_message() const { CatalogError err("dataset"); return err.message(m_error); } //********************************************************************** // Size. size_type DatasetRepository::size() const { if ( ! is_valid() ) return false; return m_prep->size(); } //********************************************************************** // Return if the transient store has a dataset. // Otherwise check in persistent store. bool DatasetRepository::has(DatasetId id) const { if ( ! is_valid() ) return false; // Exit if input ID is invalid. if ( ! id.is_valid() ) { m_error = CatalogError::invalid_id_error(); return false; } if ( ! id.is_global() ) { m_error = CatalogError::local_id_error(); return false; } // Transient store. RepMap::const_iterator ids = m_dss.find(id); if ( ids != m_dss.end() ) return true; // Persistent store. return m_prep->has(id.to_string()); } //********************************************************************** // Return the dataset associated with an ID from transient store. // Otherwise, return it from persistent store. const Dataset* DatasetRepository::extract(DatasetId id, bool readnull) { if ( ! is_valid() ) return 0; DatasetIdList dids; dids.insert(id); DatasetList dsts; extract(dids, dsts, readnull); assert( dsts.size() == 1 ); return dsts.front(); } //********************************************************************** // Return the datasets associated with a list of ID's. First try // transient store and then persistent store. int DatasetRepository:: extract(const DatasetIdList& dids, DatasetList& dsts, bool readnull) { if ( ! is_valid() ) return -1; assert( dsts.size() == 0 ); int nbad = 0; // Map of retrieved objects. RepMap dstmap; // List of IDs to retrieve from persistent store. DatasetIdList perids; // Loop over ID's and check the transisent store. for ( DatasetIdList::const_iterator idid=dids.begin(); idid!=dids.end(); ++idid ) { DatasetId id = *idid; dstmap[id] = 0; // Exit if input ID is invalid. if ( ! id.is_valid() ) { m_error = CatalogError::invalid_id_error(); ++nbad; continue; } if ( ! id.is_global() ) { m_error = CatalogError::local_id_error(); ++nbad; continue; } // Locate dataset in transient store. RepMap::iterator idst = m_dss.find(id); // ID found. if ( idst != m_dss.end() ) { assert( idst->first == id ); const Dataset* pds = idst->second; dstmap[id] = pds; // If null and readnull is set, then delete the current entry and // add it to the list to fetch from persistent store. if ( pds == 0 ) { m_error = CatalogError::null_object_error(); if ( readnull ) { perids.insert(id); m_dss.erase(idst); ManageMap::iterator img = m_mgs.find(id); if ( img == m_mgs.end() ) { cerr << "DatasetRepository::extract: " "Ignoring missing entry in manage map" << endl; } else { m_mgs.erase(img); } } else { ++nbad; } } } else { perids.insert(id); } } //assert( dstmap.size() == dids.size() ); // Fetch desired values from persistent store. if ( perids.size() ) { GenericRepository::IdList sids; for ( DatasetIdList::const_iterator idid=perids.begin(); idid!=perids.end(); ++idid ) { sids.push_back(idid->to_string()); } GenericRepository::ElementList eles; int pstat = m_prep->mget(sids, eles); assert( perids.size() >= eles.size() ); nbad += perids.size() - eles.size(); // Loop over retrieved objects. for ( GenericRepository::ElementList::const_iterator iele=eles.begin(); iele!=eles.end(); ++iele ) { string sxml = *iele; if ( sxml == "" ) { m_error = CatalogError::no_such_object_error(); ++nbad; continue; } Text::xml_to_text(sxml); XmlParser par; const XmlElement *pxml = par.parse(sxml); if ( pxml == 0 || ! pxml->is_valid() ) { m_error = CatalogError::object_parse_error(); delete pxml; ++nbad; continue; } const Dataset *pobj = DatasetCreator::create(*pxml); delete pxml; if ( pobj == 0 || ! pobj->is_valid() ) { m_error = CatalogError::object_create_error(); ++nbad; continue; } DatasetId id = pobj->id(); m_dss[id] = pobj; m_mgs[id] = true; dstmap[id] = pobj; } } //assert( dstmap.size() == dids.size() ); // Fill the output list. for ( DatasetIdList::const_iterator idid=dids.begin(); idid!=dids.end(); ++idid ) { RepMap::const_iterator idst = dstmap.find(*idid); const Dataset* pdst = idst->second; dsts.push_back(pdst); } assert( dsts.size() == dids.size() ); } //********************************************************************** // Put a dataset in the repository. DatasetId DatasetRepository::insert(const Dataset* pds, bool manage) { if ( ! is_valid() ) return DatasetId(); // Check input dataset. if ( pds == 0 ) { m_error = CatalogError::null_object_error(); return DatasetId(); } if ( ! pds->is_valid() ) { m_error = CatalogError::invalid_object_error(); return DatasetId(); } if ( ! pds->id().is_global() ) { m_error = CatalogError::local_id_error(); return DatasetId(); } // Make sure the ID is not already used. DatasetId id = pds->id(); if ( has(id) ) { m_error = CatalogError::id_assigned_error(); return DatasetId(); } // Write the dataset to persistent store. const XmlElement* pelebare = pds->xml(); if ( pelebare == 0 ) { return DatasetId(); } auto_ptr pele(pelebare); int stat = m_prep->insert(id.to_string(), pele->to_xml_text("NOCR")); if ( stat ) { m_error = CatalogError::generic_write_error(); return DatasetId(); } m_dss[id] = pds; m_mgs[id] = manage; return id; } //********************************************************************** // Remove a dataset from repository. // Returns 0 on success. int DatasetRepository::remove(DatasetId id) { if ( ! is_valid() ) return 1; if ( ! id.is_valid() ) { m_error = CatalogError::invalid_id_error(); return m_error; } if ( ! id.is_global() ) { m_error = CatalogError::local_id_error(); return m_error; } int stat = m_prep->remove(id.to_string()); if ( stat != 0 ) m_error = CatalogError::generic_write_error(); m_dss.erase(id); m_mgs.erase(id); return stat; } //********************************************************************** // Verify all entries. bool DatasetRepository::verify() const { // Exit if repository is invalid if ( ! is_valid() ) return false; XmlParser par; GenericRepository::IdList idl = m_prep->get_ids(); for( GenericRepository::IdList::const_iterator it = idl.begin(); it != idl.end(); ++it) { string sxml = m_prep->get(*it); Text::xml_to_text(sxml); // Entry with ID "0-0" is used to identify type of repository. // Do not verify that it has a valid dataset. if ( (*it) == "0-0" ) continue; const XmlElement *pxml = par.parse(sxml); if ( pxml == 0 || ! pxml->is_valid() ) { m_error = CatalogError::object_parse_error(); delete pxml; return false; } const Dataset *pdb = DatasetCreator::create(*pxml); delete pxml; if ( pdb == 0 || ! pdb->is_valid() ) { m_error = CatalogError::object_create_error(); return false; } } return true; } //********************************************************************** // Output stream. ostream& DatasetRepository::ostr(ostream& str) const { if ( ! is_valid() ) { str << "Invalid dataset repository"; return str; } size_type count = size(); str << "Dataset repository has " << count << " entr"; if ( count == 1 ) { str << "y"; } else { str << "ies"; } return str; } //********************************************************************** // Web page. // // Supported entries: // maxent=123 // did=123-456 Text DatasetRepository:: web_page(string baseurl, string entry) { // Header. Text wp; wp.append(""); bool footer = true; bool showrep = false; bool showobj = false; DatasetId id; int maxent = 100; // Split entry into first and remaining sub entries. string::size_type ipos = entry.find('?'); string entry0 = entry; string entryrem; if ( ipos != string::npos ) { entry0 = entry.substr(0, ipos); entryrem = entry.substr(ipos+1); } // Parse the first entry to determine the action to take. // No entry ==> dispaly table. if ( entry.size() == 0 ) { showrep = true; // maxent ==> display table. } else if ( entry0.size() > 7 && entry0.substr(0,7) == "maxent=" ) { Text::WordList words = Text::split(entry0, "="); if ( words.size() == 2 ) { string name = words[0]; string value = words[1]; // Display repository with explicit maxent. if ( name == "maxent" ) { istringstream ssvalue(value); ssvalue >> maxent; showrep = true; } } // Display dataset. } else if ( entry0.size() > 4 && entry0.substr(0,4) == "did=" ) { string sid = entry0.substr(4); id = DatasetId(sid); if ( id.is_valid() ) { showobj = true; } } // Show repository. if ( showrep ) { ostringstream sout; sout << *this; wp.append(sout.str()); // Show dataset. } else if ( showobj ) { const Dataset* pobj = extract(id); if ( pobj == 0 ) { wp.append("Unable to find dataset with ID " + id.to_string()); } else { string newbaseurl = baseurl + "?" + entry; wp = pobj->web_page(newbaseurl, baseurl, entryrem); footer = false; } // Error } else { wp.append("Unable resolve dataset entry " + entry); } // Footer. if ( footer ) wp.append(""); return wp; } //********************************************************************** // Free functions. //********************************************************************** // Output stream. ostream& operator<<(ostream& lhs, const DatasetRepository& rhs) { return rhs.ostr(lhs); } //**********************************************************************