/*------------------------------------------------------------------------------ Copyright (c) 2004 Media Development Loan Fund This file is part of the LiveSupport project. http://livesupport.campware.org/ To report bugs, send an e-mail to bugs@campware.org LiveSupport is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LiveSupport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LiveSupport; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author : $Author: maroy $ Version : $Revision: 1.26 $ Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/storage/src/TestStorageClient.cxx,v $ ------------------------------------------------------------------------------*/ /* ============================================================ include files */ #ifdef HAVE_CONFIG_H #include "configure.h" #endif #if HAVE_UNISTD_H #include #else #error "Need unistd.h" #endif #include #include #include "LiveSupport/Core/XmlRpcInvalidArgumentException.h" #include "LiveSupport/Core/XmlRpcIOException.h" #include "TestStorageClient.h" using namespace boost::posix_time; using namespace LiveSupport::Core; using namespace LiveSupport::Storage; /* =================================================== local data structures */ /* ================================================ local constants & macros */ /*------------------------------------------------------------------------------ * The name of the config element for this class *----------------------------------------------------------------------------*/ const std::string TestStorageClient::configElementNameStr = "testStorage"; /*------------------------------------------------------------------------------ * The name of the config element attribute for the temp files *----------------------------------------------------------------------------*/ static const std::string localTempStorageAttrName = "tempFiles"; /*------------------------------------------------------------------------------ * The XML version used to create the SMIL file. *----------------------------------------------------------------------------*/ static const std::string xmlVersion = "1.0"; /*------------------------------------------------------------------------------ * The name of the SMIL root node. *----------------------------------------------------------------------------*/ static const std::string smilRootNodeName = "smil"; /*------------------------------------------------------------------------------ * The name of the SMIL language description attribute. *----------------------------------------------------------------------------*/ static const std::string smilLanguageAttrName = "xmlns"; /*------------------------------------------------------------------------------ * The value of the SMIL language description attribute. *----------------------------------------------------------------------------*/ static const std::string smilLanguageAttrValue = "http://www.w3.org/2001/SMIL20/Language"; /*------------------------------------------------------------------------------ * The name of the SMIL real networks extension attribute. *----------------------------------------------------------------------------*/ static const std::string smilExtensionsAttrName = "xmlns:rn"; /*------------------------------------------------------------------------------ * The value of the SMIL real networks extension attribute. *----------------------------------------------------------------------------*/ static const std::string smilExtensionsAttrValue = "http://features.real.com/2001/SMIL20/Extensions"; /*------------------------------------------------------------------------------ * The name of the body node in the SMIL file. *----------------------------------------------------------------------------*/ static const std::string smilBodyNodeName = "body"; /*------------------------------------------------------------------------------ * The name of the sequential audio clip list node in the SMIL file. *----------------------------------------------------------------------------*/ static const std::string smilSeqNodeName = "seq"; /*------------------------------------------------------------------------------ * The name of the audio clip element node in the SMIL file. *----------------------------------------------------------------------------*/ static const std::string smilAudioClipNodeName = "audio"; /*------------------------------------------------------------------------------ * The name of the attribute containing the URI of the audio clip element. *----------------------------------------------------------------------------*/ static const std::string smilAudioClipUriAttrName = "src"; /*------------------------------------------------------------------------------ * The name of the sub-playlist element node in the SMIL file. *----------------------------------------------------------------------------*/ static const std::string smilPlaylistNodeName = "audio"; /*------------------------------------------------------------------------------ * The name of the attribute containing the URI of the sub-playlist element. *----------------------------------------------------------------------------*/ static const std::string smilPlaylistUriAttrName = "src"; /* =============================================== local function prototypes */ /* ============================================================= module code */ /*------------------------------------------------------------------------------ * Configure the test storage client. *----------------------------------------------------------------------------*/ void TestStorageClient :: configure(const xmlpp::Element & element) throw (std::invalid_argument) { if (element.get_name() != configElementNameStr) { std::string eMsg = "Bad configuration element "; eMsg += element.get_name(); throw std::invalid_argument(eMsg); } const xmlpp::Attribute * attribute; if (!(attribute = element.get_attribute(localTempStorageAttrName))) { std::string eMsg = "Missing attribute "; eMsg += localTempStorageAttrName; throw std::invalid_argument(eMsg); } localTempStorage = attribute->get_value(); // iterate through the playlist elements ... xmlpp::Node::NodeList nodes = element.get_children(Playlist::getConfigElementName()); xmlpp::Node::NodeList::iterator it = nodes.begin(); playlistMap.clear(); while (it != nodes.end()) { Ptr::Ref playlist(new Playlist); const xmlpp::Element * element = dynamic_cast (*it); playlist->configure(*element); playlistMap[playlist->getId()->getId()] = playlist; ++it; } // ... and the the audio clip elements nodes = element.get_children(AudioClip::getConfigElementName()); it = nodes.begin(); audioClipMap.clear(); while (it != nodes.end()) { Ptr::Ref audioClip(new AudioClip); const xmlpp::Element * element = dynamic_cast (*it); audioClip->configure(*element); audioClipMap[audioClip->getId()->getId()] = audioClip; ++it; } } /*------------------------------------------------------------------------------ * Tell if a playlist exists. *----------------------------------------------------------------------------*/ const bool TestStorageClient :: existsPlaylist(Ptr::Ref sessionId, Ptr::Ref id) const throw () { return playlistMap.count(id->getId()) == 1 ? true : false; } /*------------------------------------------------------------------------------ * Return a playlist to be displayed. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: getPlaylist(Ptr::Ref sessionId, Ptr::Ref id) const throw (XmlRpcException) { PlaylistMap::const_iterator it = playlistMap.find(id->getId()); if (it == playlistMap.end()) { throw XmlRpcException("no such playlist"); } return it->second; } /*------------------------------------------------------------------------------ * Return a playlist to be edited. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: editPlaylist(Ptr::Ref sessionId, Ptr::Ref id) const throw (XmlRpcException) { PlaylistMap::const_iterator it = playlistMap.find(id->getId()); if (it == playlistMap.end()) { throw XmlRpcException("no such playlist"); } return it->second; } /*------------------------------------------------------------------------------ * Save a playlist after editing. *----------------------------------------------------------------------------*/ void TestStorageClient :: savePlaylist(Ptr::Ref sessionId, Ptr::Ref playlist) const throw () { } /*------------------------------------------------------------------------------ * Acquire resources for a playlist. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: acquirePlaylist(Ptr::Ref sessionId, Ptr::Ref id) const throw (XmlRpcException) { PlaylistMap::const_iterator playlistMapIt = playlistMap.find(id->getId()); if (playlistMapIt == playlistMap.end()) { throw XmlRpcInvalidArgumentException("no such playlist"); } Ptr::Ref oldPlaylist = playlistMapIt->second; Ptr::Ref playlength = oldPlaylist->getPlaylength(); Ptr::Ref newPlaylist(new Playlist(UniqueId::generateId(), playlength)); Ptr::Ref smilDocument(new xmlpp::Document(xmlVersion)); xmlpp::Element * smilRootNode = smilDocument->create_root_node(smilRootNodeName); smilRootNode->set_attribute(smilLanguageAttrName, smilLanguageAttrValue); smilRootNode->set_attribute(smilExtensionsAttrName, smilExtensionsAttrValue); xmlpp::Element * smilBodyNode = smilRootNode->add_child(smilBodyNodeName); xmlpp::Element * smilSeqNode = smilBodyNode->add_child(smilSeqNodeName); Playlist::const_iterator it = oldPlaylist->begin(); while (it != oldPlaylist->end()) { Ptr::Ref plElement = it->second; Ptr::Ref fadeInfo = plElement->getFadeInfo(); if (plElement->getType() == PlaylistElement::AudioClipType) { Ptr::Ref audioClip = acquireAudioClip(sessionId, plElement ->getAudioClip() ->getId()); Ptr::Ref relativeOffset = plElement->getRelativeOffset(); newPlaylist->addAudioClip(audioClip, relativeOffset, fadeInfo); xmlpp::Element* smilAudioClipNode = smilSeqNode->add_child(smilAudioClipNodeName); smilAudioClipNode->set_attribute( smilAudioClipUriAttrName, *(audioClip->getUri()) ); ++it; } else if (plElement->getType() == PlaylistElement::PlaylistType) { Ptr::Ref playlist = acquirePlaylist(sessionId, plElement ->getPlaylist() ->getId()); Ptr::Ref relativeOffset = plElement->getRelativeOffset(); newPlaylist->addPlaylist(playlist, relativeOffset, fadeInfo); xmlpp::Element* smilPlaylistNode = smilSeqNode->add_child(smilPlaylistNodeName); smilPlaylistNode->set_attribute( smilPlaylistUriAttrName, *(playlist->getUri()) ); ++it; } else { // this should never happen throw XmlRpcInvalidArgumentException( "unexpected playlist element type " "(neither audio clip nor playlist)"); } } std::stringstream fileName; fileName << localTempStorage << std::string(*newPlaylist->getId()) << "-" << std::rand() << ".smil"; smilDocument->write_to_file(fileName.str(), "UTF-8"); Ptr::Ref playlistUri(new std::string(fileName.str())); newPlaylist->setUri(playlistUri); return newPlaylist; } /*------------------------------------------------------------------------------ * Release a playlist. *----------------------------------------------------------------------------*/ void TestStorageClient :: releasePlaylist(Ptr::Ref sessionId, Ptr::Ref playlist) const throw (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(); throw XmlRpcIOException("playlist temp file not found"); } ifs.close(); std::remove(playlist->getUri()->substr(7).c_str()); std::string eMsg = ""; Playlist::const_iterator it = playlist->begin(); while (it != playlist->end()) { Ptr::Ref plElement = it->second; if (plElement->getType() == PlaylistElement::AudioClipType) { try { releaseAudioClip(sessionId, it->second->getAudioClip()); } catch (XmlRpcException &e) { eMsg += e.what(); eMsg += "\n"; } ++it; } else if (plElement->getType() == PlaylistElement::PlaylistType) { try { releasePlaylist(sessionId, it->second->getPlaylist()); } catch (XmlRpcException &e) { eMsg += e.what(); eMsg += "\n"; } ++it; } else { // this should never happen eMsg += "unexpected playlist element type\n"; } } Ptr::Ref nullPointer; playlist->setUri(nullPointer); if (eMsg != "") { eMsg.insert(0, "some playlist elements could not be released:\n"); throw XmlRpcInvalidArgumentException(eMsg); } } /*------------------------------------------------------------------------------ * Delete a playlist. *----------------------------------------------------------------------------*/ void TestStorageClient :: deletePlaylist(Ptr::Ref sessionId, Ptr::Ref id) throw (XmlRpcException) { // erase() returns the number of entries found & erased if (!playlistMap.erase(id->getId())) { throw XmlRpcException("no such playlist"); } } /*------------------------------------------------------------------------------ * Return a listing of all the playlists in the playlist store. *----------------------------------------------------------------------------*/ Ptr::Ref> >::Ref TestStorageClient :: getAllPlaylists(Ptr::Ref sessionId) const throw () { PlaylistMap::const_iterator it = playlistMap.begin(); Ptr::Ref> >::Ref playlistVector (new std::vector::Ref>); while (it != playlistMap.end()) { playlistVector->push_back(it->second); ++it; } return playlistVector; } /*------------------------------------------------------------------------------ * Create a new playlist. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: createPlaylist(Ptr::Ref sessionId) throw () { // generate a new UniqueId -- TODO: fix UniqueId to make sure // this is really unique; not checked here! Ptr::Ref playlistId = Ptr::Ref(UniqueId :: generateId()); Ptr::Ref playLength = Ptr::Ref(new time_duration(0,0,0)); Ptr::Ref playlist = Ptr::Ref(new Playlist(playlistId, playLength)); playlistMap[playlistId->getId()] = playlist; return playlist; } /*------------------------------------------------------------------------------ * Tell if an audio clip exists. *----------------------------------------------------------------------------*/ const bool TestStorageClient :: existsAudioClip(Ptr::Ref sessionId, Ptr::Ref id) const throw () { return audioClipMap.count(id->getId()) == 1 ? true : false; } /*------------------------------------------------------------------------------ * Return an audio clip. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: getAudioClip(Ptr::Ref sessionId, Ptr::Ref id) const throw (XmlRpcException) { AudioClipMap::const_iterator it = audioClipMap.find(id->getId()); if (it == audioClipMap.end()) { throw XmlRpcException("no such audio clip"); } return it->second; } /*------------------------------------------------------------------------------ * Store an audio clip. *----------------------------------------------------------------------------*/ void TestStorageClient :: storeAudioClip(Ptr::Ref sessionId, Ptr::Ref audioClip) throw (XmlRpcException) { if (!audioClip->getId()) { audioClip->setId(UniqueId::generateId()); } audioClipMap[audioClip->getId()->getId()] = audioClip; } /*------------------------------------------------------------------------------ * Acquire resources for an audio clip. *----------------------------------------------------------------------------*/ Ptr::Ref TestStorageClient :: acquireAudioClip(Ptr::Ref sessionId, Ptr::Ref id) const throw (XmlRpcException) { AudioClipMap::const_iterator it = audioClipMap.find(id->getId()); if (it == audioClipMap.end()) { throw XmlRpcException("no such audio clip"); } Ptr::Ref storedAudioClip = it->second; if (! storedAudioClip->getUri()) { throw XmlRpcException("audio clip URI not found"); } // cut the "file:" off std::string audioClipFileName = storedAudioClip->getUri()->substr(5); std::ifstream ifs(audioClipFileName.c_str()); if (!ifs) { ifs.close(); throw XmlRpcException("could not read audio clip"); } ifs.close(); Ptr::Ref audioClip(new AudioClip(*storedAudioClip)); Ptr::Ref audioClipUri(new std::string("file://")); *audioClipUri += get_current_dir_name(); // doesn't work if current *audioClipUri += "/"; // dir = /, but OK for now *audioClipUri += audioClipFileName; audioClip->setUri(audioClipUri); return audioClip; } /*------------------------------------------------------------------------------ * Release an audio clip. *----------------------------------------------------------------------------*/ void TestStorageClient :: releaseAudioClip(Ptr::Ref sessionId, Ptr::Ref audioClip) const throw (XmlRpcException) { if (*(audioClip->getUri()) == "") { throw XmlRpcException("audio clip URI not found"); } Ptr::Ref nullPointer; audioClip->setUri(nullPointer); } /*------------------------------------------------------------------------------ * Delete an audio clip. *----------------------------------------------------------------------------*/ void TestStorageClient :: deleteAudioClip(Ptr::Ref sessionId, Ptr::Ref id) throw (XmlRpcException) { // erase() returns the number of entries found & erased if (!audioClipMap.erase(id->getId())) { throw XmlRpcException("no such audio clip"); } } /*------------------------------------------------------------------------------ * Return a listing of all the audio clips in the audio clip store. *----------------------------------------------------------------------------*/ Ptr::Ref> >::Ref TestStorageClient :: getAllAudioClips(Ptr::Ref sessionId) const throw () { AudioClipMap::const_iterator it = audioClipMap.begin(); Ptr::Ref> >::Ref audioClipVector (new std::vector::Ref>); while (it != audioClipMap.end()) { audioClipVector->push_back(it->second); ++it; } return audioClipVector; }