MythTV  0.26-pre
wsdl.cpp
Go to the documentation of this file.
00001 
00002 // Program Name: wsdl.cpp
00003 // Created     : Jan. 19, 2010
00004 //
00005 // Purpose     : WSDL XML Generation 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 "wsdl.h"
00014 #include "xsd.h"
00015 
00016 #include "servicehost.h"
00017 
00019 //
00021 
00022 Wsdl::Wsdl( ServiceHost *pServiceHost ) : m_pServiceHost( pServiceHost )
00023 {
00024     
00025 }
00026 
00028 //
00030 
00031 bool Wsdl::GetWSDL( HTTPRequest *pRequest )
00032 {
00033     m_typesToInclude.clear();
00034 
00035     if (!pRequest->m_mapParams.contains( "raw" ))
00036     {
00037         appendChild( createProcessingInstruction( "xml-stylesheet",
00038                     "type=\"text/xsl\" href=\"/xslt/service.xslt\"" ));
00039     }
00040 
00041     QDomElement  oNode;
00042 
00043     QString sClassName       = m_pServiceHost->GetServiceMetaObject().className();
00044     QString sTargetNamespace = "http://mythtv.org";
00045     
00046     m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions");
00047 
00048     m_oRoot.setAttribute( "targetNamespace", sTargetNamespace );
00049 
00050     m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
00051     m_oRoot.setAttribute( "xmlns:xs"  , "http://www.w3.org/2001/XMLSchema"      );
00052     m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
00053     m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
00054     m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" );
00055 
00056     m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) );
00057 
00058     m_oTypes    = createElement( "types"    );
00059     m_oLastMsg  = m_oTypes;
00060     m_oPortType = createElement( "portType" );
00061     m_oBindings = createElement( "binding"  );
00062     m_oService  = createElement( "service"  );
00063 
00064             appendChild( m_oRoot      );
00065     m_oRoot.appendChild( m_oTypes    );
00066     m_oRoot.appendChild( m_oPortType );
00067     m_oRoot.appendChild( m_oBindings );
00068     m_oRoot.appendChild( m_oService  );
00069 
00070     m_oPortType.setAttribute( "name", sClassName );
00071 
00072     // ----------------------------------------------------------------------
00073 
00074     QDomElement oImportNode = createElement( "xs:schema" );
00075     oImportNode.setAttribute( "targetNamespace"   , "http://MythTV.org/Imports" );
00076 
00077     m_oTypes.appendChild( oImportNode );
00078 
00079     // ----------------------------------------------------------------------
00080 
00081     oNode = createElement( "xs:schema" );
00082     oNode.setAttribute( "targetNamespace"   , sTargetNamespace );
00083     oNode.setAttribute( "elementFormDefault", "qualified"      );
00084 
00085     m_oTypes.appendChild( oNode );
00086     m_oTypes = oNode;
00087 
00088     // ----------------------------------------------------------------------
00089     // Add Bindings...
00090     // ----------------------------------------------------------------------
00091 
00092     m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName ));
00093     m_oBindings.setAttribute( "type", QString( "tns:%1"            ).arg( sClassName ));
00094 
00095     oNode = createElement( "soap:binding" );
00096     //oNode.setAttribute( "style"    , "document" );
00097     oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" );
00098 
00099     m_oBindings.appendChild( oNode );
00100 
00101     // ----------------------------------------------------------------------
00102     // Loop for each method in class
00103     // ----------------------------------------------------------------------
00104 
00105     QMapIterator< QString, MethodInfo > it( m_pServiceHost->GetMethods() );
00106 
00107     while( it.hasNext()) 
00108     {
00109         it.next();
00110 
00111         MethodInfo oInfo = it.value();
00112 
00113         QString sRequestTypeName  = oInfo.m_sName;
00114         QString sResponseTypeName = oInfo.m_sName + "Response";
00115 
00116         QString sInputMsgName  = QString( "%1_%2_InputMessage"  )
00117                                     .arg( sClassName )
00118                                     .arg( oInfo.m_sName );
00119         QString sOutputMsgName = QString( "%1_%2_OutputMessage" )
00120                                     .arg( sClassName )
00121                                     .arg( oInfo.m_sName );
00122 
00123         // ------------------------------------------------------------------
00124         // Create PortType Operations
00125         // ------------------------------------------------------------------
00126 
00127         QDomElement oOp = createElement( "operation" );
00128     
00129         oOp.setAttribute( "name", oInfo.m_sName );
00130 
00131         // ------------------------------------------------------------------
00132         // Add Operation Description
00133         // ------------------------------------------------------------------
00134 
00135         QString sDescription;
00136 
00137         if ( oInfo.m_eRequestType == RequestTypePost ) 
00138             sDescription = "POST ";
00139         else
00140             sDescription = "GET ";
00141 
00142         sDescription += ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(),
00143                                        "description" );
00144 
00145         oNode = createElement( "documentation" );
00146         oNode.appendChild( createTextNode( sDescription ));
00147 
00148         oOp.appendChild( oNode );
00149     
00150         // ------------------------------------------------------------------
00151         // Create PortType input element 
00152         // ------------------------------------------------------------------
00153     
00154         oNode = createElement( "input" );
00155         oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" )
00156                                               .arg( sTargetNamespace )
00157                                               .arg( sClassName )
00158                                               .arg( oInfo.m_sName ));
00159         oNode.setAttribute( "message"    , "tns:" + sInputMsgName );
00160     
00161         oOp.appendChild( oNode );
00162     
00163         // ------------------------------------------------------------------
00164         // Create PortType output element 
00165         // ------------------------------------------------------------------
00166     
00167         oNode = createElement( "output" );
00168         oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" )
00169                                         .arg( sTargetNamespace )
00170                                         .arg( sClassName )
00171                                         .arg( oInfo.m_sName ));
00172         oNode.setAttribute( "message", "tns:" + sOutputMsgName );
00173     
00174         oOp.appendChild( oNode );
00175 
00176         m_oPortType.appendChild( oOp );
00177 
00178         // ------------------------------------------------------------------
00179         // Create Messages
00180         // ------------------------------------------------------------------
00181 
00182         QDomElement oMsg = CreateMessage( oInfo, sInputMsgName, sRequestTypeName );
00183 
00184         m_oRoot.insertAfter( oMsg, m_oLastMsg );
00185         m_oLastMsg = oMsg;
00186 
00187         // ------------------------------------------------------------------
00188         // Create Request Type
00189         // ------------------------------------------------------------------
00190 
00191         m_oTypes.appendChild( CreateMethodType( oInfo, sRequestTypeName ) );
00192 
00193         // ------------------------------------------------------------------
00194         // Create Response message 
00195         // ------------------------------------------------------------------
00196 
00197         oMsg = CreateMessage( oInfo, sOutputMsgName, sResponseTypeName );
00198 
00199         m_oRoot.insertAfter( oMsg, m_oLastMsg );
00200         m_oLastMsg = oMsg;
00201 
00202         // ------------------------------------------------------------------
00203         // Create Response Type
00204         // ------------------------------------------------------------------
00205 
00206         m_oTypes.appendChild( CreateMethodType( oInfo, sResponseTypeName, true ) );
00207         
00208         // ------------------------------------------------------------------
00209         // Create Soap Binding Operations
00210         // ------------------------------------------------------------------
00211 
00212         m_oBindings.appendChild( CreateBindingOperation( oInfo, sClassName ));
00213     }
00214 
00215     // ----------------------------------------------------------------------
00216     // Add Service Details
00217     // ----------------------------------------------------------------------
00218 
00219     QString sServiceName = QString( "%1Services" ).arg( sClassName );
00220 
00221     m_oService.setAttribute( "name", sServiceName );
00222 
00223     // ------------------------------------------------------------------
00224     // Add Service Description
00225     // ------------------------------------------------------------------
00226 
00227     QString sDescription = "Interface Version " +
00228                            ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(), 
00229                                          "version" );
00230 
00231     sDescription += " - " + ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(), 
00232                                          "description" );
00233 
00234     oNode = createElement( "documentation" );
00235     oNode.appendChild( createTextNode( sDescription ));
00236 
00237     m_oService.appendChild( oNode );
00238 
00239     // ------------------------------------------------------------------
00240     // Add Service Port 
00241     // ------------------------------------------------------------------
00242 
00243     QDomElement oPort = createElement( "port" );
00244 
00245     oPort.setAttribute( "name"   , QString("BasicHttpBinding_%1"    ).arg( sClassName ));
00246     oPort.setAttribute( "binding", QString("tns:BasicHttpBinding_%1").arg( sClassName ));
00247 
00248     oNode = createElement( "soap:address" );
00249     oNode.setAttribute( "location", "http://" + 
00250                                     pRequest->m_mapHeaders[ "host" ] + "/" +
00251                                     m_pServiceHost->GetServiceControlURL() );
00252 
00253     oPort.appendChild( oNode );
00254     m_oService.appendChild( oPort );
00255 
00256     // ----------------------------------------------------------------------
00257     //
00258     // ----------------------------------------------------------------------
00259 
00260     if (m_typesToInclude.count() > 0)
00261     {
00262         // ------------------------------------------------------------------
00263         // Create all needed includes
00264         //
00265         //      <xs:import schemaLocation="<path to dependant schema" namespace="http://mythtv.org"/>
00266         // ------------------------------------------------------------------
00267 
00268         QString sBaseUri = "http://" + pRequest->m_mapHeaders[ "host" ] + pRequest->m_sBaseUrl + "/xsd?type=";
00269 
00270         QMap<QString, bool>::const_iterator it = m_typesToInclude.constBegin();
00271         while( it != m_typesToInclude.constEnd())
00272         {
00273             QDomElement oIncNode = createElement( "xs:import" );
00274             QString     sType    = it.key();
00275 
00276             sType.remove( "DTC::" );
00277 
00278             oIncNode.setAttribute( "schemaLocation", sBaseUri + sType );
00279             oIncNode.setAttribute( "namespace", "http://mythtv.org" );
00280     
00281             oImportNode.appendChild( oIncNode );
00282             ++it;
00283         }
00284     }
00285 
00286 
00287     // ----------------------------------------------------------------------
00288     // Return wsdl doc to caller
00289     // ----------------------------------------------------------------------
00290 
00291     QTextStream os( &(pRequest->m_response) );
00292 
00293     pRequest->m_eResponseType   = ResponseTypeXML;
00294 
00295     save( os, 0 );
00296     return true;
00297 }
00298 
00300 //
00302 
00303 QDomElement Wsdl::CreateBindingOperation( MethodInfo    &oInfo,
00304                                           const QString &sClassName)
00305 {
00306     // ------------------------------------------------------------------
00307     // Create PortType Operation
00308     // ------------------------------------------------------------------
00309 
00310     QDomElement oOp = createElement( "operation" );
00311 
00312     oOp.setAttribute( "name", oInfo.m_sName );
00313 
00314     QDomElement oNode = createElement( "soap:operation" );
00315     oNode.setAttribute( "soapAction", QString( "http://mythtv.org/%1/%2" )
00316                                          .arg( sClassName )
00317                                          .arg( oInfo.m_sName ));
00318     oNode.setAttribute( "style"     , "document" );
00319 
00320     oOp.appendChild( oNode );
00321 
00322     // ------------------------------------------------------------------
00323     // Create PortType input element 
00324     // ------------------------------------------------------------------
00325 
00326     QDomElement oDirection = createElement( "input" );
00327     oNode = createElement( "soap:body" );
00328     oNode.setAttribute( "use", "literal" );
00329 
00330     oDirection.appendChild( oNode );
00331     oOp.appendChild( oDirection );
00332 
00333     if (QString::compare( oInfo.m_oMethod.typeName(), "void", Qt::CaseInsensitive ) != 0)
00334     {
00335         // Create output element 
00336 
00337         oDirection = createElement( "output" );
00338 
00339         oNode = createElement( "soap:body" );
00340         oNode.setAttribute( "use", "literal" );
00341 
00342         oDirection.appendChild( oNode );
00343         oOp.appendChild( oDirection );
00344 
00345     }
00346 
00347     return oOp;
00348 }
00349 
00351 //
00353 
00354 QDomElement Wsdl::CreateMessage( MethodInfo   &oInfo,
00355                                  QString       sMsgName, 
00356                                  QString       sTypeName )
00357 {
00358     QDomElement oMsg = createElement( "message" );
00359 
00360     oMsg.setAttribute( "name", sMsgName );
00361 
00362     QDomElement oNode = createElement( "part" );
00363 
00364     oNode.setAttribute( "name"   , "parameters" );
00365     oNode.setAttribute( "element", "tns:" + sTypeName    );
00366 
00367     oMsg.appendChild( oNode );
00368 
00369     return oMsg;
00370 }
00371 
00373 //
00375 
00376 QDomElement Wsdl::CreateMethodType( MethodInfo   &oInfo,
00377                                     QString       sTypeName,
00378                                     bool          bReturnType /* = false */)
00379 {
00380     QDomElement oElementNode = createElement( "xs:element" );
00381 
00382     oElementNode.setAttribute( "name", sTypeName );
00383 
00384     QDomElement oTypeNode = createElement( "xs:complexType" );
00385     QDomElement oSeqNode  = createElement( "xs:sequence"    );
00386     
00387     oElementNode.appendChild( oTypeNode );
00388     oTypeNode   .appendChild( oSeqNode  );
00389 
00390     // ------------------------------------------------------------------
00391     // Create message for parameters
00392     // ------------------------------------------------------------------
00393 
00394     if (bReturnType)
00395     {
00396         QDomElement oNode = createElement( "xs:element" );
00397 
00398         QString sType = oInfo.m_oMethod.typeName();
00399 
00400         sType.remove( QChar('*') );
00401 
00402         sTypeName.remove( "Response" );
00403 
00404         oNode.setAttribute( "minOccurs", 0       );
00405         oNode.setAttribute( "name"     , sTypeName + "Result" );
00406         oNode.setAttribute( "nillable" , true    );   //-=>TODO: This may need to be determined by sParamType
00407 
00408         bool bCustomType = IsCustomType( sType );
00409 
00410         sType = Xsd::ConvertTypeToXSD( sType, bCustomType );
00411 
00412         QString sPrefix = "xs:";
00413 
00414         if (bCustomType)
00415         {
00416             sPrefix = "tns:";
00417 
00418 
00419             m_typesToInclude.insert( sType, true );
00420         }
00421 
00422         oNode.setAttribute( "type", sPrefix + sType );
00423 
00424         oSeqNode.appendChild( oNode );
00425     }
00426     else
00427     {
00428         QList<QByteArray> paramNames = oInfo.m_oMethod.parameterNames();
00429         QList<QByteArray> paramTypes = oInfo.m_oMethod.parameterTypes();
00430 
00431         for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
00432         {
00433             QString sName      = paramNames[ nIdx ];
00434             QString sParamType = paramTypes[ nIdx ];
00435 
00436             QDomElement oNode = createElement( "xs:element" );
00437 
00438             oNode.setAttribute( "minOccurs", 0     );
00439             oNode.setAttribute( "name"     , sName );
00440             oNode.setAttribute( "nillable" , true  );   //-=>TODO: This may need to be determined by sParamType
00441             oNode.setAttribute( "type"     , "xs:" + Xsd::ConvertTypeToXSD( sParamType ));
00442 
00443             oSeqNode.appendChild( oNode );
00444         }
00445     }
00446 
00447     return oElementNode;
00448 }
00449 
00451 //
00453 
00454 bool Wsdl::IsCustomType( QString &sTypeName )
00455 {
00456     sTypeName.remove( QChar('*') );
00457 
00458     int id = QMetaType::type( sTypeName.toUtf8() );
00459 
00460     switch( id )
00461     {
00462         case QMetaType::QStringList:
00463         case QMetaType::QVariantList:
00464         case QMetaType::QVariantMap:
00465             return true;
00466 
00467         default:
00468             // for now, treat QFileInfo as a string.  Need to turn into MTOM later.
00469             if (id == QMetaType::type( "QFileInfo" ))
00470                 return false;
00471             break;
00472     }
00473 
00474     if ((id == -1) || (id < QMetaType::User)) 
00475         return false;
00476 
00477     return true;
00478 }
00479 
00481 //
00483 
00484 QString Wsdl::ReadClassInfo( const QMetaObject *pMeta, const QString &sKey )
00485 {
00486     int nIdx = pMeta->indexOfClassInfo( sKey.toUtf8() );
00487 
00488     if (nIdx >=0)
00489         return pMeta->classInfo( nIdx ).value();
00490 
00491     return QString();
00492 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends