4 Replies Latest reply on Jun 16, 2010 12:37 PM by Mike Peterson

    offline data synchronization

    Amit Gadkari

       

      We are trying to use the offline data synchronization feature of DMS using data modeling.

       

      Below is the only example we found on adobe site and its working.

       

      http://help.adobe.com/en_US/LiveCycleDataServicesES/3.1/Developing/WS4ba8596dc6a25eff-56f1 c8f4126dcd963c6-8000.html

       

      Also we have read “occasionally connected client”  and Model driver applications documentation in lcds31 user guide.

       

      Is there any other example to demonstrate how to use the offline data sync?. We don’t want to generate the  Java code and use assembler class for this .

       

      In our example we are implementing the SearchCustomerList Funcationality. Based of search criteria a list of customers is displayed to the user.

      Beloew are the configuration settings

       

                              var cs:ChannelSet = new ChannelSet();

                              var customChannel:Channel = new RTMPChannel("my-rtmp",

                                          "rtmp://wpuag85393:2038");

                              cs.addChannel(customChannel);

                              this.serviceControl.channelSet=cs;

                              this.serviceControl.autoCommit = false;

                              this.serviceControl.autoConnect = false;

                              this.serviceControl.autoSaveCache = true;

                              this.serviceControl.offlineAdapter = new

                                          com.deere.itarch.adapter.MaintainCustomerBeanOfflineAdapter();

                              this.serviceControl.fallBackToLocalFill=true;

                              this.serviceControl.encryptLocalCache = true;

                              this.serviceControl.cacheID = "localCache";

      CASE I:

       

      Below is our understanding of offline data sync. for our implementation.

      ·          LCDS server is started and application is connected to the server.

      ·          User enters search criteria and clicks the Search Button.

      ·          Data is fetched and displayed on the screen.

      As autoSaveCache is set to true it should automatically save result in local cache

       

       

      ·          Shut down the LCDS server.

      ·          Close the earlier Search Window.

      ·          Run the application and open the customer search page.

      ·          Enter the same search criteria and click search.

      ·          Result : Nothing is displayed on screen. ( No data fetched from local cache)

       

      Many times we are getting error cannot connect to server ( when server is running 50% of times)

      We also tried setting reconnect strategy to instance. ( but this is also not working)

      Also can you please provide end-to-end sample for data synchronization.

       

       

       

        • 1. Re: offline data synchronization
          Mike Peterson

          I created the example that's in the documentation. I think your assumptions of the behavior are correct, but I'm going to investigate it further with the developers and quality engineers on this feature.

           

          I just tried setting autoSaveCache=true in the doc sample. In that case, the data was saved to the local cache as soon as it was loaded. I verified this my shutting down the server and restarting the client. After a short time delay, I got the result set in the client's datagrid. I did this with a new cachID to make sure everything started out clean in the local cache.

          • 2. Re: offline data synchronization
            tomj Adobe Employee

            Can you show us the changes to the example in the documentation that cause the application not to work for you?  Its hard to get a sense of the problem without a reproducable case.

             

            As Mike said, the expected behavior is that when the server is down, the offline adapter is executed and the fill should return the same results.  You can debug in to the offline adapter class to see if the WHERE clause is getting constructed correctly.  The function to set a breakpoint in would be in MaintainCustomerBeanOfflineAdapter::getQueryCriteria() and getQueryOrder().

             

            You can also try setting autoSave to false and explicitly calling saveCache() to see if this make a difference (it should not).

            • 3. Re: offline data synchronization
              Amit Gadkari Level 1

              Thanks for the quick reply. We revisited our problem again today and made some changes in configurations and it started working partially.

               

              Below are the older and newer configurations.

               

              Older Configurations:

                                      this.serviceControl.autoCommit = false;

               

                                      this.serviceControl.autoConnect = false;

               

                                      this.serviceControl.autoSaveCache = true;

               

               

              Yesterday while testing the functionality, we cleared the cache and run the application. But after clearing the cache, application stopped working(it did not return the data even if the LCDS server was up )

               

              New Configurations:

                                      this.serviceControl.autoCommit = false;

               

                                      this.serviceControl.autoConnect = true;

               

                                      this.serviceControl.autoSaveCache = true;

               

               

              Here we've just changed the autoConnect to "true" and application started working fine. Also we were able to get the data from cache when LCDS server is down. But this works only for getAll method where we don’t enter any search criteria.

               

              The clear cache function is not working in above case. Below is the Scenario

              1) LCDS Server is up.

              2) Run the application, click search button and we are able to see the records

              3) As autoSaveCache is true, data is automatically saved into cache.

              4) We click a button which will call the clearCache function to clear the cache.

              5) LCDS server is stopped.

              6) Re-run the application, click search button and records are displayed from cache.

              Problem statetment: Ideally no records should be displayed as we have cleared the cache.

               

              Offline data synchronization is not working for queries with search criteria. Below is the scenario

              1) LCDS Server is up.

              2) Run the appliacation, enter the search criteria and click search button, we are able to see the records

              3) As autoSaveCache is true, data is automatically saved into cache.

              4) LCDS server is stopped.

              5) Re-run the application, enter search criteria, click search button and records are not displayed.

              Problem Statement: Sql getting generated is just checking for equal to criteria

              For ex :

               

                   Name = amit.

               

              The sql is not generated with the "like" clause.

               

              We modified the generated code to change the "equal to" to "like", then in this case we need to enter the search criteria as value%

              Ex: amit%

               

              Please let us know how to generate sql with like clause.

              Also we have one to many relationship between order and line items , please let us know how to write the query for such relationship in offline adapter?

              • 4. Re: offline data synchronization
                Mike Peterson Level 1

                Good to see you got a little further along with your application. I'm not sure why setting autoconnect to true helped.

                 

                Regarding your search, I'm not sure how you implemented that but the easiest way to do it with model-driven development is by using a criteria filter. It will result in a new query in your offline adapter. You just add a filter element to an entity in your model and in that filter you specify your like expression. I added one to the doc sample app as an example. When you generate code for the offline adapter, you'll be able to see the proper structure for the like clause too. I'm including my fml and offline adapter source below.I've also included the MXML so you can see how I called the new filter method from the client. After I saved to the local cache, and I went offline, I successfully performed the search in the client app. There were no issues with it.

                 

                Here's my fml. The new filter is in bold text. I should have chose a better filter name, since it will generate a method called byProductName, which is very close to the existing getByProductName. But you'll get the idea. Once you add the filter, just remember to redeploy your model and regenerate your code.

                 

                Regarding your question about associations, I'll look into that, but I think you would generate offline adapters for each entity involved in the association and your relationships should behave correctly offline.

                 

                 

                <model xmlns="http://ns.adobe.com/Fiber/1.0">
                    <annotation name="DMS">
                        <item name="datasource">java:/comp/env/jdbc/ordersDB</item>
                        <item name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</item>
                    </annotation>
                    <entity name="Product" persistent="true">
                        <annotation name="ServerProperties" ServerType="LCDS"/>
                        <annotation name="DMS" Table="PRODUCT"/>
                        <annotation name="VisualModeler" width="114" height="88" x="66" y="79"/>
                        <annotation name="ActionScriptGeneration" GenerateOfflineAdapter="true" OfflineAdapterPackage="com.adobe.offline"/>
                        <id name="productid" type="integer">
                            <annotation name="DMS" ColumnName="PRODUCTID"/>
                        </id>
                        <property name="description" type="string" length="255">
                            <annotation name="DMS" ColumnName="DESCRIPTION"/>
                        </property>
                        <property name="price" type="float">
                            <annotation name="DMS" ColumnName="PRICE"/>
                        </property>
                        <property name="productname" type="string" length="255">
                            <annotation name="DMS" ColumnName="PRODUCTNAME"/>
                        </property>
                        <filter name="byProductName" criteria="productname like"/>
                    </entity>
                </model>

                 

                Here's the new query for byProductName in my offline adapter, which contains a valid like clause. That section of the adapter is in bold text.

                /**
                * This is an auto-generated offline adapter for the Product entity.
                */

                 

                package com.adobe.offline
                {
                import mx.core.mx_internal;
                import mx.data.SQLiteOfflineAdapter;
                import mx.utils.StringUtil;

                 

                use namespace mx_internal;

                 

                public class ProductOfflineAdapter extends SQLiteOfflineAdapter
                {
                    /**
                     * Return an appropriate SQL WHERE clause for a given set of fill parameters.
                     *  
                     * @param originalArgs fill parameters
                     * @return String representing the WHERE clause for a SQLite SQL
                     */
                    override protected function getQueryCriteria(originalArgs:Array):String
                    {
                        var args:Array = originalArgs.concat();
                        var filterName:String = args.shift();
                        var names:Array = new Array();
                        switch (filterName)
                        {
                ...    
                            case "byProductName":
                                // JPQL: select Product_alias from Product Product_alias where Product_alias.productname like :productname
                                // Preview: productname like :productname                
                                names.push(getTargetColumnName(["productname"]));
                                return StringUtil.substitute("{0} like :productname", names);
                                break;
                        }

                        return super.getQueryCriteria(originalArgs);
                    }

                 

                 

                 

                 

                }

                 

                 

                Here's my modified MXML. I'm still calling getAll(), but after that I use the new filter to filter the results/datagrid display to just the subset that matches the string I input in the search field. This results in a new call to productService.byProductName(), which is the client-side method generated from the filter element in my model.

                 

                <?xml version="1.0" encoding="utf-8"?>
                <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                                       xmlns:s="library://ns.adobe.com/flex/spark"
                                       xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:OfflineAIRAPP="TestOfflineApp.*"
                                       preinitialize="app_preinitializeHandler(event)"
                                       creationComplete="windowedapplication1_creationCompleteHandler(event)">
                    <fx:Script>
                        <![CDATA[
                            import com.adobe.offline.ProductOfflineAdapter;
                           
                            import mx.controls.Alert;
                            import mx.events.FlexEvent;
                            import mx.messaging.Channel;
                            import mx.messaging.ChannelSet;
                            import mx.messaging.channels.RTMPChannel;
                            import mx.messaging.events.ChannelEvent;
                            import mx.rpc.AsyncToken;
                            import mx.rpc.events.FaultEvent;
                            import mx.rpc.events.ResultEvent;
                           
                            public var myOfflineAdapter:ProductOfflineAdapter;
                            public function channelConnectHandler(event:ChannelEvent):void
                            {
                                productService.serviceControl.autoConnect=false;
                           
                               
                            }
                            protected function 
                                app_preinitializeHandler(event:FlexEvent):void
                            {
                                var cs:ChannelSet = new ChannelSet();
                                var customChannel:Channel = new RTMPChannel("my-rtmp",
                                    "rtmp://localhost:2037");
                                cs.addChannel(customChannel);
                                productService.serviceControl.channelSet=cs;
                                customChannel.addEventListener(ChannelEvent.CONNECT,
                                    channelConnectHandler);
                            }
                           
                            protected function dataGrid_creationCompleteHandler(event:FlexEvent):void
                            {
                                getAllResult.token = productService.getAll();
                            }
                           
                            protected function
                                windowedapplication1_creationCompleteHandler(event:FlexEvent):void
                            {
                                productService.serviceControl.autoCommit = false;
                                productService.serviceControl.autoConnect = true;
                                productService.serviceControl.autoSaveCache = true;                
                                productService.serviceControl.fallBackToLocalFill=true;
                                productService.serviceControl.encryptLocalCache = true;
                                productService.serviceControl.cacheID = "myOfflineCache4";

                 

                            }
                           
                            protected function connectBtn_clickHandler(event:MouseEvent):void
                            {
                                productService.serviceControl.connect();
                            }
                           
                            protected function disconnectBtn_clickHandler(event:MouseEvent):void
                            {
                                productService.serviceControl.disconnect();
                            }
                           
                            protected function commitBtn_clickHandler(event:MouseEvent):void
                            {
                                productService.serviceControl.commit();
                            }
                           
                            protected function saveCacheBtn_clickHandler(event:MouseEvent):void
                            {
                                productService.serviceControl.saveCache();
                            }
                            protected function clearCacheBtn_clickHandler(event:MouseEvent):void
                            {
                                productService.serviceControl.clearCache();
                            }            
                           
                           
                            protected function button_clickHandler(event:MouseEvent):void
                            {
                                getAllResult.token = productService.byProductName("%"+key.text+"%");
                            }
                           
                        ]]>
                    </fx:Script>
                    <fx:Declarations>
                        <mx:TraceTarget />       
                        <s:CallResponder id="getAllResult" />
                        <OfflineAIRAPP:ProductService id="productService"
                                                      fault="Alert.show(event.fault.faultString + '\n' +
                                                      event.fault.faultDetail)"/>
                        <s:CallResponder id="byProductNameResult"/>
                        </fx:Declarations>
                    <mx:DataGrid editable="true" x="141" y="10" id="dataGrid"
                                 creationComplete="dataGrid_creationCompleteHandler(event)"
                                 dataProvider="{getAllResult.lastResult}">
                        <mx:columns>
                            <mx:DataGridColumn headerText="productid" dataField="productid"/>
                            <mx:DataGridColumn headerText="description" dataField="description"/>
                            <mx:DataGridColumn headerText="price" dataField="price"/>
                            <mx:DataGridColumn headerText="productname" dataField="productname"/>
                        </mx:columns>
                    </mx:DataGrid>
                    <s:Button x="10" y="246" label="Connect" click="connectBtn_clickHandler(event)"
                              id="connectBtn" width="84" height="30"/>
                    <s:Button x="112" y="204" label="Save to Local Cache" id="saveCacheBtn"
                              click="saveCacheBtn_clickHandler(event)" height="30"/>
                    <s:Button x="110" y="246" label="Commit to Server" id="commitBtn"
                              click="commitBtn_clickHandler(event)" width="135" height="30"/>
                    <s:Button x="10" y="204" label="Disconnect" id="DisconnectBtn"
                              click="disconnectBtn_clickHandler(event)" height="30"/>
                   
                    <s:Label x="270" y="204" text="{'Commit Required: ' +
                             productService.serviceControl.commitRequired}"/>
                    <s:Label x="270" y="246" text="{'Connected: ' +
                             productService.serviceControl.connected}"/>
                    <s:TextInput x="10" y="19" id="key"/>
                    <s:Button x="10" y="49" label="Search" id="button" click="button_clickHandler(event)"/>   
                   
                </s:WindowedApplication>