Hybrid View

    Success! Looks like we've fixed this one. According to our records the fix was applied for EXTGWT-2730 in 3.0.5.
  1. #1
    Sencha User
    Join Date
    Mar 2012
    Posts
    88
    Vote Rating
    3
    esag_dk is on a distinguished road

      1  

    Default TreeBaseAppearance Exception

    TreeBaseAppearance Exception


    Version(s) of Ext GWT
    Ext GWT 3.0.1

    Browser versions and OS (and desktop environment, if applicable)
    IE 9 Windows 7

    Virtual Machine
    No

    Description
    I can't provide a test case, but I can try to describe it. I'm filling a TreeGrid using a simple RPC call (so no proxy, loader etc - just simple getStore().add methods after onSuccess). If I change my view before the data was added, I get exactly the same exception, someone posted here already. I also debugged it, and found out, that node = null in TreeBaseAppearance.onJointChange, if I leave the view before adding data. So I overrode the TreeBaseAppearance/BlueTreeAppearance and added a null check:
    Code:
            if (node != null && node.getFirstChild() != null)
                e = (Element) node.getFirstChild().insertBefore(e, jointElement);
    This solved the problem. It would be great, if you could check this and modify the original source.

    Run mode
    Development Mode

  2. #2
    Sencha User
    Join Date
    Mar 2012
    Posts
    88
    Vote Rating
    3
    esag_dk is on a distinguished road

      0  

    Default


    Any plans to fix that issue in a future release?

  3. #3
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Thanks for the reminder - a slightly related issue was fixed in 3.0.3, so I'm trying to reproduce this now in 3.0.1 and .3 before applying the fix. There isn't necessarily a problem with adding null checks, but we tend to prefer not adding that small overhead if the fix can be made in another area instead. The linked example too is incomplete - genChildrens isn't provided, so unless simply invoking clear() and add() twice reproduces the issue, I'll need a bit more detail.

    Here's a simple entrypoint that I've tried - this keeps out RPC details while still being async. I'm hopefully just missing something silly - browser specific, Web/Dev mode, timing issue, or just not replacing the nodes correctly, but it appears to meet the description here and in the linked thread.

    Code:
    public class Test implements EntryPoint {
      @Override
      public void onModuleLoad() {
        final TreeStore<BaseDto> treeStore = newTreeStore();
        replaceWithInitialStructure(treeStore);
    
        ArrayList<ColumnConfig<BaseDto, ?>> columns = new ArrayList<ColumnConfig<BaseDto, ?>>();
        ColumnConfig<BaseDto, String> column = new ColumnConfig<BaseDto, String>(new ToStringValueProvider<BaseDto>(""), 200, "Name");
        columns.add(column);
    
        TreeGrid<BaseDto> tree = new TreeGrid<BaseDto>(treeStore, new ColumnModel<BaseDto>(columns), column);
        tree.setPixelSize(500, 500);
    
        RootPanel.get().add(tree);
        RootPanel.get().add(new TextButton("Replace All Nodes", new SelectHandler() {
          @Override
          public void onSelect(SelectEvent event) {
            replaceWithInitialStructure(treeStore);
    //        replaceWithDifferentStructure(treeStore);
          }
    
        }));
      }
      
      //Two different ways to replace data, more or less the same - this is likely
      //what needs to be modified to make the example break
    
      //This is used when the treegrid is built the first time - standard setup
      private void replaceWithInitialStructure(TreeStore<BaseDto> store) {
        store.clear();
        FolderDto root = getMusicRootFolder();
        for (BaseDto base : root.getChildren()) {
          store.add(base);
          if (base instanceof FolderDto) {
            processFolder(store, (FolderDto) base);
          }
        }
      }
      //This just adds a new parent to the whole thing with no other real difference
      private void replaceWithDifferentStructure(final TreeStore<BaseDto> store) {
        store.clear();
        
        FolderDto root = getMusicRootFolder();
        store.add(root);
        processFolder(store, root);
      }
    
      private TreeStore<BaseDto> newTreeStore() {
        return new TreeStore<BaseDto>(new ModelKeyProvider<BaseDto>() {
          @Override
          public String getKey(BaseDto item) {
            return (item instanceof FolderDto ? "f-" : "m-") + item.getId().toString();
          }
        });
      }
    
    
      private void processFolder(TreeStore<BaseDto> store, FolderDto folder) {
        for (BaseDto child : folder.getChildren()) {
          store.add(folder, child);
          if (child instanceof FolderDto) {
            processFolder(store, (FolderDto) child);
          }
        }
      }
    
      //Sample Data Classes
      public static class BaseDto implements Serializable, TreeStore.TreeNode<BaseDto> {
        private int id;
        private String name;
    
        public BaseDto(int id, String name) {
          this.id = id;
          this.name = name;
        }
    
        public Integer getId() {
          return id;
        }
    
        public String getName() {
          return name;
        }
    
        @Override
        public BaseDto getData() {
          return this;
        }
    
        @Override
        public List<? extends TreeNode<BaseDto>> getChildren() {
          return null;
        }
    
        @Override
        public String toString() {
          return name != null ? name : super.toString();
        }
      }
      public static class FolderDto extends BaseDto {
        private List<BaseDto> children;
    
        public FolderDto(int id, String name) {
          super(id, name);
        }
    
        public List<BaseDto> getChildren() {
          return children;
        }
    
        public void setChildren(List<BaseDto> children) {
          this.children = children;
        }
    
        public void addChild(BaseDto child) {
          getChildren().add(child);
        }
      }
      public static class MusicDto extends BaseDto {
        private String genre;
        private String author;
    
        public MusicDto(Integer id, String name, String genre, String author) {
          super(id, name);
          this.genre = genre;
          this.author = author;
        }
    
        public String getGenre() {
          return genre;
        }
    
        public String getAuthor() {
          return author;
        }
      }
    
    
      //Sample Data Generation
      private static int autoId = 0;
      private static FolderDto getMusicRootFolder() {
        FolderDto root = makeFolder("Root");
    
        FolderDto author = makeFolder("Beethoven");
        List<BaseDto> children = new ArrayList<BaseDto>();
        children.add(author);
        root.setChildren(children);
    
        FolderDto genre = makeFolder("Quartets");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Six String Quartets", author, genre));
        genre.addChild(makeMusic("Three String Quartets", author, genre));
        genre.addChild(makeMusic("Grosse Fugue for String Quartets", author, genre));
    
        genre = makeFolder("Sonatas");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Sonata in A Minor", author, genre));
        genre.addChild(makeMusic("Sonata in F Major", author, genre));
    
        genre = makeFolder("Concertos");
        author.addChild(genre);
    
        genre.addChild(makeMusic("No. 1 - C", author, genre));
        genre.addChild(makeMusic("No. 2 - B-Flat Major", author, genre));
        genre.addChild(makeMusic("No. 3 - C Minor", author, genre));
        genre.addChild(makeMusic("No. 4 - G Major", author, genre));
        genre.addChild(makeMusic("No. 5 - E-Flat Major", author, genre));
    
        genre = makeFolder("Symphonies");
        author.addChild(genre);
    
        genre.addChild(makeMusic("No. 1 - C Major", author, genre));
        genre.addChild(makeMusic("No. 2 - D Major", author, genre));
        genre.addChild(makeMusic("No. 3 - E-Flat Major", author, genre));
        genre.addChild(makeMusic("No. 4 - B-Flat Major", author, genre));
        genre.addChild(makeMusic("No. 5 - C Minor", author, genre));
        genre.addChild(makeMusic("No. 6 - F Major", author, genre));
        genre.addChild(makeMusic("No. 7 - A Major", author, genre));
        genre.addChild(makeMusic("No. 8 - F Major", author, genre));
        genre.addChild(makeMusic("No. 9 - D Minor", author, genre));
    
        author = makeFolder("Brahms");
        root.addChild(author);
    
        genre = makeFolder("Concertos");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Violin Concerto", author, genre));
        genre.addChild(makeMusic("Double Concerto - A Minor", author, genre));
        genre.addChild(makeMusic("Piano Concerto No. 1 - D Minor", author, genre));
        genre.addChild(makeMusic("Piano Concerto No. 2 - B-Flat Major", author, genre));
    
        genre = makeFolder("Quartets");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Piano Quartet No. 1 - G Minor", author, genre));
        genre.addChild(makeMusic("Piano Quartet No. 2 - A Major", author, genre));
        genre.addChild(makeMusic("Piano Quartet No. 3 - C Minor", author, genre));
        genre.addChild(makeMusic("String Quartet No. 3 - B-Flat Minor", author, genre));
    
        genre = makeFolder("Sonatas");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Two Sonatas for Clarinet - F Minor", author, genre));
        genre.addChild(makeMusic("Two Sonatas for Clarinet - E-Flat Major", author, genre));
    
        genre = makeFolder("Symphonies");
        author.addChild(genre);
    
        genre.addChild(makeMusic("No. 1 - C Minor", author, genre));
        genre.addChild(makeMusic("No. 2 - D Minor", author, genre));
        genre.addChild(makeMusic("No. 3 - F Major", author, genre));
        genre.addChild(makeMusic("No. 4 - E Minor", author, genre));
    
        author = makeFolder("Mozart");
        root.addChild(author);
    
        genre = makeFolder("Concertos");
        author.addChild(genre);
    
        genre.addChild(makeMusic("Piano Concerto No. 12", author, genre));
        genre.addChild(makeMusic("Piano Concerto No. 17", author, genre));
        genre.addChild(makeMusic("Clarinet Concerto", author, genre));
        genre.addChild(makeMusic("Violin Concerto No. 5", author, genre));
        genre.addChild(makeMusic("Violin Concerto No. 4", author, genre));
    
        return root;
      }
      private static FolderDto makeFolder(String name) {
        FolderDto theReturn = new FolderDto(++autoId, name);
        theReturn.setChildren((List<BaseDto>) new ArrayList<BaseDto>());
        return theReturn;
      }
    
      private static MusicDto makeMusic(String name, FolderDto author, FolderDto genre) {
        return makeMusic(name, author.getName(), genre.getName());
      }
    
      private static MusicDto makeMusic(String name, String author, String genre) {
        return new MusicDto(++autoId, name, genre, author);
      }
    }
    This example is completely self-contained to make it easy to set up and modify. I've only tested with 3.0.1 with this exact case, as that is what your initial report points to, though I have unit test that this is based on that I've confirmed against 3.0.1 and latest in SVN. There are two different ways to make the 'refresh' button replace the nodes, and neither seems to trigger the issue, but I've likely missed the specific case that causes this.

    Again, we're not opposed to adding the check you've provided, but we could add null checks everywhere to 'avoid' bugs. Typically we find that those bugs are actually rooted elsewhere, and if we fix the root, then we don't need extra null checks, and other potential bugs are resolved at the same time, so we'd rather find the root cause before applying the fix.

  4. #4
    Sencha User
    Join Date
    Mar 2012
    Posts
    88
    Vote Rating
    3
    esag_dk is on a distinguished road

      0  

    Default


    First of all: thank you for the effort

    Quote Originally Posted by Colin Alworth View Post
    Here's a simple entrypoint that I've tried - this keeps out RPC details while still being async.
    How is your example async? After you call one of your replace methods, you can't execute any code in a async way. Or am I missing something?
    It took me a while to reproduce the behavior of our code in your example (including RPC). It's not exactly the same exception, but it looks very similar.

    Test.java
    Code:
    package nope.client;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    import nope.shared.BaseDto;
    import nope.shared.FolderDto;
    import nope.shared.MusicDto;
    
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.sencha.gxt.core.client.ToStringValueProvider;
    import com.sencha.gxt.data.shared.ModelKeyProvider;
    import com.sencha.gxt.data.shared.TreeStore;
    import com.sencha.gxt.widget.core.client.ContentPanel;
    import com.sencha.gxt.widget.core.client.button.TextButton;
    import com.sencha.gxt.widget.core.client.event.SelectEvent;
    import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
    import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
    import com.sencha.gxt.widget.core.client.grid.ColumnModel;
    import com.sencha.gxt.widget.core.client.treegrid.TreeGrid;
    
    
    public class Test implements EntryPoint {
    
    
    	private final ContentPanel panel = new ContentPanel();
    	private TreeGrid<BaseDto> tree;
    
    
    	@Override
    	public void onModuleLoad() {
    		final TreeStore<BaseDto> treeStore = newTreeStore();
    		replaceWithInitialStructure(treeStore);
    
    
    		ArrayList<ColumnConfig<BaseDto, ?>> columns = new ArrayList<ColumnConfig<BaseDto, ?>>();
    		ColumnConfig<BaseDto, String> column = new ColumnConfig<BaseDto, String>(
    				new ToStringValueProvider<BaseDto>(""), 200, "Name");
    		columns.add(column);
    
    
    		tree = new TreeGrid<BaseDto>(treeStore, new ColumnModel<BaseDto>(
    				columns), column);
    		tree.setPixelSize(500, 500);
    		panel.add(tree);
    		RootPanel.get().add(panel);
    		RootPanel.get().add(
    				new TextButton("Replace All Nodes", new SelectHandler() {
    					@Override
    					public void onSelect(final SelectEvent event) {
    						// replaceWithInitialStructure(treeStore);
    						// replaceWithDifferentStructure(treeStore);
    						loadRPC(treeStore);
    					}
    
    
    				}));
    	}
    
    
    	// Two different ways to replace data, more or less the same - this is
    	// likely
    	// what needs to be modified to make the example break
    
    
    	protected void loadRPC(final TreeStore<BaseDto> store) {
    		TestServiceAsync service = (TestServiceAsync) GWT
    				.create(TestService.class);
    
    
    		service.getTreeStore(new AsyncCallback<List<BaseDto>>() {
    
    
    			@Override
    			public void onSuccess(final List<BaseDto> result) {
    
    
    				store.clear();
    
    
    				// true - add child to a parent;
    				// false = add all items to the root
    				boolean useLevel = true; // true to see the bug
    
    
    				BaseDto previous = null;
    				for (BaseDto base : result) {
    					if (previous == null)
    						store.add(base);
    					else
    						store.add(previous, base);
    					if (useLevel)
    						previous = base;
    				}
    			}
    
    
    			@Override
    			public void onFailure(final Throwable caught) {
    			}
    		});
    
    
    		panel.remove(tree); // this has to be called WHILE the async RPC is
    							// beeing executed
    	}
    
    
    	// This is used when the treegrid is built the first time - standard setup
    	private void replaceWithInitialStructure(final TreeStore<BaseDto> store) {
    		store.clear();
    		FolderDto root = getMusicRootFolder();
    		for (BaseDto base : root.getChildren()) {
    			store.add(base);
    			if (base instanceof FolderDto) {
    				processFolder(store, (FolderDto) base);
    			}
    		}
    	}
    
    
    	// This just adds a new parent to the whole thing with no other real
    	// difference
    	private void replaceWithDifferentStructure(final TreeStore<BaseDto> store) {
    		store.clear();
    
    
    		FolderDto root = getMusicRootFolder();
    		store.add(root);
    		processFolder(store, root);
    	}
    
    
    	private TreeStore<BaseDto> newTreeStore() {
    		return new TreeStore<BaseDto>(new ModelKeyProvider<BaseDto>() {
    			@Override
    			public String getKey(final BaseDto item) {
    				return (item instanceof FolderDto ? "f-" : "m-")
    						+ item.getId().toString();
    			}
    		});
    	}
    
    
    	private void processFolder(final TreeStore<BaseDto> store,
    			final FolderDto folder) {
    		for (BaseDto child : folder.getChildren()) {
    			store.add(folder, child);
    			if (child instanceof FolderDto) {
    				processFolder(store, (FolderDto) child);
    			}
    		}
    	}
    
    
    	// Sample Data Generation
    	private static int autoId = 0;
    
    
    	private static FolderDto getMusicRootFolder() {
    		FolderDto root = makeFolder("Root");
    
    
    		FolderDto author = makeFolder("Beethoven");
    		List<BaseDto> children = new ArrayList<BaseDto>();
    		children.add(author);
    		root.setChildren(children);
    
    
    		FolderDto genre = makeFolder("Quartets");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Six String Quartets", author, genre));
    		genre.addChild(makeMusic("Three String Quartets", author, genre));
    		genre.addChild(makeMusic("Grosse Fugue for String Quartets", author,
    				genre));
    
    
    		genre = makeFolder("Sonatas");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Sonata in A Minor", author, genre));
    		genre.addChild(makeMusic("Sonata in F Major", author, genre));
    
    
    		genre = makeFolder("Concertos");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("No. 1 - C", author, genre));
    		genre.addChild(makeMusic("No. 2 - B-Flat Major", author, genre));
    		genre.addChild(makeMusic("No. 3 - C Minor", author, genre));
    		genre.addChild(makeMusic("No. 4 - G Major", author, genre));
    		genre.addChild(makeMusic("No. 5 - E-Flat Major", author, genre));
    
    
    		genre = makeFolder("Symphonies");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("No. 1 - C Major", author, genre));
    		genre.addChild(makeMusic("No. 2 - D Major", author, genre));
    		genre.addChild(makeMusic("No. 3 - E-Flat Major", author, genre));
    		genre.addChild(makeMusic("No. 4 - B-Flat Major", author, genre));
    		genre.addChild(makeMusic("No. 5 - C Minor", author, genre));
    		genre.addChild(makeMusic("No. 6 - F Major", author, genre));
    		genre.addChild(makeMusic("No. 7 - A Major", author, genre));
    		genre.addChild(makeMusic("No. 8 - F Major", author, genre));
    		genre.addChild(makeMusic("No. 9 - D Minor", author, genre));
    
    
    		author = makeFolder("Brahms");
    		root.addChild(author);
    
    
    		genre = makeFolder("Concertos");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Violin Concerto", author, genre));
    		genre.addChild(makeMusic("Double Concerto - A Minor", author, genre));
    		genre.addChild(makeMusic("Piano Concerto No. 1 - D Minor", author,
    				genre));
    		genre.addChild(makeMusic("Piano Concerto No. 2 - B-Flat Major", author,
    				genre));
    
    
    		genre = makeFolder("Quartets");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Piano Quartet No. 1 - G Minor", author, genre));
    		genre.addChild(makeMusic("Piano Quartet No. 2 - A Major", author, genre));
    		genre.addChild(makeMusic("Piano Quartet No. 3 - C Minor", author, genre));
    		genre.addChild(makeMusic("String Quartet No. 3 - B-Flat Minor", author,
    				genre));
    
    
    		genre = makeFolder("Sonatas");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Two Sonatas for Clarinet - F Minor", author,
    				genre));
    		genre.addChild(makeMusic("Two Sonatas for Clarinet - E-Flat Major",
    				author, genre));
    
    
    		genre = makeFolder("Symphonies");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("No. 1 - C Minor", author, genre));
    		genre.addChild(makeMusic("No. 2 - D Minor", author, genre));
    		genre.addChild(makeMusic("No. 3 - F Major", author, genre));
    		genre.addChild(makeMusic("No. 4 - E Minor", author, genre));
    
    
    		author = makeFolder("Mozart");
    		root.addChild(author);
    
    
    		genre = makeFolder("Concertos");
    		author.addChild(genre);
    
    
    		genre.addChild(makeMusic("Piano Concerto No. 12", author, genre));
    		genre.addChild(makeMusic("Piano Concerto No. 17", author, genre));
    		genre.addChild(makeMusic("Clarinet Concerto", author, genre));
    		genre.addChild(makeMusic("Violin Concerto No. 5", author, genre));
    		genre.addChild(makeMusic("Violin Concerto No. 4", author, genre));
    
    
    		return root;
    	}
    
    
    	private static FolderDto makeFolder(final String name) {
    		FolderDto theReturn = new FolderDto(++autoId, name);
    		theReturn.setChildren(new ArrayList<BaseDto>());
    		return theReturn;
    	}
    
    
    	private static MusicDto makeMusic(final String name,
    			final FolderDto author, final FolderDto genre) {
    		return makeMusic(name, author.getName(), genre.getName());
    	}
    
    
    	private static MusicDto makeMusic(final String name, final String author,
    			final String genre) {
    		return new MusicDto(++autoId, name, genre, author);
    	}
    }
    TestServiceImpl.java
    Code:
    package nope.server;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    import nope.client.TestService;
    import nope.shared.BaseDto;
    import nope.shared.FolderDto;
    
    
    import com.google.gwt.user.server.rpc.RemoteServiceServlet;
    
    
    public class TestServiceImpl extends RemoteServiceServlet implements
    		TestService {
    	public List<BaseDto> getTreeStore() {
    
    
    		List<BaseDto> models = new ArrayList<BaseDto>();
    		models.add(new FolderDto(1000, "1"));
    		models.add(new FolderDto(2000, "2"));
    		models.add(new FolderDto(3000, "3"));
    		models.add(new FolderDto(4000, "4"));
    		models.add(new FolderDto(5000, "5"));
    
    
    		try {
    			Thread.sleep(4000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    
    		return models;
    	}
    
    
    }
    TestServiceAsync.java
    Code:
    package nope.client;
    
    import java.util.List;
    
    
    import nope.shared.BaseDto;
    
    
    import com.google.gwt.user.client.rpc.AsyncCallback;
    
    
    public interface TestServiceAsync {
    	void getTreeStore(AsyncCallback<List<BaseDto>> callback);
    }
    TestService.java
    Code:
    package nope.client;
    
    
    import java.util.List;
    
    
    import nope.shared.BaseDto;
    
    
    import com.google.gwt.user.client.rpc.RemoteService;
    import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
    
    
    @RemoteServiceRelativePath("app/activity")
    public interface TestService extends RemoteService {
    	List<BaseDto> getTreeStore();
    }
    web.xml
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
             version="2.5"
             xmlns="http://java.sun.com/xml/ns/javaee">
    
    
      <!-- Servlets -->
      
      <!-- Default page to serve -->
      <welcome-file-list>
        <welcome-file>GXT_3_0.html</welcome-file>
      </welcome-file-list>
    
    
    
    
        <servlet>
            <servlet-name>activityServiceImpl</servlet-name>
            <servlet-class>nope.server.TestServiceImpl</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>activityServiceImpl</servlet-name>
            <url-pattern>/gxt_3_0/app/activity</url-pattern>
        </servlet-mapping>
    
    
    </web-app>
    GXT_3_0.gwt.xml
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <module rename-to='gxt_3_0'>
      <inherits name='com.google.gwt.user.User'/>
      <inherits name='com.sencha.gxt.ui.GXT'/>
      <entry-point class='nope.client.Test'/>
      
      <source path='client'/>
      <source path='shared'/>
    
    
    </module>
    BaseDto.java (had to add the default constructor for serialization)
    Code:
    package nope.shared;
    
    
    import java.io.Serializable;
    import java.util.List;
    
    
    import com.sencha.gxt.data.shared.TreeStore;
    import com.sencha.gxt.data.shared.TreeStore.TreeNode;
    
    
    // Sample Data Classes
    public class BaseDto implements Serializable, TreeStore.TreeNode<BaseDto> {
    	private int id;
    	private String name;
    
    
    	public BaseDto() {
    
    
    	}
    
    
    	public BaseDto(final int id, final String name) {
    		this.id = id;
    		this.name = name;
    	}
    
    
    	public Integer getId() {
    		return id;
    	}
    
    
    	public String getName() {
    		return name;
    	}
    
    
    	@Override
    	public BaseDto getData() {
    		return this;
    	}
    
    
    	@Override
    	public List<? extends TreeNode<BaseDto>> getChildren() {
    		return null;
    	}
    
    
    	@Override
    	public String toString() {
    		return name != null ? name : super.toString();
    	}
    }
    As far as I can see, this is causing the problem:
    1. Use async way to load data
    2. While fetching this data, remove the treegrid from the panel
    3. When the async method returns, add an element using add(parent, child). Using add(child) instead is working just fine.

    And that's the exception:
    com.google.gwt.event.shared.UmbrellaException: One or more exceptions caught, see full set in UmbrellaException#getCauses
    at com.google.gwt.event.shared.HandlerManager.fireEvent(HandlerManager.java:129)
    at com.sencha.gxt.data.shared.Store.fireEvent(Store.java:609)
    at com.sencha.gxt.data.shared.TreeStore.insert(TreeStore.java:996)
    at com.sencha.gxt.data.shared.TreeStore.add(TreeStore.java:360)
    at nope.client.Test$2.onSuccess(Test.java:81)
    at nope.client.Test$2.onSuccess(Test.java:1)
    at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:232)
    at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
    at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
    at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:337)
    at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:218)
    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
    at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
    at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
    at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:213)
    at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
    at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:292)
    at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:546)
    at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:363)
    at java.lang.Thread.run(Unknown Source)

    Caused by: com.google.gwt.core.client.JavaScriptException: (TypeError): this.insertBefore is not a function
    at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:248)
    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
    at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
    at com.google.gwt.dom.client.Node$.insertBefore$(Node.java)
    at com.sencha.gxt.theme.base.client.tree.TreeBaseAppearance.onJointChange(TreeBaseAppearance.java:201)
    at com.sencha.gxt.widget.core.client.treegrid.TreeGridView.onJointChange(TreeGridView.java:170)
    at com.sencha.gxt.widget.core.client.treegrid.TreeGrid.refresh(TreeGrid.java:610)
    at com.sencha.gxt.widget.core.client.treegrid.TreeGrid.onAdd(TreeGrid.java:902)
    at com.sencha.gxt.widget.core.client.treegrid.TreeGrid$1.onAdd(TreeGrid.java:246)
    at com.sencha.gxt.data.shared.event.StoreAddEvent.dispatch(StoreAddEvent.java:125)
    at com.sencha.gxt.data.shared.event.StoreAddEvent.dispatch(StoreAddEvent.java:1)
    at com.google.gwt.event.shared.GwtEvent.dispatch(GwtEvent.java:1)
    at com.google.web.bindery.event.shared.EventBus.dispatchEvent(EventBus.java:40)
    at com.google.web.bindery.event.shared.SimpleEventBus.doFire(SimpleEventBus.java:193)
    at com.google.web.bindery.event.shared.SimpleEventBus.fireEvent(SimpleEventBus.java:88)
    at com.google.gwt.event.shared.HandlerManager.fireEvent(HandlerManager.java:127)
    at com.sencha.gxt.data.shared.Store.fireEvent(Store.java:609)
    at com.sencha.gxt.data.shared.TreeStore.insert(TreeStore.java:996)
    at com.sencha.gxt.data.shared.TreeStore.add(TreeStore.java:360)
    at nope.client.Test$2.onSuccess(Test.java:81)
    at nope.client.Test$2.onSuccess(Test.java:1)
    at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:232)
    at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
    at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
    at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:337)
    at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:218)
    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
    at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
    at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
    at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:213)
    at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
    at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:292)
    at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:546)
    at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:363)
    at java.lang.Thread.run(Unknown Source)
    Again, we're not opposed to adding the check you've provided, but we could add null checks everywhere to 'avoid' bugs. Typically we find that those bugs are actually rooted elsewhere, and if we fix the root, then we don't need extra null checks, and other potential bugs are resolved at the same time, so we'd rather find the root cause before applying the fix.
    That's exactly the way I think about null checks. In my case it was just a workaround and I hope you can now find the real cause of this nasty little bug.

  5. #5
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Thanks for the in-depth elaboration on the issue - this really helps to try to narrow down the problem.

    The button is the 'async' part - in terms of timing, it makes no difference if we

    click a button ==> call the server ==> wait four seconds ==> send data back ==> modify store,

    or if we just

    wait four seconds ==> click button ==> modify store

    Both have the same effect as far as the store, and in turn the grid is concerned - if there is something unique about loading data from RPC instead of some other source, we are in really big trouble.

    That said, I can reproduce your full test case, but so far have been unable to distill it down to something we can unit test.

  6. #6
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Code:
    panel.remove(tree)
    was the link I was missing from my example - putting that at the top of the button's select handler causes the error to occur. This slight modification to my example is enough to make this happen:

    Code:
        RootPanel.get().add(
            new TextButton("Replace All Nodes", new SelectHandler() {
              @Override
              public void onSelect(final SelectEvent event) {
                panel.remove(tree);
                replaceWithInitialStructure(treeStore);
              }
            }));
    It appears that the TreeGrid makes the assumption that after it is attached, if any change is made, it will still be attached. It can be invisible, but removing it from the DOM prevents some code from finding the DOM elements it needs to build the new data. There is a fairly easy change that can be made to fix this in the tree's internals, but it will result in slower element-finding code.

    Why does your use case remove the tree before modifying it? If it is for performance sake, consider hiding it instead - you should get nearly the same benefit. If I'm correct, looking up newly built elements will actually be slower when the tree isn't attached at all than when it is merely hidden, but I will investigate further.

    I've filed this internally, and we'll update this with any progress or other workarounds.

Thread Participants: 1