MythTV  0.26-pre
servicehost.cpp
Go to the documentation of this file.
00001 
00002 // Program Name: servicehost.cpp
00003 // Created     : Jan. 19, 2010
00004 //
00005 // Purpose     : Service Host Abstract Class 
00006 //                                                                            
00007 // Copyright (c) 2010 David Blain <dblain@mythtv.org>
00008 //                                          
00009 // Licensed under the GPL v2 or later, see COPYING for details                    
00010 //
00012 
00013 #include <QDomDocument>
00014 
00015 #include "mythlogging.h"
00016 #include "servicehost.h"
00017 #include "wsdl.h"
00018 #include "xsd.h"
00019 
00020 #define _MAX_PARAMS 256
00021 
00023 //
00025 
00026 MethodInfo::MethodInfo()
00027 {
00028     m_eRequestType = (RequestType)(RequestTypeGet | RequestTypePost |
00029                                    RequestTypeHead);
00030 }
00031 
00033 //
00035 
00036 QVariant MethodInfo::Invoke( Service *pService, const QStringMap &reqParams )
00037 {
00038     HttpRedirectException exception;
00039     bool                  bExceptionThrown = false;
00040 
00041     if (!pService)
00042         throw;
00043 
00044     QList<QByteArray> paramNames = m_oMethod.parameterNames();
00045     QList<QByteArray> paramTypes = m_oMethod.parameterTypes();
00046 
00047     // ----------------------------------------------------------------------
00048     // Create Parameter array (Can't have more than _MAX_PARAMS parameters)....
00049     // switched to static array for performance.
00050     // ----------------------------------------------------------------------
00051 
00052     void *param[ _MAX_PARAMS ];
00053     int   types[ _MAX_PARAMS ];
00054 
00055     memset( param, 0, _MAX_PARAMS * sizeof(void *));
00056     memset( types, 0, _MAX_PARAMS * sizeof(int));
00057 
00058     try
00059     {
00060         // --------------------------------------------------------------
00061         // Add a place for the Return value
00062         // --------------------------------------------------------------
00063 
00064         int nRetIdx = QMetaType::type( m_oMethod.typeName() ); 
00065 
00066         if (nRetIdx != 0)
00067         {
00068             param[ 0 ] = QMetaType::construct( nRetIdx );    
00069             types[ 0 ] = nRetIdx;
00070         }
00071         else
00072         {
00073             param[ 0 ] = NULL;    
00074             types[ 0 ] = 0;
00075         }
00076 
00077         // --------------------------------------------------------------
00078         // Fill in parameters from request values
00079         // --------------------------------------------------------------
00080 
00081         for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
00082         {
00083             QString sValue     = reqParams[ paramNames[ nIdx ] ];
00084             QString sParamType = paramTypes[ nIdx ];
00085 
00086             int     nId        = QMetaType::type( paramTypes[ nIdx ] );
00087             void   *pParam     = NULL;
00088 
00089             if (nId != 0)
00090             {
00091                 pParam = QMetaType::construct( nId );
00092             }
00093             else
00094             {
00095                 LOG(VB_GENERAL, LOG_ERR,
00096                     QString("MethodInfo::Invoke - Type unknown '%1'")
00097                         .arg(sParamType));
00098             }
00099 
00100             types[nIdx+1] = nId;
00101             param[nIdx+1] = pService->ConvertToParameterPtr( nId, sParamType,
00102                                                              pParam, sValue );
00103         }
00104 
00105 #if 0
00106         QThread *currentThread = QThread::currentThread();
00107         QThread *objectThread  = pService->thread();
00108 
00109         if (currentThread == objectThread)
00110             LOG(VB_UPNP, LOG_DEBUG, "*** Threads are same ***");
00111         else
00112             LOG(VB_UPNP, LOG_DEBUG, "*** Threads are Different!!! ***");
00113 #endif
00114 
00115         pService->qt_metacall( QMetaObject::InvokeMetaMethod, 
00116                                m_nMethodIndex, 
00117                                param );
00118 
00119         // --------------------------------------------------------------
00120         // Delete param array, skip return parameter since not dynamically
00121         // created.
00122         // --------------------------------------------------------------
00123 
00124         for (int nIdx=1; nIdx < paramNames.length()+1; nIdx++)
00125         {
00126             if ((types[ nIdx ] != 0) && (param[ nIdx ] != NULL))
00127                 QMetaType::destroy( types[ nIdx ], param[ nIdx ] );
00128         }
00129     }
00130     catch (QString &sMsg)
00131     {
00132         LOG(VB_GENERAL, LOG_ERR,
00133             QString("MethodInfo::Invoke - An Exception Occurred: %1")
00134                  .arg(sMsg));
00135 
00136         if  ((types[ 0 ] != 0) && (param[ 0 ] != NULL ))
00137             QMetaType::destroy( types[ 0 ], param[ 0 ] );
00138 
00139         throw;
00140     }
00141     catch (HttpRedirectException &ex)
00142     {
00143         bExceptionThrown = true;
00144         exception = ex;
00145     }
00146     catch (...)
00147     {
00148         LOG(VB_GENERAL, LOG_INFO,
00149             "MethodInfo::Invoke - An Exception Occurred" );
00150     }
00151 
00152     // --------------------------------------------------------------
00153     // return the result after converting to a QVariant
00154     // --------------------------------------------------------------
00155 
00156     QVariant vReturn;
00157   
00158     if ( param[ 0 ] != NULL)
00159     {
00160         vReturn = pService->ConvertToVariant( types[ 0 ], param[ 0 ] );
00161 
00162         if  (types[ 0 ] != 0)
00163             QMetaType::destroy( types[ 0 ], param[ 0 ] );
00164     }
00165 
00166     // --------------------------------------------------------------
00167     // Re-throw exception if needed.
00168     // --------------------------------------------------------------
00169 
00170     if (bExceptionThrown)
00171         throw exception;
00172 
00173     return vReturn;
00174 }
00175 
00178 //
00179 //
00180 //
00183 
00184 ServiceHost::ServiceHost(const QMetaObject &metaObject,
00185                          const QString     &sExtensionName,
00186                          const QString     &sBaseUrl,
00187                          const QString     &sSharePath ) 
00188             : HttpServerExtension ( sExtensionName,   sSharePath )
00189 {
00190     m_oMetaObject = metaObject;
00191     m_sBaseUrl    = sBaseUrl;
00192 
00193     // --------------------------------------------------------------
00194     // Read in all callable methods and cache information about them
00195     // --------------------------------------------------------------
00196 
00197     for (int nIdx = 0; nIdx < m_oMetaObject.methodCount(); nIdx++)
00198     {
00199         QMetaMethod method = m_oMetaObject.method( nIdx );
00200 
00201         if ((method.methodType() == QMetaMethod::Slot   ) &&
00202             (method.access()     == QMetaMethod::Public ))
00203         {
00204             QString sName( method.signature() );
00205 
00206             // ------------------------------------------------------
00207             // Ignore the following methods...
00208             // ------------------------------------------------------
00209 
00210             if (sName == "deleteLater()")
00211                 continue;
00212 
00213             // ------------------------------------------------------
00214 
00215             MethodInfo oInfo;
00216 
00217             oInfo.m_nMethodIndex = nIdx;
00218             oInfo.m_sName        = sName.section( '(', 0, 0 );
00219             oInfo.m_oMethod      = method;
00220             oInfo.m_eRequestType = (RequestType)(RequestTypeGet |
00221                                                  RequestTypePost |
00222                                                  RequestTypeHead);
00223 
00224             QString sMethodClassInfo = oInfo.m_sName + "_Method";
00225 
00226             int nClassIdx =
00227                 m_oMetaObject.indexOfClassInfo(sMethodClassInfo.toAscii());
00228 
00229             if (nClassIdx >=0)
00230             {
00231                 QString sRequestType =
00232                     m_oMetaObject.classInfo(nClassIdx).value();
00233 
00234                 if (sRequestType == "POST")
00235                     oInfo.m_eRequestType = RequestTypePost;
00236                 else if (sRequestType == "GET" )
00237                     oInfo.m_eRequestType = (RequestType)(RequestTypeGet |
00238                                                          RequestTypeHead);
00239             }
00240 
00241             m_Methods.insert( oInfo.m_sName, oInfo );
00242         }
00243     }
00244 }
00245 
00247 //
00249 
00250 ServiceHost::~ServiceHost()
00251 {
00252 }
00253 
00255 //
00257 
00258 QStringList ServiceHost::GetBasePaths() 
00259 { 
00260     return QStringList( m_sBaseUrl );
00261 }
00262 
00264 //
00266 
00267 bool ServiceHost::ProcessRequest( HTTPRequest *pRequest )
00268 {
00269     bool     bHandled = false;
00270     Service *pService = NULL;
00271 
00272     try
00273     {
00274         if (pRequest)
00275         {
00276             if (pRequest->m_sBaseUrl != m_sBaseUrl)
00277                 return false;
00278 
00279             LOG(VB_UPNP, LOG_INFO,
00280                 QString("ServiceHost::ProcessRequest: %1 : %2")
00281                     .arg(pRequest->m_sMethod) .arg(pRequest->m_sRawRequest));
00282 
00283             // --------------------------------------------------------------
00284             // Check to see if they are requesting the WSDL service Definition
00285             // --------------------------------------------------------------
00286 
00287             if (( pRequest->m_eType   == RequestTypeGet ) &&
00288                 ( pRequest->m_sMethod == "wsdl"         ))
00289             {
00290                 pService =  qobject_cast<Service*>(m_oMetaObject.newInstance());
00291 
00292                 Wsdl wsdl( this );
00293 
00294                 wsdl.GetWSDL( pRequest );
00295 
00296                 delete pService;
00297                 return true;
00298             }
00299 
00300             // --------------------------------------------------------------
00301             // Check to see if they are requesting XSD - Type Definition
00302             // --------------------------------------------------------------
00303 
00304             if (( pRequest->m_eType   == RequestTypeGet ) &&
00305                 ( pRequest->m_sMethod == "xsd"          ))
00306             {
00307                 if ( pRequest->m_mapParams.count() > 0)
00308                 {
00309                     pService =  qobject_cast<Service*>(m_oMetaObject.newInstance());
00310 
00311                     Xsd xsd;
00312 
00313                     xsd.GetXSD( pRequest, pRequest->m_mapParams[ "type" ] );
00314 
00315                     delete pService;
00316                 }
00317 
00318                 return true;
00319             }
00320 
00321             if (( pRequest->m_eType   == RequestTypeGet ) &&
00322                 ( pRequest->m_sMethod == "version"         ))
00323             {
00324 
00325                 int nClassIdx = m_oMetaObject.indexOfClassInfo( "version" );
00326 
00327                 if (nClassIdx >=0)
00328                 {
00329                     QString sVersion =
00330                         m_oMetaObject.classInfo(nClassIdx).value();
00331 
00332                     return FormatResponse( pRequest, QVariant( sVersion ));
00333                 }
00334             }
00335 
00336             // --------------------------------------------------------------
00337             // Allow a more REST like calling convention.  If the Method 
00338             // Name isn't found, search for one with the request method 
00339             // appended to the name ( "Get" or "Put" for POST)
00340             // --------------------------------------------------------------
00341 
00342             QString sMethodName  = pRequest->m_sMethod;
00343             bool    bMethodFound = false;
00344 
00345             if (m_Methods.contains(sMethodName))
00346                 bMethodFound = true;
00347             else
00348             {
00349                 switch( pRequest->m_eType )
00350                 {
00351                     case RequestTypeHead:
00352                     case RequestTypeGet :
00353                         sMethodName = "Get" + sMethodName;
00354                         break;
00355                     case RequestTypePost:
00356                         sMethodName = "Put" + sMethodName;
00357                         break;
00358                 }
00359 
00360                 if (m_Methods.contains(sMethodName))
00361                     bMethodFound = true;
00362             }
00363 
00364             if (bMethodFound)
00365             {
00366                 MethodInfo oInfo = m_Methods.value( sMethodName );
00367 
00368                 if (( pRequest->m_eType & oInfo.m_eRequestType ) != 0)
00369                 {
00370                     // ------------------------------------------------------
00371                     // Create new Instance of the Service Class so
00372                     // it's guaranteed to be on the same thread
00373                     // since we are making direct calls into it.
00374                     // ------------------------------------------------------
00375 
00376                     pService = 
00377                         qobject_cast<Service*>(m_oMetaObject.newInstance());
00378 
00379                     QVariant vResult = oInfo.Invoke(pService,
00380                                                     pRequest->m_mapParams);
00381 
00382                     bHandled = FormatResponse( pRequest, vResult );
00383                 }
00384             }
00385 
00386             if (!bHandled)
00387                 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction );
00388         }
00389     }
00390     catch (HttpRedirectException &ex)
00391     {
00392         UPnp::FormatRedirectResponse( pRequest, ex.hostName );
00393         bHandled = true;
00394     }
00395     catch (HttpException &ex)
00396     {
00397         LOG(VB_GENERAL, LOG_ERR, ex.msg);
00398         UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, ex.msg );
00399 
00400         bHandled = true;
00401 
00402     }
00403     catch (QString &sMsg)
00404     {
00405         LOG(VB_GENERAL, LOG_ERR, sMsg);
00406         UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg );
00407 
00408         bHandled = true;
00409     }
00410     catch ( ...)
00411     {
00412         QString sMsg( "ServiceHost::ProcessRequest - Unexpected Exception" );
00413 
00414         LOG(VB_GENERAL, LOG_ERR, sMsg);
00415         UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg );
00416 
00417         bHandled = true;
00418     }
00419 
00420     if (pService != NULL)
00421         delete pService;
00422 
00423     return bHandled;
00424 }
00425 
00427 //
00429 
00430 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QObject *pResults )
00431 {
00432     if (pResults != NULL)
00433     {
00434         Serializer *pSer = pRequest->GetSerializer();
00435 
00436         pSer->Serialize( pResults );
00437 
00438         pRequest->FormatActionResponse( pSer );
00439 
00440         delete pResults;
00441 
00442         return true;
00443     }
00444     else
00445         UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, "Call to method failed" );
00446 
00447     return false;
00448 }
00449 
00451 //
00453 
00454 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QFileInfo oInfo )
00455 {
00456     QString sName = oInfo.absoluteFilePath();
00457 
00458     if (oInfo.exists())
00459     {
00460         if (oInfo.isSymLink())
00461             pRequest->FormatFileResponse( oInfo.symLinkTarget() );
00462         else
00463             pRequest->FormatFileResponse( oInfo.absoluteFilePath() );
00464     }
00465     else
00466     {
00467         // force return as a 404...
00468         pRequest->FormatFileResponse( "" );
00469     }
00470 
00471     return true;
00472 }
00473 
00474 
00476 //
00478 
00479 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QVariant vValue )
00480 {
00481     if ( vValue.canConvert< QObject* >()) 
00482     { 
00483         const QObject *pObject = vValue.value< QObject* >(); 
00484 
00485         return FormatResponse( pRequest, (QObject *)pObject );
00486     }
00487 
00488     if ( vValue.canConvert< QFileInfo >()) 
00489     {
00490         const QFileInfo oFileInfo = vValue.value< QFileInfo >(); 
00491 
00492         return FormatResponse( pRequest, oFileInfo );
00493     }
00494 
00495     // ----------------------------------------------------------------------
00496     // Simple Variant... serialize it.
00497     // ----------------------------------------------------------------------
00498     
00499     Serializer *pSer = pRequest->GetSerializer();
00500 
00501     pSer->Serialize( vValue, vValue.typeName() );
00502 
00503     pRequest->FormatActionResponse( pSer );
00504 
00505     return true;
00506 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends