1 /* 2 * Copyright (C) 2008-2009 WaveMaker Software, Inc. 3 * 4 * This file is part of the WaveMaker Client Runtime. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 dojo.provide("wm.base.components.ServiceCall"); 19 dojo.require("wm.base.components.Service"); 20 dojo.require("wm.base.components.ServiceQueue"); 21 22 //=========================================================================== 23 // Provides basic service calling infrastructure 24 //=========================================================================== 25 // Note: wm.ServiceCall is not a component. This primarily so that it can be combined 26 // with components that have other capabilities. 27 /** 28 Infrastructure for encapsulating a {@link wm.Service} configuration with a trigger 29 to invoke the configured service. 30 @name wm.ServiceCall 31 @class 32 @noindex 33 */ 34 dojo.declare("wm.ServiceCall", null, { 35 /** @lends wm.ServiceCall.prototype */ 36 /** 37 Set true to automatically <a href="#update">update</a> this service when 38 the service configuration or input is modified. 39 @type String 40 */ 41 autoUpdate: false, 42 /** 43 Set true to automatically <a href="#update">update</a> this service when it's created. 44 @type String 45 */ 46 startUpdate: false, 47 /** 48 Name of the service called by this object. 49 @type String 50 */ 51 service: "", 52 /** 53 Name of the operation to invoke on the service. 54 @type String 55 */ 56 operation: "", 57 _operationInfo: {}, 58 destroy: function() { 59 this.inherited(arguments); 60 wm.fire(this._requester, "cancel"); 61 }, 62 postInit: function() { 63 this.inherited(arguments); 64 this.connectStartUpdate(); 65 if (!this.$.queue) 66 new wm.ServiceQueue({name: "queue", owner: this}); 67 this.initInput(); 68 this.setService(this.service); 69 this._setOperation(this.operation); 70 }, 71 initInput: function() { 72 this.input = this.$.input; 73 if (!this.input) 74 this.input = this.createInput(); 75 this.subscribe(this.input.getRuntimeId() + "-changed", this, "inputChanged"); 76 }, 77 //======================================================= 78 // Service 79 //======================================================= 80 setService: function(inService) { 81 this.service = inService; 82 this._service = wm.services.getService(this.service) || new wm.Service({}); 83 }, 84 //======================================================= 85 // Operation 86 //======================================================= 87 _setOperation: function(inOperation) { 88 this.operation = inOperation; 89 this._operationInfo = this.getOperationInfo(this.operation); 90 this.operationChanged(); 91 }, 92 setOperation: function(inOperation) { 93 this._setOperation(inOperation); 94 this.doAutoUpdate(); 95 }, 96 getOperationInfo: function(inOperation) { 97 return (this._service && this._service.getOperation(inOperation)) || {}; 98 }, 99 operationChanged: function() { 100 this.input.operationChanged(this.operation, this._operationInfo.parameters); 101 }, 102 //======================================================= 103 // Input 104 //======================================================= 105 createInput: function() { 106 var i = new wm.ServiceInput({name: "input", owner: this }); 107 i.operationChanged(this.operation, this._operationInfo.parameters); 108 return i; 109 }, 110 inputChanged: function() { 111 this.doAutoUpdate(); 112 }, 113 //======================================================= 114 // Updating 115 //======================================================= 116 connectStartUpdate: function() { 117 if (this.owner && this.owner.start) 118 this.connect(this.owner, "start", this, "doStartUpdate"); 119 }, 120 setAutoUpdate: function(inAutoUpdate) { 121 this.autoUpdate = inAutoUpdate; 122 this.doAutoUpdate(); 123 }, 124 doStartUpdate: function() { 125 if (this.startUpdate && !this._loading) 126 this.update(); 127 }, 128 doAutoUpdate: function() { 129 if (this.autoUpdate && !this._loading) 130 this.update(); 131 }, 132 /** 133 Invoke the service. 134 Use the <a href="onResult">onResult</a>, 135 <a href="onSuccess">onSuccess</a>, 136 and/or <a href="onError">onError</a> events 137 to monitor the outcome of the service call. 138 */ 139 update: function() { 140 return this.isDesignLoaded() ? this.doDesigntimeUpdate() : this._update(); 141 }, 142 _update: function() { 143 if (this.canUpdate()) { 144 this.onBeforeUpdate(this.input); 145 return this.request(); 146 } 147 }, 148 /** 149 Return a boolean value used to determine if the service can be updated. 150 Use the <a href="onCanUpdate">onCanUpdate</a>, 151 event to control the output of canUpdate. 152 */ 153 canUpdate: function() { 154 var info = {canUpdate: this._getCanUpdate() }; 155 this.onCanUpdate(info); 156 return info.canUpdate; 157 }, 158 _getCanUpdate: function() { 159 return this._service && this.operation && !Boolean(this._requester); 160 }, 161 getArgs: function() { 162 return this.input.getArgs(); 163 }, 164 request: function(inArgs) { 165 inArgs = inArgs || this.getArgs(); 166 wm.logging && console.debug("request", this.getId(), "operation", this.operation, "args", inArgs); 167 var d = this._requester = this._service.invoke(this.operation, inArgs); 168 return this.processRequest(d); 169 }, 170 processRequest: function(inDeferred) { 171 var d = inDeferred; 172 if (d) { 173 d.canceller = function(inDeferred) { 174 inDeferred.fired = 1; 175 } 176 d.addBoth(dojo.hitch(this, function(r) { 177 this._requester = false; 178 return r; 179 })); 180 d.addCallbacks(dojo.hitch(this, "result"), dojo.hitch(this, "error")); 181 return d; 182 } 183 }, 184 //======================================================= 185 // Result Processing 186 //======================================================= 187 result: function(inResult) { 188 this.processResult(inResult); 189 return inResult; 190 }, 191 processResult: function(inResult) { 192 this.onResult(inResult); 193 this.onSuccess(inResult); 194 this.$.queue.update(); 195 }, 196 error: function(inError) { 197 this.processError(inError); 198 return inError; 199 }, 200 processError: function(inError) { 201 this.onResult(inError); 202 this.onError(inError); 203 }, 204 //======================================================= 205 // Events 206 //======================================================= 207 /** 208 onCanUpdate event fires before a service is invoked. 209 @param {Any} ioUpdate An object containing a canUpdate flag. 210 Setting this flag to false will prevent the service from updating. 211 @event 212 */ 213 onCanUpdate: function(ioUpdate) { 214 }, 215 /** 216 onBeforeUpdate event fires before a service is invoked. 217 @param {wm.ServiceInput} ioInput The input object used to determine what data 218 will be passed to the service. 219 @event 220 */ 221 onBeforeUpdate: function(ioInput) { 222 }, 223 /** 224 onResult event fires whenever a service returns, whether the 225 service returned successfully or reported an error. 226 @param {Any} inData Result data. The format of this data on the service. 227 @event 228 */ 229 // fires on success or error 230 onResult: function(inData) { 231 }, 232 /** 233 onSuccess event fires whenever a service returns successfully. 234 @param {Any} inData Result data. The format of this data on the service. 235 @event 236 */ 237 // fires only on success 238 onSuccess: function(inData) { 239 }, 240 /** 241 onError event fires whenever a service reports an error. 242 @param {Any} inData Result data. The format of this data on the service. 243 @event 244 */ 245 // fires only on error 246 onError: function(inError) { 247 } 248 }); 249 250 /**#@+ @design */ 251 wm.ServiceCall.extend({ 252 clearInput: "(clear input)", 253 updateNow: "(update now)", 254 queue: "(serviceCalls)", 255 /** @lends wm.ServiceCall.prototype */ 256 doDesigntimeUpdate: function() { 257 return studio.makeLiveDataCall(dojo.hitch(this, "_update")); 258 }, 259 doClearInput: function() { 260 this.input.destroy(); 261 this.input = this.createInput(); 262 }, 263 set_operation: function(inOperation) { 264 this.setOperation(inOperation); 265 if (this.isDesignLoaded() && studio.selected == this) 266 studio.inspector.inspect(studio.selected); 267 }, 268 getServicesList: function() { 269 return [""].concat(wm.services.getNamesList()||[]); 270 }, 271 showQueueDialog: function() { 272 var d = wm.ServiceQueue.dialog, q = this.$.queue; 273 if (d) { 274 d.page.binding = q; 275 d.page.update(); 276 }else{ 277 wm.ServiceQueue.dialog = d = new wm.PageDialog({ 278 name: "queueDialog", 279 owner: studio, 280 contentWidth: 600, 281 contentHeight: 400, 282 hideControls: true, 283 pageName: "QueueDialog", 284 pageProperties: {binding: q} 285 }); 286 } 287 d.show(); 288 }, 289 makePropEdit: function(inName, inValue, inDefault) { 290 switch (inName) { 291 case "service": 292 return makeSelectPropEdit(inName, inValue, this.getServicesList(), inDefault); 293 case "operation": 294 var 295 s = this._service, 296 valueOk = s && s.getOperation(inValue), 297 methods = s && s.getOperationsList(); 298 if (!valueOk){ 299 inValue = methods ? methods[0] : ""; 300 if (inValue) 301 this.set_operation(inValue); 302 } 303 if (methods) 304 return makeSelectPropEdit(inName, inValue, methods, inDefault); 305 break; 306 case "queue": 307 case "updateNow": 308 case "clearInput": 309 return makeReadonlyButtonEdit(inName, inValue, inDefault); 310 } 311 return this.inherited(arguments); 312 }, 313 editProp: function(inName, inValue, inInspector) { 314 switch (inName) { 315 case "updateNow": 316 return this.update(); 317 case "queue": 318 this.showQueueDialog(); 319 return; 320 case "clearInput": 321 return this.doClearInput(); 322 } 323 return this.inherited(arguments); 324 } 325 }); 326 327 //=========================================================================== 328 // Variable used as a service input 329 //=========================================================================== 330 /** 331 Variable used as a service input 332 @name wm.ServiceInput 333 @class 334 @noindex 335 @extends wm.Variable 336 */ 337 dojo.declare("wm.ServiceInput", wm.Variable, { 338 /** @lends wm.ServiceInput.prototype */ 339 _allowLazyLoad: false, 340 isDataProp: function(inProp) { 341 // Note: it's important we assume all properties are data properties unless _dataSchema is set 342 // Since the dataSchema is set externally, 343 // bindings may set data properties before data schema is set, creating errors. 344 return (inProp in this._dataSchema) || wm.isEmpty(this._dataSchema); 345 }, 346 operationChanged: function(inType, inSchema) { 347 this.setType(inType + "Inputs"); 348 this.setDataSchema(inSchema); 349 // input bindings may need to reinitialize after gleaning 350 // operation type information (in light of constants) 351 this.$.binding.refresh(); 352 353 }, 354 getArgs: function() { 355 var data= this.getData(), args=[], d; 356 // convert to array 357 for (var p in this._dataSchema) { 358 if (data !== undefined) 359 d = data[p]; 360 args.push(d !== undefined ? d : null); 361 } 362 return args; 363 } 364 }); 365 366 wm.Object.extendSchema(wm.ServiceInput, { 367 dataSet: { ignore: 1, defaultBindTarget: false, isObject: true, type: "any"} 368 }); 369 370 wm.ServiceInputVariable = wm.ServiceInput; 371