/***************************************************************************
* 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 <GElement.h>
#include <GElementID.h>
#include <GMatrix44.h>
#include <GVector3.h>
#include <qapplication.h>
#include <qtimer.h>
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<GCS::GElementID> GXmlDataController::getChildren(const GCS::GElementID& parent) const
{
QValueList<GCS::GElementID> 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<GCS::GElementID> GXmlDataController::getListOfOpenElements()
{
QValueList<GCS::GElementID> list(this->OpenElements.keys());
return list;
}
const QValueList<GCS::GElementID> GXmlDataController::getListOfAllElements()
{
QValueList<GCS::GElementID> 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<GCS::GElementID> oe = OpenElements.keys();
QValueList<GCS::GElementID>::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<QString> known = this->getAllKnownServers();
QValueList<QString>::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<GCS::GIDRange*>::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<GCS::GElementID>::iterator it;
QDateTime current = QDateTime::currentDateTime();
QValueList<GCS::GElementID> 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<GCS::GElementID> list = this->getListOfAllElements();
QValueList<GCS::GElementID>::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<GCS::GElementID> to_be_deleted = this->Storage->getElementsForServer(server);
Storage->unlock();
QValueList<GCS::GElementID>::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; i<list.length(); i++)
{
QDomElement range = list.item(i).toElement();
if (!range.isNull())
{
QDomElement start = range.elementsByTagName("from").item(0).toElement();
if (!start.isNull())
{
QDomElement end = range.elementsByTagName("to").item(0).toElement();
if (!end.isNull())
{
bool ok=false;;
unsigned long from = start.text().toULong(&ok);
if (ok)
{
unsigned long to = end.text().toULong(&ok);
if (ok)
{
GCS::GElementID::addFreeIDRange(from,to);
}
}
}
}
}
}
}
else if (message == "body") // <-- THAT's an USER MESSAGE
{
if (data.isElement())
{
QString text = data.toElement().text();
qDebug(" ");
qDebug(QString("RECEIVED USER MESSAGE FROM %1:").arg(sender));
qDebug(" ");
qDebug(text);
qDebug(" ");
qDebug("USER MESSAGE END");
qDebug(" ");
emit this->userMessageReceived(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());
}
}
syntax highlighted by Code2HTML, v. 0.9.1