// Application.cxx #include "dial_app/Application.h" #include #include #include #include #include "dataset_util/ssystem.h" #include "dataset_util/mkdir.h" #include "dataset_util/FileName.h" #include "dataset_util/WorkingDirectory.h" #include "dataset_util/XmlElement.h" #include "dataset_util/DtdRegistry.h" #include "dataset_id/SimpleUniqueIdGenerator.h" #include "dataset_credential/GssCredentialManager.h" using std::string; using std::ostream; using std::cout; using std::cerr; using std::endl; using std::auto_ptr; using dset::GssCredentialManager; using dial::ApplicationId; using dial::Application; typedef XmlElement::ElementList ElementList; typedef Application::Name Name; typedef Application::NameList NameList; typedef Application::Time Time; typedef std::vector DirectoryList; typedef FileDirectory::FileList FileList; //********************************************************************** // Local definitions. //********************************************************************** namespace { const NameList NO_NAMES; // Register the DTD. DtdRegistry::Status stat_Application_dtd = DtdRegistry::register_dtd("dial"); // Include dataset DTD in dial DTD. DtdRegistry::Status ISREG = DtdRegistry::instance("dial").add_dtd("dataset"); //********************************************************************** // Fetch all regular files in a directory tree. int treefiles(string dir, NameList& names, bool allow_unreadable =false, bool allow_irregular =false) { // Loop over files. DirectoryList dirs; dirs.push_back(FileDirectory(dir)); for ( DirectoryList::size_type idir=0; idir!=dirs.size(); ++idir ) { const FileDirectory& dir = dirs[idir]; const FileList& dirfiles = dir.files(); // Loop over files. for ( FileList::const_iterator ifil=dirfiles.begin(); ifil!=dirfiles.end(); ++ifil ) { const FileStatus fstat = *ifil; if ( ! fstat.is_directory() ) { if ( !fstat.is_regular() && !allow_irregular) { cerr << "treefiles: Found irregular file:" << endl; cerr << " " << fstat.name(); return 2; } if ( !fstat.is_readable() && !allow_unreadable ) { cerr << "treefiles: Found unreadable file:" << endl; cerr << " " << fstat.name(); return 3; } string name = fstat.name(); if ( name.substr(0,2) == "./" ) { name = name.substr(2); } names.push_back(name); } } // Append subdirectories to directory list. dirs.insert(dirs.end(), dir.subdirs().begin(), dir.subdirs().end()); } return 0; } //********************************************************************** } // end unnamed namespace //********************************************************************** // Static member functions. //********************************************************************** // DTD. // // Items flagged 1.20-1.30 were added between versions 1.20 and 1.30 // and are temporarily left IMPLIED to allow backward compatibility. const Text& Application::dtd() { static Text txt; if ( txt.size() == 0 ) { txt.append(""); txt.append(""); } return txt; } //********************************************************************** // Return the ID generator. UniqueIdGenerator& Application::generator() { static UniqueIdGenerator* pgen = 0; if ( pgen == 0 ) { pgen = UniqueIdGenerator::find_generator(Application::id_context()); if ( pgen == 0 ) { pgen = new SimpleUniqueIdGenerator; } } assert( pgen != 0 ); return *pgen; } //********************************************************************** // Test instance. const Application& Application::test_instance() { static bool first = true; static Application testapp; if ( first ) { TextList txts; Text tbld("build_task"); tbld.append("# build_task"); tbld.append("date"); txts.push_back(tbld); Text trun("run"); trun.append("# run"); trun.append("date"); txts.push_back(trun); testapp = Application(txts); first = false; } return testapp; } //********************************************************************** // Member functions. //********************************************************************** // Default constructor. Application::Application() : m_create_time(time(0)) { } //********************************************************************** // Constructor from filenames. Application::Application(const NameList& innames, Name dir) : m_status(0), m_id(generator().next()), m_owner(GssCredentialManager::owner()), m_create_time(time(0)) { string prefix = "Application::ctor: "; // Invalidate if ID generation failed. if ( ! m_id.is_valid() ) { m_id = ApplicationId(); cerr << prefix << "Unable to generate ID." << endl; m_status = 10; return; } // Check current directory. WorkingDirectory wd; if ( ! wd.is_valid() ) { m_status = 17; return; } // Change to the specified directory. if ( dir.size() ) { wd.cd(dir); if ( wd.size() != 2 ) { cerr << prefix << "Unable to change to directory " << dir << endl; m_status = 18; return; } } // Loop over filenames and create text objects. for ( NameList::const_iterator inam=innames.begin(); inam!=innames.end(); ++inam ) { Text::Name name = *inam; // Make sure name is not already used. if ( find(files().begin(), files().end(), name) != files().end() ) { cerr << prefix << "File " << name << " is duplicate" << endl; m_status = 11; break; } // Make sure the filename is relative. if ( ! FileName(name).is_relative() ) { cerr << prefix << "File " << name << " is not relative" << endl; m_status = 12; break; } // Create text. Text txt(name); // Make sure text is not empty. if ( txt.size() == 0 ) { cerr << prefix << "File " << name << " is empty" << endl; m_status = 13; break; } assert( txt.name() == name ); m_names.push_back(name); m_texts.push_back(txt); } } //********************************************************************** // Constructor from filename string. Application::Application(string snames, Name dir) : m_status(0), m_id(generator().next()), m_owner(GssCredentialManager::owner()), m_create_time(time(0)) { // Invalidate if ID generation failed. if ( ! m_id.is_valid() ) { m_id = ApplicationId(); m_status = 10; return; } // Check current directory. WorkingDirectory wd; if ( ! wd.is_valid() ) { m_status = 17; return; } // Change to the specified directory. if ( dir.size() ) { wd.cd(dir); if ( wd.size() != 2 ) { m_status = 18; return; } } // Create the list of filenames. NameList names; if ( snames == "*" ) { int nstat = treefiles(".", names); if ( nstat != 0 ) { m_status = 50 + nstat; return; } sort(names.begin(), names.end()); } else { names = Text::split(snames); } // Loop over filenames and create text objects. for ( NameList::const_iterator inam=names.begin(); inam!=names.end(); ++inam ) { Text::Name name = *inam; // Make sure name is not already used. if ( find(files().begin(), files().end(), name) != files().end() ) { m_status = 11; break; } // Make sure the filename is relative. if ( ! FileName(name).is_relative() ) { m_status = 12; break; } // Create text. Text txt(name); // Make sure text is not empty. if ( txt.size() == 0 ) { m_status = 13; break; } assert( txt.name() == name ); m_names.push_back(name); m_texts.push_back(txt); } } //********************************************************************** // Constructor from text objects. Application::Application(const TextList& intexts) : m_status(0), m_id(generator().next()), m_owner(GssCredentialManager::owner()), m_create_time(time(0)) { // Invalidate if ID generation failed. if ( ! m_id.is_valid() ) { m_id = ApplicationId(); m_status = 10; return; } // Loop over text objects. for ( TextList::const_iterator itxt=intexts.begin(); itxt!=intexts.end(); ++itxt ) { const Text& txt = *itxt; Text::Name name = txt.name(); // Make sure name is not blank. if ( name.size() == 0 ) { m_status = 21; break; } // Make sure name is not already used. if ( find(m_names.begin(), m_names.end(), name) != m_names.end() ) { m_status = 22; break; } // Make sure the filename is relative. if ( ! FileName(name).is_relative() ) { m_status = 23; break; } // Make sure text is not empty. if ( txt.size() == 0 ) { m_status = 24; break; } assert( txt.name() == name ); m_names.push_back(name); m_texts.push_back(txt); } } //********************************************************************** // XML constructor. Application::Application(const XmlElement& ele) : m_status(0), m_create_time(0) { // Check the input. if ( ele.name() != xml_name() ) return; // Check XML for ID. if ( ! ele.has_attribute("id") ) return; // Fetch XML for text objexts. const XmlElement::ElementList& xtxts = ele.children(Text::xml_name()); for ( XmlElement::ElementList::const_iterator iele=xtxts.begin(); iele!=xtxts.end(); ++iele ) { Text txt(**iele); m_names.push_back(txt.name()); m_texts.push_back(txt); } // Fill the data. m_id = ApplicationId(ele.attribute("id")); if ( ele.has_attribute("owner") ) { m_owner = ele.attribute("owner"); } else { m_owner = "unknown"; } m_create_time = ele.attribute_as_int("create_time"); // Check this. if ( ! is_valid() ) { m_id = ApplicationId(); } } //********************************************************************** // Validity. bool Application::is_valid() const { m_emsg = ""; return m_status == 0 && id().is_valid(); } //********************************************************************** // Identifier. ApplicationId Application::id() const { m_emsg = ""; return m_id; } //********************************************************************** // Owner. Name Application::owner() const { m_emsg = ""; return m_owner; } //********************************************************************** // Return the create time. Time Application::create_time() const { m_emsg = ""; return m_create_time; } //********************************************************************** // File string. string Application::file_string() const { m_emsg = ""; if ( ! is_valid() ) { m_emsg = "Invalid application"; return ""; } string snames; for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { const Text& txt = *itxt; Name fname = txt.name(); if ( itxt != texts().begin() ) { snames += " "; } snames += fname; } return snames; } //********************************************************************** // File string. bool Application::has(Name name) const { if ( ! is_valid() ) { m_emsg = "Invalid application"; return false; } for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { const Text& txt = *itxt; if ( txt.name() == name ) return true; } return false; } //********************************************************************** // Fetch text object. const Text& Application::text(Name name) const { m_emsg = ""; for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { const Text& txt = *itxt; if ( txt.name() == name ) { m_emsg = ""; return txt; } } const static Text empty; return empty; } //********************************************************************** // Write files. int Application::write_files(Name indir, bool create, bool overwrite) const { m_emsg = ""; m_odir = ""; // Check current directory. WorkingDirectory wd; if ( ! wd.is_valid() ) { m_emsg = "Unable to access current working directory"; return 1; } // Create directory name. string dir = indir; if ( ! dir.size() ) { dir = "app_"; dir += id().to_string(); } // If create is set, create new directory. FileStatus dstat(dir); if ( create ) { if ( dstat.exists() ) { m_emsg = "New directory already exists: " + dir; return 2; } int mstat = mkfulldir(dir); if ( mstat != 0 ) { m_emsg = "Unable to create new directory: " + dir; return 3; } dstat.update(); } // Change directory. int cstat = wd.cd(dir); if ( cstat != 0 ) { m_emsg = "Unable to change to directory: " + dir; return 4; } // Check if any files are existing. for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { const Text& txt = *itxt; Name fname = txt.name(); FileStatus fstat(fname); if ( fstat.exists() ) { if ( overwrite ) { unlink(fname.c_str()); } fstat.update(); if ( fstat.exists() ) { m_emsg = "File already exists: " + fstat.name(); return 5; } } } // Write files. int stat = 0; for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { const Text& txt = *itxt; stat += txt.write(); ssystem("chmod +x " + txt.name()); } if ( stat != 0 ) { m_emsg = "Unable to write file"; return 100 + stat; } m_odir = dir; return 0; } //********************************************************************** // Help. int Application::help() const { Text treadme = text("readme.txt"); if ( treadme.size() == 0 ) return 1; std::cout << treadme << endl; return 0; } //********************************************************************** // Write to XML. const XmlElement* Application::xml(bool global_id) const { m_emsg = ""; // Check validity. if ( ! is_valid() ) return 0; // Check ID. if ( global_id && ! id().is_global() ) { cerr << "Application::xml: ID is not global" << endl; m_emsg = "ID is not global"; return 0; } // Require owner. if ( owner().size() == 0 ) { cerr << "Application::xml: Owner is not defined" << endl; m_emsg = "Owner is not defined"; return 0; } // Require create time unless owner is unknown. if ( create_time() == 0 && owner() != "unknown" ) { cerr << "Application::xml: Create time is not defined" << endl; m_emsg = "Create time is not defined"; return 0; } // Create top XML elment. auto_ptr pele(new XmlElement(xml_name())); // Add ID. pele->add_attribute("id", id().to_string()); // Add owner. pele->add_attribute("owner", owner()); // Add create time. pele->add_attribute_as_int("create_time", create_time()); // Add text files. for ( TextList::const_iterator itxt=texts().begin(); itxt!=texts().end(); ++itxt ) { XmlElement* pele_txt = itxt->xml(); if ( pele_txt == 0 ) { cerr << "Task::xml: Unable to write extract file: " << itxt->name(); m_emsg = "Unable to write extract file: " + itxt->name(); return 0; } pele->add_child(pele_txt); } // Return the XML. return pele.release(); } //********************************************************************** // Display. void Application::display() const { m_emsg = ""; std::cout << *this << std::endl; } //********************************************************************** // Error message. string Application::error_message() const { return m_emsg; } //********************************************************************** // Output directory. string Application::output_directory() const { return m_odir; } //********************************************************************** // Web page. Text Application::web_page(string baseurl, string entry) const { Text wp; wp.append(""); // Display application. if ( entry == "" ) { string header = "Application " + id().to_string(); wp.append("" + header + ""); wp.append("

