All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartclient.debug.public.sc.client.application.WebService.js Maven / Gradle / Ivy

The newest version!
/*
 * Isomorphic SmartClient
 * Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
 * Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
 * "SmartClient" is a trademark of Isomorphic Software, Inc.
 *
 * [email protected]
 *
 * http://smartclient.com/license
 */


//> @class WebService
// Class representing a WebService definition derived from a WSDL file.
// 

// A Web Service object allows you to invoke operations (via // +link{method:WebService.callOperation(),callOperation()}), inspect schema declared in the // WSDL file (+link{method:WebService.getSchema(),getSchema()}), and perform simple read-only // databinding +link{method:WebService.getFetchDS()}. //

// Once a WebService has been loaded, a DataSource can be declared with a // +link{attr:DataSource.serviceNamespace} to connect it to the web service, allowing DataSource // data to be loaded and saved to the web service using // +link{class:OperationBinding,operationBindings}. // // @group webService // @treeLocation Client Reference/Data Binding // @visibility xmlBinding //< isc.defineClass("Schema", "DataSource").addProperties({ dataFormat : "xml", // since the schema already knows the types and can be consulted for type information at // any time, when decoding data we drop all namespace declarations on nodes. dropNamespaceDeclarations:true, // NOTE: currently all subclasses of Schema are generated from various XML formats. We // assume they shouldn't be global variables, which is really just intended as a // convenience for user-authored DataSources. addGlobalId:false }); isc.defineClass("WSDLMessage", "Schema").addMethods({ getWSOperation : function (dsRequest) { var service = this.getWebService(dsRequest); // being invoked by way of an entity DataSource performing an operation if (dsRequest && dsRequest.wsOperation) return service.getOperation(dsRequest.wsOperation); // being invoke standalone, eg, callOperation() else return service.getOperationForMessage(this.ID.substring(8)); } }); isc.defineClass("XSElement", "Schema"); isc.defineClass("XSComplexType", "Schema"); //> @class SchemaSet // A set of schema derived from the <xsd:schema> element in a WSDL or XML schema file // loaded by +link{XMLTools.loadWSDL()} or +link{XMLTools.loadXMLSchema()}. // // @treeLocation Client Reference/Data Binding // @visibility xmlBinding //< isc.defineClass("SchemaSet").addMethods({ //> @attr schemaSet.schemaNamespace (URI : null : R) // Namespace of this SchemaSet, derived from the targetNamespace // attribute of the <schema> element. // // @group webService // @visibility xmlBinding //< init : function () { this.ns.ClassFactory.addGlobalID(this); // register the schemaSet globally with the SchemaSet class var schemaNamespace = this.schemaNamespace, registry = isc.SchemaSet.schemaSets, existingSchema = registry[schemaNamespace]; // an xs:schema that contains only an xs:import generates an empty SchemaSet. Don't // clobber an existing, non-empty schemaset with an empty version of the schemaset // loaded later if (existingSchema == null || // empty existing schemaset ((existingSchema.schema == null && existingSchema.schema.length == 0) && // non-empty new schemaset (this.schema != null && this.schema.length != 0))) { registry[schemaNamespace] = this; } // index all schema within this schemaset var serviceNamespace = this.serviceNamespace; if (this.schema) { this._typeIndex = {}; this._elementIndex = {}; this._simpleTypeIndex = {}; for (var i = 0; i < this.schema.length; i++) { var schema = this.schema[i]; // ensure all schema that belong to this set have their schemaNamespace // attribute set schema.serviceNamespace = serviceNamespace; schema.schemaNamespace = schemaNamespace; schema.location = this.location; // make an index of all the schema in this SchemaSet if (isc.isA.SimpleType(schema)) { if (schema.inheritsFrom && schema.inheritsFrom == schema.name && schema.xmlSource == "XSElement") continue; this._simpleTypeIndex[schema.name] = schema; } else if (schema.ID) { if (isc.isAn.XSElement(schema)) { this._elementIndex[schema.ID] = schema; } else { this._typeIndex[schema.ID] = schema; } } } } // for loadXMLSchema() callback to return loaded SchemaSet isc.SchemaSet._lastLoaded = this; }, //> @method schemaSet.getSchema() [A] // Get the schema definition of any complexType or element of complexType defined within // the <schema> element this SchemaSet represents. // // @param schemaName (String) name of the schema to retrieve // @param [schemaType] (String) optional type of schema to return, either "element" for // xs:element definitions only or "type" for xs:complexType // definitions. If unspecified, either will be returned, // with types preferred if names collide // @return (DataSource) the schema if found, or null // @visibility xmlBinding // @example xmlSchemaImport //< getSchema : function (schemaName, schemaType, alreadyVisited) { // xs:schema routinely import each other, so avoid looping getSchema calls if (!alreadyVisited) alreadyVisited = [this]; else alreadyVisited.add(this); var schema; // try local, type-specific indexes if (schemaType == isc.DS._$element) schema = this._elementIndex[schemaName]; else if (schemaType == isc.DS._$type) schema = this._typeIndex[schemaName]; // if schemaType wasn't specified take either type of schema locally if (schemaType == null) { schema = this._typeIndex[schemaName] || this._elementIndex[schemaName]; if (schema != null) return schema; } // resolve all tags to find already loaded schemaSets we imported if (!this._lookedUpImports) { isc.SchemaSet.findLoadedImports(this); this._lookedUpImports = true; } // try imported schema if present var schemaSets = this._schemaSets; if (schemaSets != null) { for (var i = 0; i < schemaSets.length; i++) { var schemaSet = schemaSets[i]; if (alreadyVisited.contains(schemaSet)) continue; schema = schemaSet.getSchema(schemaName, schemaType, alreadyVisited); if (schema != null) return schema; } } }, getSimpleType : function (typeName, alreadyVisited) { // xs:schema routinely import each other, so avoid looping getSchema calls if (!alreadyVisited) alreadyVisited = [this]; else alreadyVisited.add(this); var simpleType; if (this._simpleTypeIndex) { simpleType = this._simpleTypeIndex[typeName]; if (simpleType) return simpleType; } if (this._schemaSets != null) { for (var i = 0; i < this._schemaSets.length; i++) { var schemaSet = this._schemaSets[i]; if (alreadyVisited.contains(schemaSet)) continue; simpleType = schemaSet.getSimpleType(typeName, alreadyVisited); if (simpleType != null) return simpleType; } } }, setLocation : function (location) { this.location = location; if (!this.schema) return; for (var i = 0; i < this.schema.length; i++) { var schema = this.schema[i]; schema.location = location; } }, loadImports : function (callback) { isc.SchemaSet.loadImports(callback, this); }, // override point for changing how imported schema are loaded // NOTE: duplicated in WebService loadImport : function (namespace, location, callback, isWSDL) { return isc.SchemaSet.loadImport(namespace, location, callback, isWSDL, this); }, doneImporting : function () { this.fireCallback(this._doneImportingCallback); }, // when captureXML has been set in loadWSDL / loadXMLSchema, this method is called on the // initiator of a series of schema loads so that complete source is available addImportXMLSource : function (xmlText, location) { this.importSources = this.importSources || []; this.importSources.add({ xmlText : xmlText, location : location }) }, addSchemaSet : function (schemaSet, namespace) { this._imports = this._imports || []; this._imports.add(schemaSet); } }); isc.SchemaSet.addClassMethods({ schemaSets : {}, //> @classMethod SchemaSet.get() [A] // Retrieve a SchemaSet object by it's schemaNamespace. // // @param schemaNamespace (String) uri from the "targetNamespace" attribute of the // <xsd:schema> element from the XML Schema or WSDL file this SchemaSet was derived // from. // @return (SchemaSet) the requested SchemaSet, or null if not loaded // // @visibility xmlBinding //< get : function (schemaNamespace) { return this.schemaSets[schemaNamespace]; }, // find already loaded SchemaSets to fulfill s from within a WSDL or XML Schema // file. Note loadImports() is what would actually attempt to load imported files from // the "location" attribute provided on the tag (if any) // "loader" may be an instance of WebService or SchemaSet findLoadedImports : function (loader) { var imports = this.getAllImports(loader); if (!imports) return; var schemaSets = loader._schemaSets = loader._schemaSets || []; var webServices = loader._webServices = loader._webServices || []; for (var i = 0; i < imports.length; i++) { var importDef = imports[i], isWSDL = importDef.isWSDL, importNamespace = importDef.namespace; if (this._ignoreImports.contains(importNamespace)) continue; // we already have it if ((isWSDL && webServices.find("serviceNamespace", importNamespace)) || (!isWSDL && schemaSets.find("schemaNamespace", importNamespace))) continue; var importObj = isWSDL ? isc.WebService.get(importNamespace) : isc.SchemaSet.get(importNamespace); if (importObj == null) { var preamble; if (isc.isA.WebService(loader)) { preamble = "WebService with targetNamespace '" + loader.serviceNamespace; } else { preamble = "SchemaSet with targetNamespace '" + loader.schemaNamespace; } // it's common for WSDL or XSD to contain a bunch of imports for which there is // no actual file, in a usage almost like "marker interfaces" in Java. If // there's no location, don't consider this a warning. var logMethod = importDef.location ? "logWarn" : "logInfo"; loader[logMethod](preamble + "' could not find " + (isWSDL ? "webService" : "SchemaSet") + " for namespace: '" + importNamespace + "'. Pass autoLoadImports to loadWSDL()/loadXMLSchema() or " + "separately load via loadWSDL/loadXMLSchema jsp tag or method", "schemaLoader"); continue; } // any schemaSet that was independently loaded would have it's own location // already set. If it's got no location assume it was loaded with this web // service. XXX not true of schema loaded by JSP tags if (importObj.location == null) importObj.setLocation(loader.location); isWSDL ? webServices.add(importObj) : schemaSets.add(importObj); } }, getAllImports : function (loader) { var imports = loader.schemaImports; if (loader.wsdlImports) { loader.wsdlImports.setProperty("isWSDL", true); imports = imports || []; imports = imports.concat(loader.wsdlImports); } return imports; }, // fulfill s from within a WSDL or XML Schema by actually attempting to load // the imported XML Schema from the "location" attribute provided on the tag // (if any) loadImports : function (callback, loader) { loader._doneImportingCallback = callback; var imports = this.getAllImports(loader); // if nothing to import, fire callback immediately if (!imports) return loader.doneImporting(); loader._importCount = 0; //loader.logWarn("imports are: " + loader.echoFull(imports)); for (var i = 0; i < imports.length; i++) { var importDef = imports[i], namespace = importDef.namespace; if (namespace) { var alreadyLoaded = (importDef.isWSDL ? isc.WebService.get(namespace) : isc.SchemaSet.get(namespace)); if (alreadyLoaded != null) { loader.logDebug("import already loaded: " + namespace + ", skipping", "schemaLoader"); continue; } } if (importDef.location && importDef.location != loader.location) { var requested = loader.loadImport(namespace, importDef.location, function (loadedObj) { if (isc.isA.WebService(loadedObj)) { loader.addWebService(loadedObj, namespace); } else { loader.addSchemaSet(loadedObj, namespace); } loader._importCount--; loader.logInfo(loader + " loaded import: " + loadedObj + " as namespace: " + namespace + ", remaining imports: " + loader._importCount, "schemaLoader"); if (loader._importCount == 0) loader.doneImporting(); }, importDef.isWSDL); if (requested) loader._importCount++; } } // no attempts to load imports, fire callback now if (loader._importCount == 0) loader.doneImporting(); }, loadImport : function (namespace, location, callback, isWSDL, loader) { // "location" attribute is intended to the location of the file doing the import, so // combine URLs var baseDir = loader.location.substring(0, loader.location.lastIndexOf("/")); if (!baseDir.endsWith("/")) baseDir += "/"; var url = isc.Page.combineURLs(baseDir, location); // strangely XML schema files sometimes import themselves if (url == loader.location) { loader.logDebug("skipping self-reference import: " + url, "schemaLoader"); return false; } // skip certain pedantic imports like importing the XML namespace if (this._ignoreImports.contains(url)) { loader.logDebug("skipping pedantic import: " + url, "schemaLoader"); return false; } // skip definitely redundant loads (this page has already loaded from this URL) if (this._allImports.contains(url)) { loader.logDebug("skipping redundant import: " + url, "schemaLoader"); return false; } this._allImports.add(url); loader.logInfo("loading import from: " + url + "\nschema/service base dir: " + baseDir + "\nimport location: " + location, "schemaLoader"); var method = isWSDL ? "loadWSDL" : "loadXMLSchema"; isc.xml[method](url, function (schemaSet) { loader.fireCallback(callback, "schemaSet", [schemaSet]); }, null, true, { // track the initiator of any series of schema loads (first WebService / SchemaSet // to have loaded) initiator:loader.initiator || loader, // recursively capture XML if the original call asked for it captureXML:loader.captureXML }); return true; }, _ignoreImports : [ "http://www.w3.org/2001/xml.xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/XML/1998/namespace" ], _allImports : [] }); isc.SchemaSet.getPrototype().toString = function () { return "[" + this.Class + " ns=" + this.echoLeaf(this.schemaNamespace) + (this.location ? " location=" + isc.Page.getLastSegment(this.location) : "") + "]"; }; //> @class WSRequest // A WSRequest (or "web service request") is an extended RPCRequest with additional properties // applicable to WSDL/SOAP web services. //

// All properties which are legal on +link{class:RPCRequest} are legal on a WSRequest, in // addition to the properties listed here. // // @treeLocation Client Reference/Data Binding // @see RPCRequest // @visibility external //< //> @attr wsRequest.wsOperation (String : null : IR) // Name of the web service operation to invoke. // // @visibility external //< //> @attr wsRequest.data (any : null : IR) // Data to be serialized to XML to form the SOAP body. // // @visibility external //< //> @attr wsRequest.useFlatFields (boolean : null : IR) // @include dsRequest.useFlatFields // @visibility external //< //> @attr wsRequest.xmlNamespaces (Object : null : IR) // Optional object declaring namespace prefixes for use in evaluating the // resultType parameter of +link{WebService.callOperation()}, if resultType is an // XPath. //

// Format is identical to +link{operationBinding.xmlNamespaces}, and default namespaces // bindings are also identical. // // @visibility external //< //> @attr wsRequest.xmlResult (boolean : false : IR) // Valid only with +link{WebService.callOperation()}. If set, do not transform XML results to // JavaScript. Instead just return the XML nodes selected by the passed XPath or recordName, // or all nodes within the SOAP body if no XPath was passed. // // @visibility external //< //> @attr wsRequest.headerData (any : null : IR) // Data to be serialized to form the SOAP headers, as a map from the header part name to the // data. For example, given WSDL like this: //

