History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: LPP-363
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: P1 P1
Assignee: André Bargull
Reporter: Don Hopkins
Votes: 1
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
OpenLaszlo

Dataset destruction problem and view datapath rebinding weirdness

Created: 18/May/05 04:34 PM   Updated: 17/Apr/08 05:49 AM
Component/s: LFC - Data
Affects Version/s: 3.0
Fix Version/s: Yodel

Time Tracking:
Not Specified

File Attachments: 1. XML File foo.xml (0.1 kb)
2. File testdataset.lzx (4 kb)


Severity: Major
Fixed in Change#: 6,496
Platform: All
Runtime: SWF7


 Description  « Hide
I?m having a weird problem destroying and recreating datasets and rebinding views to them (for clearing the subfolder datasets when you log out).

When I call dataset.destroy(), it doesn?t clean up all the references to it in canvas and _root and canvas.datasets.

I get the following message when I log back in and it recreates another dataset with the same name:

WARNING: a dataset already exists with the name 'mail.folders.:LzDataset : mail.folders. - use explicit datasources in your datapaths.

But even when I delete those references myself, and also destroy its parent LzHTTPDatasource, the views still don?t rebind to the new datasets, even when I reset their datapath?s to null, then back to the original name (of the new dataset by the same name).

Here is a simple example that shows how dataset.delete doesn?t zap all the references, and how there?s still some weird behaviors with views of those datasets even when I delete all the references myself.

When you run this example you get some buttons (run it with debug=true to get the debug window, so you can see the diagnostic messages):

Make Dataset (makes a dataset called foobar)

Destroy Dataset (destroys the dataset called foobar, but leaves the other references)

