#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cctk.h" #include "cctk_Parameters.h" #include "cctk_Version.h" #include "util_Network.h" #include "util_String.h" #include "Publish.h" #include "rdf.hh" #include "senddata.hh" namespace Formaline { using namespace std; // The jobID is shared between this source file and PublishAsRDF.cc // ES 2008-04-29: Check this, I think this is wrong string jobID; // NUM_RDF_ENTRIES must match the size of the // Formaline::rdf_hostname and Formaline::rdf_port parameter arrays int const NUM_RDF_ENTRIES = 5; // Number of space chars for indentation // int const NUM_INDENT_SPACES = 2; #if 0 static list parse (char const * const key, string& node); #endif rdf:: rdf (char const * const id, enum state const st, cGH const * const cctkGH, char const * const n, rdf * const par) : storage (st), groupname (n), parent (par) { if (parent) return; // set the unique ID for this simulation jobID = clean (string (id)); // // document contents // switch (get_state()) { case initial: Initial (); break; case update: case final: Update (cctkGH); break; default: assert (0); // invalid state } } void rdf::Initial (void) { // // This code was copied over from function Formaline_AnnounceInitial() // in announce.cc and modified here to create a valid RDF/XML (rather then // an unstructured plain XML document). // DECLARE_CCTK_PARAMETERS; if (verbose) CCTK_INFO ("Announcing initial RDF metadata information"); // // general metadata with inlined attribute values // char hostbuf[512] = ""; Util_GetHostName (hostbuf, sizeof (hostbuf)); const string host = clean (hostbuf); const cGH* const cctkGH = NULL; const int nprocs = CCTK_nProcs (cctkGH); #if 0 const string user = clean (CCTK_RunUser()); #else const string user = clean (getenv ("USER")); #endif char** argv; CCTK_CommandLine (&argv); const string executable = clean (argv[0]); const string version = clean (CCTK_FullVersion ()); const string compiled_at (clean (CCTK_CompileDateTime ())); char* rundatebuf = Util_CurrentDateTime (); const string started_at (clean (rundatebuf)); free (rundatebuf); char buf[512] = ""; getcwd (buf, sizeof (buf)); const string cwd (clean (buf)); buf[0] = 0; CCTK_RunTitle (sizeof (buf) - 1, buf); const string title(clean(buf)); const string configID(clean(static_cast (UniqueConfigID(0)))); const string buildID(clean(static_cast (UniqueBuildID(0)))); const string bbhID(clean(static_cast (UniqueSimulationID(0)))); const string runID(clean(static_cast (UniqueRunID(0)))); msgbuf << "" << endl << "\t" << nprocs << "" << endl << "\t" << compiled_at << "" << endl << "\t" << started_at << "" << endl << "\t" << started_at << "" << endl; // // metadata as references to other nodes // msgbuf << "\t" << endl << "\t" << endl << "" << endl << endl; #ifdef ALSO_STORE_THORNLIST_AND_PARAMETERS // store thorn list msgbuf << "" << endl; const int numthorns = CCTK_NumCompiledThorns (); for (int thorn = 0; thorn < numthorns; ++ thorn) { const char* const thornname = CCTK_CompiledThorn (thorn); msgbuf << "\t" << endl; } msgbuf << "" << endl << endl; #endif // store parameter file name and contents char parfilebuf[512] = ""; CCTK_ParameterFilename (sizeof (parfilebuf), parfilebuf); const string parfile = clean (parfilebuf); msgbuf << "" << endl << "\t"; ifstream file (parfile.c_str()); char c; ostringstream filebuf; while (filebuf and file.get (c)) filebuf.put (c); file.close(); msgbuf << clean (filebuf.str()); filebuf.clear(); msgbuf << "" << endl << "" << endl << endl; #ifdef ALSO_STORE_THORNLIST_AND_PARAMETERS // store all parameters which have been set in the parfile msgbuf << "" << endl << "" << endl << "" << endl; ostringstream parambuf; parambuf << "" << endl << "" << endl << "" << endl; const bool list_all_parameters = CCTK_Equals (out_save_parameters, "all"); for (int thorn = 0; thorn < numthorns; ++ thorn) { const char* const thornname = CCTK_CompiledThorn (thorn); msgbuf << "" << endl; // skip parameters that belong to inactive thorns const bool is_active = CCTK_IsThornActive (thornname); msgbuf << "\t" << (is_active ? "true" : "false") << "" << endl; // loop over all parameters of this thorn (if it is active) if (is_active) { for (int first = 1; ; first = 0) { char* fullname = NULL; const cParamData* pdata = NULL; // get the first/next parameter const int ierr = CCTK_ParameterWalk (first, thornname, &fullname, &pdata); assert (ierr >= 0); if (ierr > 0) break; // brackets in array parameter names have to be escaped msgbuf << "\tthorn << "/" << cleanURI (pdata->name) << "\"/>" << endl; // get its value const void* const pvalue = CCTK_ParameterGet (pdata->name, pdata->thorn, NULL); assert (pvalue); if (pdata->n_set or list_all_parameters) { const char* paramtype; const char* paramdatatype; ostringstream paramvaluebuf; switch (pdata->type) { case PARAMETER_BOOLEAN: { paramtype = "BooleanParameter"; paramdatatype = "boolean"; const CCTK_INT v = *static_cast (pvalue); paramvaluebuf << (v ? "true" : "false"); } break; case PARAMETER_INT: { paramtype = "IntegerParameter"; paramdatatype = "integer"; const CCTK_INT v = *static_cast (pvalue); paramvaluebuf << v; } break; case PARAMETER_REAL: { paramtype = "RealParameter"; paramdatatype = "double"; CCTK_REAL const v = *static_cast (pvalue); paramvaluebuf << v; } break; case PARAMETER_KEYWORD: { paramtype = "KeywordParameter"; paramdatatype = "string"; const char* const v = *static_cast (pvalue); paramvaluebuf << clean (v); } break; case PARAMETER_STRING: { paramtype = "StringParameter"; paramdatatype = "string"; const char* const v = *static_cast (pvalue); paramvaluebuf << clean (v); } break; default: assert (0); // invalid parameter type } // switch (pdata->type) // brackets in array parameter names have to be escaped parambuf << "thorn << "/" << cleanURI (pdata->name) << "\"" << endl << "\tcctk:name=\"" << fullname <<"\">" << endl << "\t" << paramvaluebuf.str() << "" << endl << "" << endl; } // if (pdata->n_set or list_all_parameters) free (fullname); } // loop over all parameters of this thorn } // if (is_active) msgbuf << "" << endl; } // loop over all thorns msgbuf << endl << parambuf.str(); #endif } static string AppendToTransaction(const string operation, const string& subject, const string& predicate, const string& object) { ostringstream buf; buf << "<" << operation << ">\n" " &simID;" << subject << "\n" " &cctk;" << predicate << "\n" " " << object << "\n" " \n" " &context;\n" " \n" "\n\n"; return buf.str(); } void rdf::Update (cGH const * const cctkGH) { DECLARE_CCTK_PARAMETERS; if (verbose) { if (get_state() == update) { CCTK_INFO ("Announcing RDF metadata information update"); } else { CCTK_INFO ("Announcing final RDF metadata information"); } } if (CCTK_IsFunctionAliased ("PublishBoolean")) { const int retval = PublishBoolean (cctkGH, get_state() == final ? 1 : 2, get_state() == final ? 1 : 0, "Simulation finished ?", CCTK_THORNSTRING " runtime info"); if (retval < 0) { CCTK_VWarn (1, __LINE__, __FILE__, CCTK_THORNSTRING, "Failed to publish runtime information (error code %d)", retval); } } // check if there was anything published if (rdfPublishList.empty()) return; char* rundatebuf = Util_CurrentDateTime (); const string started_at (clean (rundatebuf)); free (rundatebuf); static int publishedItems = 0; for (size_t i = 0; i < rdfPublishList.size(); i++) { ostringstream object; object << "&simID;Publish/" << (publishedItems + i) << ""; msgbuf << AppendToTransaction("add", jobID, "publish", object.str()); } for (size_t i = 0; i < rdfPublishList.size(); i++, publishedItems++) { const rdfPublishItem& item = rdfPublishList[i]; ostringstream subject, object; subject << "Publish/" << publishedItems; object << "" << item.datetime << ""; msgbuf << AppendToTransaction("add", subject.str(), "datetime", object.str()); object.str(""); object << "" << item.key << ""; msgbuf << AppendToTransaction("add", subject.str(), "key", object.str()); if (not item.name.empty()) { object.str(""); object << "" << item.name << ""; msgbuf << AppendToTransaction("add", subject.str(), "name", object.str()); } if (item.hasCCTKinfo) { object.str(""); object << "" << item.cctk_time << ""; msgbuf << AppendToTransaction("add", subject.str(), "time", object.str()); object.str(""); object << "" << item.cctk_iteration << ""; msgbuf << AppendToTransaction("add", subject.str(), "iteration", object.str()); } ostringstream tablebuf; if (item.isTable) { for (size_t j = 0; j < item.table.size(); j++) { const rdfTableEntry& entry = item.table[j]; subject.str(""); subject << "Publish/" << publishedItems; object.str(""); object << "&simID;Publish/" << publishedItems << "/" << j << ""; msgbuf << AppendToTransaction("add", subject.str(), "tableEntry", object.str()); subject.str(""); subject << "Publish/" << publishedItems << "/" << j; object.str(""); object << "" << entry.key << ""; msgbuf << AppendToTransaction("add", subject.str(), "key", object.str()); object.str(""); object << "" << entry.value.value << ""; msgbuf << AppendToTransaction("add", subject.str(), "value", object.str()); } } else { object.str(""); object << "" << item.scalar.value << ""; msgbuf << AppendToTransaction("add", subject.str(), "value", object.str()); } } rdfPublishList.clear(); } rdf:: ~ rdf () { DECLARE_CCTK_PARAMETERS; if (parent) { parent->msgbuf << "" << endl << msgbuf.str() << "" << endl; return; } // check if anything needs to be done if (msgbuf.str().empty()) return; // this simulation's RDF context const string context = "CactusSimulations:" + jobID; const string contextURI = cleanURI("<" + context + ">"); // RDF/XML document header with some namespace definitions string header = "\n" "\n" "\t\n" "\t\n" "\t\n" "\t\n" "]>\n\n"; if (get_state() == initial) { header += "\n\n"; } else { header += "\n\n"; } // RDF/XML document footer const string footer = get_state() == initial ? "\n\n" : "\n\n"; if (get_state() != initial) { char* dateBuffer = Util_CurrentDateTime(); const string lastUpdated = "" + clean(dateBuffer) + ""; free(dateBuffer); msgbuf << AppendToTransaction("remove", jobID, "lastUpdated", ""); msgbuf << AppendToTransaction("add", jobID, "lastUpdated", lastUpdated); } const int len = header.length() + msgbuf.str().length() + footer.length(); // Loop over all destinations for (int i = 0; i < NUM_RDF_ENTRIES; i++) { if (*rdf_hostname[i]) { // Create the data // use PUT to create a new context and // POST to add metadata to an existing one ostringstream databuf; databuf << (get_state() == initial ? "PUT" : "POST") << " /openrdf-sesame/repositories/Simulations/statements" "?context=" << contextURI << "&baseURI=" << contextURI; //<< " /context/CactusSimulations/" << jobID; // set a metadata lifetime if requested by the user // (Formaline::time_to_live is hours but the RDF service wants seconds) // if (metadata_lifetime) databuf << "?ttl=" << (metadata_lifetime * 3600); databuf << " HTTP/1.0\r\n" << "Host: " << rdf_hostname[i] << "\r\n" << "Content-Type: application/" << (get_state() == initial ? "rdf+xml" : "x-rdftransaction") << "\r\n" << "Content-Length: " << len << "\r\n\r\n" << header << msgbuf.str() << footer << "\r\n\r\n"; // Send the data SendData (rdf_hostname[i], rdf_port[i], databuf.str()); } } // loop over all destinations } rdf * rdf:: open_group (char const * const name) { return new rdf (0, get_state (), 0, name, this); } void rdf:: store (char const * const key, bool const value) { const void* dummy = &dummy; dummy = &key; dummy = &value; #if 0 ostringstream valuebuf; valuebuf << (value ? "true" : "false"); string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, CCTK_INT const value) { const void* dummy = &dummy; dummy = &key; dummy = &value; #if 0 ostringstream valuebuf; valuebuf << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, CCTK_REAL const value) { const void* dummy = &dummy; dummy = &key; dummy = &value; #if 0 int const prec = numeric_limits::digits10; ostringstream valuebuf; valuebuf << setprecision(prec) << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, char const * const value) { const void* dummy = &dummy; dummy = &key; dummy = &value; #if 0 // don't store keys with empty string values if (not *value) return; ostringstream valuebuf; valuebuf << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string // FIXME: is the default datatype for RDF objects ?? << "" // " rdf:datatype=\"&xsd;string\">" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } string clean (string const & txt) { ostringstream buf; for (string::const_iterator p = txt.begin(); p != txt.end(); ++ p) { switch (* p) { case '<': buf << "<"; break; case '&': buf << "&"; break; default: buf << * p; } } return buf.str(); } string cleanURI (string const & uri) { const string allowed_charset ("-_.!~*'()/"); ostringstream buf; for (string::const_iterator p = uri.begin(); p != uri.end(); ++ p) { if (isalnum (*p) or allowed_charset.find (*p, 0) != string::npos) { buf << *p; } else if (*p == ' ') { buf << '+'; } else { buf << '%' << hex << int (*p); } } return buf.str(); } #if 0 static list parse (char const * const key, string& node) { assert (key); string str(key); list strs; size_t p = 0; for (;;) { size_t const s = str.find ("/", p); if (s == string::npos) break; strs.push_back (str.substr (p, s - p)); p = s + 1; } node = str.substr (p); return strs; } #endif } // namespace Formaline