//     <soap:header part="SessionHeader" message="tns:HeaderMessage"/>
//     <soap:header part="CallOptions" message="tns:HeaderMessage/>
// 
// headerData like this might be provided: //
//     dsRequest.headerData = 
//         { SessionHeader : data
//           CallOptions : data };
// 
// The provided data will be serialized to XML by the // +link{webService.getInputHeaderSchema,SOAP header schema} via // +link{dataSource.xmlSerialize()} // // @visibility external //< isc.defineClass("WebService").addMethods({ //> @attr webService.serviceNamespace (URI : null : R) // Namespace of this WebService, derived from the targetNamespace // attribute of the <wsdl:definitions> element. // // @group webService // @visibility xmlBinding //< init : function () { // mark all messages with the service namespace var namespace = this.serviceNamespace; if (this.messages) { for (var i = 0; i < this.messages.length; i++) { this.messages[i].serviceNamespace = namespace; } } // register globally this.logInfo("registered service with serviceNamespace: " + namespace + " service name: " + this.name); isc.WebService.services.add(this); // for loadWSDL() callback to return WebService isc.WebService._lastLoaded = this; }, loadImports : function (callback) { isc.SchemaSet.loadImports(callback, this); }, // override point for changing how imported schema are loaded // NOTE: duplicated in SchemaSet loadImport : function (namespace, location, callback, isWSDL) { return isc.SchemaSet.loadImport(namespace, location, callback, isWSDL, this); }, doneImporting : function () { this.fireCallback(this._doneImportingCallback); }, addSchemaSet : function (schemaSet, namespace) { this._schemaSets = this._schemaSets || []; this._schemaSets.add(schemaSet); }, addWebService : function (webService, namespace) { this._webServices = this._webServices || []; this._webServices.add(webService); }, // when captureXML has been set in loadWSDL / loadXMLSchema, this method is called on the // initiator of a series of schema loads so that complete source is available addImportXMLSource : function (xmlText, location) { this.importSources = this.importSources || []; this.importSources.add({ xmlText : xmlText, location : location }) }, getOperation : function (operationName, portTypeName) { if (isc.isAn.Object(operationName)) return operationName; // ensure we've looked up related WSDL/XMLSchema imports if (!this._lookedUpImports) { isc.SchemaSet.findLoadedImports(this); this._lookedUpImports = true; } // look up the binding and portType definitions var bindingOperation = this.getBindingOperation(operationName, portTypeName); var portTypeOperation = this.getPortTypeOperation(operationName, portTypeName); if (!bindingOperation && !portTypeOperation) { this.logWarn(this + ": no such operation: '" + operationName + "'"); return null; } // and combine into a structure that has everything we need to know return isc.addProperties({}, portTypeOperation, bindingOperation); }, // find an operation from a list of bindings or portTypes findOperation : function (operationName, portTypeName, bindingList, isPortType) { if (!bindingList) return; // if portTypeName is specified, look in only s or s of that name if (portTypeName) bindingList = bindingList.findAll("portTypeName", portTypeName); if (!bindingList) return; // check imported web service definitions if (this._webServices) { for (var i = 0; i < this._webServices.length; i++) { var webService = this._webServices[i], method = isPortType ? "getPortTypeOperation" : "getBindingOperation", operation = webService[method](operationName, portTypeName); if (operation != null) return operation; } } // otherwise look in any of them, so that only operationName needs to be specified so // long as it's unique for (var i = 0; i < bindingList.length; i++) { var operations = bindingList[i].operation; if (!isc.isAn.Array(operations)) operations = [operations]; var operation = operations.find("name", operationName); if (operation != null) return operation; } }, // get an definition from a // portTypes contain the inputMessage and outputMessage getPortTypeOperation : function (operationName, portTypeName) { return this.findOperation(operationName, portTypeName, this.portTypes, true); }, // get an definition from a // bindings contain the soapAction, parts (which parts of the message to use), soapEncoding // style, input and output namespace (for encodings that cause an element to be output // corresponding to the operation name) getBindingOperation : function (operationName, portTypeName) { return this.findOperation(operationName, portTypeName, this.bindings); }, getOperationForMessage : function (messageName) { var operations = this.getOperations(); if (!operations) return; var operation = operations.find("inputMessage", messageName); if (operation) return operation; operation = operations.find("outputMessage", messageName); if (operation) return operation; }, //> @method webService.getOperationNames() // @return (Array) names of the available operations supported by this service (array of strings) // @group webService // @visibility xmlBinding //< getOperationNames : function () { // return cached list var operationNames = this.operationNames; if (operationNames) return operationNames; // ensure we've looked up related WSDL/XMLSchema imports to connect to separately // loaded portTypes if (!this._lookedUpImports) { isc.SchemaSet.findLoadedImports(this); this._lookedUpImports = true; } operationNames = this.operationNames = []; if (this.bindings) { for (var i = 0; i < this.bindings.length; i++) { var binding = this.bindings[i], operations = binding.operation; if (!isc.isAn.Array(operations)) operations = [operations]; operationNames.addList(operations.getProperty("name")); // find the corresponding portType operation and mark it as having a binding for (var j = 0; j < operationNames.length; j++) { var ptOperation = this.getPortTypeOperation(operationNames[j], binding.portTypeName); if (ptOperation) ptOperation.hasBinding = true; } } } // add all operations on portType that don't have a binding if (this.portTypes) { for (var i = 0; i < this.portTypes.length; i++) { var portType = this.portTypes[i], operations = portType.operation; if (!isc.isAn.Array(operations)) operations = [operations]; var unbound = operations.findAll("hasBinding", true); if (unbound) { operations = operations.duplicate(); operations.removeAll(unbound); } operationNames.addList(operations.getProperty("name")); } } // expensive, so cache it return (this.operationNames = operationNames); }, // get operation definitions for all of the operations supported by this web service getOperations : function (boundOnly) { var operationNames = this.getOperationNames(), operations = []; for (var i = 0; i < operationNames.length; i++) { var operation = this.getOperation(operationNames[i]); if (boundOnly && !operation.hasBinding) continue; operations.add(operation); } return operations; }, //> @method webService.getSchema() // Get the schema definition of any complexType or element of complexType defined in any // <schema> blocks in the WSDL file this WebService represents. // // @param schemaName (String) name of type or element // @param [schemaType] (String) optional type of schema to return, either "element" for // xs:element definitions only or "type" for xs:complexType // definitions. If unspecified, either will be returned, // with types preferred if names collide // @return (DataSource) requested schema // @group webService // @visibility xmlBinding //< getSchema : function (name, schemaType) { // look up all the schemaSets that the WSDL file referred to. // do this lazily so order of creation doesn't matter for SchemaSets and WebServices // loaded from one WSDL file if (!this._lookedUpImports) { isc.SchemaSet.findLoadedImports(this); this._lookedUpImports = true; } var schemaSets = this._schemaSets; if (schemaSets != null) { // look through each schemaSet for a schema of this name for (var i = 0; i < schemaSets.length; i++) { var schemaSet = schemaSets[i]; var schema = schemaSet.getSchema(name, schemaType); if (schema) return schema; } } // finally, look globally. This is key for discovering schema loaded from separate // files via separate calls to loadXMLSchema. return isc.DS.get(name, null, null, schemaType); }, // get the request or response message schema getRequestMessage : function (operationName) { var operation = this.getOperation(operationName); return this.getMessage(operation.inputMessage); }, getResponseMessage : function (operationName) { var operation = this.getOperation(operationName); return this.getMessage(operation.outputMessage); }, getMessage : function (messageName) { var message = this.messages.find("ID", "message:" + messageName); if (message) return message; // ensure we're connected to any imported WSDL services, which may contain message // definitions if (!this._lookedUpImports) { isc.SchemaSet.findLoadedImports(this); this._lookedUpImports = true; } // look in imported services if (this._webServices) { for (var i = 0; i < this._webServices.length; i++) { var webService = this._webServices[i]; message = webService.getMessage(messageName); if (message) return message; } } }, getBodyPartNames : function (operationName, isOutput) { var operation = this.getOperation(operationName), bodyParts = isOutput ? operation.outputParts : operation.inputParts; if (bodyParts == null || isc.isAn.emptyString(bodyParts)) { // all body parts should be used var message = isOutput ? this.getResponseMessage(operationName) : this.getRequestMessage(operationName); return message.getFieldNames(); } else { return bodyParts.split(" "); } }, //> @attr webService.globalNamespaces (Object : ... : IRW) // @include dataSource.globalNamespaces //< globalNamespaces : { xsi: "http://www.w3.org/2001/XMLSchema-instance", xsd: "http://www.w3.org/2001/XMLSchema" }, //> @method webService.callOperation() // Invoke a web service operation. //

