MythTV  master
wsdl.cpp
Go to the documentation of this file.
1 // Program Name: wsdl.cpp
3 // Created : Jan. 19, 2010
4 //
5 // Purpose : WSDL XML Generation Class
6 //
7 // Copyright (c) 2010 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see COPYING for details
10 //
12 
13 #include "wsdl.h"
14 #include "xsd.h"
15 
16 #include "servicehost.h"
17 
19 //
21 
22 bool Wsdl::GetWSDL( HTTPRequest *pRequest )
23 {
24  m_typesToInclude.clear();
25 
26  if (!pRequest->m_mapParams.contains( "raw" ))
27  {
28  appendChild( createProcessingInstruction( "xml-stylesheet",
29  "type=\"text/xsl\" href=\"/xslt/service.xslt\"" ));
30  }
31 
32  QDomElement oNode;
33 
34  QString sClassName = m_pServiceHost->GetServiceMetaObject().className();
35  QString sTargetNamespace = "http://mythtv.org";
36 
37  m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions");
38 
39  m_oRoot.setAttribute( "targetNamespace", sTargetNamespace );
40 
41  m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
42  m_oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema" );
43  m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
44  m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
45  m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" );
46 
47  m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) );
48 
49  m_oTypes = createElement( "types" );
51  m_oPortType = createElement( "portType" );
52  m_oBindings = createElement( "binding" );
53  m_oService = createElement( "service" );
54 
55  appendChild( m_oRoot );
56  m_oRoot.appendChild( m_oTypes );
57  m_oRoot.appendChild( m_oPortType );
58  m_oRoot.appendChild( m_oBindings );
59  m_oRoot.appendChild( m_oService );
60 
61  m_oPortType.setAttribute( "name", sClassName );
62 
63  // ----------------------------------------------------------------------
64 
65  QDomElement oImportNode = createElement( "xs:schema" );
66  oImportNode.setAttribute( "targetNamespace" , "http://MythTV.org/Imports" );
67 
68  m_oTypes.appendChild( oImportNode );
69 
70  // ----------------------------------------------------------------------
71 
72  oNode = createElement( "xs:schema" );
73  oNode.setAttribute( "targetNamespace" , sTargetNamespace );
74  oNode.setAttribute( "elementFormDefault", "qualified" );
75 
76  m_oTypes.appendChild( oNode );
77  m_oTypes = oNode;
78 
79  // ----------------------------------------------------------------------
80  // Add Bindings...
81  // ----------------------------------------------------------------------
82 
83  m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName ));
84  m_oBindings.setAttribute( "type", QString( "tns:%1" ).arg( sClassName ));
85 
86  oNode = createElement( "soap:binding" );
87  //oNode.setAttribute( "style" , "document" );
88  oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" );
89 
90  m_oBindings.appendChild( oNode );
91 
92  // ----------------------------------------------------------------------
93  // Loop for each method in class
94  // ----------------------------------------------------------------------
95 
96  QMapIterator< QString, MethodInfo > it( m_pServiceHost->GetMethods() );
97 
98  while( it.hasNext())
99  {
100  it.next();
101 
102  MethodInfo oInfo = it.value();
103 
104  QString sRequestTypeName = oInfo.m_sName;
105  QString sResponseTypeName = oInfo.m_sName + "Response";
106 
107  QString sInputMsgName = QString( "%1_%2_InputMessage" )
108  .arg( sClassName )
109  .arg( oInfo.m_sName );
110  QString sOutputMsgName = QString( "%1_%2_OutputMessage" )
111  .arg( sClassName )
112  .arg( oInfo.m_sName );
113 
114  // ------------------------------------------------------------------
115  // Create PortType Operations
116  // ------------------------------------------------------------------
117 
118  QDomElement oOp = createElement( "operation" );
119 
120  oOp.setAttribute( "name", oInfo.m_sName );
121 
122  // ------------------------------------------------------------------
123  // Add Operation Description
124  // ------------------------------------------------------------------
125 
126  QString sDescription;
127 
128  if ( oInfo.m_eRequestType == RequestTypePost )
129  sDescription = "POST ";
130  else
131  sDescription = "GET ";
132 
134  "description" );
135 
136  oNode = createElement( "documentation" );
137  oNode.appendChild( createTextNode( sDescription ));
138 
139  oOp.appendChild( oNode );
140 
141  // ------------------------------------------------------------------
142  // Create PortType input element
143  // ------------------------------------------------------------------
144 
145  oNode = createElement( "input" );
146  oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" )
147  .arg( sTargetNamespace )
148  .arg( sClassName )
149  .arg( oInfo.m_sName ));
150  oNode.setAttribute( "message" , "tns:" + sInputMsgName );
151 
152  oOp.appendChild( oNode );
153 
154  // ------------------------------------------------------------------
155  // Create PortType output element
156  // ------------------------------------------------------------------
157 
158  oNode = createElement( "output" );
159  oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" )
160  .arg( sTargetNamespace )
161  .arg( sClassName )
162  .arg( oInfo.m_sName ));
163  oNode.setAttribute( "message", "tns:" + sOutputMsgName );
164 
165  oOp.appendChild( oNode );
166 
167  m_oPortType.appendChild( oOp );
168 
169  // ------------------------------------------------------------------
170  // Create Messages
171  // ------------------------------------------------------------------
172 
173  QDomElement oMsg = CreateMessage( sInputMsgName, sRequestTypeName );
174 
175  m_oRoot.insertAfter( oMsg, m_oLastMsg );
176  m_oLastMsg = oMsg;
177 
178  // ------------------------------------------------------------------
179  // Create Request Type
180  // ------------------------------------------------------------------
181 
182  m_oTypes.appendChild( CreateMethodType( oInfo, sRequestTypeName ) );
183 
184  // ------------------------------------------------------------------
185  // Create Response message
186  // ------------------------------------------------------------------
187 
188  oMsg = CreateMessage( sOutputMsgName, sResponseTypeName );
189 
190  m_oRoot.insertAfter( oMsg, m_oLastMsg );
191  m_oLastMsg = oMsg;
192 
193  // ------------------------------------------------------------------
194  // Create Response Type
195  // ------------------------------------------------------------------
196 
197  m_oTypes.appendChild( CreateMethodType( oInfo, sResponseTypeName, true ) );
198 
199  // ------------------------------------------------------------------
200  // Create Soap Binding Operations
201  // ------------------------------------------------------------------
202 
203  m_oBindings.appendChild( CreateBindingOperation( oInfo, sClassName ));
204  }
205 
206  // ----------------------------------------------------------------------
207  // Add Service Details
208  // ----------------------------------------------------------------------
209 
210  QString sServiceName = QString( "%1Services" ).arg( sClassName );
211 
212  m_oService.setAttribute( "name", sServiceName );
213 
214  // ------------------------------------------------------------------
215  // Add Service Description
216  // ------------------------------------------------------------------
217 
218  QString sDescription = "Interface Version " +
220  "version" );
221 
222  sDescription += " - " + ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(),
223  "description" );
224 
225  oNode = createElement( "documentation" );
226  oNode.appendChild( createTextNode( sDescription ));
227 
228  m_oService.appendChild( oNode );
229 
230  // ------------------------------------------------------------------
231  // Add Service Port
232  // ------------------------------------------------------------------
233 
234  QDomElement oPort = createElement( "port" );
235 
236  oPort.setAttribute( "name" , QString("BasicHttpBinding_%1" ).arg( sClassName ));
237  oPort.setAttribute( "binding", QString("tns:BasicHttpBinding_%1").arg( sClassName ));
238 
239  oNode = createElement( "soap:address" );
240  oNode.setAttribute( "location", "http://" +
241  pRequest->m_mapHeaders[ "host" ] + "/" +
243 
244  oPort.appendChild( oNode );
245  m_oService.appendChild( oPort );
246 
247  // ----------------------------------------------------------------------
248  //
249  // ----------------------------------------------------------------------
250 
251  if (m_typesToInclude.count() > 0)
252  {
253  // ------------------------------------------------------------------
254  // Create all needed includes
255  //
256  // <xs:import schemaLocation="<path to dependant schema" namespace="http://mythtv.org"/>
257  // ------------------------------------------------------------------
258 
259  QString sBaseUri = "http://" + pRequest->m_mapHeaders[ "host" ] + pRequest->m_sBaseUrl + "/xsd";
260 
261  QMap<QString, TypeInfo>::const_iterator it2 = m_typesToInclude.constBegin();
262  while( it2 != m_typesToInclude.constEnd())
263  {
264  QDomElement oIncNode = createElement( "xs:import" );
265  QString sType = it2.key();
266  TypeInfo info = it2.value();
267 
268  sType.remove( "DTC::" );
269 
270  QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri )
271  .arg( info.sAttrName )
272  .arg( sType );
273 
274  if (!info.sContentType.isEmpty())
275  sValue += "&name=" + info.sContentType;
276 
277  oIncNode.setAttribute( "schemaLocation", sValue );
278  oIncNode.setAttribute( "namespace" , "http://mythtv.org" );
279 
280  oImportNode.appendChild( oIncNode );
281  ++it2;
282  }
283  }
284 
285 
286  // ----------------------------------------------------------------------
287  // Return wsdl doc to caller
288  // ----------------------------------------------------------------------
289 
290  QTextStream os( &(pRequest->m_response) );
291 
292  pRequest->m_eResponseType = ResponseTypeXML;
293 
294  save( os, 0 );
295  return true;
296 }
297 
299 //
301 
303  const QString &sClassName)
304 {
305  // ------------------------------------------------------------------
306  // Create PortType Operation
307  // ------------------------------------------------------------------
308 
309  QDomElement oOp = createElement( "operation" );
310 
311  oOp.setAttribute( "name", oInfo.m_sName );
312 
313  QDomElement oNode = createElement( "soap:operation" );
314  oNode.setAttribute( "soapAction", QString( "http://mythtv.org/%1/%2" )
315  .arg( sClassName )
316  .arg( oInfo.m_sName ));
317  oNode.setAttribute( "style" , "document" );
318 
319  oOp.appendChild( oNode );
320 
321  // ------------------------------------------------------------------
322  // Create PortType input element
323  // ------------------------------------------------------------------
324 
325  QDomElement oDirection = createElement( "input" );
326  oNode = createElement( "soap:body" );
327  oNode.setAttribute( "use", "literal" );
328 
329  oDirection.appendChild( oNode );
330  oOp.appendChild( oDirection );
331 
332  if (QString::compare( oInfo.m_oMethod.typeName(), "void", Qt::CaseInsensitive ) != 0)
333  {
334  // Create output element
335 
336  oDirection = createElement( "output" );
337 
338  oNode = createElement( "soap:body" );
339  oNode.setAttribute( "use", "literal" );
340 
341  oDirection.appendChild( oNode );
342  oOp.appendChild( oDirection );
343 
344  }
345 
346  return oOp;
347 }
348 
350 //
352 
353 QDomElement Wsdl::CreateMessage( const QString& sMsgName,
354  const QString& sTypeName )
355 {
356  QDomElement oMsg = createElement( "message" );
357 
358  oMsg.setAttribute( "name", sMsgName );
359 
360  QDomElement oNode = createElement( "part" );
361 
362  oNode.setAttribute( "name" , "parameters" );
363  oNode.setAttribute( "element", "tns:" + sTypeName );
364 
365  oMsg.appendChild( oNode );
366 
367  return oMsg;
368 }
369 
371 //
373 
374 QDomElement Wsdl::CreateMethodType( MethodInfo &oInfo,
375  QString sTypeName,
376  bool bReturnType /* = false */)
377 {
378  QDomElement oElementNode = createElement( "xs:element" );
379 
380  oElementNode.setAttribute( "name", sTypeName );
381 
382  QDomElement oTypeNode = createElement( "xs:complexType" );
383  QDomElement oSeqNode = createElement( "xs:sequence" );
384 
385  oElementNode.appendChild( oTypeNode );
386  oTypeNode .appendChild( oSeqNode );
387 
388  // ------------------------------------------------------------------
389  // Create message for parameters
390  // ------------------------------------------------------------------
391 
392  if (bReturnType)
393  {
394  QDomElement oNode = createElement( "xs:element" );
395 
396  QString sType = oInfo.m_oMethod.typeName();
397 
398  sTypeName.remove( "Response" );
399 
400  oNode.setAttribute( "minOccurs", 0 );
401  oNode.setAttribute( "name" , sTypeName + "Result" );
402  oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
403 
404  bool bCustomType = IsCustomType( sType );
405 
406  sType = Xsd::ConvertTypeToXSD( sType, bCustomType );
407 
408  QString sPrefix = "xs:";
409 
410  if (bCustomType)
411  {
412  sPrefix = "tns:";
413 
414  sType = AddTypeInfo( sType );
415  }
416 
417  oNode.setAttribute( "type", sPrefix + sType );
418 
419  oSeqNode.appendChild( oNode );
420  }
421  else
422  {
423  QList<QByteArray> paramNames = oInfo.m_oMethod.parameterNames();
424  QList<QByteArray> paramTypes = oInfo.m_oMethod.parameterTypes();
425 
426  for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
427  {
428  QString sName = paramNames[ nIdx ];
429  QString sParamType = paramTypes[ nIdx ];
430 
431  bool bCustomType = IsCustomType( sParamType );
432 
433  sParamType = Xsd::ConvertTypeToXSD( sParamType, bCustomType );
434 
435  QString sPrefix = "xs:";
436 
437  if (bCustomType)
438  {
439  sPrefix = "tns:";
440 
441  sParamType = AddTypeInfo( sParamType );
442  }
443 
444  QDomElement oNode = createElement( "xs:element" );
445 
446  oNode.setAttribute( "minOccurs", 0 );
447  oNode.setAttribute( "name" , sName );
448  oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
449  oNode.setAttribute( "type" , sPrefix + sParamType );
450 
451  oSeqNode.appendChild( oNode );
452  }
453  }
454 
455  return oElementNode;
456 }
457 
459 //
461 
462 bool Wsdl::IsCustomType( QString &sTypeName )
463 {
464  int id = QMetaType::type( sTypeName.toUtf8() );
465 
466  switch( id )
467  {
468  case QMetaType::QStringList:
469  case QMetaType::QVariantList:
470  case QMetaType::QVariantMap:
471  return true;
472 
473  default:
474  // for now, treat QFileInfo as a string. Need to turn into MTOM later.
475  if (id == QMetaType::type( "QFileInfo" ))
476  return false;
477  break;
478  }
479 
480  return !(id < QMetaType::User);
481 }
482 
484 //
486 
487 QString Wsdl::AddTypeInfo( QString sType )
488 {
489  QString sCustomAttr = "type";
490 
491  // if sParamType contains "::", then assume it's a enum
492  // (this needs to be looked at again for a better approach)
493 
494  if (sType.contains( "::" ))
495  {
496  sType = sType.replace( "::", "." );
497  sCustomAttr = "enum";
498  }
499 
500  TypeInfo info = { sCustomAttr, QString() };
501 
502  m_typesToInclude.insert( sType, info );
503 
504  return sType;
505 }
506 
508 //
510 
511 QString Wsdl::ReadClassInfo( const QMetaObject *pMeta, const QString &sKey )
512 {
513  int nIdx = -1;
514 
515  if (pMeta)
516  nIdx = pMeta->indexOfClassInfo( sKey.toUtf8() );
517 
518  if (nIdx >=0)
519  return pMeta->classInfo( nIdx ).value();
520 
521  return QString();
522 }
QMetaMethod m_oMethod
Definition: servicehost.h:39
static bool IsCustomType(QString &sTypeName)
Definition: wsdl.cpp:462
bool GetWSDL(HTTPRequest *pRequest)
Definition: wsdl.cpp:22
const MetaInfoMap & GetMethods()
Definition: servicehost.h:92
QString m_sBaseUrl
Definition: httprequest.h:125
ServiceHost * m_pServiceHost
Definition: wsdl.h:39
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: wsdl.cpp:511
virtual QString GetServiceControlURL()
Definition: servicehost.h:89
Definition: xsd.h:72
QDomElement m_oService
Definition: wsdl.h:47
QMap< QString, TypeInfo > m_typesToInclude
Definition: wsdl.h:40
QString m_sName
Definition: servicehost.h:38
QDomElement CreateBindingOperation(MethodInfo &oInfo, const QString &sClassName)
Definition: wsdl.cpp:302
QDomElement m_oPortType
Definition: wsdl.h:45
QDomElement m_oRoot
Definition: wsdl.h:42
QDomElement CreateMethodType(MethodInfo &oInfo, QString sTypeName, bool bReturnType=false)
Definition: wsdl.cpp:374
QString AddTypeInfo(QString sType)
Definition: wsdl.cpp:487
HttpRequestType m_eRequestType
Definition: servicehost.h:40
QDomElement m_oTypes
Definition: wsdl.h:43
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: xsd.cpp:842
QStringMap m_mapParams
Definition: httprequest.h:129
QBuffer m_response
Definition: httprequest.h:155
HttpResponseType m_eResponseType
Definition: httprequest.h:147
QDomElement m_oBindings
Definition: wsdl.h:46
QString sContentType
Definition: xsd.h:72
const QMetaObject & GetServiceMetaObject()
Definition: servicehost.h:91
QStringMap m_mapHeaders
Definition: httprequest.h:130
QDomElement m_oLastMsg
Definition: wsdl.h:44
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: wsdl.cpp:353
QString sAttrName
Definition: xsd.h:72