Continued on the Library::Remote. The Query::ListBase and Query::ListSelection is starting to work.

This commit is contained in:
Daniel Önnerby 2008-08-14 20:44:34 +00:00
parent 41769da567
commit 252f06e1b4
12 changed files with 247 additions and 103 deletions

View File

@ -502,7 +502,12 @@ bool Indexer::Startup(utfstring setLibraryPath){
}
// start the thread
this->oThread = new boost::thread(boost::bind(&Indexer::ThreadLoop,this));
try{
this->oThread = new boost::thread(boost::bind(&Indexer::ThreadLoop,this));
}
catch(...){
return false;
}
return true;
}

View File

@ -200,7 +200,7 @@ bool Library::Base::AddQuery( const Query::Base &query,unsigned int options ){
if( options & Query::Options::CancelQueue ){
oCheckQuery = this->incomingQueries.erase(oCheckQuery);
}else if( options & Query::Options::CancelSimilar ){
if( (*oCheckQuery)->iQueryId == queryCopy->iQueryId ){
if( (*oCheckQuery)->queryId == queryCopy->queryId ){
oCheckQuery = this->incomingQueries.erase(oCheckQuery);
}else{
++oCheckQuery;
@ -222,7 +222,7 @@ bool Library::Base::AddQuery( const Query::Base &query,unsigned int options ){
if( options & Query::Options::CancelQueue ){
(*oCheckQuery)->status |= Query::Base::Status::Canceled;
}else if( options & Query::Options::CancelSimilar ){
if( (*oCheckQuery)->iQueryId == queryCopy->iQueryId ){
if( (*oCheckQuery)->queryId == queryCopy->queryId ){
(*oCheckQuery)->status |= Query::Base::Status::Canceled;
}
}
@ -236,7 +236,7 @@ bool Library::Base::AddQuery( const Query::Base &query,unsigned int options ){
if( options & Query::Options::CancelQueue ){
bCancelCurrentQuery = true;
}else if( options & Query::Options::CancelSimilar ){
if( this->runningQuery->iQueryId == queryCopy->iQueryId ){
if( this->runningQuery->queryId == queryCopy->queryId ){
bCancelCurrentQuery = true;
}
}

View File

@ -103,7 +103,12 @@ utfstring Library::LocalDB::GetInfo(){
bool Library::LocalDB::Startup(){
// Start the library thread
this->threads.create_thread(boost::bind(&Library::LocalDB::ThreadLoop,this));
try{
this->threads.create_thread(boost::bind(&Library::LocalDB::ThreadLoop,this));
}
catch(...){
return false;
}
return true;
}

View File

@ -112,49 +112,79 @@ bool Library::Remote::Startup(){
//////////////////////////////////////////
void Library::Remote::ReadThread(){
this->address = "localhost";
this->port = "10543";
boost::asio::ip::tcp::resolver resolver(this->ioService);
boost::asio::ip::tcp::resolver::query resolverQuery(this->address,this->port);
boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(resolverQuery);
boost::asio::ip::tcp::resolver::iterator end;
try{
boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(resolverQuery);
boost::asio::ip::tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpointIterator!=end){
this->socket.close();
this->socket.connect(*endpointIterator++, error);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpointIterator!=end){
this->socket.close();
this->socket.connect(*endpointIterator++, error);
}
if (error){
this->Exit();
return;
}
}
if (error){
catch(boost::system::system_error &error){
this->Exit();
return;
}
catch(...){
this->Exit();
return;
}
// Successfully connected to server
// Start the WriteThread
this->threads.create_thread(boost::bind(&Library::Remote::WriteThread,this));
try{
this->threads.create_thread(boost::bind(&Library::Remote::WriteThread,this));
}
catch(...){
this->Exit();
return;
}
// Lets start recieving queries
xml::Parser parser(&this->socket);
if( xml::ParserNode rootNode=parser.ChildNode("musik")){
while(xml::ParserNode node=rootNode.ChildNode()){
if(node.Name()=="queryresults"){
try{
// Lets start recieving queries
xml::Parser parser(&this->socket);
if( xml::ParserNode rootNode=parser.ChildNode("musik")){
while(xml::ParserNode node=rootNode.ChildNode()){
if(node.Name()=="queryresults"){
unsigned int queryId = boost::lexical_cast<unsigned int>(node.Attributes()["id"]);
Query::Ptr currentQuery;
// This is a query node
// Find the query in the outgoingQueries list
{
boost::mutex::scoped_lock lock(this->libraryMutex);
// Reverse loop since it's most likely the query is in the end
for(QueryList::reverse_iterator query=this->outgoingQueries.rbegin();query!=this->outgoingQueries.rend();++query){
if( (*query)->iQueryId==queryId ){
currentQuery = *query;
unsigned int queryId = boost::lexical_cast<unsigned int>(node.Attributes()["id"]);
Query::Ptr currentQuery;
// This is a query node
// Find the query in the outgoingQueries list
{
boost::mutex::scoped_lock lock(this->libraryMutex);
// Reverse loop since it's most likely the query is in the end
for(QueryList::reverse_iterator query=this->outgoingQueries.rbegin();query!=this->outgoingQueries.rend();++query){
if( (*query)->queryId==queryId ){
currentQuery = *query;
}
}
}
if(currentQuery){
if(currentQuery->RecieveResults(node,this)){
currentQuery->status |= Query::Base::Status::Ended;
}else{
currentQuery->status |= Query::Base::Status::Canceled;
}
}
}
}
}
}
catch(...){
}
this->Exit();
}
@ -191,7 +221,11 @@ void Library::Remote::WriteThread(){
// Lets send the query
xml::WriterNode queryNode(rootNode,"query");
queryNode.Attributes()["type"] = query->Name();
queryNode.Attributes()["id"] = boost::lexical_cast<std::string>(query->iQueryId);
queryNode.Attributes()["id"] = boost::lexical_cast<std::string>(query->queryId);
if(query->options){
queryNode.Attributes()["options"] = boost::lexical_cast<std::string>(query->options);
}
query->SendQuery(queryNode);

View File

@ -41,12 +41,15 @@
using namespace musik::core;
Query::Base::Base(void) : status(0){
Query::Base::Base(void)
:status(0)
,options(0)
{
// This will guarantee that the query id is uniq for each query, but copies will not.
// This is usefull when canceling similar queries
static unsigned int iUniqueQueryId(0);
iUniqueQueryId++;
iQueryId = iUniqueQueryId;
static unsigned int uniqueQueryId(0);
uniqueQueryId++;
this->queryId = uniqueQueryId;
}
Query::Base::~Base(void){
@ -90,7 +93,7 @@ bool Query::Base::SendQuery(musik::core::xml::WriterNode &queryNode){
///\returns
///true when successfully recieved
//////////////////////////////////////////
bool Query::Base::RecieveResults(musik::core::xml::ParserNode &queryNode){
bool Query::Base::RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library){
return false;
}

View File

@ -121,7 +121,7 @@ class Base : public sigslot::has_slots<> {
///
///Used for comparing queries and find similar queries.
//////////////////////////////////////////
unsigned int iQueryId;
unsigned int queryId;
//////////////////////////////////////////
///\brief
@ -203,7 +203,7 @@ class Base : public sigslot::has_slots<> {
virtual bool RecieveQuery(musik::core::xml::ParserNode &queryNode);
virtual bool SendQuery(musik::core::xml::WriterNode &queryNode);
virtual bool RecieveResults(musik::core::xml::ParserNode &queryNode);
virtual bool RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library);
virtual bool SendResults(musik::core::xml::WriterNode &queryNode,Library::Base *library);
};

View File

@ -269,6 +269,52 @@ bool Query::ListBase::SendResults(musik::core::xml::WriterNode &queryNode,Librar
return true;
}
bool Query::ListBase::RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library){
while( musik::core::xml::ParserNode node = queryNode.ChildNode() ){
if( node.Name()=="metadata"){
std::string metakey(node.Attributes()["key"]);
MetadataValueVector tempMetadataValues;
tempMetadataValues.reserve(10);
int row(0);
{
boost::mutex::scoped_lock lock(library->oResultMutex);
this->metadataResults[metakey];
}
while( musik::core::xml::ParserNode metaDataNode = node.ChildNode("md") ){
metaDataNode.WaitForContent();
tempMetadataValues.push_back(
MetadataValuePtr(
new MetadataValue(
boost::lexical_cast<unsigned int>( metaDataNode.Attributes()["id"] ),
ConvertUTF16(metaDataNode.Content()).c_str()
)
)
);
if( (++row)%10==0 ){
boost::mutex::scoped_lock lock(library->oResultMutex);
this->metadataResults[metakey].insert(this->metadataResults[metakey].end(),tempMetadataValues.begin(),tempMetadataValues.end());
tempMetadataValues.clear();
tempMetadataValues.reserve(10);
}
}
if(!tempMetadataValues.empty()){
boost::mutex::scoped_lock lock(library->oResultMutex);
this->metadataResults[metakey].insert(this->metadataResults[metakey].end(),tempMetadataValues.begin(),tempMetadataValues.end());
}
}
}
return true;
}
void Query::ListBase::DummySlot(MetadataValueVector*,bool){
}

View File

@ -93,6 +93,7 @@ namespace musik{ namespace core{
UINT64 trackInfoSize;
virtual bool SendResults(musik::core::xml::WriterNode &queryNode,Library::Base *library);
virtual bool RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library);
public:
MetadataSignal& OnMetadataEvent(const char* metatag);

View File

@ -620,4 +620,43 @@ std::string Query::ListSelection::Name(){
return "ListSelection";
}
bool Query::ListSelection::SendQuery(musik::core::xml::WriterNode &queryNode){
/// <selections>
/// <selection key="genre">1,3,5,7</selection>
/// <selection key="artist">6,7,8</selection>
/// </selections>
/// <listeners>genre,artist,album</listeners>
xml::WriterNode selectionsNode(queryNode,"selections");
// Start with the selection nodes
for(SelectedMetadata::iterator selection=this->selectedMetadata.begin();selection!=this->selectedMetadata.end();++selection){
xml::WriterNode selectionNode(selectionsNode,"selection");
selectionNode.Attributes()["key"] = selection->first;
std::string selectionIDs;
for(SelectedMetadataIDs::iterator id=selection->second.begin();id!=selection->second.end();++id){
if(!selectionIDs.empty()){
selectionIDs.append(",");
}
selectionIDs.append(boost::lexical_cast<std::string>(*id));
}
selectionNode.Content() = selectionIDs;
}
// Then the listeners
xml::WriterNode listenersNode(queryNode,"listeners");
for(MetadataSignals::iterator listener=this->metadataEvent.begin();listener!=this->metadataEvent.end();++listener){
if( listener->second.has_connections() ){
if(!listenersNode.Content().empty()){
listenersNode.Content().append(",");
}
listenersNode.Content().append(listener->first);
}
}
return true;
}

View File

@ -36,6 +36,16 @@
#pragma once
//////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////
namespace musik{ namespace core{
namespace Library{
class Base;
}
} }
//////////////////////////////////////////////////////////////
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <set>
@ -47,79 +57,73 @@
#include <core/Query/ListBase.h>
//////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////
namespace musik{ namespace core{
namespace Library{
class Base;
}
} }
namespace musik{ namespace core{ namespace Query{
//////////////////////////////////////////
///\brief
///ListSelection is the query used when listing tracks and metalists from a metalist (genres, artists, etc) selection.
///
///Write detailed description for ListSelection here.
///
///\see
///Query::ListBase
//////////////////////////////////////////
class ListSelection : public Query::ListBase{
public:
ListSelection(void);
~ListSelection(void);
void SelectMetadata(const char* metakey,DBINT metadataId);
void RemoveMetadata(const char* metakey,DBINT metadataId);
void ClearMetadata(const char* metakey=NULL);
void SelectionOrderSensitive(bool sensitive);
protected:
friend class Library::Base;
friend class Library::LocalDB;
friend class server::Connection;
virtual std::string Name();
virtual bool ParseQuery(Library::Base *library,db::Connection &db);
Ptr copy() const;
virtual bool RecieveQuery(musik::core::xml::ParserNode &queryNode);
virtual bool SendQuery(musik::core::xml::WriterNode &queryNode);
private:
typedef std::set<DBINT> SelectedMetadataIDs;
typedef std::map<std::string,SelectedMetadataIDs> SelectedMetadata;
namespace musik{ namespace core{
namespace Query{
//////////////////////////////////////////
///\brief
///ListSelection is the query used when listing tracks and metalists from a metalist (genres, artists, etc) selection.
///
///Write detailed description for ListSelection here.
///
///\see
///Query::ListBase
///A map of selected metakeys
//////////////////////////////////////////
class ListSelection : public Query::ListBase{
public:
ListSelection(void);
~ListSelection(void);
SelectedMetadata selectedMetadata;
void SelectMetadata(const char* metakey,DBINT metadataId);
void RemoveMetadata(const char* metakey,DBINT metadataId);
void ClearMetadata(const char* metakey=NULL);
//////////////////////////////////////////
///\brief
///Setting if selection is sensitive to order of selection
//////////////////////////////////////////
bool selectionOrderSensitive;
void SelectionOrderSensitive(bool sensitive);
protected:
friend class Library::Base;
friend class Library::LocalDB;
friend class server::Connection;
virtual std::string Name();
virtual bool ParseQuery(Library::Base *library,db::Connection &db);
Ptr copy() const;
virtual bool RecieveQuery(musik::core::xml::ParserNode &queryNode);
private:
typedef std::map<std::string,std::set<DBINT>> SelectedMetadata;
//////////////////////////////////////////
///\brief
///A list of the metakeys selection order
//////////////////////////////////////////
std::vector<std::string> metakeySelectionOrder;
//////////////////////////////////////////
///\brief
///A map of selected metakeys
//////////////////////////////////////////
SelectedMetadata selectedMetadata;
inline void SQLPrependWhereOrAnd(std::string &sql);
void SQLSelectQuery(const char *metakey,const char *sqlStart,const char *sqlEnd,std::set<std::string> &metakeysSelected,std::string &sqlSelectTrackWhere,Library::Base *library);
void QueryForMetadata(const char *metakey,const char *sql,std::set<std::string> &metakeysQueried,Library::Base *library,db::Connection &db);
//////////////////////////////////////////
///\brief
///Setting if selection is sensitive to order of selection
//////////////////////////////////////////
bool selectionOrderSensitive;
};
//////////////////////////////////////////
///\brief
///A list of the metakeys selection order
//////////////////////////////////////////
std::vector<std::string> metakeySelectionOrder;
inline void SQLPrependWhereOrAnd(std::string &sql);
void SQLSelectQuery(const char *metakey,const char *sqlStart,const char *sqlEnd,std::set<std::string> &metakeysSelected,std::string &sqlSelectTrackWhere,Library::Base *library);
void QueryForMetadata(const char *metakey,const char *sql,std::set<std::string> &metakeysQueried,Library::Base *library,db::Connection &db);
};
}
} }
//////////////////////////////////////////////////////////////
} } }
//////////////////////////////////////////////////////////////

View File

@ -105,11 +105,12 @@ void Connection::ReadThread(){
if(queryIt!=queryMap.end()){
// Query type exists, lets create a copy
musik::core::Query::Ptr query( queryIt->second->copy() );
query->queryId = boost::lexical_cast<unsigned int>(queryNode.Attributes()["id"]);
if(query->RecieveQuery(queryNode)){
// TODO: check for AddQuery options in tag
this->AddQuery( *query );
this->AddQuery( *query,boost::lexical_cast<unsigned int>(queryNode.Attributes()["options"]) );
}
@ -211,6 +212,7 @@ void Connection::WriteThread(){
{
musik::core::xml::WriterNode queryNode(musikNode,"queryresults");
queryNode.Attributes()["type"] = sendQuery->Name();
queryNode.Attributes()["id"] = boost::lexical_cast<std::string>(sendQuery->queryId);
sendQuery->SendResults(queryNode,this);
}

View File

@ -171,10 +171,15 @@ void Writer::Send(){
}
}
// Time to send the buffer
if(!sendBuffer.empty()){
boost::asio::write(*(this->socket),boost::asio::buffer(sendBuffer));
sendBuffer.clear();
try{
// Time to send the buffer
if(!sendBuffer.empty()){
boost::asio::write(*(this->socket),boost::asio::buffer(sendBuffer));
sendBuffer.clear();
}
}
catch(...){
this->Exit();
}
}