// The data parameter will be serialized to XML to form the input message for // the operation, as described by +link{method:DataSource.xmlSerialize()}. Namespacing, // element ordering, and SOAP encoding rules are automatically followed. If the web // service you are trying to contact requires a complicated nested structure, consider // using +link{wsRequest.useFlatFields} to simplify the required JavaScript input data. //

// The resultType selects what part of the message should be decoded to // JavaScript and made available as the "data" variable in the callback. The // resultType parameter can be either: //

    //
  • an XPath. "data" will be always be an Array, containing the selected elements as // decoded by +link{XMLTools.toJS()}. All properties will have String value. //
  • the name of an XML Schema type found somewhere in the response. You can use the // WSDL tab of the Developer Console to analyze the WSDL file for an appropriate type name. // "data" will be an Array, containing the decoded elements as decoded by // +link{dataSource.recordsFromXML()}. In this case, since the XML Schema type of the // selected data is known, properties will have correct type (eg "date" fields will // have JavaScript Date objects) //
  • null. "data" will an Object representing the entire <SOAP:Body> as decoded // to JavaScript. As above, properties will have correct type. //
// In the callback, you also receive the XML document returned by the web service as // "xmlDoc". //

// NOTE: callOperation() is appropriate for simple operations that do not // involve DataBound Components, such as logging into a web service, or retrieving simple // String data. callOperation() can also be used to retrieve small, read-only // datasets such as the option list for a SelectItem, but only if the dataset is guaranteed // to remain small enough for paging to be unnecessary. For any larger datasets or // anything that will be edited, DataSource integration is more appropriate. // // @param operationName (String) Name of the operation to invoke // @param data (Object) data to serialize as XML to form the inbound message of // the operation // @param resultType (Type or ElementName or XPath) Type, Element name, or XPath that // should be selected from the result. For XPaths, see // +link{wsRequest.xmlNamespaces} for available namespace // prefixes and how to add more. // @param callback (Callback) Callback to invoke on completion. Signature // callback(data, xmlDoc, rpcResponse, wsRequest) // @param requestProperties (WSRequest Properties) Additional properties for the WSRequest, such // as HTTPHeaders // // @group webService // @visibility xmlBinding // @example wsdlOperation //< callOperation : function (operationName, data, resultType, callback, requestProperties) { var operation = this.getOperation(operationName); if (operation == null) { this.logWarn("No such operation: " + operationName); return; } requestProperties = requestProperties || isc.emptyObject; var wsRequest = isc.addProperties({ actionURL: this.getDataURL(operationName), httpMethod: "POST", contentType: "text/xml", data : data, serviceNamespace : this.serviceNamespace, // NOTE: this ensures that all DataSources involved in serialization consistently // lookup the WebService instance that callOperation was called on, even if there // are multiple WSDL files that defined s in a common namespace serviceName : this.name, wsOperation : operationName }, requestProperties); wsRequest.httpHeaders = isc.addProperties({}, { SOAPAction : operation.soapAction || '""' }, requestProperties.httpHeaders); wsRequest.headerData = requestProperties.headerData || this.getHeaderData(wsRequest); // create the SOAP message based on the WSRequest wsRequest.data = this.getSoapMessage(wsRequest); wsRequest.clientContext = { _callOperationCallback : callback, _operationName : operationName, _resultType : resultType, // special flag to return selected XML nodes without JS translation _xmlResult : requestProperties.xmlResult }; if (this.spoofResponses) { var sampleResponse = this.getSampleResponse(operationName); if (this.logIsDebugEnabled("xmlBinding")) { this.logDebug("spoofed response:\n" + sampleResponse, "xmlBinding"); } this.delayCall("_callOperationReply", [isc.xml.parseXML(sampleResponse), sampleResponse, {status:0, clientContext:wsRequest.clientContext, httpResponseCode:200, httpResponseText:sampleResponse}, wsRequest]); return; } wsRequest.callback = { target:this, methodName:"_callOperationReply" }; isc.xml.getXMLResponse(wsRequest); }, _callOperationReply : function (xmlDoc, xmlText, rpcResponse, rpcRequest) { var context = rpcRequest.clientContext, operationName = context._operationName, resultType = context._resultType; // If there was an error, we will only arrive here if willHandleError is true. // Just fire the user's callback and let them decide what to do. if (rpcResponse.status < 0) { this.fireCallback(context._callOperationCallback, "data,xmlDoc,rpcResponse,wsRequest", [rpcResponse.data,xmlDoc,rpcResponse,rpcRequest]); return; } xmlDoc.addNamespaces(this.getOutputNamespaces(operationName)); if (rpcRequest.xmlNamespaces) { xmlDoc.addNamespaces(rpcRequest.xmlNamespaces); } // we were passed a type (FIXME crude detection) var passedXPath = (resultType != null && resultType.contains("/")), xPath = (passedXPath ? resultType : null), data; if (passedXPath) { // apply XPath selector if passed one or passed resultType data = xmlDoc.selectNodes(xPath); } else if (resultType) { data = this.selectByType(xmlDoc, operationName, resultType); } else { // if no XPath or resultType was given, select the soap body data = xmlDoc.selectNodes("//s:Body/*", { s:"http://schemas.xmlsoap.org/soap/envelope/" }); // don't create a spurious Array for the most common case of a singular body // element if (data.length == 1) data = data[0]; } if (this.logIsDebugEnabled()) { this.logDebug("selected response data is: " + this.echoFull(data)); } if (context._xmlResult) { // just return the raw XML nodes this.fireCallback(context._callOperationCallback, "data,xmlDoc,rpcResponse,wsRequest", [data,xmlDoc,rpcResponse,rpcRequest]); return; } // transform to JS var schema; if (passedXPath) { // if an xpath was passed, we don't know the schema of the selected elements, just // use schemaless transform schema = null; } else if (resultType) { // if we were passed a resultType, use that as the schema to transform nodes with // correct typing schema = this.getSchema(context._resultType); } else { // passed neither an xPath nor a resultType, so we selected the whole SOAP body. // We can use the message schema to decode the entire SOAP body, with correct // typing. var messageSchema = this.getSchema("message:"+this.getOperation(operationName).outputMessage); if (this.getSoapStyle(operationName) != "document") { schema = messageSchema; } else { var firstField = messageSchema.getFieldNames().first(); schema = messageSchema.getSchema(messageSchema.getField(firstField).type); } } //this.logWarn("transforming reply for operation: " + operationName + // " toJS using schema " + schema); data = isc.xml.toJS(data, null, schema); this.fireCallback(context._callOperationCallback, "data,xmlDoc,rpcResponse,wsRequest", [data,xmlDoc,rpcResponse,rpcRequest]); }, // when applying an XPath selector to the output of a web service, our default namespacing // strategy of providing all the namespaces declared on the document element // generally fails because we just get SOAP-related namespaces. Furthermore, the web // service may use auto-generated prefixes for namespaces, so in general we can't rely on // the returned document alone for reasonable namespace prefixes. Instead, provide the // schema namespace from the outermost element, and the service namespace getOutputNamespaces : function (operation, namespaces) { var schema = this.getDefaultOutputDS(operation); return isc.addProperties({ "default" : schema.schemaNamespace || this.serviceNamespace, schema : schema.schemaNamespace, service : this.serviceNamespace }, namespaces); }, getDataURL : function (operationName) { // NOTE: per-operation URLs can't be defined in WSDL, this is here for spoofing var operation = this.getOperation(operationName); if (operation && operation.dataURL) return operation.dataURL; return this.dataURL; }, // SOAP message serialization // --------------------------------------------------------------------------------------- //> @method webService.getMessageSerializer() [A] // Get the schema used to serialize the entire request // // @param operationName (String or WSRequest Properties) name of the web service operation, // or a WSRequest specifying it // @param forResponse (boolean) whether a serializer is request for the response message, // as opposed to the request message (the default) // @return (DataSource) schema used for serialization //< getMessageSerializer : function (operationName, forResponse) { var serializer = forResponse ? this.getResponseMessage(operationName) : this.getRequestMessage(operationName); if (serializer == null) { this.logWarn("no " + (forResponse ? "response" : "request") + " message definition found for operation: '" + operationName + "'"); return; } // in rpc-style soap, the outermost element of the body is named after the message // name. In document-style soap, there is no element that corresponds to the message // name, only it's contents. Therefore for document-style SOAP if there is exactly one // subelement of the message (the most common style by far), use that as the input // schema. This means that when a message is supposed to look like this: // // bob // mebob // // The JS data you need to pass is: // { username:"bob", password:"mebob" } // .. instead of the surprising and less obvious: // { login : { username:"bob", password:"mebob" } } if (this.getSoapStyle(operationName) != "document") return serializer; var fieldNames = serializer.getFieldNames(); if (fieldNames.length == 1 && serializer.fieldIsComplexType(fieldNames[0])) { var field = serializer.getField(fieldNames[0]); //this.logWarn("skipping message element and using field: " + this.echo(field)); serializer = serializer.getSchema(field.type, field.xsElementRef ? "element" : null); if (serializer == null) { this.logWarn("can't find schema: " + field.type + ", part of " + (forResponse ? "response" : "request") + " message for operation '" + operationName + "'"); } } return serializer; }, // whether this operation uses simplified inputs, that is, does not expect data to contain // an object named after the message name, since the message name does not appear in the // generated message itself. Useful for callers who form a data structure that exactly // corresponds to the message structure (ServiceOperation). useSimplifiedInputs : function (operationName, forResponse) { var normalSerializer = forResponse ? this.getResponseMessage(operationName) : this.getRequestMessage(operationName); return this.getMessageSerializer(operationName, forResponse) != normalSerializer; }, //> @method webService.getSoapMessage() [A] // Return the SOAP message that will be formed from this WSRequest. // // @param wsRequest (WSRequest Properties) web service request object // @return (String) SOAP message // @visibility xmlBinding //< getSoapMessage : function (wsRequest, flags) { wsRequest.serviceNamespace = wsRequest.serviceNamespace || this.serviceNamespace; var operationName = wsRequest.wsOperation; if (this.getOperation(operationName) == null) { this.logWarn("no such operation: '" + operationName + "' in service: " + this.serviceNamespace); return ""; } var messageSerializer = this.getMessageSerializer(wsRequest.wsOperation, flags && flags.generateResponse); // already warned about in getMessageSerializer if (messageSerializer == null) return ""; return messageSerializer.getXMLRequestBody(wsRequest, flags); }, getSampleResponse : function (operationName, data, flags, returnRequest) { return this.getSoapMessage({ wsOperation : operationName, data : data || {} }, isc.addProperties({ spoofData:true, generateResponse:!returnRequest }, flags)); }, getSampleRequest : function (operationName, data, flags) { return this.getSampleResponse(operationName, data, flags, true); }, // get the soap style, "document" or "rpc", which can be specified per operation or for the // service as a whole getSoapStyle : function (operationName) { return this.getOperation(operationName).soapStyle || this.soapStyle; }, // --------------------------------------------------------------------------------------- //> @method webService.getInputDS() // Get a DataSource representing the input message to a web service operation. //

// This DataSource is suitable for use as // +link{DataBoundComponent.dataSource,form.dataSource} for a form that the user fills out // when providing inputs to call this web service operation. // // @param operationName (String) name of the web service operation whose inputs the // returned DataSource will represent // @return (DataSource) DataSource representing the input message of a web service // operation // @visibility xmlBinding // @example wsdlBinding //< getInputDS : function (operationName) { return this.getMessageSerializer(operationName); }, getHeaderSchema : function (operationName, isInput) { var operation = this.getOperation(operationName), headers = isInput ? operation.inputHeaders : operation.outputHeaders; if (!headers) return null; var headerSchema = {}; for (var i = 0; i < headers.length; i++) { var partName = headers[i].part, messageSchema = this.getSchema("message:"+headers[i].message); //this.logWarn("messageSchema: " + messageSchema); var partField = messageSchema.getPartField(partName); //this.logWarn("partField: " + this.echo(partField)); // NOTE: simple type headers are legal, in which case we just return the field // definition headerSchema[partName] = this.getSchema(partField.type) || partField; } return headerSchema; }, //> @method webService.getInputHeaderSchema() // Get the schema for each part of the SOAP header for the input message of a given // operation, as a mapping from part name to schema. For example, given WSDL like: //

    //     <soap:header part="SessionHeader" message="tns:HeaderMessage"/>
    //     <soap:header part="CallOptions" message="tns:HeaderMessage/>
    // 