Murderize Dataset (destroys the dataset and its parent LzHTTPDatasource, and zaps all references I can find (_root, canvas, canvas.datasets).

Frob Dataset (change the dataset by setting the foo element?s arg attribute to a random number, so we can distinguish one dataset from another and tell if the view is updating from the same dataset.

View Dataset (make a view of the dataset?s foo element arg attribute)

Deview Dataset (destroy the view)

Show View Datapath (show the datapath and dataset that the view is viewing)

You can demonstrate that dataset.destroy doesn?t zap all references, by pressing ?Make Dataset?, ?Destroy Dataset? and then ?Make Dataset? again, and it will print:

WARNING: a dataset already exists with the name 'foobar':LzDataset :foobar - use explicit datasources in your datapaths.

You can demonstrate that murderize (which deletes all references I know about) prevents that message from being printed, by refreshing the page, pressing ?Make Dataset?, ?Murderize Dataset? and then ?Make Dataset? again, and it won?t print the warning message.

Now for the weird part: even when you murderize the old view, the view seems to be getting stuck on the first dataset you created, even if the view is created later. So there must be another reference to the first dataset you create hanging around somewhere, that the views datapath picks up.

You can demonstrate that the view tracks changes to the first dataset, by refreshing the page, pressing ?Make Dataset?, pressing ?Request Dataset? to request the xml, pressing ?View Dataset? to create a view of the foo element?s arg attribute (the initial value is ?123456?), pressing ?Show View Datapath? to print out the datapath and dataset it?s viewing, pressing ?Frob Dataset? to randomize the arg attribute, and the yellow view will change from ?123456? to show a new random number.

Note the number and the <<LzDataset#0 .foobar>> link in the debug window, which is the original dataset that the view is showing.

Now delete both the original view and the original dataset by pressing ?Deview Dataset?, then pressing ?Murderize Dataset?.

Now make a new dataset and a new view by pressing ?Make Dataset? (note it now prints out <<LzDataset#5|.foobar>> or some other number), pressing ?Request Dataset?, pressing ?View Dataset? then pressing ?Show View Datapath?.

Note that the view is displaying the previous random number, instead of the initial value ?123456?! (Since you deleted the dataset, made a new one and reloaded it, it should have been reinitialized, but we?re getting the old value instead!)

Also note that the link in the debugger window you get when you press ?Show View Datapath? is to the old <<LzDataset#0| .foobar>> that you murderized, instead of the new <<LzDataset#5|.foobar>> that we just created!

So somewhere is must still have a mapping of foobar to <<LzDataset#0|.foobar>> that the view?s datapath is using as a context, instead of the new one. The new dataset by the same name is not overriding the original binding. But it?s not coming from _root, canvas or canvas.datasets, because murderize cleaned them all out!

I have attached the two files testdataset.lzx and foo.xml that it loads, so please put them in the same directory like my-apps and run with testdataset.lzx?debug=true to see the debug output.

            -Don


 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Don Hopkins - 18/May/05 04:36 PM
test case, requires foo.xml

Don Hopkins - 18/May/05 04:36 PM
test data for testdataset.lzx

Henry Minsky - 06/Jun/05 10:34 AM
I am just psyching myself up to look at this...

Henry Minsky - 18/Aug/05 11:11 AM
I'm assigning this to PTW, just on the off chance it is fixed by some of his recent changes, or that it somehow rings a bell. I know nothing about how dataset binding works. Another likely asignee might be max...

André Bargull - 04/Sep/07 04:38 PM
This could be linked to "LPP-4214". Investigating...

André Bargull - 04/Sep/07 05:02 PM
Yes, it is definitely linked to "LPP-4214".

If you add this code to "destroyDataset()", the testcase will work as expected:
[code]
lz.datapointer.prototype.ppcache["foobar:/foo/@arg"] = null;
[/code]

Therefore I'll reassign this issue to Henry.

André Bargull - 15/Sep/07 01:04 PM
The LzParsedPath caching-bug has been resolved as of LPP-4214.

But be aware, that you need to manually re-set the datapath's xpath to re-enable databinding. Therefore, destroying the dataset "ds" and then re-creating a new dataset named "ds", won't automatically rebind any datapaths to the new dataset! Also see LPP-4688 for this issue.

Mamye Kratt - 19/Mar/08 02:30 PM
(trunk 4 build r8276)

Error with original testfile:
testdataset.lzx:26:26: The `event` property of methods is deprecated. Please update your source to use the `<handler>` tag.

Modified testfile:
<canvas
  width="100%" height="100%">

  <debug x="10" y="100" width="800" height="300"/>

  <simplelayout axis="y"/>

  <class
    name="myview"
    extends="text"
    bgcolor="$once{0xffff00}"
    datapath="foobar:/foo/@arg"/>

  <view>
    <simplelayout axis="x"/>
    <button onclick="canvas.makeDataset()">Make Dataset</button>
    <button onclick="canvas.destroyDataset()">Destroy Dataset</button>
    <button onclick="canvas.murderizeDataset()">Murderize Dataset</button>
    <button onclick="canvas.requestDataset()">Request Dataset</button>
    <button onclick="canvas.frobDataset()">Frob Dataset</button>
    <button onclick="canvas.viewDataset()">View Dataset</button>
    <button onclick="canvas.deviewDataset()">Deview Dataset</button>
    <button onclick="canvas.showViewDatapath()">Show View Datapath</button>
  </view>

  <handler name="oninit"><![CDATA[
      Debug.write("Press the buttons to make, destroy and manually murderize a dataset named 'foobar'.");
    ]]>
  </handler>

  <method name="makeDataset"><![CDATA[
      Debug.write("Making new dataset...");

      var args = {
name: 'foobar',
src: 'http:foo.xml',
type: 'http'
      };
      global.ds = new LzDataset(null, args);
      var dsParent = global.ds.parent;
      var dsParentName = dsParent.name;

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="destroyDataset"><![CDATA[
      Debug.write("Destroying dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      global.ds.destroy();
      global.ds = null;

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="murderizeDataset"><![CDATA[
      Debug.write("Murderize dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      var dsName = global.ds.name;
      var dsParent = global.ds.parent;

      global.ds.destroy();
      global.ds = null;

      Debug.write("Destroying ds parent", dsParent);
      dsParent.destroy();

      Debug.write("Deleting _root[dsName]", dsName, _root[dsName]);
      delete _root[dsName];

      Debug.write("Deleting canvas[dsName]", dsName, canvas[dsName]);
      delete canvas[dsName];

      Debug.write("Deleting canvas.datasets[dsName]", dsName, canvas.datasets[dsName]);
      delete canvas.datasets[dsName];

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="requestDataset"><![CDATA[
      Debug.write("Requesting dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      global.ds.doRequest();

      Debug.write("Done!", ds, canvas);
    ]]>
  </method>

  <method name="frobDataset"><![CDATA[
      Debug.write("Frobbing dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      ds.data.setAttr("arg", Math.random() + "");

      Debug.write("Done!", ds, canvas);
    ]]>
  </method>

  <method name="viewDataset"><![CDATA[
      Debug.write("Viewing dataset...");

      if (global['v'] != null) {
        Debug.write("I would, but I already am.");
        return;
      } // if

      global.v = new myview(canvas);

      Debug.write("Done!", v, canvas);
    ]]>
  </method>

  <method name="deviewDataset"><![CDATA[
      Debug.write("Deviewing dataset...");

      if (global['v'] == null) {
        Debug.write("I would, but I haven't viewed it yet.");
        return;
      } // if

      global.v.destroy();
      global.v = null;
    ]]>
  </method>

  <method name="showViewDatapath"><![CDATA[
      Debug.write("Show view datapath...");

      if (global['v'] == null) {
        Debug.write("I would, but I haven't viewed it yet.");
        return;
      } // if

      Debug.write("View datapath", global.v.datapath, "context", global.v.datapath.context);
    ]]>
  </method>

</canvas>

Results in swf:
Press Make Dataset
Press Murderize Dataset

Press the buttons to make, destroy and manually murderize a dataset named 'foobar'.
Making new dataset...
WARNING @testdataset-fixed.lzx#48: reference to undefined property 'name'
Done! «lz.dataset#1| <foobar/>» «lz.canvas#2| This is the canvas»
Murderize dataset... «lz.dataset#1| <foobar/>»
Destroying ds parent null
ERROR @testdataset-fixed.lzx#84: call to undefined method 'destroy'
Deleting _root[dsName] foobar undefined
Deleting canvas[dsName] foobar undefined
Deleting canvas.datasets[dsName] foobar undefined
Done! null «lz.canvas#2| This is the canvas»

Results in dhtml:
Press Make Dataset
Press Murderize Dataset

Press the buttons to make, destroy and manually murderize a dataset named 'foobar'.
Making new dataset...
ERROR @http://127.0.0.1:8080/lps-4.1.x/my-apps/testdataset-fixed.lzx?lzt=object&lzt=object&debug=true&lzr=dhtml&lzbacktrace=false#115: $2_dsParent has no properties
Destroying dataset... «lz.dataset#2| <foobar/>»
ERROR @http://127.0.0.1:8080/lps-4.1.x/lps/includes/lfc/LFCdhtml-debug.js#4102: this.parent has no properties

André Bargull - 06/Apr/08 03:08 PM
testcase was broken, updated testcase:

<canvas debug="true" >

  <simplelayout axis="y"/>

  <class
    name="myview"
    extends="text"
    bgcolor="$once{0xffff00}"
    datapath="foobar:/foo/@arg"/>

  <view>
    <simplelayout axis="x"/>
    <button onclick="canvas.makeDataset()">Make Dataset</button>
    <button onclick="canvas.destroyDataset()">Destroy Dataset</button>
    <button onclick="canvas.murderizeDataset()">Murderize Dataset</button>
    <button onclick="canvas.requestDataset()">Request Dataset</button>
    <button onclick="canvas.frobDataset()">Frob Dataset</button>
    <button onclick="canvas.viewDataset()">View Dataset</button>
    <button onclick="canvas.deviewDataset()">Deview Dataset</button>
    <button onclick="canvas.showViewDatapath()">Show View Datapath</button>
  </view>

  <handler name="oninit"><![CDATA[
      Debug.write("Press the buttons to make, destroy and manually murderize a dataset named 'foobar'.");
    ]]>
  </handler>

  <method name="makeDataset"><![CDATA[
      Debug.write("Making new dataset...");

      var args = {
name: 'foobar',
src: 'http:LPP-363.xml',
type: 'http'
      };
      global.ds = new LzDataset(canvas, args);
      var dsParent = global.ds.parent;
      var dsParentName = dsParent.name;

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="destroyDataset"><![CDATA[
      Debug.write("Destroying dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      global.ds.destroy();
      global.ds = null;

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="murderizeDataset"><![CDATA[
      Debug.write("Murderize dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      //better return here or you'll destroy the canvas
      return;

      var dsName = global.ds.name;
      var dsParent = global.ds.parent;

      global.ds.destroy();
      global.ds = null;

      Debug.write("Destroying ds parent", dsParent);
      dsParent.destroy();

      Debug.write("Deleting _root[dsName]", dsName, _root[dsName]);
      delete _root[dsName];

      Debug.write("Deleting canvas[dsName]", dsName, canvas[dsName]);
      delete canvas[dsName];

      Debug.write("Deleting canvas.datasets[dsName]", dsName, canvas.datasets[dsName]);
      delete canvas.datasets[dsName];

      Debug.write("Done!", global.ds, canvas);
    ]]>
  </method>

  <method name="requestDataset"><![CDATA[
      Debug.write("Requesting dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      global.ds.doRequest();

      Debug.write("Done!", ds, canvas);
    ]]>
  </method>

  <method name="frobDataset"><![CDATA[
      Debug.write("Frobbing dataset...", global.ds);

      if (global.ds == null) {
        Debug.write("I would, but it's null.");
        return;
      } // if

      ds.data.setAttr("arg", Math.random() + "");

      Debug.write("Done!", ds, canvas);
    ]]>
  </method>

  <method name="viewDataset"><![CDATA[
      Debug.write("Viewing dataset...");

      if (global['v'] != null) {
        Debug.write("I would, but I already am.");
        return;
      } // if

      global.v = new lz.myview(canvas);

      Debug.write("Done!", v, canvas);
    ]]>
  </method>

  <method name="deviewDataset"><![CDATA[
      Debug.write("Deviewing dataset...");

      if (global['v'] == null) {
        Debug.write("I would, but I haven't viewed it yet.");
        return;
      } // if

      global.v.destroy();
      global.v = null;
    ]]>
  </method>

  <method name="showViewDatapath"><![CDATA[
      Debug.write("Show view datapath...");

      if (global['v'] == null) {
        Debug.write("I would, but I haven't viewed it yet.");
        return;
      } // if

      Debug.write("View datapath", global.v.datapath, "context", global.v.datapath.context);
    ]]>
  </method>

</canvas>