|
MythTV
0.26-pre
|
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 }
1.7.6.1