// The following schema would be returned: //
    //     { SessionHeader : sessionHeaderPartSchema,
    //       CallOptions : callOptionsPartSchema }
    // 
// The schema are instances of +link{DataSource} that can be inspected to discover the // elements and types that are legal in that header part, and can construct a valid SOAP // header part if +link{dataSource.xmlSerialize()} is invoked. // // @param operationName (String) name of an operation from this web service // @return (Object) mapping from partName to schema // @visibility xmlBinding //< getInputHeaderSchema : function (operationName) { return this.getHeaderSchema(operationName, true); }, //> @method webService.getOutputHeaderSchema() // Get the schema for each part of the SOAP header for the output message of a given // operation, as a mapping from part name to schema. For example, given WSDL like: //
    //     <soap:header part="SessionHeader"/>
    //     <soap:header part="CallOptions"/>
    // 
// The following schema would be returned: //
    //     { SessionHeader : sessionHeaderPartSchema,
    //       CallOptions : callOptionsPartSchema }
    // 
// The schema are instances of +link{DataSource} that can be inspected to discover the // elements and types that are legal in that header part, and can construct a valid SOAP // header part if +link{dataSource.xmlSerialize()} is invoked. // // @param operationName (String) name of an operation from this web service // @return (Object) mapping from partName to schema // @visibility xmlBinding //< getOutputHeaderSchema : function (operationName) { return this.getHeaderSchema(operationName, false); }, //> @method webService.getHeaderData() // Override this method to return data that should be serialized as SOAP headers for the // current operation, such as a sessionId. //

// Format of the returned data is the same as that documented for // +link{dsRequest.headerData}. //

// The object passed to this method will be a true DSRequest in the case of a DataSource // operation, or just an Object with a "data" property for web service operations // initiated by +link{webService.callOperation}. //

// If headerData is instead provided via either dsRequest.headerData or as // part of the requestProperties parameter to // +link{webService.callOperation,callOperation()}, this method will never be called. // // @param dsRequest (DSRequest) // @return (Object) data for SOAP headers // // @visibility xmlBinding //< getHeaderData : function (dsRequest) { }, // create an XPath selector that will select objects of the targetSchema from the output // message of the specified web service operation. // This is needed when we are interested in records of type "myObject", but which actually // have the tagName "records" in the result selectByType : function (xmlResponse, operationName, schemaName) { var operation = this.getOperation(operationName), outputMessage = this.getSchema("message:" + operation.outputMessage), targetSchema = this.getSchema(schemaName); if (targetSchema == null) { this.logWarn("selectByType: type '" + schemaName + "' not present in schema for message: " + operation.outputMessage); return null; } // find the tagName the target schema will appear as in the response message var tagLocation = outputMessage.findTagOfType(targetSchema.ID); if (tagLocation == null) { this.logWarn("selectByType: no tag of type '" + schemaName + "' could be found in message: " + operation.outputMessage); return null; } var tagLocationDS = tagLocation[0], tagName = tagLocation[1], parentSchema = tagLocation[2], parentSchemaTagName = tagLocation[3], field = tagLocationDS.getField(tagName); // if we couldn't find the tagName, use the type name as a fallback (this may indicate // a response message which is not completely specified in schema, eg xsd:any) tagName = tagName || targetSchema.ID; // element definitions that were top-level in the WSDL file have a schemaNamespace // attribute and must be namespaced within the response message. Non-top-level element // definitions must not be, unless the element declares // elementFormDefault="qualified", in which case everything must be qualified. var qualify = targetSchema.mustQualify, namespace = targetSchema.schemaNamespace, xpath = "//" + (qualify ? "ns0:" : "") + tagName; /* if (parentSchema && !isc.isA.WSDLMessage(parentSchema) && targetSchema.getFieldNames().length == 1) { qualify = parentSchema.mustQualify; namespace = parentSchema.schemaNamespace; xpath = "//" + (qualify ? "ns0:" : "") + parentSchemaTagName + "/*"; this.logWarn("targetting parentSchema: " + parentSchema + " fieldName " + parentSchemaTagName + " namespace: " + namespace); } */ // handle SOAP Array encoding, which specifies essentially that there is a container // tag whose children are of a specified type, which we represent as field.multiple if (field && field.multiple) xpath = xpath + "/*"; var elements = isc.xml.selectNodes(xmlResponse, xpath, { ns0 : namespace }); if (this.logIsDebugEnabled("xmlBinding")) { this.logDebug("selecting type: '" + targetSchema + "' within message '" + operation.outputMessage + " via XPath: " + xpath + (qualify ? " using ns0: " + targetSchema.schemaNamespace : "") + " got " + elements.length + " elements", "xmlBinding"); } return elements; }, // find the schema best suited for binding a grid or editor form to the results of a // web service operation. Note this getInputDS() gives you the schema best suited for eg a // SearchForm. getDefaultOutputDS : function (operationName) { var schema = this.getResponseMessage(operationName); if (!schema) return null; // skip one level of pointless containment: a complexType with just one subelement, // which is also a complexType. var fieldNames = schema.getFieldNames(); if (fieldNames.length == 1 && schema.fieldIsComplexType(fieldNames[0])) { return schema.getSchema(schema.getField(fieldNames[0]).type); } // improvements: find the first Array-like structure of elements containing simple type // fields. return schema; }, //> @method webService.getFetchDS() // Retrieve a DataSource that provides read-only access to records returned by a web // service operation. //

// +link{interface:DataBoundComponent,DataBound Components} can be bound to the returned // DataSource, and the +link{ListGrid.fetchData(),fetchData()} method can be invoked // to retrieve data from the web service. //

