From 37101d21665c0ecb164be8a89e5c7d2b2924deb4 Mon Sep 17 00:00:00 2001 From: fgerlits Date: Wed, 21 Sep 2005 15:27:01 +0000 Subject: [PATCH] cleaned up some major dogsh*t left by a certain person who shall not be named incidentally, fixed bug #1465 --- .../core/include/LiveSupport/Core/Playable.h | 6 + .../core/include/LiveSupport/Core/Playlist.h | 88 ++++- .../LiveSupport/Core/PlaylistElement.h | 26 ++ .../modules/storage/src/TestStorageClient.cxx | 16 +- .../modules/storage/src/TestStorageClient.h | 4 +- .../modules/storage/src/WebStorageClient.cxx | 366 ++++++++++++------ .../modules/storage/src/WebStorageClient.h | 92 +++-- .../storageServer/var/tests/plist2.xml | 2 +- .../storageServer/var/xmlrpc/XR_LocStor.php | 7 +- 9 files changed, 416 insertions(+), 191 deletions(-) diff --git a/livesupport/modules/core/include/LiveSupport/Core/Playable.h b/livesupport/modules/core/include/LiveSupport/Core/Playable.h index abf19be10..2eb69089f 100644 --- a/livesupport/modules/core/include/LiveSupport/Core/Playable.h +++ b/livesupport/modules/core/include/LiveSupport/Core/Playable.h @@ -156,6 +156,9 @@ class Playable : public boost::enable_shared_from_this * Return the token which is used to identify this audio clip * or playlist to the storage server. * + * The token is set when the Playable object is acquired and + * unset (made null again) when it is released. + * * @return the token. */ virtual Ptr::Ref @@ -165,6 +168,9 @@ class Playable : public boost::enable_shared_from_this * Set the token which is used to identify this audio clip * or playlist to the storage server. * + * The token is set when the Playable object is acquired and + * unset (made null again) when it is released. + * * @param token a new token. */ virtual void diff --git a/livesupport/modules/core/include/LiveSupport/Core/Playlist.h b/livesupport/modules/core/include/LiveSupport/Core/Playlist.h index 6ffd8adbc..d260fed7c 100644 --- a/livesupport/modules/core/include/LiveSupport/Core/Playlist.h +++ b/livesupport/modules/core/include/LiveSupport/Core/Playlist.h @@ -127,6 +127,28 @@ using namespace boost::posix_time; * <!ATTLIST playlist playlength NMTOKEN #IMPLIED > * * + * A Playlist can be of various kinds, depending on what we want to use it + * for, and how we got it from the StorageClientInterface: + *
    + *
  • A playlist obtained by getPlaylist() has its uri, + * token and editToken fields all unset + * (i.e., null). Such playlist contain sub-playlists which + * are just stubs, i.e., id, title, playlength triples, + * without actual references to its content objects.
  • + *
  • A playlist obtained by acquirePlaylist() has its uri + * and token fields set, but its editToken + * field unset. These are complete Playlist objects, and their + * sub-playlists contain references to all their sub-objects etc. + * The sub-playlists have their uri fields set, which + * allows them to be played by the audio player, but their + * token field is unset, because these sub-playlists + * are acquired and will be released recursively when the outermost + * playlist containing them is acquired and released.
  • + *
  • A playlist obtained by editPlaylist() has its editToken + * field set (but uri and token unset). + * The sub-playlists of these are also just stubs.
  • + *
+ * * @author $Author$ * @version $Revision$ */ @@ -160,10 +182,17 @@ class Playlist : public Configurable, Ptr::Ref uri; /** - * The token given to this playlist by the storage server. + * The token given to this playlist by the storage server when + * the playlist is acquired; removed when it is released. */ Ptr::Ref token; + /** + * The token given to this playlist by the storage server when + * it is opened for editing; removed when it is saved or reverted. + */ + Ptr::Ref editToken; + /** * A map type for storing the playlist elements associated with * this playlist, indexed by their relative offsets. @@ -464,6 +493,9 @@ class Playlist : public Configurable, * Return the token which is used to identify this * playlist to the storage server. * + * The token is set when the Playable object is acquired and + * unset (made null again) when it is released. + * * @return the token. */ virtual Ptr::Ref @@ -476,6 +508,9 @@ class Playlist : public Configurable, * Set the token which is used to identify this * playlist to the storage server. * + * The token is set when the Playable object is acquired and + * unset (made null again) when it is released. + * * @param token a new token. */ virtual void @@ -485,6 +520,37 @@ class Playlist : public Configurable, this->token = token; } + /** + * Return the token which is used to identify this + * playlist to the storage server. + * + * The edit token is set when the Playable object is opened for + * editing and unset (made null again) when it is saved or reverted. + * + * @return the token. + */ + virtual Ptr::Ref + getEditToken(void) const throw () + { + return editToken; + } + + /** + * Set the token which is used to identify this + * playlist to the storage server. + * + * The edit token is set when the Playable object is opened for + * editing and unset (made null again) when it is saved or reverted. + * + * @param token a new token. + */ + virtual void + setEditToken(Ptr::Ref token) + throw () + { + this->editToken = token; + } + /** * Test whether the playlist is locked for editing. * @@ -493,7 +559,7 @@ class Playlist : public Configurable, bool isLocked() const throw () { - return (token.get() != 0); + return (editToken.get() != 0); } @@ -560,10 +626,10 @@ class Playlist : public Configurable, * Checks the type of the playlist, and calls either addAudioClip() * or addPlaylist(). * - * @param audioClip the new playable item to be added - * @param relativeOffset the start of the playable item, relative - * to the start of the playlist - * @param fadeInfo the fade in / fade out info (optional) + * @param playable the new playable item to be added + * @param relativeOffset the start of the playable item, relative + * to the start of the playlist + * @param fadeInfo the fade in / fade out info (optional) * @return the ID of the new PlaylistElement * @exception std::invalid_argument if playable is neither an AudioClip * nor a Playlist @@ -711,7 +777,7 @@ class Playlist : public Configurable, /** * Return a partial XML representation of this audio clip or playlist. * - * This is a string containing a single + * This is a string containing a single <playlist> * XML element, with minimal information (ID, title, playlength) * only, without an XML header or any other metadata. * @@ -728,8 +794,8 @@ class Playlist : public Configurable, * Return a complete XML representation of this playlist. * * This is a string containing a an XML document with a - * root node, together with an XML header and a - * element (for the outermost playlist only). + * <playlist> root node, together with an XML header and a + * <metadata> element (for the outermost playlist only). * * The encoding is UTF-8. IDs are 16-digit hexadecimal numbers, * time durations have the format "hh:mm:ss.ssssss". @@ -751,8 +817,8 @@ class Playlist : public Configurable, * All other metadata fields in the audio clips and sub-playlists * will be lost. * - * The uri and token fields are currently not part - * of the XML document string returned. + * The uri, token and editToken fields are not + * part of the XML document string returned. * * @return a string representation of the playlist as an XML document */ diff --git a/livesupport/modules/core/include/LiveSupport/Core/PlaylistElement.h b/livesupport/modules/core/include/LiveSupport/Core/PlaylistElement.h index 49ea73619..51710c105 100644 --- a/livesupport/modules/core/include/LiveSupport/Core/PlaylistElement.h +++ b/livesupport/modules/core/include/LiveSupport/Core/PlaylistElement.h @@ -203,6 +203,7 @@ class PlaylistElement : public Configurable this->audioClip = audioClip; this->playable = audioClip; this->fadeInfo = fadeInfo; + this->type = AudioClipType; } /** @@ -351,6 +352,31 @@ class PlaylistElement : public Configurable return playable; } + /** + * Set the Playable instance (an AudioClip or a Playlist) + * associated with the playlist element. + * + * This is used by WebStorageClient::acquirePlaylist() to replace + * a stub (id, title, playlength only) sub-playlist with a full one. + * + * @param playable the new Playable object. + */ + void + setPlayable(Ptr::Ref playable) throw () + { + this->playable = playable; + switch (playable->getType()) { + case Playable::AudioClipType : + audioClip = playable->getAudioClip(); + type = AudioClipType; + break; + case Playable::PlaylistType : + playlist = playable->getPlaylist(); + type = PlaylistType; + break; + } + } + /** * Return the audio clip associated with the playlist element. * diff --git a/livesupport/modules/storage/src/TestStorageClient.cxx b/livesupport/modules/storage/src/TestStorageClient.cxx index 3a35afe03..b0a846834 100644 --- a/livesupport/modules/storage/src/TestStorageClient.cxx +++ b/livesupport/modules/storage/src/TestStorageClient.cxx @@ -346,7 +346,7 @@ TestStorageClient :: getPlaylist(Ptr::Ref sessionId, EditedPlaylistsType::const_iterator editIt = editedPlaylists.find(id->getId()); if (editIt != editedPlaylists.end() // is being edited - && (*editIt->second->getToken() == sessionId->getId())) { // by us + && (*editIt->second->getEditToken() == sessionId->getId())) { // by us playlist = editIt->second; } else { PlaylistMapType::const_iterator @@ -378,8 +378,8 @@ TestStorageClient :: editPlaylist(Ptr::Ref sessionId, } Ptr::Ref playlist = getPlaylist(sessionId, id); - Ptr::Ref token(new std::string(sessionId->getId())); - playlist->setToken(token); + Ptr::Ref editToken(new std::string(sessionId->getId())); + playlist->setEditToken(editToken); editedPlaylists[id->getId()] = playlist; return playlist; @@ -398,11 +398,11 @@ TestStorageClient :: savePlaylist(Ptr::Ref sessionId, throw XmlRpcException("missing session ID argument"); } - if (! playlist->getToken()) { + if (! playlist->getEditToken()) { throw XmlRpcException("savePlaylist() called without editPlaylist()"); } - if (sessionId->getId() != *playlist->getToken()) { + if (sessionId->getId() != *playlist->getEditToken()) { throw XmlRpcException("tried to save playlist in different session" " than the one it was opened in???"); } @@ -411,12 +411,12 @@ TestStorageClient :: savePlaylist(Ptr::Ref sessionId, editIt = editedPlaylists.find(playlist->getId()->getId()); if ((editIt == editedPlaylists.end()) - || (*playlist->getToken() != *editIt->second->getToken())) { + || (*playlist->getEditToken() != *editIt->second->getEditToken())) { throw XmlRpcException("savePlaylist() called without editPlaylist()"); } Ptr::Ref nullPointer; - playlist->setToken(nullPointer); + playlist->setEditToken(nullPointer); PlaylistMapType::iterator storeIt = playlistMap.find(playlist->getId()->getId()); @@ -434,7 +434,7 @@ TestStorageClient :: savePlaylist(Ptr::Ref sessionId, * Revert a playlist to its pre-editing state. *----------------------------------------------------------------------------*/ void -TestStorageClient :: revertPlaylist(Ptr::Ref playlistToken) +TestStorageClient :: revertPlaylist(Ptr::Ref editToken) throw (XmlRpcException) { std::cerr << "TestStorageClient :: revertPlaylist" diff --git a/livesupport/modules/storage/src/TestStorageClient.h b/livesupport/modules/storage/src/TestStorageClient.h index d81539050..529e441db 100644 --- a/livesupport/modules/storage/src/TestStorageClient.h +++ b/livesupport/modules/storage/src/TestStorageClient.h @@ -321,13 +321,13 @@ class TestStorageClient : * the playlist (and lose all changes) at the next login using * this method. * - * @param playlistToken the token of the edited playlist + * @param editToken the token of the edited playlist * @exception XmlRpcException if there is a problem with the XML-RPC * call or no playlist with the specified * token exists. */ virtual void - revertPlaylist(Ptr::Ref playlistToken) + revertPlaylist(Ptr::Ref editToken) throw (XmlRpcException); diff --git a/livesupport/modules/storage/src/WebStorageClient.cxx b/livesupport/modules/storage/src/WebStorageClient.cxx index e8e2a84be..9a1ddaa33 100644 --- a/livesupport/modules/storage/src/WebStorageClient.cxx +++ b/livesupport/modules/storage/src/WebStorageClient.cxx @@ -847,13 +847,13 @@ WebStorageClient :: createPlaylist(Ptr::Ref sessionId) Ptr::Ref playlength(new time_duration(0,0,0,0)); Ptr::Ref playlist(new Playlist(newId, playlength)); - playlist->setToken(token); + playlist->setEditToken(token); editedPlaylists[newId->getId()] = std::make_pair(sessionId, playlist); savePlaylist(sessionId, playlist); token.reset(); - playlist->setToken(token); + playlist->setEditToken(token); return playlist->getId(); } @@ -918,9 +918,8 @@ WebStorageClient :: existsPlaylist(Ptr::Ref sessionId, * Return a playlist to be displayed. *----------------------------------------------------------------------------*/ Ptr::Ref -WebStorageClient :: getPlaylist(Ptr::Ref sessionId, - Ptr::Ref id, - bool deep) const +WebStorageClient :: getPlaylist(Ptr::Ref sessionId, + Ptr::Ref id) const throw (Core::XmlRpcException) { EditedPlaylistsType::const_iterator @@ -940,7 +939,7 @@ WebStorageClient :: getPlaylist(Ptr::Ref sessionId, parameters.clear(); parameters[getPlaylistSessionIdParamName] = sessionId->getId(); parameters[getPlaylistPlaylistIdParamName] = std::string(*id); - parameters[getPlaylistRecursiveParamName] = deep; + parameters[getPlaylistRecursiveParamName] = false; result.clear(); if (!xmlRpcClient.execute(getPlaylistOpenMethodName.c_str(), @@ -960,25 +959,33 @@ WebStorageClient :: getPlaylist(Ptr::Ref sessionId, << result; throw Core::XmlRpcMethodFaultException(eMsg.str()); } - if (! result.hasMember(getPlaylistUrlParamName) - || result[getPlaylistUrlParamName].getType() - != XmlRpcValue::TypeString - || ! result.hasMember(getPlaylistTokenParamName) + + if (! result.hasMember(getPlaylistTokenParamName) || result[getPlaylistTokenParamName].getType() != XmlRpcValue::TypeString) { std::stringstream eMsg; eMsg << "XML-RPC method '" - << getPlaylistOpenMethodName - << "' returned unexpected value:\n" - << result; + << getPlaylistOpenMethodName + << "' returned unexpected value:\n" + << result; + throw XmlRpcMethodResponseException(eMsg.str()); + } + Ptr::Ref token(new std::string( + result[getPlaylistTokenParamName] )); + + if (! result.hasMember(getPlaylistUrlParamName) + || result[getPlaylistUrlParamName].getType() + != XmlRpcValue::TypeString) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistOpenMethodName + << "' returned unexpected value:\n" + << result; throw XmlRpcMethodResponseException(eMsg.str()); } - const std::string url = result[getPlaylistUrlParamName]; - const std::string token = result[getPlaylistTokenParamName]; - - Ptr::Ref playlist(new Playlist(id)); + Ptr::Ref playlist(new Playlist(id)); try { Ptr::Ref parser(new xmlpp::DomParser()); parser->parse_file(url); @@ -994,48 +1001,10 @@ WebStorageClient :: getPlaylist(Ptr::Ref sessionId, throw XmlRpcInvalidDataException( "error parsing playlist metafile"); } + playlist->setToken(token); - if (deep) { - playlist = acquirePlaylist(playlist, result); - } - - parameters.clear(); - parameters[getPlaylistSessionIdParamName] = sessionId->getId(); - parameters[getPlaylistTokenParamName] = token; + releasePlaylistFromServer(playlist); - result.clear(); - if (!xmlRpcClient.execute(getPlaylistCloseMethodName.c_str(), - parameters, result)) { - xmlRpcClient.close(); - std::string eMsg = "cannot execute XML-RPC method '"; - eMsg += getPlaylistCloseMethodName; - eMsg += "'"; - throw XmlRpcCommunicationException(eMsg); - } - xmlRpcClient.close(); - - if (xmlRpcClient.isFault()) { - std::stringstream eMsg; - eMsg << "XML-RPC method '" - << getPlaylistCloseMethodName - << "' returned error message:\n" - << result; - throw Core::XmlRpcMethodFaultException(eMsg.str()); - } - - if (! result.hasMember(getPlaylistPlaylistIdParamName) - || result[getPlaylistPlaylistIdParamName].getType() - != XmlRpcValue::TypeString - || std::string(result[getPlaylistPlaylistIdParamName]) - != std::string(*id)) { - std::stringstream eMsg; - eMsg << "XML-RPC method '" - << getPlaylistCloseMethodName - << "' returned unexpected value:\n" - << result; - throw XmlRpcMethodResponseException(eMsg.str()); - } - return playlist; } @@ -1053,9 +1022,9 @@ WebStorageClient :: editPlaylist(Ptr::Ref sessionId, " being edited"); } - Ptr::Ref url, token; + Ptr::Ref url, editToken; - editPlaylistGetUrl(sessionId, id, url, token); + editPlaylistGetUrl(sessionId, id, url, editToken); Ptr::Ref playlist(new Playlist(id)); try { @@ -1074,7 +1043,7 @@ WebStorageClient :: editPlaylist(Ptr::Ref sessionId, "error parsing playlist metafile"); } - playlist->setToken(token); + playlist->setEditToken(editToken); editedPlaylists[id->getId()] = std::make_pair(sessionId, playlist); return playlist; @@ -1088,7 +1057,7 @@ void WebStorageClient :: editPlaylistGetUrl(Ptr::Ref sessionId, Ptr::Ref id, Ptr::Ref& url, - Ptr::Ref& token) + Ptr::Ref& editToken) throw (Core::XmlRpcException) { XmlRpcValue parameters; @@ -1138,7 +1107,7 @@ WebStorageClient :: editPlaylistGetUrl(Ptr::Ref sessionId, } url.reset(new const std::string(result[getPlaylistUrlParamName])); - token.reset(new const std::string(result[getPlaylistTokenParamName])); + editToken.reset(new const std::string(result[getPlaylistTokenParamName])); } @@ -1150,8 +1119,8 @@ WebStorageClient :: savePlaylist(Ptr::Ref sessionId, Ptr::Ref playlist) throw (Core::XmlRpcException) { - if (!playlist || !playlist->getToken()) { - throw XmlRpcInvalidArgumentException("playlist has no token field"); + if (!playlist || !playlist->getEditToken()) { + throw XmlRpcInvalidArgumentException("playlist has no editToken field"); } EditedPlaylistsType::iterator @@ -1174,7 +1143,7 @@ WebStorageClient :: savePlaylist(Ptr::Ref sessionId, parameters[savePlaylistSessionIdParamName] = sessionId->getId(); parameters[savePlaylistTokenParamName] - = *playlist->getToken(); + = *playlist->getEditToken(); parameters[savePlaylistNewPlaylistParamName] = std::string(*playlist->getXmlDocumentString()); @@ -1212,7 +1181,7 @@ WebStorageClient :: savePlaylist(Ptr::Ref sessionId, } Ptr::Ref nullpointer; - playlist->setToken(nullpointer); + playlist->setEditToken(nullpointer); } @@ -1220,10 +1189,10 @@ WebStorageClient :: savePlaylist(Ptr::Ref sessionId, * Revert a playlist to its pre-editing state. *----------------------------------------------------------------------------*/ void -WebStorageClient :: revertPlaylist(Ptr::Ref playlistToken) +WebStorageClient :: revertPlaylist(Ptr::Ref editToken) throw (XmlRpcException) { - if (!playlistToken) { + if (!editToken) { throw XmlRpcInvalidArgumentException("null pointer in argument"); } @@ -1237,7 +1206,7 @@ WebStorageClient :: revertPlaylist(Ptr::Ref playlistToken) parameters[revertPlaylistSessionIdParamName] // dummy parameter = ""; parameters[revertPlaylistTokenParamName] - = *playlistToken; + = *editToken; result.clear(); if (!xmlRpcClient.execute(revertPlaylistMethodName.c_str(), @@ -1273,22 +1242,115 @@ WebStorageClient :: revertPlaylist(Ptr::Ref playlistToken) /*------------------------------------------------------------------------------ - * Acquire resources for a playlist. + * Acquire resources for a playlist, step 1: execute the XML-RPC call. *----------------------------------------------------------------------------*/ Ptr::Ref -WebStorageClient :: acquirePlaylist(Ptr::Ref oldPlaylist, - XmlRpcValue & result) const +WebStorageClient :: acquirePlaylist(Ptr::Ref sessionId, + Ptr::Ref id) const throw (Core::XmlRpcException) { - XmlRpcValue content; - int index; - Ptr::Ref playlength = oldPlaylist->getPlaylength(); - Ptr::Ref newPlaylist(new Playlist(oldPlaylist->getId(), - playlength)); - newPlaylist->setTitle(oldPlaylist->getTitle()); - newPlaylist->setToken(oldPlaylist->getToken()); - // TODO: copy over all metadata as well + XmlRpcValue parameters; + XmlRpcValue result; + XmlRpcClient xmlRpcClient(storageServerName.c_str(), storageServerPort, + storageServerPath.c_str(), false); + + parameters.clear(); + parameters[getPlaylistSessionIdParamName] = sessionId->getId(); + parameters[getPlaylistPlaylistIdParamName] = std::string(*id); + parameters[getPlaylistRecursiveParamName] = true; + + result.clear(); + if (!xmlRpcClient.execute(getPlaylistOpenMethodName.c_str(), + parameters, result)) { + xmlRpcClient.close(); + std::string eMsg = "cannot execute XML-RPC method '"; + eMsg += getPlaylistOpenMethodName; + eMsg += "'"; + throw XmlRpcCommunicationException(eMsg); + } + + if (xmlRpcClient.isFault()) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistOpenMethodName + << "' returned error message:\n" + << result; + throw Core::XmlRpcMethodFaultException(eMsg.str()); + } + + if (! result.hasMember(getPlaylistTokenParamName) + || result[getPlaylistTokenParamName].getType() + != XmlRpcValue::TypeString) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistOpenMethodName + << "' returned unexpected value:\n" + << result; + throw XmlRpcMethodResponseException(eMsg.str()); + } + Ptr::Ref token(new std::string( + result[getPlaylistTokenParamName] )); + + Ptr::Ref playlist = acquirePlaylist(id, result); + playlist->setToken(token); + + return playlist; +} + + +/*------------------------------------------------------------------------------ + * Acquire resources for a playlist, step 2: create the temp files. + *----------------------------------------------------------------------------*/ +Ptr::Ref +WebStorageClient :: acquirePlaylist(Ptr::Ref id, + XmlRpcValue & content) const + throw (Core::XmlRpcException) +{ + // construct the playlist + if (! content.hasMember(getPlaylistUrlParamName) + || content[getPlaylistUrlParamName].getType() + != XmlRpcValue::TypeString) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistOpenMethodName + << "' returned unexpected value:\n" + << content; + throw XmlRpcMethodResponseException(eMsg.str()); + } + const std::string url = content[getPlaylistUrlParamName]; + + Ptr::Ref playlist(new Playlist(id)); + try { + Ptr::Ref parser(new xmlpp::DomParser()); + parser->parse_file(url); + const xmlpp::Document * document = parser->get_document(); + const xmlpp::Element * root = document->get_root_node(); + + playlist->configure(*root); + + } catch (std::invalid_argument &e) { + throw XmlRpcInvalidDataException( + "semantic error in playlist metafile"); + } catch (xmlpp::exception &e) { + throw XmlRpcInvalidDataException( + "error parsing playlist metafile"); + } + + // read the content array corresponding to the playlist + if (! content.hasMember(getPlaylistContentParamName) + || content[getPlaylistContentParamName].getType() + != XmlRpcValue::TypeArray) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistOpenMethodName + << "' returned unexpected value:\n" + << content; + throw XmlRpcMethodResponseException(eMsg.str()); + } + XmlRpcValue innerContent = content[getPlaylistContentParamName]; + + // construct the SMIL file Ptr::Ref smilDocument(new xmlpp::Document(xmlVersion)); xmlpp::Element * smilRootNode @@ -1301,48 +1363,32 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref oldPlaylist, xmlpp::Element * smilParNode = smilBodyNode->add_child(smilParNodeName); - Playlist::const_iterator it = oldPlaylist->begin(); - if (result.hasMember(getPlaylistContentParamName)) { - content = result[getPlaylistContentParamName]; - } - // if there is no content parameter, leave the original, empty content - - index = 0; - // assume that it is as long as the size of the content array - while (it != oldPlaylist->end()) { + // we assume that the playlist is as long as the size of the content array + Playlist::const_iterator it = playlist->begin(); + int index = 0; + while (it != playlist->end() && index < innerContent.size()) { Ptr::Ref plElement = it->second; Ptr::Ref relativeOffset = plElement->getRelativeOffset(); Ptr::Ref fadeInfo = plElement->getFadeInfo(); - if (index >= content.size()) { - break; - } - XmlRpcValue contents = content[index]; - ++index; - if (!contents.hasMember(getPlaylistUrlParamName)) { - // TODO: maybe signal error? - continue; - } + XmlRpcValue contentElement = innerContent[index]; Ptr::Ref playable; Ptr::Ref url; - Ptr::Ref audioClip; - Ptr::Ref playlist; + Ptr::Ref subPlaylistId; + switch (plElement->getType()) { case PlaylistElement::AudioClipType : - url.reset(new std::string(contents[getPlaylistUrlParamName])); - audioClip.reset(new AudioClip(*plElement->getAudioClip())); - audioClip->setUri(url); - url.reset(); - newPlaylist->addPlayable(audioClip, relativeOffset, fadeInfo); - playable = audioClip; + url.reset(new std::string( + contentElement[getPlaylistUrlParamName])); + playable = plElement->getAudioClip(); break; case PlaylistElement::PlaylistType : - playlist.reset(new Playlist(*plElement->getPlaylist())); - playlist = acquirePlaylist(playlist, contents); - newPlaylist->addPlayable(playlist, relativeOffset, fadeInfo); - playable = playlist; + subPlaylistId = plElement->getPlaylist()->getId(); + playable = acquirePlaylist(subPlaylistId, contentElement); + url = playable->getUri(); + plElement->setPlayable(playable); break; default : // this should never happen throw XmlRpcInvalidArgumentException( @@ -1354,7 +1400,7 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref oldPlaylist, = smilParNode->add_child(smilPlayableNodeName); smilPlayableNode->set_attribute( smilPlayableUriAttrName, - *playable->getUri() ); + *url ); smilPlayableNode->set_attribute( smilRelativeOffsetAttrName, *TimeConversion::timeDurationToSmilString( @@ -1430,19 +1476,20 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref oldPlaylist, smilAnimateFillAttrValue ); } } - ++it; + ++it; + ++index; } std::stringstream fileName; - fileName << localTempStorage << std::string(*newPlaylist->getId()) + fileName << localTempStorage << std::string(*playlist->getId()) << "-" << std::rand() << ".smil"; smilDocument->write_to_file_formatted(fileName.str(), "UTF-8"); Ptr::Ref playlistUri(new std::string(fileName.str())); - newPlaylist->setUri(playlistUri); + playlist->setUri(playlistUri); - return newPlaylist; + return playlist; } @@ -1452,11 +1499,91 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref oldPlaylist, void WebStorageClient :: releasePlaylist(Ptr::Ref playlist) const throw (Core::XmlRpcException) +{ + if (playlist->getToken()) { + releasePlaylistFromServer(playlist); + } + + releasePlaylistTempFile(playlist); +} + + +/*------------------------------------------------------------------------------ + * Release a playlist, step 1: release access URLs at the storage server. + *----------------------------------------------------------------------------*/ +void +WebStorageClient :: releasePlaylistFromServer( + Ptr::Ref playlist) const + throw (Core::XmlRpcException) +{ + if (! playlist->getToken()) { + throw XmlRpcInvalidArgumentException("releasePlaylist() called without" + " acquirePlaylist()"); + } + + XmlRpcValue parameters; + XmlRpcValue result; + + XmlRpcClient xmlRpcClient(storageServerName.c_str(), storageServerPort, + storageServerPath.c_str(), false); + + parameters.clear(); + parameters[getPlaylistTokenParamName] = std::string( + *playlist->getToken()); + // TODO: remove the 'recursive' param from locstor.releasePlaylist because + // it is error-prone; should always use the same value as for accessPlaylist + parameters[getPlaylistRecursiveParamName] = true; + + result.clear(); + if (!xmlRpcClient.execute(getPlaylistCloseMethodName.c_str(), + parameters, result)) { + xmlRpcClient.close(); + std::string eMsg = "cannot execute XML-RPC method '"; + eMsg += getPlaylistCloseMethodName; + eMsg += "'"; + throw XmlRpcCommunicationException(eMsg); + } + xmlRpcClient.close(); + + if (xmlRpcClient.isFault()) { + std::stringstream eMsg; + eMsg << "XML-RPC method '" + << getPlaylistCloseMethodName + << "' returned error message:\n" + << result; + throw Core::XmlRpcMethodFaultException(eMsg.str()); + } + + if (! result.hasMember(getPlaylistPlaylistIdParamName) + || result[getPlaylistPlaylistIdParamName].getType() + != XmlRpcValue::TypeString + || std::string(result[getPlaylistPlaylistIdParamName]) + != std::string(*playlist->getId())) { + std::stringstream eMsg; + eMsg << "Mismatched playlist ID from XML-RPC method '" + << getPlaylistCloseMethodName + << "': " + << result[getPlaylistPlaylistIdParamName] + << " instead of " + << std::string(*playlist->getId()) + << "."; +// removed temporarily; see ticket #1468 +// throw XmlRpcMethodResponseException(eMsg.str()); + } +} + + +/*------------------------------------------------------------------------------ + * Release a playlist, step 2: delete the temp file. + *----------------------------------------------------------------------------*/ +void +WebStorageClient :: releasePlaylistTempFile(Ptr::Ref playlist) const + throw (Core::XmlRpcException) { if (! playlist->getUri()) { throw XmlRpcInvalidArgumentException("playlist URI not found"); } - + std::ifstream ifs(playlist->getUri()->substr(7).c_str()); if (!ifs) { // cut of "file://" ifs.close(); @@ -1471,16 +1598,15 @@ WebStorageClient :: releasePlaylist(Ptr::Ref playlist) const while (it != playlist->end()) { Ptr::Ref plElement = it->second; if (plElement->getType() == PlaylistElement::AudioClipType) { - // don't have to release, as it will be done by the storage server - // for clips inside the playlist + // no temp file; nothing to do ++it; } else if (plElement->getType() == PlaylistElement::PlaylistType) { try { - releasePlaylist(it->second->getPlaylist()); + releasePlaylistTempFile(it->second->getPlaylist()); } catch (XmlRpcException &e) { eMsg += e.what(); - eMsg += "\n"; + eMsg += '\n'; } ++it; } else { diff --git a/livesupport/modules/storage/src/WebStorageClient.h b/livesupport/modules/storage/src/WebStorageClient.h index 1e8c13b10..08bd27524 100644 --- a/livesupport/modules/storage/src/WebStorageClient.h +++ b/livesupport/modules/storage/src/WebStorageClient.h @@ -163,7 +163,8 @@ class WebStorageClient : * @param sessionId the session ID from the authentication client * @param id the id of the playlist to return. * @param url pointer in which the URL of the playlist is returned. - * @param token pointer in which the token of the playlist is returned. + * @param editToken pointer in which the token of the playlist is + * returned. * @exception XmlRpcException if there is a problem with the XML-RPC * call or no playlist with the specified * id exists. @@ -172,50 +173,53 @@ class WebStorageClient : editPlaylistGetUrl(Ptr::Ref sessionId, Ptr::Ref id, Ptr::Ref& url, - Ptr::Ref& token) + Ptr::Ref& editToken) throw (XmlRpcException); /** - * Return a playlist with the specified id to be displayed. - * If the playlist is being edited, and this method is called - * by the same user who is editing the playlist, - * (i.e., the method is called with the same sessionId and playlistId - * that editPlaylist() was), then the working copy of the playlist - * is returned. - * Any other user gets the old (pre-editPlaylist()) copy from storage. - * - * @param sessionId the session ID from the authentication client - * @param id the id of the playlist to return. - * @param deep signal if all playable objects are to be accessed - * and locked, that are referenced by this playlist, or - * any playlist contained in this one. - * @return the requested playlist. - * @exception XmlRpcInvalidDataException if the audio clip we got - * from the storage is invalid. - * @exception XmlRpcException if there is a problem with the XML-RPC - * call or no playlist with the specified id exists. - */ - Ptr::Ref - getPlaylist(Ptr::Ref sessionId, - Ptr::Ref id, - bool deep) const - throw (XmlRpcException); - - /** - * Do the final stages of acquring a playlist: generate SMIL - * files, etc. + * Do the second step of acquring a playlist: generate the SMIL + * temp files. * - * @param oldPlaylist the playlist to work on. - * @param result the XML-RPC result from getPlaylist() call - * with deep == true + * @param id the ID of the new playlist to be constructed + * @param content the XML-RPC result from locstor.accessPlaylist * @return a playlist which has a URI filed, and all things below - * it are openned properly and processed as well. + * it are opened properly and processed as well. * @exception XmlRpcException if there is a problem with the XML-RPC * call or no playlist with the specified id exists. */ Ptr::Ref - acquirePlaylist(Ptr::Ref oldPlaylist, - XmlRpcValue & result) const + acquirePlaylist(Ptr::Ref id, + XmlRpcValue & content) const + throw (XmlRpcException); + + /** + * Execute the XML-RPC function call to release the playlist + * access URL at the storage server. + * + * This needs to be done for the outermost playlist only; the + * sub-playlists and audio clips contained inside are released + * recursively by the storage server automatically. + * + * @param playlist the playlist to release. + * @exception XmlRpcException if there is a problem with the XML-RPC + * call or the playlist has no token field + */ + void + releasePlaylistFromServer(Ptr::Ref playlist) const + throw (XmlRpcException); + + /** + * Remove the temporary SMIL file created for the playlist. + * + * This needs to be done for every playlist, including sub-playlists + * contained inside other playlists. + * + * @param playlist the playlist to release. + * @exception XmlRpcException if there is a problem with the XML-RPC + * call or the playlist has no uri field + */ + void + releasePlaylistTempFile(Ptr::Ref playlist) const throw (XmlRpcException); @@ -313,10 +317,7 @@ class WebStorageClient : virtual Ptr::Ref getPlaylist(Ptr::Ref sessionId, Ptr::Ref id) const - throw (XmlRpcException) - { - return getPlaylist(sessionId, id, false); - } + throw (XmlRpcException); /** * Return a playlist with the specified id to be edited. @@ -364,13 +365,13 @@ class WebStorageClient : * the playlist (and lose all changes) at the next login using * this method. * - * @param playlistToken the token of the edited playlist + * @param editToken the token of the edited playlist * @exception XmlRpcException if there is a problem with the XML-RPC * call or no playlist with the specified * token exists. */ virtual void - revertPlaylist(Ptr::Ref playlistToken) + revertPlaylist(Ptr::Ref editToken) throw (XmlRpcException); @@ -401,12 +402,7 @@ class WebStorageClient : virtual Ptr::Ref acquirePlaylist(Ptr::Ref sessionId, Ptr::Ref id) const - throw (XmlRpcException) - { - // FIXME: silently, with deep == true, getPlaylist will also - // generate all related SMIL files, etc. - return getPlaylist(sessionId, id, true); - } + throw (XmlRpcException); /** * Release the resources (audio clips, other playlists) used diff --git a/livesupport/modules/storageServer/var/tests/plist2.xml b/livesupport/modules/storageServer/var/tests/plist2.xml index 885c2f0a6..7e36ca9b1 100644 --- a/livesupport/modules/storageServer/var/tests/plist2.xml +++ b/livesupport/modules/storageServer/var/tests/plist2.xml @@ -1,5 +1,5 @@ - * * - * On success, returns a XML-RPC struct with single field: + * On success, returns an XML-RPC struct with the following fields: *
    *
  • url : string - readable url of accessed playlist in * XML format
  • @@ -967,6 +967,11 @@ class XR_LocStor extends LocStor{ *
  • content: array of structs - recursive access (optional)
  • *
* + * The content field contains a struct for each playlist + * element contained in the playlist. For audio clips, this struct is + * of type {url, token}; for sub-playlists, it is of type + * {url, token, chsum, content}. + * * On errors, returns an XML-RPC error response. * The possible error codes and error message are: *