" + header + "

"); wp.append("Owner: " + owner()); time_t time = create_time(); string stime = asctime(gmtime(&time)); wp.append("
Created: " + stime + " UTC"); wp.append("

Description:"); Text trm = text("readme.txt"); if ( trm.size() ) { wp.append("
");
      wp.append(trm);
      wp.append("
"); } else { wp.append("Application does not have readme.txt"); wp.append("

"); } wp.append("Files:"); string hpref = "
  " + *inam + ""); } // Display a file. } else if ( entry.size()>5 && entry.substr(0,5) == "file=" ) { string file = entry.substr(5); string header = "Application " + id().to_string() + " file " + file; wp.append("" + header + ""); //return text(file); wp.append("

");
    wp.append(text(file));
    wp.append("
"); } else { wp.append("Application::web_page: Invalid entry " + entry); } wp.append(""); return wp; } //********************************************************************** // Free functions. //********************************************************************** // Output stream. ostream& operator<<(ostream& lhs, const Application& rhs) { if ( ! rhs.is_valid() ) { lhs << "invalid application (error " << rhs.status() << ")"; return lhs; } lhs << "Application "; lhs << rhs.id().to_string(); if ( rhs.owner().size() ) { lhs << "\n Owner: " << rhs.owner(); } Time tim = rhs.create_time(); if ( tim > 0 ) { char ctim[256]; const char* fmt = "%Y %B %d %X"; strftime(ctim, 256, fmt, localtime(&tim)); lhs << "\n Create time: " << ctim; } if ( rhs.files().size() == 1 ) { lhs << "\n Holds has 1 file: " << rhs.files().front(); } else { lhs << "\n Holds " << rhs.files().size() << " files"; if ( rhs.files().size() != 0 ) lhs << ":"; for ( NameList::const_iterator inam=rhs.files().begin(); inam!=rhs.files().end(); ++inam ) { lhs << "\n " << *inam; } } return lhs; } //**********************************************************************