// The returned DataSource is only capable of the "fetch" // +link{group:dataSourceOperations,DataSource operation}, not "update", "add" or // "remove". To create a DataSource capable of full read-write access, use // +link{DataSource.operationBindings} with the // +link{OperationBinding.wsOperation,wsOperation} property set to associate each // DataSource operation with a web service operation. // // @param operationName (String) name of the web service operation to invoke to fetch // records // @param resultType (String) tag or type name of the XML element to be returned as // DataSource records // @param [operationBindingProperties] (OperationBinding Properties) // Optional additional properties for the operationType:"fetch" // +link{OperationBinding,operationBinding} which this method automatically creates. This // can be used to set properties such as +link{operationBinding.useFlatFields} or // +link{operationBinding.recordXPath} // // @group webService // @visibility xmlBinding //< getFetchDS : function (operationName, resultType, operationBindingProperties) { // if no resultType is specified, pick the first non-trivial structure if (resultType == null) resultType = this.getDefaultOutputDS(operationName); resultType = isc.isA.Object(resultType) ? resultType.ID : resultType; if (resultType != null && this.getSchema(resultType) == null) { this.logWarn("getFetchDS: resultType: '" + resultType + "' not present in web service - missing XML files?"); } // we subclass because we need operation-specific properties on this DataSource, // where it may be shared as the inputs or part of the inputs for another operation var fetchDS = isc.DS.create({ // critical so this DS can find this WebService serviceNamespace : this.serviceNamespace, inheritsFrom : resultType, operationBindings : [ isc.addProperties({ operationType: "fetch", wsOperation:operationName, recordName:resultType }, operationBindingProperties) ] }); return fetchDS; }, //> @method webService.setLocation() [A] // Set location can be used when the actual URL where a service will be accessible isn't // known until runtime, or changes at runtime, hence can't be embedded in the service // definition. //

// With an operation parameter, setLocation() can be used to set a distinct // URL for each web service operation. This is a development-time only feature that allows // XML flat files to be placed at various URLs on a server, to serve as spoofed responses // for each web service operation. // // @param location (URL) URL where web service can be contacted // @param [operation] (String) optional operation name to set the location for, for // debugging only // @group webService // @visibility xmlBinding //< setLocation : function (location, operation) { if (operation) this.getBindingOperation(operation).dataURL = location; else this.dataURL = location; } }); isc.WebService.addClassMethods({ // NOTE: we create one WebService per .wsdl file, however, two s can // appear in two different files with different elements. In this case // the different s can be distinguished by the @name attribute on the // . services : [], //> @classMethod WebService.get() // Retrieve a WebService object by the targetNamespace declared on the <wsdl:definitions> // element in the WSDL file from which the WebService was derived. //

