/*************************************************************************** * Copyright (C) 2004 - 2005 by Raphael Langerhorst * * raphael-langerhorst@gmx.at * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * ***************************************************************************/ #include "GXmlDataController.h" #include "GCoreXmlSerializer.h" #include "GStorage.h" #include "GXmlNetwork.h" #include "GXmppNetwork.h" #include #include #include #include #include #include namespace GWE { GXmlDataController::GXmlDataController(GStorage* storage, GXmlNetwork* network, const QString& master_server, QObject *parent, const char *name) : GDataController(parent,name), Serializer(new GCoreXmlSerializer(this,this,"XML Serializer")), Storage(storage), Network(network), MasterServer(master_server) { if (master_server == this->Network->getNetworkId()) // we can't be our own master this->MasterServer = ""; connect(network,SIGNAL(networkConnected()),this,SLOT(registerWithMaster())); connect(network,SIGNAL(dataAvailable(QDomElement, const QString& )),this,SLOT(receiveData(QDomElement, const QString& ))); connect(network,SIGNAL(presenceChanged(QString, bool )),this,SLOT(updateServerPresence(QString, bool ))); // connect(this,SIGNAL(elementUpdated(const GCS::GElementID& )),this,SLOT(postSyndication(const GCS::GElementID& ))); connect(this,SIGNAL(elementAdded(const GCS::GElementID& )),this,SLOT( postSyndication(const GCS::GElementID&))); QTimer* checksyndication = new QTimer(this,"check syndication timer"); connect(checksyndication,SIGNAL(timeout()),this,SLOT(checkElementsForSyndication())); checksyndication->start(207); //distribute performance nicely - not all events should be at the same time (1 sec interval) //called by the GweController // connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(shutdown())); if (this->isMasterServer()) { int size_uint = sizeof(unsigned long); unsigned long upper_bound = 65535; // init to 2^16 by default // if (size_uint >= 8) // { // upper_bound = 18446744073709551615; // init to 2^64 - 1 // } // else if (size_uint >= 6) // { // upper_bound = 281474976710655; // init to 2^48 - 1 // } if (size_uint >= 4) { upper_bound = 4294967295; // init to 2^32 - 1 } else { qWarning("The length of the unsigned long data type is very short, it should be at least 4 Bytes."); } GCS::GElementID::addFreeIDRange(1,upper_bound); //use 1 for the galaxy } else { QTimer* t = new QTimer(this,"free ID timer"); connect(t,SIGNAL(timeout()),this,SLOT(checkFreeIDs())); t->start(5000); //5 seconds should be fine for now, don't set it lower than 3000 } } GXmlDataController::~GXmlDataController() { // we don't delete the serializer because GXmlDataController is the parent // - in terms of QObject - of the serializer (it is automatically deleted) if (Storage) { delete Storage; Storage=NULL; } if (Network) { delete Network; Network=NULL; } } bool GXmlDataController::add(GCS::GElement* element) { try { Storage->lock(); Storage->addElement(element,this->Network->getNetworkId()); Storage->unlock(); this->OpenElements.insert(element->getElementID(),element); this->prepareOpenedElement(element); emit this->elementAdded(element->getElementID()); return true; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't add element with ID %1 to storage!").arg(element->getElementID().getID())); qWarning(e.toString()); return false; } } bool GXmlDataController::writeOpenElementToStorage(const GCS::GElementID& id) { if (this->OpenElements.contains(id)) { GCS::GElement* element = this->OpenElements[id]; if (element) { try { Storage->lock(); this->Storage->updateElement(element); Storage->unlock(); return true; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't write open element with ID %1 to storage!").arg(id.getID())); qWarning(e.toString()); return false; } } else { qWarning(QString("INCONSISTENCY DETECTED: element %1 detected as open, but could not be found.").arg(id.getID())); } } else { qWarning(QString("Can't write open element to storage because element with ID %1 is not open").arg(id.getID())); } return false; } const GCS::GElement* GXmlDataController::read(const GCS::GElementID& id) const { if (id.getID() == 0) { qWarning("read: given element id is 0, returning NULL"); return NULL; } if (this->OpenElements.contains(id)) { return OpenElements[id]; } else { try { Storage->lock(); GCS::GElement* element = this->Storage->getElement(id,this); Storage->unlock(); return element; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't read element with ID %1 from storage!").arg(id.getID())); qWarning(e.toString()); return NULL; } } } QValueList GXmlDataController::getChildren(const GCS::GElementID& parent) const { QValueList children; try { Storage->lock(); children = this->Storage->getChildren(parent); Storage->unlock(); } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't read children of element with ID %1 from storage!").arg(parent.getID())); qWarning(e.toString()); } return children; } GCS::GElement* GXmlDataController::open(const GCS::GElementID& id) { if (id.getID() == 0) { qWarning("open: given element id is 0, returning NULL"); return NULL; } if (this->OpenElements.contains(id)) { qWarning("Element already opened, returning open element"); return this->getOpenElement(id); } try { Storage->lock(); GCS::GElement* element = this->Storage->getElement(id,this); Storage->unlock(); prepareOpenedElement(element); emit this->elementOpened(id); return element; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't read element with ID %1 from storage!").arg(id.getID())); qWarning(e.toString()); return NULL; } } GCS::GElement* GXmlDataController::getOpenElement(const GCS::GElementID& id) { return OpenElements[id]; } const QValueList GXmlDataController::getListOfOpenElements() { QValueList list(this->OpenElements.keys()); return list; } const QValueList GXmlDataController::getListOfAllElements() { QValueList list; try { Storage->lock(); list = this->Storage->getAllElementIDs(); Storage->unlock(); } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't get element IDs from storage!")); qWarning(e.toString()); } return list; } bool GXmlDataController::close(const GCS::GElementID& id) { if (this->OpenElements.contains(id)) { GCS::GElement* element = this->getOpenElement(id); Q_CHECK_PTR(element); if (element) { bool resume_later = true; if (element->isParked()) resume_later = false; element->parkElement(); if (this->writeOpenElementToStorage(id)) { this->OpenElements.remove(id); element->deleteLater(); emit this->elementClosed(id); return true; } else { qWarning("Couldn't store element back to storage!"); if (resume_later) { qWarning(" Resuming element execution."); element->executeElement(); //resume } return false; } } else { qWarning("INCONSISTENCY DETECTED: Couldn't find element although it was declared open!"); return false; } } else { qWarning("Can't close element, it is not opened, returning true"); return true; } } bool GXmlDataController::postDelete(const GCS::GElementID& id) { //if an element should be deleted that does not belong to this server... this can't happen because an element can only delete itself (think about it). //note: elements are also deleted by the data controller when a child server unregisters. qDebug(QString("Deleting element %1").arg(QString::number(id.getID()))); if (this->close(id)) { qWarning("Element was still open, it is now closed"); } try { Storage->lock(); this->Storage->removeElement(id); Storage->unlock(); } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't delete element with ID %1 from storage!").arg(id.getID())); qWarning(e.toString()); } //add back the element ID to the available free IDs //this is NOT optimal, we should be able to consolidate ranges. //@todo add GCS::GElementID::consolidateRanges() which looks for ranges that have upper bound 1 + 1 = lower bound 2 GCS::GElementID::addFreeIDRange(id.getID(),id.getID()); emit this->elementDeleted(id); return true; } bool GXmlDataController::isMasterServer() { if (this->MasterServer.isEmpty()) return true; return false; } QString GXmlDataController::getManagingServerOfElement(const GCS::GElementID& id) const { try { Storage->lock(); QString server = Storage->getManagingServerForElement(id); Storage->unlock(); return server; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't get managing server for element %1 from storage!").arg(id.toString())); qWarning(e.toString()); return ""; } } QStringList GXmlDataController::getAllKnownServers() const { QStringList servers; try { Storage->lock(); QStringList servers = Storage->getAllServers(); Storage->unlock(); return servers; } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't get servers from storage!")); qWarning(e.toString()); return ""; } } void GXmlDataController::shutdown() { qDebug("Shutting down XML Data Controller"); QValueList oe = OpenElements.keys(); QValueList::iterator it; for (it = oe.begin(); it != oe.end(); ++it) { this->close(*it); } this->LastSyndicationTime.clear(); //make sure syndication can be done... this->checkElementsForSyndication(); //syndicate all remaining updates... if (!this->PendingSyndication.isEmpty()) { qWarning("Pending Syndications NOT empty after final syndication!"); this->PendingSyndication.clear(); //this *should* already be empty, but just to make sure... } if (!this->isMasterServer()) { this->sendFreeIDs(this->MasterServer,GCS::GElementID::countFreeIDs()); } this->unregisterFromAllKnownServers(); //we need to implement a way to "wait" for all data to be sent, flush should do this... this->Network->flushOutput(); // this->Network->closeNetwork(); } void GXmlDataController::registerWithMaster() { if (!Network->isConnected()) { qWarning("Can't register with master server because network is not connected!"); } else if (this->MasterServer.isEmpty()) { qWarning("Can't register with master server because no master server is set!"); qWarning("This usually means that this server is the master server!"); } else { qDebug("Registering with master server"); QDomDocument data; QDomElement e = data.createElement("register"); data.appendChild(e); QDomElement version = data.createElement("version"); e.appendChild(version); version.appendChild(data.createTextNode("0.5.1")); this->Network->send(e,this->MasterServer); // this->requestFreeIDs(1000); } } void GXmlDataController::unregisterFromMaster() { if (!Network->isConnected()) { qWarning("Can't unregister from master server because network is not connected!"); } else if (this->MasterServer.isEmpty()) { qWarning("Can't unregister from master server because no master server is set!"); } else { //@todo send back all free IDs, send back all primary element data qDebug("Unregistering from master server"); QDomDocument data; QDomElement e = data.createElement("unregister"); data.appendChild(e); this->Network->send(e,this->MasterServer); } } void GXmlDataController::unregisterFromAllKnownServers() { if (!Network->isConnected()) { qWarning("Can't unregister from all servers because network is not connected!"); } else { qDebug("Unregistering from all known servers"); QDomDocument data; QDomElement e = data.createElement("unregister"); data.appendChild(e); QValueList known = this->getAllKnownServers(); QValueList::iterator it; for (it = known.begin(); it != known.end(); ++it) { qDebug(QString("Unregistering from %1").arg(*it)); this->Network->send(e,*it); } } } void GXmlDataController::checkFreeIDs() { //in case we're already really low! if (GCS::GElementID::countFreeIDs() < 400) { requestFreeIDs(2000); } else if (GCS::GElementID::countFreeIDs() < 800) { requestFreeIDs(500); } } void GXmlDataController::requestFreeIDs(unsigned long amount) { if (amount == 0) { qWarning("I'm not going to request 0 free IDs!"); return; } if (this->MasterServer.isEmpty()) { qWarning("Can't request free GElementIDs because no master server is set!"); return; } if (!this->Network->isConnected()) { qWarning("Can't request free GElementIDs because network is not connected!"); return; } qDebug(QString("Requesting %1 free element IDs").arg(QString::number(amount))); QDomDocument d; QDomElement e = d.createElement("requestfreeids"); d.appendChild(e); e.appendChild(d.createTextNode(QString::number(amount))); this->Network->send(e,this->MasterServer); } void GXmlDataController::sendFreeIDs(QString server, unsigned long amount) { GCS::GIDContainer c = GCS::GElementID::getFreeIDRange(amount); QDomDocument d; QDomElement e = d.createElement("freeids"); d.appendChild(e); QValueList::iterator it; for (it = c.begin(); it != c.end(); ++it) { QDomElement range = d.createElement("range"); e.appendChild(range); QDomElement from = d.createElement("from"); range.appendChild(from); from.appendChild(d.createTextNode(QString::number((*it)->getLowerBound()))); QDomElement to = d.createElement("to"); range.appendChild(to); to.appendChild(d.createTextNode(QString::number((*it)->getUpperBound()))); } this->Network->send(e,server); } void GXmlDataController::postSyndication(const GCS::GElementID& id) { if (!this->PendingSyndication.contains(id)) this->PendingSyndication.append(id); } void GXmlDataController::checkElementsForSyndication() { QValueList::iterator it; QDateTime current = QDateTime::currentDateTime(); QValueList to_be_or_not_to_be; for (it = this->PendingSyndication.begin(); it != this->PendingSyndication.end(); ++it) { bool send = false; if (this->LastSyndicationTime.contains(*it)) { QDateTime datetime = this->LastSyndicationTime[*it]; if (datetime.secsTo(current)>3) // THIS is the minimum time between two syndications of the same element send = true; } else { send = true; } if (send) { this->syndicateElementData(*it); to_be_or_not_to_be.append(*it); // this->PendingSyndication.remove(*it); // NEVER EVER DO THAT } } for (it = to_be_or_not_to_be.begin(); it != to_be_or_not_to_be.end(); ++it) { this->PendingSyndication.remove(*it); //remove by value } } void GXmlDataController::syndicateElementDataToServer(const GCS::GElementID& id,const QString& server) { if (server == this->Network->getNetworkId()) { qWarning(QString("Not syndicating element %1 to self!").arg(id.toString())); } else { QString managing_server; try { Storage->lock(); managing_server = this->Storage->getManagingServerForElement(id); Storage->unlock(); if (managing_server == server) { qWarning(QString("Not syndicating element %1 to server %2 because given server manages given element").arg(QString::number(id.getID())).arg(server)); return; } } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Could not get managing server for element %1 from storage!").arg(id.toString())); } QDomElement e; if (OpenElements.contains(id)) { e = Serializer->serializeElement(OpenElements[id],"GElement"); } else { try { Storage->lock(); e = Serializer->serializeElement(Storage->getElement(id,this),"GElement"); Storage->unlock(); } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Couldn't get element %1 from storage!").arg(id.toString())); qWarning(exception.toString()); } } if (e.isNull()) { qWarning(QString("Couldn't get data for element with ID %1").arg(id.getID())); } else { if (!managing_server.isEmpty()) e.setAttribute("owner",managing_server); this->Network->send(e,server); } } } void GXmlDataController::syndicateElementData(const GCS::GElementID& id) { if (this->LastSyndicationTime.contains(id)) this->LastSyndicationTime.replace(id,QDateTime::currentDateTime()); //only applies to syndicateElementData() else this->LastSyndicationTime.insert(id,QDateTime::currentDateTime()); //only applies to syndicateElementData() //@todo use syndicateElementDataToServer(const GCS::GElementID& id,const QString& server) // if (this->PrimaryElements.contains(id) || this->SecondaryElements.contains(id) && ChildServers.contains(ElementServerMapping[id])) // { QDomElement e; if (OpenElements.contains(id)) { e = Serializer->serializeElement(OpenElements[id],"GElement"); } else { try { Storage->lock(); e = Serializer->serializeElement(Storage->getElement(id,this),"GElement"); Storage->unlock(); } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Could not get element %1 from storage").arg(id.toString())); } } if (e.isNull()) { qWarning(QString("Couldn't get data for element with ID %1").arg(id.getID())); } else { QString managing_server; try { Storage->lock(); managing_server = Storage->getManagingServerForElement(id); Storage->unlock(); } catch (GStorageException e) { Storage->unlock(); qWarning(QString("Couldn't get managing server for element %1 from storage!").arg(id.toString())); qWarning(e.toString()); } if (!managing_server.isEmpty()) e.setAttribute("owner",managing_server); //@todo this is a HACK until subscriptions are used! //The gweserver table in the GStorage has a column with master servers //for every server, this way a hierarchical structure can be retrieved //for this particular element - use it to define syndication logic without //creating loops. Also consider subscriptions(?) //send to master server if this is NOT a master server if (!this->isMasterServer()) { //@todo this is probably inefficient, maybe find a way to efficiently send element data to multiple destinations (see XMPP specs, since this needs to be routed). if (!this->MasterServer.isEmpty()) { if (managing_server != this->MasterServer) this->Network->send(e,this->MasterServer); } } else //else send to all child servers { try { Storage->lock(); //@todo update this HACK as soon as subscriptions are in use // QStringList child_servers = this->Storage->getServersForElementChildren(id); QStringList child_servers = this->Storage->getAllServers(); Storage->unlock(); QStringList::iterator it; for (it = child_servers.begin(); it != child_servers.end(); ++it) { if (managing_server != (*it) && this->Network->getNetworkId() != (*it)) { this->Network->send(e,(*it)); } } } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Couldn't get list of servers for children of element %1 from storage! Element not syndicated to child servers!").arg(id.toString())); qWarning(exception.toString()); } } } // } } void GXmlDataController::syndicateAllElementDataToServer(const QString& server) { qWarning(QString("Syndicating all known elements to %1, this could cause high network load!").arg(server)); const QValueList list = this->getListOfAllElements(); QValueList::const_iterator it; for (it=list.begin(); it != list.end(); ++it) { syndicateElementDataToServer((*it),server); } } void GXmlDataController::updateServerPresence(QString server, bool available) { QString presence = "0"; if (available) presence = "1"; bool internal = false; try { Storage->lock(); if (this->Storage->getServerExists(server)) //else its external { internal = true; this->Storage->updateServerPresence(server,presence); } Storage->unlock(); } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Couldn't update server presence for server %1 with presence %2!").arg(server).arg(presence)); qWarning(exception.toString()); } emit this->serverPresenceChanged(server,available,internal); //HACK for pre XMPP 1.0 protocol if (!available) this->processUnregister(server); } void GXmlDataController::prepareOpenedElement(GCS::GElement* element) { this->OpenElements.insert(element->getElementID(),element); connect(element,SIGNAL(parentChanged(GCS::GElement*, const GCS::GElementID&, const GCS::GElementID&, const GCS::GMatrix44& )), this,SLOT(processReparenting(GCS::GElement*, const GCS::GElementID&, const GCS::GElementID&, const GCS::GMatrix44& ))); connect(element,SIGNAL(influenceReceived(const GCS::GElementInfluence& )), this,SLOT(processInfluencing(const GCS::GElementInfluence& ))); connect(element,SIGNAL(agentChanged(const GCS::GAgent& )),this,SLOT(processAgentChanged(const GCS::GAgent& ))); } void GXmlDataController::processReparenting(GCS::GElement* element, const GCS::GElementID& oldParent, const GCS::GElementID& newParent,const GCS::GMatrix44& transformation) { QDomDocument d; QDomElement e = d.createElement("reparent"); d.appendChild(e); e.appendChild(d.importNode(this->Serializer->serializeElementID(element->getElementID(),"element"),true)); e.appendChild(d.importNode(this->Serializer->serializeElementID(oldParent,"from"),true)); e.appendChild(d.importNode(this->Serializer->serializeElementID(newParent,"to"),true)); e.appendChild(d.importNode(this->Serializer->serializeMatrix44(transformation,"transformation"),true)); QString managing_server_element; QString managing_server_old_parent; QString managing_server_new_parent; QString remote; //used temporary QStringList sentto; // perform reparenting try { Storage->lock(); this->Storage->reparentElement(element->getElementID(),oldParent,newParent); Storage->unlock(); } catch (GStorageException exception) { Storage->unlock(); qWarning("Could not reparent element in storage!"); } //element itself try { Storage->lock(); managing_server_element = this->Storage->getManagingServerForElement(element->getElementID()); Storage->unlock(); remote = managing_server_element; if (!remote.isEmpty() && remote != this->Network->getNetworkId() && !sentto.contains(remote)) { this->Network->send(e,remote); sentto.append(remote); } } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Could find managing server of element %1 in storage!").arg(element->getElementID().toString())); } //elements do not directly know about their children, so we do not need to update either old or new parent // //old parent // try // { // Storage->lock(); // managing_server_old_parent = this->Storage->getManagingServerForElement(oldParent); // Storage->unlock(); // remote = managing_server_old_parent; // // if (!remote.isEmpty() && remote != this->Network->getNetworkId() && !sentto.contains(remote)) // { // this->Network->send(e,remote); // sentto.append(remote); // } // } // catch (GStorageException exception) // { // Storage->unlock(); // qWarning("Could find old parent element in storage!"); // } // // //new parent // try // { // Storage->lock(); // managing_server_new_parent = this->Storage->getManagingServerForElement(newParent); // Storage->unlock(); // remote = managing_server_new_parent; // // if (!remote.isEmpty() && remote != this->Network->getNetworkId() && !sentto.contains(remote)) // { // this->Network->send(e,remote); // sentto.append(remote); // } // } // catch (GStorageException exception) // { // Storage->unlock(); // qWarning("Could find old parent element in storage!"); // } //all processed or should we send to master server as well? // remote = this->MasterServer; // if ( (managing_server_element.isEmpty() || managing_server_old_parent.isEmpty() || managing_server_new_parent.isEmpty()) && !sentto.contains(remote)) // { // this->Network->send(e,remote); // sentto.append(remote); // } //make sure others are notified // this->postSyndication(oldParent); // this->postSyndication(newParent); this->postSyndication(element->getElementID()); } void GXmlDataController::processInfluencing(const GCS::GElementInfluence& influence) { const QObject* sender_generic = sender(); if (sender_generic==NULL) { qWarning("GXmlDataController::processInfluencing() called without a sender()!"); return; } Q_ASSERT(sender_generic->inherits("GCS::GElement")); //sender MUST be a GElement; if (!sender_generic->inherits("GCS::GElement")) return; const GCS::GElement* const_element = (const GCS::GElement*)sender_generic; QString managing_server; try { Storage->lock(); managing_server = this->Storage->getManagingServerForElement(const_element->getElementID()); Storage->unlock(); } catch(GStorageException exception) { Storage->unlock(); qWarning(QString("Could not find managing server for element %1").arg(const_element->getElementID().toString())); qWarning(exception.toString()); return; } if (managing_server != this->Network->getNetworkId()) { QDomElement e = this->Serializer->serializeElementInfluence(influence,const_element->getElementID(),"GElementInfluence"); if (e.isNull()) { qWarning("Serializing influence returned a NULL element"); } else { this->Network->send(e,managing_server); } } } void GXmlDataController::processAgentChanged(const GCS::GAgent& agent) { this->postSyndication(agent.getElementID()); } void GXmlDataController::processUnregister(const QString& server) { if (!this->MasterServer.isEmpty() && this->MasterServer == server) { //@todo we should manage this more elegantly! qWarning(" "); qWarning("SHUTTING DOWN BECAUSE MASTER SERVER HAS SHUT DOWN!!!"); qWarning("IF THIS IS UNEXPECTED TO YOU, PLEASE CONTACT THE MASTER SERVER ADMINISTRATOR"); qWarning("OR CONTACT THE G SYSTEM TEAM."); qWarning(" "); this->shutdown(); qWarning("Shutting down in 2 seconds..."); QTimer::singleShot(2000,qApp,SLOT(quit())); } else { //remove associated elements qDebug(QString("Removing all elements associated with GWE Server %1").arg(server)); try { Storage->lock(); QValueList to_be_deleted = this->Storage->getElementsForServer(server); Storage->unlock(); QValueList::iterator it_del; for (it_del = to_be_deleted.begin(); it_del != to_be_deleted.end(); ++it_del) { this->postDelete(*it_del); } } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Could not retrieve elements associated with GWE Server %1").arg(server)); qWarning(exception.toString()); } //remove server try { Storage->lock(); this->Storage->removeServer(server); if (this->Storage->getServerExists(server)) { qWarning(QString("Removed %1 from storage, but the entry still exists! There is sth wrong with the storage!").arg(server)); } Storage->unlock(); } catch (GStorageException exception) { Storage->unlock(); qWarning(QString("Could not remove GWE Server %1").arg(server)); qWarning(exception.toString()); } } } void GXmlDataController::receiveData(QDomElement data, const QString& sender) { QString message = data.tagName(); if (message == "GElement") { qDebug("Received a secondary GCS::GElement"); QString managing_server = sender; if (data.hasAttribute("owner")) managing_server = data.attribute("owner",sender); try { this->Storage->lock(); if (!sender.isEmpty()) { if (!this->Storage->getServerExists(sender)) this->Storage->addServer(sender,"1"); } if (!managing_server.isEmpty() && managing_server != sender) { if (!this->Storage->getServerExists(managing_server)) this->Storage->addServer(managing_server,"1"); } this->Storage->unlock(); } catch (GStorageException exc) { this->Storage->unlock(); qWarning("Could not update server information in storage!"); qWarning(exc.toString()); } emit this->serverPresenceChanged(sender,true,true); if (managing_server != sender) emit this->serverPresenceChanged(managing_server,true,true); if (managing_server == this->Network->getNetworkId()) { qWarning(QString("INCONSISTENCY DETECTED: Received data from server %1 about a primary element on this server!!").arg(sender)); qWarning(" GWE Servers are probably inconsistent!!"); qWarning(" Another GWE Server probably has this element stored as primary as well!!"); // qWarning(QString(" Element ID is: %1").arg(id.getID())); return; } //basically it looks like we don't need to create the element, //but we want to check if the received data is valid. // -- we need it for checking secondary elements. GCS::GElement* element = this->Serializer->createElement(data); if (element) { const GCS::GElementID id = element->getElementID(); qDebug(QString("adding/updating secondary element with ID %1").arg(id.toString())); if (this->OpenElements.contains(id)) { qDebug("received an update for an open element, temporarily closing element for update"); this->close(id); } bool element_added = false; bool element_updated = false; try { this->Storage->lock(); if (this->Storage->getElementExists(id)) { this->Storage->updateElement(element); this->Storage->updateManagingServer(id,managing_server); element_updated = true; } else { this->Storage->addElement(element,managing_server); element_added = true; } } catch (GStorageException exception) { qWarning("Failed to update element in storage!"); qWarning(exception.toString()); } this->Storage->unlock(); this->OpenElements.insert(element->getElementID(),element); this->prepareOpenedElement(element); if (element_added) emit this->elementAdded(id); if (element_updated) emit this->elementUpdated(id); emit this->elementOpened(id); //@todo only syndicate on element_added, it is a HACK until subscriptions are used if (this->isMasterServer()) { this->postSyndication(id); } } else { qWarning(QString("received element from ") + sender + " but couldn't deserialize it! Received XML Document: "); qWarning(data.ownerDocument().toString()); } } else if (message == "GElementInfluence") { //@todo continue from here: GCS::GElementID id(this->Serializer->getInfluenceTarget(data)); if (id.getID() != 0) { GCS::GElement* element = this->getOpenElement(id); if (element == NULL) { element = this->open(id); } GCS::GElementInfluence influence = this->Serializer->createElementInfluence(data); element->receiveInfluence(influence); } } else if (message == "reparent") { QDomElement dom_element = data.elementsByTagName("element").item(0).toElement(); QDomElement dom_from = data.elementsByTagName("from").item(0).toElement(); QDomElement dom_to = data.elementsByTagName("to").item(0).toElement(); QDomElement dom_transform = data.elementsByTagName("transformation").item(0).toElement(); if (dom_element.isNull() || dom_from.isNull() || dom_to.isNull()) { qWarning(QString("Can't interpret reparent message from %1, required XML elements are missing").arg(sender)); qWarning(" XML document:"); qWarning(data.ownerDocument().toString()); } else { GCS::GElementID element = this->Serializer->createElementID(dom_element); GCS::GElementID from = this->Serializer->createElementID(dom_from); GCS::GElementID to = this->Serializer->createElementID(dom_to); GCS::GMatrix44 transform; if (!dom_transform.isNull()) { transform = this->Serializer->createMatrix44(dom_transform); } if (element.getID() == 0 || from.getID() == 0 || to.getID() == 0) { qWarning("One or more of the received IDs for reparenting are zero, this is likely to be invalid."); } try { Storage->lock(); this->Storage->reparentElement(element,from,to); Storage->unlock(); } catch(GStorageException exception) { Storage->unlock(); qWarning("Could not perform reparenting in storage!"); qWarning(exception.toString()); } //update element in case it's currently open if (this->OpenElements.contains(element)) { GCS::GElement* open_element = this->OpenElements[element]; open_element->reparent(from,to,transform); } } } else if (message == "register") { qDebug(QString("Registering server: %1").arg(sender)); try { Storage->lock(); if (this->Storage->getServerExists(sender)) { this->Storage->updateServerPresence(sender,"1"); Storage->unlock(); qDebug(QString("Server registration updated: %1").arg(sender)); } else { this->Storage->addServer(sender,"1"); Storage->unlock(); qDebug(QString("New server registered: %1, syndicating element data.").arg(sender)); this->syndicateAllElementDataToServer(sender); } } catch (GStorageException exception) { Storage->unlock(); qWarning("Could not update server data in storage!"); } } else if (message == "unregister") { qDebug(QString("UNREGISTER MESSAGE RECEIVED FROM %1").arg(sender)); processUnregister(sender); } else if (message == "requestfreeids") { bool ok; unsigned long amount = data.text().toULong(&ok); if (ok) { //send free IDs: this->sendFreeIDs(sender,amount); } else { qWarning("Couldn't read the requested amount of free element IDs"); } } else if (message == "freeids") { QDomNodeList list = data.elementsByTagName("range"); for (unsigned int i=0; iuserMessageReceived(text,sender,data.hasAttribute("g")); } else { qWarning("Received an user message, but XML tag is not an element!"); } } else { qWarning(("received unknown XML message: ") + data.tagName()); } } bool GXmlDataController::checkConsistency(bool& ok) { //@todo check primary, secondary and open elements as well as storage(against primary and secondary) qWarning("Consistency check not yet implemented, returning true"); ok=true; return true; } void GXmlDataController::sendUserMessage(QString message, QString destination) { //@todo: this doesn't work??? // XMPP::Jid destjid; // destjid.set(destination); // if (!destjid.isValid()) // { // qWarning(QString("Destination %1 not a valid JID, not sending message!").arg(destination)); // return; // } QDomDocument d; QDomElement e = d.createElement("message"); d.appendChild(e); e.setAttribute("type","chat"); e.setAttribute("to",destination); QDomElement body = d.createElement("body"); e.appendChild(body); //add an identification to differentiate between G internal and external messages body.setAttribute("g","1"); body.appendChild(d.createTextNode(message)); this->Network->send(d.toString()); } }