Qore WSDL Module Reference: docs/qlib/WSDL.qm.dox.h Source File

Qore WSDL Module Reference 0.3.4

- Main Page
- Namespaces
- Classes

All Classes Namespaces Functions Variables Pages
WSDL.qm.dox.h
1 // -*- mode: c++; indent-tabs-mode: nil -*-
3
4 /* WSDL.qm Copyright 2012 David Nichols
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23 */
24
25 // make sure we have the required qore version
26
27 // requires XML functionality
28
29 // need mime definitions
30
31
32
33 /* Version History
34 * 2012-05-31 v0.3.4: David Nichols <david@qore.org>: updated to a user module
35 * v0.3.3: added initial support for the anyAttribute element of complexType
36 added initial support for SOAP header processing
37 added initial support for multiple portType and bindings in a WSDL
38
39 * v0.3.2: fixed bugs de/serializing negative values for "int" and "short"
40 * v0.3.1: improved XSD imports and namespace handling
41
42 * v0.3.0: implemented WSDLLib class of helper functions
43 implemented support for xsd import statements in WSDLs
44
45 * v0.2.0: use parseXMLAsData() instead of parseXML()
46 implemented initial simpleType support
47 fixed xsd:date serialization and deserialization
48
49
50 WSDL classes
51 provides some minimal WSDL and XSD support for SOAP messaging used by the SoapClient class and the SoapHandler
52
53 not complete, needs namespace verification, improved XSD support, element groups, etc
54 */
55
74
75 namespace WSDL {
77 const version = "0.3.4";
78
79 const XET_ALL = "ALL";
80 const XET_CHOICE = "CHOICE";
81 const XET_SEQUENCE = "SEQUENCE";
82
84 const SOAP_11_ENV = "http://schemas.xmlsoap.org/soap/envelope/";
86 const SOAP_12_ENV = "http://www.w3.org/2003/05/soap-envelope";
87
89 const SOAP_12_NS = "http://schemas.xmlsoap.org/wsdl/soap12/";
90
92 const XSD_NS = "http://www.w3.org/2001/XMLSchema";
94 const XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
95
97 const HTTP_NS = "http://schemas.xmlsoap.org/wsdl/http/";
99 const MIME_NS = "http://schemas.xmlsoap.org/wsdl/mime/";
100
102 const ENVELOPE_11_NS =
103 ( "soapenv:Envelope" :
104 ( "^attributes^" :
105 ( "xmlns:soapenv" : SOAP_11_ENV,
106 "xmlns:xsd" : XSD_NS,
107 "xmlns:xsi" : XSI_NS ) ) );
108
110 const ENVELOPE_12_NS =
111 ( "soapenv:Envelope" :
112 ( "^attributes^" :
113 ( "xmlns:soapenv" : SOAP_12_ENV,
114 "xmlns:xsd" : XSD_NS,
115 "xmlns:xsi" : XSI_NS ) ) );
116
118 const SOAP_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/";
119
121 const any_type_map =
122 ( Type::String : "string",
123 Type::Int : "long",
124 Type::Boolean : "boolean",
125 Type::Date : "dateTime",
126 Type::Float : "decimal",
127 Type::NothingType : "string",
128 Type::NullType : "string",
129 Type::Binary : "base64Binary" );
130
131 // error codes
132 const SOAP_SERIALIZATION_ERROR = "SOAP-SERIALIZATION-ERROR";
133 const SOAP_DESERIALIZATION_ERROR = "SOAP-DESERIALIZATION-ERROR";
134 const WSDL_ERROR = "WSDL-ERROR";
135
137 const SOAP_TRANSPORT_HTTP = "http://schemas.xmlsoap.org/soap/http";
138
140 const RANGE_SHORT = (-32768, 32767);
141
143 const RANGE_INT = (-2147483648, 2147483647);
144 };
145
147 class WSDL::WSDLLib {
148
149 public:
151 const SoapMimeTypes = (MimeTypeSoapXml, MimeTypeXml, MimeTypeXmlApp);
152
154
155 static string getFile(string $fn) {
156 my File $f();
157 $f.open2($fn);
158 return $f.read(-1);
159 }
160
161 #! retrieves a file from a URL with HTTP and returns the file's contents as a string
162
163 static string getHTTP(string url, any path, any hc, any headers);
164
166
167 static binary getFTP(string $url, string $path) {
168 my string $file = basename($path);
169 if (!exists $file)
170 throw "WSDL-LIB-ERROR", sprintf("missing file name in URL %n", $url);
171
172 my FtpClient $f($url);
173 $f.connect();
174
175 my string $dir = dirname($path);
176 if (exists $dir)
177 $f.cwd($dir);
178
179 return $f.getAsBinary($file);
180 }
181
182 #! retrieves a file from a URL
183 static any getFileFromURL(string $url, string $def_protocol, any $http_client, any $http_headers) {
184 my hash $u = parse_url($url);
185
186 if (!exists $u.protocol)
187 $u.protocol = exists $def_protocol ? $def_protocol : "file";
188
189 switch ($u.protocol) {
190 case "file":
191 return WSDLLib::getFile($u.host + $u.path);
192
193 case /^http(s)?$/:
194 return WSDLLib::getHTTP($url, $u.path, $http_client, $http_headers);
195
196 case /^ftp(s)?$/:
197 return WSDLLib::getFTP($url, $u.path);
198
199 default:
200 throw "WSDL-LIB-ERROR", sprintf("do not know how to retrieve data with protocol %n given in URL %n", $u.protocol, $url);
201 }
202 }
203
204 #! returns the argument
205 static WebService getWSDL(WebService $wsdl) {
206 return $wsdl;
207 }
208
209 #! returns a WSDL string form a file name, optional HTTPClient object and optional header hash
210 static string getWSDL(string $wsdl, *HTTPClient $http_client, *hash $http_headers) {
211 if (strlen($wsdl) > 256 && $wsdl !~ /^\w+:\/\/.*$/)
212 return $wsdl;
213 return WSDLLib::getFileFromURL($wsdl, "file", $http_client, $http_headers);
214 }
215
216 #! takes a hash representation of a SOAP message and parses it to a Qore data structure; handles multipart messages, checks the content-type, and handles hrefs in the message
217 static hash parseSOAPMessage(hash $msg) {
218 if (exists $msg."_qore_multipart") {
219 if ($msg."_qore_multipart" != "related")
220 throw "SOAP-MESSAGE-ERROR", sprintf("don't understand multipart/%s messages, expected multipart/related", $msg."_qore_multipart");
221
222 my any $bdry = $msg."_qore_multipart_boundary";
223 if (!strlen($bdry))
224 throw "SOAP-MESSAGE-ERROR", sprintf("multipart message received without multipart boundary; headers=%n", $msg - "body");
225
226 my hash $mpmsg;
227 my list $l = split("\r\n--" + $bdry, $msg.body);
228
229 #printf("l=%N\n", $l);
230 #my File $f(); $f.open2("t.bin", O_CREAT|O_WRONLY|O_TRUNC); $f.write(binary($l[3])); exit();
231 #my File $f(); $f.open2("t.bin", O_CREAT|O_WRONLY|O_TRUNC); $f.write(binary($msg.body)); exit();
232
233 for (my int $i = 1; $i < (elements $l - 1); ++$i) {
234 my string $m = $l[$i];
235
236 my int $ie = index($m, "\r\n\r\n");
237 if ($ie == -1) {
238 throw "SOAP-MESSAGE-ERROR", sprintf("part %d has no headers: %n", $i, $m);
239 }
240 my hash $hh;
241 foreach my string $hl in (split("\r\n", substr($m, 2, $ie))) {
242 trim $hl;
243 my (string $hi, any $ignore, string $ha) = $hl =~ x/^(.*):([ \t])*(.*)$/;
244 $hi = tolower($hi);
245 #printf("hl=%n hi=%n ha=%n\n", $hl, $hi, $ha);exit();
246 $hh.$hi = $ha;
247 }
248 if (!exists $hh."content-id")
249 throw "SOAP-MESSAGE-ERROR", sprintf("expecting part header Content-ID in part %d; headers: %n", $i, $hh);
250
251 my any $b;
252 if ($hh."content-transfer-encoding" == "binary") {
253 # unfortunately we have to do some tricks to get the binary data out here
254 # FIXME: tricks probably not necessary with qore 0.8.1+
255 $m = force_encoding($m, "ascii");
256 # recalculate byte offset
257 $ie = index($m, "\r\n\r\n");
258 #printf("ie=%d m=%d\n", $ie, strlen($m));exit();
259 $b = binary(substr($m, $ie + 4));
260
261 #my File $f(); $f.open2("t.bin", O_CREAT|O_WRONLY|O_TRUNC); $f.write($b); exit();
262 }
263 else {
264 $b = substr($m, $ie + 4);
265
266 if ($hh."content-type" =~ /charset=/) {
267 my string $c = ($hh."content-type" =~ x/charset=([^;]+)/)[0];
268 $b = force_encoding($b, $c);
269 }
270 }
271
272 if ($hh."content-id" !~ /^\<.*\>$/)
273 throw "SOAP-MESSAGE-ERROR", sprintf("expected part ID to have the following format: <id>, instead got %s", $hh."content-id");
274
275 my hash $p = ( "hdr" : $hh,
276 "body" : $b );
277
278 if ((!exists $msg."_qore_multipart_start" && $i == 1)
279 @htmlonly <style><!-- td.qore { background-color: #5b9409; color: white; } --></style> @endhtmlonly
280 <table>
281 <tr>
282 <td></td>
283 <td>(exists $msg."_qore_multipart_start" && $msg."_qore_multipart_start" == $hh."content-id")) {</td>
284 </tr>
285 </table>
286 $mpmsg.body = $p;
287 }
288 else {
289 my string $id = substr($hh."content-id", 1, -1);
290 $mpmsg.part.$id = $p;
291 }
292 }
293 # check content-type
294 WSDLLib::checkSOAPContentType($mpmsg.body.hdr."content-type");
295
296 #printf("part %d hh=%N\nbody=%s (%d)\n", $i, $hh, type($b), elements($b)); #exit();
297 my hash $xmldata = parseXMLAsData($mpmsg.body.body);
298
299 # parse entire data structure to find "href"s or href attributes
300 WSDLLib::substHref(\$xmldata, $mpmsg.part);
301 return $xmldata;
302 }
303
304 WSDLLib::checkSOAPContentType($msg."content-type");
305
306 return parseXMLAsData($msg.body);
307 }
308
309 private static checkSOAPContentType(string $ct) {
310 foreach my string $sct in (SoapMimeTypes) {
311 if (bindex($ct, $sct) != -1)
312 return;
313 }
314
315 throw "SOAP-MESSAGE-ERROR", sprintf("don't know how to handle content-type %n (expecting one of: %y)", $ct, SoapMimeTypes);
316 }
317
318 private static processHref(reference $xmldata, string $hr, hash $parts) {
319 if ($hr !~ /^cid:/)
320 throw "SOAP-MESSAGE-ERROR", sprintf("messages references non-local part %n; cannot handle non-local parts", $hr);
321 $hr = substr($hr, 4);
322 if (!exists $parts.$hr)
323 throw "SOAP-MESSAGE-ERROR", sprintf("message references non-existent part %n", $hr);
324 $xmldata = $parts.$hr.body;
325 }
326
327 private static substHref(reference $xmldata, hash $parts) {
328 foreach my string $k in (keys $xmldata) {
329 if (exists $xmldata.$k."^attributes^".href)
330 WSDLLib::processHref(\$xmldata.$k, $xmldata.$k."^attributes^".href, $parts);
331 else if (exists $xmldata.$k.href)
332 WSDLLib::processHref(\$xmldata.$k, $xmldata.$k.href, $parts);
333 else if ($xmldata.$k.typeCode() == NT_LIST) {
334 foreach my any $e in (\$xmldata.$k)
335 WSDLLib::substHref(\$e, $parts);
336 }
337 else if ($xmldata.$k.typeCode() == NT_HASH)
338 WSDLLib::substHref(\$xmldata.$k, $parts);
339 }
340 }
341 }
342
343 # abstract class providing helper methods to subclasses
344 public class WSDL::XSDBase {
345 static private hash doType(string $t) {
346 #printf("DEBUG: XSDBase::doType(%n, %n)\n", $t, $nsinfo);
347 my (*string $ns, *string $type) = $t =~ x/(\w+):(\w+)/;
348 return !exists $type ? ("val": $t) : ("ns": $ns, "val": $type);
349 }
350
351 static private any doType(string $t, hash $nsinfo) {
352 #printf("DEBUG: XSDBase::doType(%n, %n)\n", $t, $nsinfo);
353 my (*string $ns, *string $type) = $t =~ x/(\w+):(\w+)/;
354 if (!exists $type)
355 return ( "val" : $t );
356
357 # if this is in the XML Schema namespace, then it's a base type
358 if ($nsinfo.xml_schema.$ns)
359 return new XSDBaseType($type);
360
361 return ( "ns" : $ns,
362 "val" : $type );
363 }
364
365 static removeNS(reference $h) {
366 foreach my string $k in (keys $h) {
367 my (*string $ns, *string $name) = $k =~ x/(\w+):(\w+)/;
368 if (exists $ns) {
369 if ($h.$k.typeCode() == NT_HASH)
370 $h.$k.ns = $ns;
371 $h.$name = $h.$k;
372 $h -= $k;
373 }
374 }
375 }
376
377 static removeNS2(reference $h) {
378 foreach my string $k in (keys $h) {
379 my (*string $ns, *string $name) = $k =~ x/(\w+):(\w+)/;
380 if (exists $ns) {
381 if ($h.$k.typeCode() == NT_HASH)
382 $h.$k.".ns" = $ns;
383 $h.$name = $h.$k;
384 $h -= $k;
385 }
386 }
387 }
388 }
389
390 # abstract type common to all XSD classes
391 public class WSDL::XSDData inherits XSDBase {
392 any getValue(any $mrh, any $val) {
393 if (exists $val."^attributes^".href) {
394 my string $href = substr($val."^attributes^".href, 1);
395
396 if (!exists $mrh.$href)
397 throw "INVALID-REFERENCE", sprintf("multiRef id=%n does not exist", $href);
398
399 return $mrh.$href;
400 }
401 return $val;
402 }
403 }
404
405 # abstract type common to all XSD classes with a "name" attribute
406 public class WSDL::XSDNamedData inherits XSDData {
407 public {
408 *string $.name;
409 *string $.ns;
410 }
411 constructor(reference $e) {
412 WSDL::XSDBase::removeNS(\$e);
413
414 $.name = $e."^attributes^".name;
415 }
416
417 string getName() {
418 return exists $.name ? $.name : "<unnamed type>";
419 }
420
421 any getNS() {
422 return $.ns;
423 }
424 }
425
426 # class for XSD base types
427 public class WSDL::XSDBaseType inherits XSDData {
428 public {
429 string $.type;
430 string $.nstype;
431 }
432 constructor(string $t) {
433 $.type = $t;
434 $.nstype = "xsd:" + $t;
435 }
436
437 any serialize(any $val, *softbool $omit_type, *softbool $omit_ns) {
438 my (any $type, string $nstype);
439 # set type according to Qore type if xsd type is anyType
440 if ($.type == "anyType") {
441 # we have to specify the type in this case
442 $omit_type = False;
443 $type = any_type_map{type($val)};
444 $nstype = "xsd:" + $type;
445 if (!exists $type)
446 throw SOAP_SERIALIZATION_ERROR, sprintf("cannot serialize xsd type anyType from Qore type %n", type($val));
447 }
448 else {
449 $type = $.type;
450 $nstype = $.nstype;
451 }
452
453 switch ($type) {
454 case "byte": {
455 my int $v = int($val);
456 if (($v & 0xff) != $v)
457 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
458 break;
459 }
460
461 case "short": {
462 my int $v = int($val);
463 if ($v < RANGE_SHORT[0] || $v > RANGE_SHORT[1])
464 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
465 break;
466 }
467
468 case "int": {
469 my int $v = int($val);
470 if ($v < RANGE_INT[0] || $v > RANGE_INT[1])
471 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
472 break;
473 }
474
475 case "unsignedByte": {
476 my int $v = int($val);
477 if (($v & 0xff) != $v)
478 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
479 if ($v < 0)
480 throw SOAP_SERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $type, $v);
481 break;
482 }
483
484 case "unsignedShort": {
485 my int $v = int($val);
486 if ($v < 0 || $v > RANGE_SHORT[1])
487 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
488 break;
489 }
490
491 case "unsignedInt": {
492 my int $v = int($val);
493 if ($v < 0 || $v > RANGE_INT[1])
494 throw SOAP_SERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $v, $type);
495 break;
496 }
497
498 case "unsignedLong": {
499 my int $v = int($val);
500 if ($v < 0)
501 throw SOAP_SERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $type, $v);
502 break;
503 }
504
505 case "positiveInteger":
506 if ($val <= 0)
507 throw SOAP_SERIALIZATION_ERROR, sprintf("type %n only accepts positive values (value supplied: %d)", $type, $val);
508 break;
509
510 case "date":
511 $val = format_date("YYYY-MM-DD", date($val));
512 break;
513
514 case "dateTime":
515 $val = format_date("YYYY-MM-DDTHH:mm:SS", date($val));
516 break;
517
518 case "boolean":
519 $val = $val ? "true" : "false";
520 break;
521
522 case "time":
523 $val = format_date("hh:mm:ss.ms", date($val));
524 break;
525
526 case "base64Binary":
527 $val = makeBase64String($val);
528 break;
529
530 case "hexBinary":
531 $val = makeHexString($val);
532 break;
533 }
534
535 #printf("DEBUG: FORCE: type=%n, nstype=%n, val=%n\n", $type, $.nstype, $val);
536 if ($omit_type == "ns")
537 return ( "^attributes^" : ( "xmlns:xsi" : XSI_NS, "xsi:type" : $nstype ), "^value^" : $val );
538 return $omit_type ? $val : ( "^attributes^" : ( "xsi:type" : $nstype ), "^value^" : $val );
539 }
540
541 any deserialize(any $types, any $mrh, any $val) {
542 my string $type;
543 if ($val.typeCode() == NT_HASH) {
544 $type = $val."^attributes^"."xsi:type";
545 my string $t = ($type =~ x/\w+:(\w+)/)[0];
546 if (exists $t)
547 $type = $t;
548
549 if ($.type != "anyType" && $type != $.type)
550 throw SOAP_DESERIALIZATION_ERROR, sprintf("expecting base type %n, got %n", $.type, $val."^attributes^"."xsi:type");
551 if (exists $val."^value^")
552 $val = $val."^value^";
553 }
554 else
555 $type = $.type;
556
557 switch ($type) {
558 case "string":
559 case "anyURI":
560 # note that we do not convert xsd:integer to a qore integer to avoid losing precision
561 case "integer":
562 return $val;
563
564 case "byte":
565 $val = int($val);
566 if (($val & 0xff) != $val)
567 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
568 return $val;
569
570 case "short":
571 $val = int($val);
572 if ($val < RANGE_SHORT[0] || $val > RANGE_SHORT[1])
573 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
574 return $val;
575
576 case "int":
577 $val = int($val);
578 if ($val < RANGE_INT[0] || $val > RANGE_INT[1])
579 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
580 return $val;
581
582 case "long":
583 return int($val);
584
585 case "unsignedByte":
586 $val = int($val);
587 if (($val & 0xff) != $val)
588 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
589 if ($val < 0)
590 throw SOAP_DESERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $.type, $val);
591 return $val;
592
593 case "unsignedShort":
594 $val = int($val);
595 if (($val & 0xffff) != $val)
596 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
597 if ($val < 0)
598 throw SOAP_DESERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $.type, $val);
599 return $val;
600
601 case "unsignedInt":
602 $val = int($val);
603 if (($val & 0xffffffff) != $val)
604 throw SOAP_DESERIALIZATION_ERROR, sprintf("value %d is out of range for type %n", $val, $.type);
605 if ($val < 0)
606 throw SOAP_DESERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $.type, $val);
607 return $val;
608
609 case "unsignedLong":
610 if ($val < 0)
611 throw SOAP_DESERIALIZATION_ERROR, sprintf("type %n does not accept negative values (value supplied: %d)", $.type, $val);
612 return int($val);
613
614 case "positiveInteger":
615 if ($val <= 0)
616 throw SOAP_DESERIALIZATION_ERROR, sprintf("type %n only accepts positive values (value supplied: %d)", $.type, $val);
617 return int($val);
618
619 case "date":
620 # remove dashes from date
621 $val =~ s/-//g;
622 return date($val);
623
624 case "dateTime":
625 return date(substr($val, 0, 4) + substr($val, 5, 2) + substr($val, 8, 2) +
626 substr($val, 11, 2) + substr($val, 14, 2) + substr($val, 17, 2));
627
628 case "time":
629 return date("19700101" + substr($val, 0, 2) + substr($val, 3, 2) + substr($val, 6, 2)) +
630 milliseconds(substr($val, 9, 3));
631
632 case "boolean":
633 if ($val =~ /true/i)
634 return True;
635 if ($val =~ /false/i)
636 return False;
637 return boolean($val);
638
639 case "decimal":
640 return float($val);
641
642 case "base64Binary":
643 return parseBase64String($val);
644
645 case "hexBinary":
646 return parseHexString($val);
647
648 default: {
649 if ($.type == "anyType") {
650 if (exists $types.$type)
651 return $types.$type.deserialize($types, $mrh, $val);
652 }
653 throw SOAP_DESERIALIZATION_ERROR, sprintf("don't know how to handle type %n", $.type);
654 }
655 }
656 }
657
658 string getName() {
659 return $.type;
660 }
661
662 string getNameWithNS() {
663 return $.nstype;
664 }
665 }
666
667 # class for XSD array types; currently only supports "binary"; used, for example with HTTP MultiPart messages
668 public class WSDL::XSDArrayType inherits XSDData {
669 public {
670 string $.type;
671 }
672 constructor(string $t) {
673 $.type = $t;
674
675 if ($t != "binary")
676 throw "XSD-ARRAYTYPE-ERROR", sprintf("don't know how to handle arrays of type %n", $t);
677 }
678 any serialize(any $val, any $omit_type) {
679 switch ($.type) {
680 case "binary": {
681 my int $t = $val.typeCode();
682 if ($t === NT_STRING)
683 $val = binary($val);
684 else if ($t !== NT_BINARY)
685 throw SOAP_SERIALIZATION_ERROR, sprintf("cannot serialize type %n from type %n; requires string or binary", $.type, $t);
686 return $val;
687 }
688
689 default: {
690 throw SOAP_SERIALIZATION_ERROR, sprintf("don't know how to handle type %n", $.type);
691 }
692 }
693 }
694 any deserialize(any $types, any $mrh, any $val) {
695 switch ($.type) {
696 case "binary": {
697 if ($val.typeCode() != NT_BINARY)
698 throw SOAP_DESERIALIZATION_ERROR, sprintf("cannot deserialize type %n from type %n; requires binary", $.type, $val.typeName());
699 return $val;
700 }
701
702 default: {
703 throw SOAP_DESERIALIZATION_ERROR, sprintf("don't know how to handle type %n", $.type);
704 }
705 }
706 }
707 }
708
709 # XSD element class
710 public class WSDL::XSDElement inherits XSDNamedData {
711 public {
712 any $.type;
713 int $.minOccurs = 1;
714 int $.maxOccurs = 1;
715 bool $.nillable = False;
716 }
717 constructor(hash $e, any $nsinfo, XSDLateResolverHelper $unresolved) : XSDNamedData(\$e) {
718 if ($nsinfo instanceof XSDData) {
719 $.type = $nsinfo;
720 return;
721 }
722
723 my any $a = $e."^attributes^";
724
725 if (exists $a.minOccurs)
726 $.minOccurs = int($a.minOccurs);
727
728 if (exists $a.maxOccurs)
729 if ($a.maxOccurs == "unbounded")
730 $.maxOccurs = -1;
731 else
732 $.maxOccurs = int($a.maxOccurs);
733
734 if ($.maxOccurs != -1 && $.minOccurs > $.maxOccurs)
735 throw "XSD-ELEMENT-ERROR", sprintf("minOccurs (%d) > maxOccurs (%d) for element %s", $.minOccurs, $.maxOccurs, $.name);
736
737 if ($a.nillable == "true")
738 $.nillable = True;
739
740 if (exists $a.type) {
741 $.type = WSDL::XSDBase::doType($a.type, $nsinfo);
742
743 # add self to unresolved list if element type cannot be resolved
744 if (!($.type instanceof XSDData)) {
745 #printf("DEBUG: self=%n\n", $self);
746 $unresolved.add($self);
747 }
748 }
749 else if (exists $e.simpleType)
750 $.type = new XSDSimpleType($e.simpleType, $nsinfo, $unresolved);
751 else if (exists $e.complexType)
752 $.type = new XSDComplexType($e.complexType, $nsinfo, $unresolved);
753
754 #printf("DEBUG: XSDElement self=%N\n", $self);
755 }
756
757 any serialize(any $h, *softbool $omit_type, *softbool $omit_ns, any $key, any $typename) {
758 #printf("DEBUG: XSDElement::serialize() name=%y h=%y key=%y typename=%y\n", $.name, $h, $key, $typename);
759
760 if (!exists $h) {
761 if ($.minOccurs) {
762 if ($.nillable) {
763 my hash $rh = ("xsi:nil" : "true");
764 if (!$omit_type)
765 $rh += ("xsi:type" : $.type.getNameWithNS());
766 return ("^attributes^" : $rh);
767 }
768 else {
769 if (exists $key && exists $typename)
770 throw SOAP_SERIALIZATION_ERROR, sprintf("missing value for %s.%s (minOccurs=%d, type %n)", $typename, $key, $.minOccurs, $.type.getName());
771 else
772 throw SOAP_SERIALIZATION_ERROR, sprintf("missing element value (minOccurs=%d, type %n)", $.minOccurs, $.type.getName());
773 }
774 }
775 else
776 return;
777 }
778 if ($h.typeCode() == NT_LIST) {
779 if (elements $h == 1)
780 $h = $h[0];
781 else {
782 if ($.maxOccurs == 1)
783 throw SOAP_SERIALIZATION_ERROR, sprintf("cannot serialize element %n of type %n from a list because maxOccurs = 1", $.name, $.type.getName());
784 if (elements $h > $.maxOccurs && $.maxOccurs > 0)
785 throw SOAP_SERIALIZATION_ERROR, sprintf("list for element %n of type %n has %d element%s, but maxOccurs = %d", $.name, $.type.getName(), elements $h, elements $h == 1 ? "" : "s", $.maxOccurs);
786 if (elements $h < $.minOccurs)
787 throw SOAP_SERIALIZATION_ERROR, sprintf("list for element %n of type %n has %d element%s, but minOccurs = %d", $.name, $.type.getName(), elements $h, elements $h == 1 ? "" : "s", $.minOccurs);
788
789 my list $l = ();
790 foreach my any $e in ($h) {
791 $l += $.type.serialize($e, $omit_type, $omit_ns);
792 }
793 return $l;
794 }
795 }
796 if ($.minOccurs > 1)
797 throw SOAP_SERIALIZATION_ERROR, sprintf("only one element passed to element %n of type $n, but minOccurs = %d", $.name, $.type.getName(), $.minOccurs);
798 #printf("DEBUG: element %n omit_type=%n omit_ns=%n\n", $.name, $omit_type, $omit_ns);
799 #printf("DEBUG: type=%N\n", $.type);
800 return $.type.serialize($h, $omit_type, $omit_ns);
801 }
802
803 any deserialize(any $types, any $mrh, any $val) {
804 my any $a = $val."^attributes^";
805 WSDL::XSDBase::removeNS(\$a);
806
807 if (!exists $val || $a.nil == "true") {
808 if ($.nillable || !$.minOccurs)
809 return;
810 throw SOAP_DESERIALIZATION_ERROR, sprintf("NOTHING passed for element %n, but nillable=False and minOccurs=%d", $.name, $.minOccurs);
811 }
812
813 if ($val.typeCode() == NT_LIST) {
814 my int $el = elements $val;
815 if ($.maxOccurs != -1 && $el > $.maxOccurs)
816 throw SOAP_DESERIALIZATION_ERROR, sprintf("error deserializing element %n, maxOccurs=%d but list is %d elements long", $.name, $.maxOccurs, $el);
817 if ($el < $.minOccurs)
818 throw SOAP_DESERIALIZATION_ERROR, sprintf("error deserializing element %n, minOccurs=%d but list is %d elements long", $.name, $.minOccurs, $el);
819
820 my list $l;
821 foreach my any $e in ($val)
822 $l[elements $l] = $.type.deserialize($types, $mrh, $.getValue($mrh, $e));
823 return $l;
824 }
825
826 if ($.minOccurs > 1)
827 throw SOAP_DESERIALIZATION_ERROR, sprintf("single value passed for element %n, but minOccurs=%d", $.name, $.minOccurs);
828
829 return $.type.deserialize($types, $mrh, $.getValue($mrh, $val));
830 }
831 }
832
833 # XSD simple type class
834 public class WSDL::XSDSimpleType inherits XSDNamedData {
835 public {
836 any $.nsinfo;
837 list $.enum = ();
838 any $.type;
839 }
840
841 constructor(hash $st, any $nsinfo, XSDLateResolverHelper $unresolved) : XSDNamedData(\$st) {
842 $.nsinfo = $nsinfo;
843 #my any $a = $st."^attributes^";
844 delete $st."^attributes^";
845
846 WSDL::XSDBase::removeNS(\$st);
847
848 if (exists $st.restriction) {
849 my any $r = $st.restriction;
850
851 my any $base = $r."^attributes^".base;
852 if (!exists $base)
853 throw "XSD-SIMPLETYPE-ERROR", sprintf("missing 'base' attribute in simpleType %n restriction", $.name);
854
855 $.type = WSDL::XSDBase::doType($base, $nsinfo);
856
857 # add base type to unresolved list if type cannot be resolved
858 if (!($.type instanceof XSDData))
859 $unresolved.add($self);
860
861 WSDL::XSDBase::removeNS(\$r);
862
863 if (exists $r.enumeration)
864 $.enum = map $1."^attributes^".value, $r.enumeration;
865 else
866 throw "XSD-SIMPLETYPE-ERROR", sprintf("missing enumeration element in simpleType %n restriction", $.name);
867 }
868 else
869 throw "XSD-SIMPLETYPE-ERROR", sprintf("missing restriction element in simpleType %n", $.name);
870
871 #printf("DEBUG: st=%N\n", $self); exit();
872 }
873
874 any serialize(any $val, *softbool $omit_type, *softbool $omit_ns) {
875 if (!inlist($val, $.enum))
876 throw "SOAP-SERIALIZATION-ERROR", sprintf("value %n passed to simpleType %n is not in the enumeration list (%n)", $val, $.name, $.enum);
877
878 return $.type.serialize($val, $omit_type, $omit_ns);
879 }
880
881 any deserialize(any $types, any $mrh, any $val) {
882 my any $v = $.type.deserialize($types, $mrh, $val);
883
884 if (!inlist($v, $.enum))
885 throw "SOAP-DESERIALIZATION-ERROR", sprintf("value %n passed to simpleType %n is not in the enumeration list (%n)", $v, $.name, $.enum);
886
887 return $v;
888 }
889
890 string getNameWithNS() {
891 return "ns1:" + $.name;
892 }
893 }
894
895 # XSD complex type class
896 public class WSDL::XSDComplexType inherits XSDNamedData {
897 public {
898 any $.nsinfo;
899 any $.array;
900 any $.restriction;
901 any $.extension;
902 any $.hash_type;
903 any $.elements;
904
905 bool $.anyAttribute = False;
906 }
907 constructor(any $ct, any $nsinfo, XSDLateResolverHelper $unresolved) : XSDNamedData(\$ct) {
908 $.nsinfo = $nsinfo;
909 #my any $a = $ct."^attributes^";
910 delete $ct."^attributes^";
911
912 my any $d = $ct.complexContent;
913 if (exists $d) {
914 WSDL::XSDBase::removeNS(\$d);
915 if (exists $d.restriction) {
916 WSDL::XSDBase::removeNS(\$d.restriction);
917
918 my any $base = $d.restriction."^attributes^".base;
919
920 # FIXME: handle namespace
921 my (any $ns, any $tn) = $base =~ x/(\w+):(\w+)/;
922 if (exists $tn) {
923 if ($tn == "Array") {
924 # FIXME check that namespace is SOAP encoding
925 my any $aa = $d.restriction.attribute."^attributes^";
926 WSDL::XSDBase::removeNS(\$aa);
927 if (!exists $aa.arrayType)
928 throw WSDL_ERROR, sprintf("cannot parse complexType restriction: %n", $d.restriction);
929
930 # FIXME: handle multiple dimensions?
931 my (any $ans, any $atn) = $aa.arrayType =~ x/(\w+):(\w+)\[\]$/;
932 if (exists $atn) {
933 $.array.val = $atn;
934 $.array.ns = $ans;
935 }
936 else
937 $.array.val = $aa.arrayType;
938
939 delete $d.restriction.attribute;
940 #printf("DEBUG: ans=%n atn=%n aa=%N\n", $ans, $atn, $aa);
941 return;
942 }
943 else {
944 $.restriction = $tn;
945 }
946 }
947
948 delete $d.restriction."^attributes^";
949
950 $.parseData($d.restriction, $unresolved);
951 }
952 else if (exists $d.extension) {
953 $.extension = $d.extension."^attributes^".base;
954
955 # FIXME: check for soap encoding namespace
956 $.extension =~ s/(.*:)(.*)/$2/;
957 delete $d.extension."^attributes^";
958 WSDL::XSDBase::removeNS(\$d.extension);
959
960 $.parseData($d.extension, $unresolved);
961 }
962 else
963 throw "XSD-COMPLEXCONTENT-ERROR", sprintf("can't parse complexContent %n information", $d.firstKey());
964 }
965 else
966 $.parseData($ct, $unresolved);
967 }
968
969 private parseData(any $d, XSDLateResolverHelper $unresolved) {
970 delete $d.ns;
971 my list $el = keys $d;
972 if (inlist("anyAttribute", $el)) {
973 $.anyAttribute = True;
974 delete $d.anyAttribute;
975 $el = keys $d;
976 }
977
978 if (elements $d > 1)
979 throw WSDL_ERROR, sprintf("%s: expecting a single element in the complexType hash, got: %n", $.name, $el);
980
981 my string $k = $el[0];
982 if ($k == "all") {
983 $.hash_type = XET_ALL;
984 WSDL::XSDBase::removeNS(\$d.all);
985 $.elements = $.parseElements($d.all.element, $unresolved);
986 }
987 else if ($k == "choice") {
988 $.hash_type = XET_CHOICE;
989 WSDL::XSDBase::removeNS(\$d.choice);
990 $.elements = $.parseElements($d.choice.element, $unresolved);
991 }
992 else if ($k == "sequence") {
993 $.hash_type = XET_SEQUENCE;
994 WSDL::XSDBase::removeNS(\$d.sequence);
995 if (exists $d.sequence.element)
996 $.elements = $.parseElements($d.sequence.element, $unresolved);
997 else
998 $.elements = hash();
999 }
1000 else
1001 throw "XSD-COMPLEXTYPE-ERROR", sprintf("unknown keys in %n", $d);
1002 }
1003
1004 private hash parseElements(any $el, XSDLateResolverHelper $unresolved) {
1005 #printf("DEBUG: XSDComplexType::parseElements(%n)\n", $el);
1006 my hash $h;
1007 foreach my any $e in ($el) {
1008 my XSDElement $elem($e, $.nsinfo, $unresolved);
1009 $h.($elem.name) = $elem;
1010 }
1011 return $h;
1012 }
1013
1014 any serialize(any $h, *softbool $omit_type, *softbool $omit_ns) {
1015 if (exists $.array)
1016 return $.array.serialize($h, $omit_type, $omit_ns);
1017
1018 if ($h.typeCode() != NT_HASH)
1019 throw SOAP_SERIALIZATION_ERROR, sprintf("expecting hash argument to serialize from complexType %n (got %n, type %n)", $.getName(), $h, type($h));
1020
1021 my hash $rh;
1022
1023 if ($.hash_type == XET_SEQUENCE || $.hash_type == XET_ALL) {
1024 foreach my string $p in (keys $.elements) {
1025 #printf("DEBUG element=%s=%y\nvalue=%y\n", $p, $.elements.$p, $h.$p);
1026 my any $e = $.elements.$p.serialize($h.$p, $omit_type, $omit_ns, $p, $.name);
1027 if (exists $e) {
1028 my any $key = $omit_ns ? $p : ("ns1:" + $p);
1029 $rh.$key = $e;
1030 }
1031 delete $h.$p;
1032 }
1033 if (elements $h)
1034 throw SOAP_SERIALIZATION_ERROR, sprintf("%n %s of type %n (valid elements: %n)", (my string $kl = keys $h), elements $kl == 1 ? "is an invalid member" : "are invalid members", $.getName(), keys $.elements);
1035 }
1036 else { # "choice" - union
1037 if (elements $h > 1)
1038 throw SOAP_SERIALIZATION_ERROR, sprintf("cannot serialize type %s with more than 1 member (%n)", $.getName(), keys $h);
1039 my *string $key = keys $h.firstKey();
1040 if (!exists $.elements.$key)
1041 throw SOAP_SERIALIZATION_ERROR, sprintf("%n is an invalid member of type %n", $key, $.getName());
1042
1043 # add namespace if necessary
1044 my string $nskey = $omit_ns ? $key : ("ns1:" + $key);
1045 $rh.$nskey = $.elements.$key.serialize($h.$key, $omit_type, $omit_ns, $key, $.name);
1046 }
1047
1048 if (exists $.name && !$omit_type)
1049 $rh."^attributes^" = ( "xsi:type" : "ns1:" + $.name );
1050 #printf("complex type %s returning %n from %n\n", $.name, $rh, $h);
1051 return $rh;
1052 }
1053
1054 hash deserialize(any $types, any $mrh, any $val) {
1055 if (exists $.array)
1056 return $.array.deserialize($types, $mrh, $val);
1057
1058 if ($val.typeCode() != NT_HASH)
1059 throw SOAP_DESERIALIZATION_ERROR, sprintf("cannot deserialize type %n from qore type %n (expecting hash)", $.getName(), type($val));
1060
1061 my hash $rh;
1062
1063 my any $attr = $val."^attributes^";
1064 delete $val."^attributes^";
1065
1066 # ensure types match
1067 my any $tn = $attr."xsi:type";
1068 if (exists $tn) {
1069 my (any $ns, any $name) = $tn =~ x/(.*):(.*)/;
1070 if (exists $name)
1071 $tn = $name;
1072 if ($tn != $.name)
1073 throw SOAP_DESERIALIZATION_ERROR, sprintf("expecting ComplexType type %n, got %n", $.getName(), $tn);
1074 }
1075
1076 WSDL::XSDBase::removeNS2(\$val);
1077
1078 #my any $ns = $val.".ns";
1079 $val -= ".ns";
1080
1081 if ($.hash_type == XET_SEQUENCE || $.hash_type == XET_ALL) {
1082 foreach my string $p in (keys $.elements) {
1083 #printf("element %n\n", $p);
1084 $rh.$p = $.elements.$p.deserialize($types, $mrh, $.getValue($mrh, $val.$p));
1085 delete $val.$p;
1086 }
1087 delete $val."^attributes^";
1088 if (elements $val)
1089 throw SOAP_DESERIALIZATION_ERROR, sprintf("invalid element %n passed in type %n (expecting %n)", (keys $val)[0], $.getName(), keys $.elements);
1090 }
1091 else { # "choice" - union
1092 my any $kl = keys $val;
1093 if (elements $kl > 1)
1094 throw SOAP_DESERIALIZATION_ERROR, sprintf("too many elements supplied for union type %n (%n)", $.getName(), $kl);
1095 $kl = $kl[0];
1096 if (!exists $.elements.$kl)
1097 throw SOAP_DESERIALIZATION_ERROR, sprintf("element %n is not a valid element for union type %n", $kl, $.getName());
1098
1099 $rh.$kl = $.elements.$kl.deserialize($types, $mrh, $.getValue($mrh, $val.$kl));
1100 }
1101 return $rh;
1102 }
1103
1104 string getNameWithNS() {
1105 return "ns1:" + $.name;
1106 }
1107 }
1108
1109 #! web service operation class
1110 public class WSDL::WSOperation inherits XSDNamedData {
1111 public {
1112 WSMessage $.input;
1113 WSMessage $.output;
1114 any $.types;
1115 string $.targetns;
1116 bool $.soap12 = False;
1117 bool $.usedocns = False;
1118 *string $.soapAction;
1119 *string $.request_name;
1120 any $.in;
1121 any $.out;
1122 bool $.docstyle = False;
1123
1124 # info about soap header requirements
1125 hash $.iheader;
1126 hash $.oheader;
1127 }
1128
1129 constructor(any $p, any $types, string $targetns, any $messages, bool $soap12 = False, bool $usedocns = False) : XSDNamedData(\$p) {
1130 $.types = $types;
1131 $.targetns = $targetns;
1132 $.soap12 = $soap12;
1133 $.usedocns = $usedocns;
1134
1135 my any $msghash = $.processNSValue($p.input."^attributes^");
1136
1137 my *WSMessage $msg = $messages.($msghash.message.val);
1138 if (!exists $msg)
1139 throw WSDL_ERROR, sprintf("missing definition for input message %n required by operation %n", $msghash.name.val, $.name);
1140 $.input = $msg;
1141
1142 if (exists $p.output) {
1143 $msghash = $.processNSValue($p.output."^attributes^");
1144
1145 $msg = $messages.($msghash.message.val);
1146 if (!exists $msg)
1147 throw WSDL_ERROR, sprintf("missing definition for output message %n required by operation %n", $msghash.name.val, $.name);
1148
1149 $.output = $msg;
1150 }
1151
1152 my *string $op_ns = $.targetns;
1153 if (exists $op_ns && $op_ns !~ /\/$/)
1154 $op_ns += "/";
1155 $.soapAction = exists $op_ns ? $op_ns + $.name : $.name;
1156 }
1157
1158 setDocStyle(reference $idocmap) {
1159 $.docstyle = True;
1160
1161 if (elements $.input.args > 1)
1162 throw WSDL_ERROR, sprintf("don't know how to handle document-style messages for operation %n that has more than one top-level part (%n)",
1163 $.name, keys $.input.args);
1164
1165 my any $arg = ($.input.args).firstKey();
1166 my any $element = $.input.args.$arg.element.name;
1167 $.request_name = $element;
1168 $idocmap.$element = $.input.args.$arg.element;
1169 }
1170
1171 setTopLevelRequestElement(string $name) {
1172 $.request_name = $name;
1173 }
1174
1175 string getTopLevelRequestName() {
1176 return exists $.request_name ? $.request_name : $.name;
1177 }
1178
1179 #! serializes a request to an XML string for the operation
1180
1187 hash serializeRequest(any h, any header, *string enc);
1188
1189
1191
1197 hash serializeResponse(any h, *string enc);
1198
1199
1200
1201 private:
1202 list processMultiRef(hash body);
1203 public:
1204
1205
1207
1210 any deserializeRequest(hash o);
1211
1212
1214
1217 any deserializeResponse(hash o);
1218
1219
1220
1221 private:
1222 hash processNSValue(hash h);
1223 public:
1224
1225
1227
1229 bool isSoap12();
1230
1231
1233
1235 string getTargetNS();
1236
1237
1238 setOutputMultipart(any v);
1239
1240
1241
1242 private:
1243 parsePart(reference msg, any part);
1244 public:
1245
1246
1247 addOutputPart(any part);
1248
1249
1250 setInputMultipart(any v);
1251
1252
1253 addInputPart(any part);
1254
1255
1256 setInputHeader(string part, WSMessage msg, bool encoded);
1257
1258 };
1259
1260 // web service message class
1261 class WSDL::WSMessage : public XSDNamedData {
1262
1263 public:
1264 public :
1265 hash args;
1266 bool encoded = False;
1267
1268 public:
1269 constructor(any m, any element_map);
1270
1271
1272 hash serialize(any msginfo, any mpm, any name, any h);
1273
1274
1275 hash serializeDocument(any msginfo, any mpm, any h, bool force_ns);
1276
1277
1278 any deserialize(any types, any mrh, any val);
1279
1280
1281 any deserializeDocument(any types, any mrh, any val);
1282
1283 };
1284
1285 // private helper class for lazy name resolution
1286 class WSDL::XSDLateResolverHelper {
1287
1288 public:
1289 private:
1290 list l = ();
1291 public:
1292
1293 constructor();
1294
1295
1296 add(any v);
1297
1298
1299 list getList();
1300
1301 };
1302
1303 // private helper class to save namespaces in the WebService class, parses the imported definitions, and restores the namespace info on exit
1304 class WSDL::XSDImportHelper {
1305
1306 public:
1307 public :
1308 any ws;
1309 hash sp;
1310
1311 public:
1312
1313 constructor(WebService ws, any xsd);
1314
1315 destructor();
1316
1317 };
1318
1320 class WSDL::Binding : public XSDNamedData {
1321
1322 public:
1323 public :
1324 bool docStyle = False;
1325
1326 public:
1327
1328 constructor(hash data, hash opmap, reference idocmap, *hash messages);
1329
1330 };
1331
1333
1335 class WSDL::WebService : public XSDBase {
1336
1337 public:
1338 public :
1339 string wsdl;
1340 hash ns;
1341 bool soap12 = False;
1342 hash base_type;
1343 hash services;
1344 list wsdl_services = ();
1345 hash idocmap;
1346 hash opmap;
1347 hash binding;
1348 hash element_map;
1349 hash messages;
1350 hash types;
1351 bool usedocns = False;
1352 hash portType;
1353
1354 public:
1355
1357
1362 constructor(string str, *hash opts);
1363
1364
1366
1368 *hash getOperationMap(any name);
1369
1370
1371
1372 private:
1373 getNSPrefixes(any a);
1374 public:
1375
1376
1377
1378 private:
1379 XSDBaseType getBaseType(any t);
1380 public:
1381
1382
1383
1384 private:
1385 any resolveType(hash v);
1386 public:
1387
1388
1389 // parse XSD schema types
1390
1391 private:
1392 parseTypes(any data, any http_client, any http_headers);
1393 public:
1394
1395
1396
1397 private:
1398 parseMessage(any message);
1399 public:
1400
1401
1402
1403 private:
1404 parseService(any svc);
1405 public:
1406
1407
1408
1409 private:
1410 parsePortType(any data);
1411 public:
1412
1413
1414
1415 private:
1416 parseBinding(any bindings);
1417 public:
1418
1419
1421
1423 bool isSoap12();
1424
1425
1427
1429 string getWSDL();
1430
1431 };