// If you have more than one <wsdl:service> in the same target namespace, use // +link{classMethod:WebService.getByName} to disambiguate. // // @param serviceNamespace (String) uri from the "targetNamespace" attribute of the // <wsdl:definitions> element in the WSDL file // @return (WebService) the requested WebService, or null if not loaded // // @group webService // @visibility xmlBinding // @example wsdlBinding //< get : function (serviceNamespace) { return this.services.find("serviceNamespace", serviceNamespace); }, //> @classMethod WebService.getByName() // Retrieve a WebService object by the name attribute declared on the <wsdl:service> tag. // // @param serviceName (String) name attribute from the <wsdl:service> tag // @param [serviceNamespace] (String) optional serviceNamespace if needed to disambiguate // @return (WebService) the requested WebService, or null if not loaded // // @group webService // @visibility xmlBinding //< getByName : function (serviceName, serviceNamespace) { if (serviceName == "") serviceName = null; if (serviceNamespace != null) { return this.services.find({name: serviceName, serviceNamespace: serviceNamespace}); } else { return this.services.find("name", serviceName); } } }); isc.WebService.getPrototype().toString = function () { return "[" + this.Class + " ns=" + this.echoLeaf(this.serviceNamespace) + (this.location ? " location=" + isc.Page.getLastSegment(this.location) : "") + "]"; }; //> @groupDef wsdlBinding // SmartClient supports automated integration with WSDL-described web services. This support // consists of: //

    //
  • creation of SOAP XML messages from JavaScript application data, with automatic // namespacing, and support for both "literal" and "encoded" SOAP messaging, and "document" and // "rpc" WSDL-SOAP bindings //
  • automatic decode of SOAP XML messages to JavaScript objects, with strong typing (eg an // XML schema "date" type becomes a JavaScript Date object) //
  • +link{XMLTools.loadXMLSchema,import of XML Schema} (contained in WSDL, or external), // including translating XML Schema "restrictions" to ISC +link{Validator,Validators} //
//

// WSDL services can be contacted by using +link{XMLTools.loadWSDL()} or the // +link{group:loadWSDLTag,<isc:loadWSDL> JSP tag} to load the service definition, then // invoking methods on the resulting +link{WebService} object. //

// +link{WebService.callOperation()} can be used to manually invoke operations for // custom processing (example using +explorerExample{wsdlOperation,public zipcode service}, // examples using .NET at // +externalLink{/examples/databinding/dotNET/temperatureConvert.jsp,/examples/databinding/dotNET/temperatureConvert.jsp}). //

// Fetch-only DataSource binding //

// To bind a component to a web service operation, call //

//   +link{WebService.getFetchDS(),WebService.getFetchDS(operationName,elementName)} //

// to obtain a DataSource which describes the structure of an XML element or XML Schema type // named elementName, which appears in the response message for the operation named // operationName. A component bound to this DataSource will show fields corresponding // to the structure of the chosen XML element or type, that is, one field per subelement or // attribute. +link{ListGrid.fetchData(),fetchData()} called on this DataSource (or on a // component bound to it) will invoke the specified web service operation, using the // +link{Criteria} passed to fetchData() to fill out the input message via // +link{dataSource.xmlSerialize()}, and using the specified XML element from the response // message as data. //

// Similarly, +link{WebService.getInputDS,WebService.getInputDS(operationName)} returns // a DataSource suitable for binding to a form that a user will fill out to provide inputs to // the specified web service operation. Typical use is to let the user fill in the form, then // pass the results of +link{dynamicForm.getValues(),form.getValues()} to // +link{listGrid.fetchData(),fetchData()} as criteria. //

// If the input message to the web service has extra nesting, consider using // the +link{operationBinding.useFlatFields,useFlatFields} property to simplify the inputs // required for fetchData(), and/or to simplify form databinding via // +link{dataBoundComponent.useFlatFields,component.useFlatFields}. //

// Note that the WSDL tab in the Developer Console can provide a clean, simplified view of any // WSDL file, making it easier to pick out the appropriate operationName and // elementName parameters to pass to getFetchDS() and other // +link{WebService} methods. //

// Take a look at the +explorerExample{wsdlBinding,Google SOAP Search example} and the // +externalLink{/examples/databinding/dotNET/customerSearch.jsp,.NET example} // (/examples/databinding/dotNET/customerSearch.jsp). // //

// Binding with Customized Presentation //

// Because XML Schema lacks key presentation metadata such as user-viewable titles, typically // you cannot directly use the DataSources derived from XML Schema embedded in a WSDL file to // drive visual component DataBinding in your final application. //

// You can create a DataSource that has custom fields and invokes a web // service operation by setting +link{dataSource.serviceNamespace} to match the targetNamespace // of the +link{WebService} (found on the <definitions> element from the // WSDL file), and setting +link{operationBinding.wsOperation,wsOperation} to the name of the // web service operation to invoke. fetchData() called on such a DataSource will // invoke the web service operation named by +link{operationBinding.wsOperation,wsOperation}, // just like a DataSource returned by +link{webService.getFetchDS()}. //

// In contrast to getFetchDS(), creating a DataSource in this way gives you the // opportunity to: //

    //
  • declare arbitrary fields, with SmartClient presentation attributes such as titles and // formatters //
  • extract any data from the response message, via // +link{operationBinding.recordXPath,operationBinding.recordXPath} and // +link{dataSourceField.valueXPath,field.valueXPath}, and transform it with // +link{dataSource.transformResponse,transformResponse()} //
  • transform the inbound data, if necessary, in order to add metadata such as // +link{dsRequest.startRow} for paging, or a sessionId for a service requiring authentication //
// These techniques are shown in the +explorerExample{wsdlBinding,Google SOAP Search example}. //

// XML Schema Reuse //

// Having loaded a WSDL file, all of the XML Schema definitions within the service definition // get translated to SmartClient +link{DataSource,DataSources} and // +link{SimpleType,SimpleTypes} via the rules described by +link{XMLTools.loadXMLSchema()}, // and are available to you via +link{webService.getSchema()} and +link{dataSourceField.type}. //

// You can use the +link{dataSource.inheritsFrom} property to create DataSources that extend // from XML schema definitions, then add presentation metadata not found in XML schema. //

// Even if you choose to declare all fields manually, you can leverage XML Schema // <simpleType> definitions by setting +link{DataSourceField.type,field.type} to the name // of an XML Schema simple type embedded in the WSDL file. //

// Round Trip Binding [fetch -> edit -> save] //

// For full read-write integration with a service that supports the basic // +link{group:dataSourceOperations,DataSource operations} on persistent data, // +link{OperationBinding,OperationBindings} can be declared for each DataSource operation, and // the +link{operationBinding.wsOperation,wsOperation} property can be used to to bind each // +link{group:dataSourceOperations,DataSource operation} (fetch, update, add, remove) to a // corresponding web service operation. //

// For example, this code accomplishes part of the binding to the // +externalLink{http://www.google.com/search?q=sforce+partner+wsdl,SalesForce partner web services} // (additional code is required to handle authentication and other details): //

// isc.DataSource.create({
//    serviceNamespace : "urn:partner.soap.sforce.com",
//    operationBindings : [
//        { operationType:"fetch", wsOperation:"query", recordName: "sObject" },
//        { operationType:"update", wsOperation:"update", recordName: "SaveResult" },
//        { operationType:"add", wsOperation:"create", recordName: "SaveResult" },
//        { operationType:"remove", wsOperation:"delete", recordName: "DeleteResult" }
//    ],
//    ...
// }); 
// 
// NOTE: additional code is required to handle authentication and other details, see the // complete code in smartclientSDK/examples/databinding/SalesForce. //

// In this usage, any DSRequest performed on this DataSource invokes the web service operation // named by the wsOperation property on the corresponding operationBinding, and // +link{dsRequest.data} is serialized via +link{dataSource.xmlSerialize()} to form the input // message to send to the web service. For example, if a +link{DynamicForm.saveData()} is // invoked and triggers a DSRequest with operationType:"add", the DataSource above will invoke // the "create" operation, and +link{DynamicForm.getValues(),form.values} will become // +link{dsRequest.data} and be serialized to form the input message of the "create" web // service operation. //

// Typical usage is: //

    //
  1. declare a DataSource that represents the fields of the object as you want them // represented in the UI. This DataSource is considered the "entity DataSource". It may // extend from an XML Schema complex type via +link{dataSource.inheritsFrom}. //
  2. use +link{operationBinding,operationBindings} to configure the entity DataSource to // call the appropriate web service operations for each DataSource operation, and extract // results via // +link{operationBinding.recordXPath,recordXPath}/+link{operationBinding.recordName,recordName} //
  3. bind components as follows: //
      //
    • bind +link{listGrid,grids} to the entity DataSource //
    • bind +link{SearchForm,SearchForms} to the input message of the fetch operation // (obtained via +link{WebService.getInputDS,webService.getInputDS("operationName")}. This is // done because search inputs are frequently unrelated to the structure of the objects being // searched for //
    • bind forms use for editing ("add" and "update" operations) to the entity DataSource //
    //
  4. use // +link{dataSource.transformRequest,transformRequest}/+link{dataSource.transformResponse,transformResponse}, // +link{operationBinding.useFlatFields} and +link{operationBinding.responseDataSchema} to // handle inconsistencies between the WSDL operations and the data you want in the presentation // layer. //
// A complete example of binding to the SalesForce "partner" web service, including // authentication via SOAP headers, saving data and cache sync, inline editing, validation // error handling and data paging, can be found in [webroot]/examples/databinding/SalesForce. //

// This requires a SalesForce account. SalesForce currently offers // +externalLink{http://www.google.com/search?hl=en&q=salesforce+developer+account,free developer accounts}. // Please note: this application deals with live data and if you using inline editing // it will save to SalesForce. //

// Deployment //

// For best performance, using the +link{group:loadWSDLTag,<isc:loadWSDL> JSP tag} // is recommended, as it automatically caches a translated form of the WSDL file. If you are // not using the SmartClient server, the WSDL tab in the Developer Console allows you // to save a .js file representing a WebService object, which can then be loaded and cached // like a normal JavaScript file. //

// Creating New WSDL Services //

// If you have no existing WSDL web service but would like to use web services for integration, // you can implement the "SmartClientOperations" web service described by the // ${isc.DocUtils.externalLink(isc.Page.getIsomorphicDir()+"system/schema/SmartClientOperations.wsdl","WSDL file")} // included in the SDK. This simple, 4 operation web service can support any number of // DataSources. In this case, you create your DataSources as client-side instances of // +link{WSDataSource} (general client-side DataSource creation is described under // +link{group:dataSourceDeclaration,Creating DataSources}). To change the URL where ISC // expects to find the SmartClientOperations web service, use +link{WebService.setLocation()} // like so:

//      var service = isc.WebService.get("urn:operations.smartclient.com");
//      service.setLocation("myURL");
// 
//

// To implement a web service starting from a WSDL file: //

    //
  • In the .NET framework, you will use the Web Services Description Language Tool // +externalLink{http://www.google.com/search?q=wsdl.exe,(wsdl.exe)} to generate C# stubs that // you will add business logic to //
  • In Java, +externalLink{http://ws.apache.org/axis/,Apache Axis} can be used to generate // Java stubs for implementing a web service //
  • In Perl, the +externalLink{http://soaplite.com,SOAP:Lite} module can be used to // implement web services without code generation //
  • for PHP, the NuSoap module can likewise be used to implement web services without code // generation //
// // @visibility xmlBinding // @treeLocation Client Reference/Data Binding // @title WSDL Binding //<




© 2015 - 2024 Weber Informatics LLC | Privacy Policy