Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Best way to call ServerObject

    Hi,

    Scenario is as follows. I have a ListGrid with a datasource defined, then I'm selecting a row in the grid and clicking button which passing selected record to the ServerObject call (lookupStyle="spring").

    The spring bean method doesn't fit int any of fetch/add/update/remove convention and it returns object of custom class type. The returned object is heavy and can't be serialized as a whole (I don't even want to try that despite the fact that isc serializer falls into recursion on it).

    Main things I would like to achieve is to keep SmartClient built-in auto-serialization for fields only specified in the datasource and auto-refresh of the grid record on response while keeping code clean as possible.

    There are at least three ways I know of with which I can try to call the server side spring bean method:
    1. DMI.call('appId', 'serverObjID', 'visibleMethod', record)
    2. datasource.performCustomOperation('customOperationId', record)
    3. datasource.updateData(record, callback, {operationId: 'updateOperationId'})

    I tested all three above approaches with enough of XML config (app/ds) to get them working and got into results:

    Ad.1. This solution is most desirable with usage of <isc:loadDMIStubs/> in terms of clean code, but the call fails as serializer tries to serialize whole returned object. Besides I don't expect auto-refresh would've worked even when serialized object received as there is no reference between datasource and the server call.
    Question: Is there any way of using DMI.call() to get only defined fields serialized and also to get a record in the grid auto-refreshed?

    Ad.2.
    This solution would be good enough, but again. Serializer tried to serialize the whole returned object despite the call goes through datasource.
    Question: Is there any way to make operationType="custom" binding serialize only fields defined in datasource from returned object? (I have most hope for this one)

    Ad.3.
    This solution works fine, but in terms of clean code it's the worst one. The 1st reason is because my operation isn't an typical 'update' so it may leads team mate/maintainer into confusion. 2nd reason, operation id has to be passed as string for every call in the code, which may easy lead to typo - which tiggers MethodMissingException on a server side, or even worse - for SQL datasource type, a direct SQL update will be triggered. Moreover it's not convenient for refactoring and maitenance.
    Question: Maybe there is a way to define some kind of an alias for updateData() with operationId so the call on a datasource may look like: datasource.serverMethodName(record) ?


    SmartClient version 8.3 PE

    #2
    We'd recommend #1 or #2, #3 is clearly a hack.

    See the JSTranslater docs for all the ways you can control how data is serialized. See also DataTools/DataSource.getProperties() for quick approaches for turning any bean into a Map which you can then immediately provide to dsResponse.setData().

    Comment


      #3
      I have the same use case and use the suggested approach of converting my complex bean to a Map that only has the properties specified in the xml datasource via

      Code:
      Map data = DataSourceManager.get(<DS_NAME>).getProperties(bean);
      I'm using DMI.call to invoke the method on my server object. This is what you might be already doing but figured it's worth mentioning : I have the non-CRUD method in the same DataSource DMI class that has the various CRUD methods but instead of calling datasource.performCustomOperation(..), I configure the same spring bean used in the DataSource in the app.xml file as <ServerObject> supports lookupStyle="spring".

      This allows logic related to the DataSource DMI to be logically contained instead of having a separate server object. I prefer invoking non-CRUD methods using DMI.call(..) instead of datasource.performCustomOperation(..)

      Comment


        #4
        Thanks for replies,

        I try to use the way with DataSourceManager...getProperties() plus DMI.call()
        But what about record auto-refresh, will it work this way? If not, how can I update the record the best/simplest way based on response data without another server call like fetch?

        @sjivan: Yup, this the exact same approach I like to achieve. Logical client-side separation, though using the same server-side bean for CRUD and non-CRUD operations.
        Last edited by topr; 11 Apr 2013, 02:41.

        Comment


          #5
          For auto-refresh, here's what I do on the client side in the DMI callback.

          Code:
          datasource.updateCaches(dsResponse, dsRequest)
          This will auto-refresh all widgets that are bound to this datasource. Also ensure that dsRequest has the appropriate operationType set on it (update / add) before calling updateCaches.
          Last edited by sjivan; 11 Apr 2013, 03:52. Reason: added not about operationType

          Comment


            #6
            Well, this seems like a good advice, not works for me though.

            Here is some debug data:

            RPCRequest:
            Code:
            {
                "actionURL":"http://localhost:8080/ko-web/isomorphic/IDACall", 
                "showPrompt":false, 
                "transport":"xmlHttpRequest", 
                "promptStyle":"cursor", 
                "data":{
                    "appID":"ko", 
                    "className":"projectFacade", 
                    "methodName":"deployOnTest", 
                    "arguments":[
                        {
                            "id":1, 
                            "name":"The Project!", 
                            "code":"ABC-123456", 
                            "_selection_2":true
                        }
                    ], 
                    "is_ISC_RPC_DMI":true
                }
            }
            Response (raw):
            Code:
            [
                {
                    status:0, 
                    data:{
                        code:"ABC-123456", 
                        deployEnvironment:"TEST", 
                        deployExecuted:new Date(1365674400000), 
                        deployResponse:"sukces", 
                        deployStatus:true, 
                        id:1, 
                        name:"The Project!"
                    }
                }
            ]
            Grid datasource definition:
            Code:
            <DataSource
                ID="project"
                serverType="sql"
                tableName="project"
            >
                <fields>
                    <field name="id"                title="Id"             type="integer" primaryKey="true" />
                    <field name="name"              title="Nazwa"          type="text" />
                    <field name="code"              title="Kod"            type="text" />
                    <field name="deployStatus"      title="Status"         type="integer" nativeName="last_deploy_success"/>
                    <field name="deployResponse"    title="Odpowiedź"      type="text"    nativeName="last_deploy_response" />
                    <field name="deployExecuted"    title="Data wdrożenia" type="date"    nativeName="last_deploy_executed" />
                    <field name="deployEnvironment" title="Aneks"          type="text"    nativeName="last_deploy_environment" />
                </fields>
            
                <serverObject lookupStyle="spring" bean="projectFacadeService"/>
            </DataSource>
            App server method binding:
            Code:
            <Application>
                <rpcBindings>
                    <ServerObject ID="projectFacade" lookupStyle="spring" bean="projectFacadeService">
                        <visibleMethods>
                            <method name="deployOnTest"/>
                        </visibleMethods>
                    </ServerObject>
                </rpcBindings>
            </Application>
            Spring bean code (Groovy):
            Code:
            class ProjectFacadeService extends SmartClientDataSource {
            
                Map deployOnTest(Map params) {
                    def project = Project.get(params.id)
                    project.deployOnTest()
            
                    return extractRelevantProperties(project)
                }
            }
            Base class for datasource facades (Groovy):
            Code:
            import com.isomorphic.datasource.DataSourceManager
            
            abstract class SmartClientDataSource {
            
                def add(Map params) { throw new MissingMethodException('add', this.class, [:]) }
                def update(Map params) { throw new MissingMethodException('update', this.class, [:]) }
                def remove(Map params) { throw new MissingMethodException('remove', this.class, [:]) }
            
                Map extractRelevantProperties(def bean, String dsName = dataSourceName) {
                    DataSourceManager.get(dsName).getProperties(bean, true, false)
                }
            
                protected getDataSourceName() {
                    (this.class.simpleName - 'FacadeService').toLowerCase()
                }
            }
            Client side code (CoffeeScript):
            Code:
            project = @projectList.getSelectedRecord()
            		datasource = DataSource.get('project')
            		DMI.call "ko", "projectFacade", "deployOnTest", project, (dsResponse, data, dsRequest) =>
            			datasource.updateCaches(dsResponse, dsRequest)
            			msg = if data.deployStatus then 'Wdrożenie powiodło się.' else 'Wdrożenie nie powiodło się.'
            			isc.say(msg, null, {title: 'Rezultat wdrożenia'})
            @ stands for this
            => stands for function() {} with reference to 'parent this' kept inside function's body (like using var that = this, etc...)

            Anyway, all above code works as expected, without refreshing grid record though.

            EDIT:

            I got it working by replacing at client side code line:
            Code:
            datasource.updateCaches(dsResponse, dsRequest)
            with:
            Code:
            @projectList.setData([data])
            But why datasource.updateCaches() didn't worked still bothers me.
            Besides I'll be glad to hear if my new approach is fine.
            Last edited by topr; 11 Apr 2013, 05:29. Reason: new solution

            Comment


              #7
              if you are hoping to have data appear in a grid which has no DataSource, or which has a DataSource but has never had fetchData() called, updateCaches() won't do that. Only a grid using a ResultSet starts listening for updates.

              Comment


                #8
                Originally posted by Isomorphic View Post
                if you are hoping to have data appear in a grid which has no DataSource, or which has a DataSource but has never had fetchData() called, updateCaches() won't do that. Only a grid using a ResultSet starts listening for updates.
                The grid does have a DataSource and fetchData() is called (autoFetchData: true). I see the rows at the grid. So I believe ResultSet is on its place, isn't it? :)

                I just don't uderstand why the difference when the same datasource calls for the same spring bean method, but different behaviour:
                - operationType="update" - does auto-refresh
                - operationType="custom" - does not auto-refresh

                Comment


                  #9
                  That's exactly what you'd expect, because a ResultSet managing a cache has no idea what should happen for operationType="custom" - remove the record with matching primary key? do nothing because it was just a fetch of data that isn't even a Record per se?

                  If you expect an update, use operationType "update". If that's not causing an update, see the FAQ on grids not updating for troubleshooting steps.

                  Comment


                    #10
                    Well, seems reasonable.
                    Thanks for support :)

                    Comment

                    Working...
                    X