Announcement

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

    addGlobalID collision

    Background:

    I'm using a pair of ListGrids to serve as a master/details inspector for a large amount of data. The structure of both grids is completely dynamic. The data for both comes from some legacy GWT-RPC code.

    The master grid does not use any DataSource. I simply populate it via a call to "setData(ListGridRecord[])". [This grid, by the way, is using the new multi-level spanning headers -- they're working out great, thanks].

    The details grid is backed by a custom DataSource, again with a completely dynamic structure. I want to use the DS in this case because I want to make use of AdvancedCriteria for filtering, and grouping and automatic-summary behaviors on the grid.

    The master grid has an addCellClickHandler. In that handler, I determine what "key" is being inspected, and then generate an AdvancedCriteria with the information and call 'filterData(AdvancedCriteria,DSCallback) on the details ListGrid.

    Problems:

    1. After my data model has generated (GWT-RPC) and when I set the DataSource on the details grid, I get the following stack trace:

    Code:
    ERROR: 15:08:19.009:click8:WARN:Log:ClassFactory.addGlobalID: ID:'dsByNode' for object '[DataSource ID:dsByNode]' collides with ID of existing object '[DataSource ID:dsByNode]'. The global reference to this object will be replaced. com.smartgwt.client.core.JsObject$SGWT_WARN: 15:08:19.009:click8:WARN:Log:ClassFactory.addGlobalID: ID:'dsByNode' for object '[DataSource ID:dsByNode]' collides with ID of existing object '[DataSource ID:dsByNode]'. The global reference to this object will be replaced
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    	at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
    	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.smartgwt.client.data.DataSource.create(DataSource.java)
    	at com.smartgwt.client.core.BaseClass.getOrCreateJsObj(BaseClass.java:112)
    	at com.smartgwt.client.widgets.grid.ListGrid.setDataSource(ListGrid.java:15889)
    	at com.myco.myapp.client.panels.TestPanel.configureGridByNode(TestPanel.java:522)
    2. When I try to call 'filterData' on the details grid, I get this:

    Code:
    === 2012-06-18 14:16:24,488 [l0-2] INFO  IDACall - Performing 1 operation(s)
    === 2012-06-18 14:16:24,497 [l0-2] WARN  RequestContext - dsRequest.execute() failed: 
    java.lang.Exception: Can't find dataSource: isc_MyCustomDS_0 - please make sure that you have a isc_MyCustomDS_0.ds.xml file for it in [webRoot]/shared/ds
    	at com.isomorphic.application.AppBase.executeDefaultDSOperation(AppBase.java:672)
    	at com.isomorphic.application.AppBase.executeAppOperation(AppBase.java:658)
    	at com.isomorphic.application.AppBase.execute(AppBase.java:491)
    	at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:1973)
    	at com.isomorphic.servlet.IDACall.handleDSRequest(IDACall.java:199)
    	at com.isomorphic.servlet.IDACall.processRPCTransaction(IDACall.java:156)
    	at com.isomorphic.servlet.IDACall.processRequest(IDACall.java:121)
    	at com.isomorphic.servlet.IDACall.doPost(IDACall.java:73)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    	at com.isomorphic.servlet.BaseServlet.service(BaseServlet.java:152)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    	at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    	at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
    	at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
    	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
    	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    	at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
    	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    	at org.mortbay.jetty.Server.handle(Server.java:324)
    	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
    	at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
    	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
    	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
    	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
    	at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
    	at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
    Grid settings are:
    Code:
      grid.setHeaderHeight(40);
      grid.setCanEdit(false);
      grid.setShowAllRecords(true);
      grid.setKeepInParentRect(true);
      grid.setCanRemoveRecords(false);
      grid.setLeaveScrollbarGap(false);
      grid.setUseAllDataSourceFields(true);
      grid.setAutoFetchData(false);
      grid.setShowGridSummary(true);
      grid.setShowGroupSummary(true);
      grid.setShowGroupSummaryInHeader(true);
      grid.setGroupStartOpen(GroupStartOpen.ALL);
      grid.setGroupByField("groupID");
    DataSource settings are:
    Code:
    public class MyCustomDS extends DataSource{...}
    
    public MyCustomDS()
    {
        setID("dataByNode");
        setClientOnly(true);
       setAddGlobalId(true);    // or 'false'... makes no difference
    }


    So far I've:
    1. Searched all around the docs for 'addGlobalID', "collides with ID of existing object" and found little to go on.
    2. Ensured that I've called 'setID()' in the constructor of my DataSource.
    2. Experimented with 'setAddGlobalID' in my DataSource's constructor
    3. Experimented with 'window.isc_useSimpleNames = false' in my html file
    4. Rebuilt my entire project, cleared its caches, restarted my browser, etc..

    The fields of the details ListGrid will render based on the DataSourceFields that have been dynamically generated within my DataSource. However, no rows are shown ("No items to show.")

    I don't have any .ds.xml file for this DataSource because its content is completely dynamic and generated on the fly. I also don't understand why IDACall is being called because I've called 'setClientOnly(true)' in the constructor of the DataSource.

    Any clues here?

    (using SmartGWT Pro 3.1d June 12th build with Firefox 11)
    Last edited by dzarras; 18 Jun 2012, 11:09.

    #2
    Geez... found the problem with this, but I would still appreciate any additional explanations or insights:

    It turns out that I was calling a different constructor in my DataSource class, where the call to 'setAddGlobalId' was NOT being made. However, note that calling that method with 'true' produced errors, but calling it with 'false' removed all errors.

    Comment


      #3
      The errors you originally described here sounds like you have 2 dataSources being defined in your application with the same (global) ID, meaning that when the second one is created the first one is destroyed.
      Then when you attempt to interact with the dataSource it was failing to pick up the settings you'd applied (such as making it clientOnly), leading to the errors where it attempted to contact the IDACall servlet etc. This was probably due to you having applied settings to the datasource that got clobbered by the definition for a second dataSource with the same ID.

      Setting addGlobalID to false would allow two dataSources to exist with the same ID so, if you create two DataSources with the same ID the first one wouldn't get clobbered by the second and potentially resolve this issue.

      That's all we can give you at a high level from this description. Exactly how you're defining the 2 dataSources with the colliding IDs would depend on your application code - we can't really comment further on whether there's a bug we're unaware of or (more likely) a usage issue with your code without seeing some example code that demonstrates what's going on.

      Regards
      Isomorphic Software

      Comment


        #4
        I have only this one custom DataSource in this part of the application, and it now has its own ID, which is certainly unique from any other object in the app. That said, each time a new GWT-RPC call is made for new data from the server, I create a new instance of this DataSource and create a new ListGrid instance, both of which I configure with the new data results. The field structure can be very different each time.

        Questions:
        1. Is there some other approach where I could maintain the same DataSource and/or ListGrid instance, and simply re-define their respective fields as needed?
        2. In the current approach, is each instance being registered somewhere in a way that I should be cleaning up prior to creating the new instances?
        3. DataSource.setAddGlobalId is described as "an advanced setting." Is this something I shouldn't have to mess around with? Is it 'true' by default?

        Comment


          #5
          I have only this one custom DataSource in this part of the application, and it now has its own ID, which is certainly unique from any other object in the app. That said, each time a new GWT-RPC call is made for new data from the server, I create a new instance of this DataSource and create a new ListGrid instance, both of which I configure with the new data results. The field structure can be very different each time.
          From this description it sounds like each time you create a new DataSource instance you're using the same ID, which, if addGlobalID is true, would cause the previously defined DataSource with the same ID to be destroyed. In most usage scenarios, each DataSource instance should have a unique ID (as it is a separate object).

          To answer your specific questions:
          1. Is there some other approach where I could maintain the same DataSource and/or ListGrid instance, and simply re-define their respective fields as needed?
          You don't typically want to try to change DataSource fields at runtime - the DataSource is a description of the records it contains.
          You can of course specify component-level fields and DataSource level fields, meaning that the listGrid can display any arbitrary subset of of fields from the dataSource, which can be readily changed at runtime via the setFields API.

          However it sounds like you're talking about completely unrelated data objects, in which case separate DataSource instances for each data type is appropriate.

          Obviously we can't comment on your application in depth without knowing specifics, but presumably you aren't really creating a new DataSource with every operation: For example if you have a list grid, every filter, sort, edit etc of the data within that grid would be acting upon the same dataSource. If you then show another grid with different fields and data, that typically would be bound to a separate dataSource.

          2. In the current approach, is each instance being registered somewhere in a way that I should be cleaning up prior to creating the new instances?
          If a dataSource is no longer being used you can "destroy()" it. this will clean up any JavaScript references to it. This is the same API you'd use to clean up components (see ListGrid.destroy(), etc). If you also have code that directly references it in Java you should also clear those references to allow the object to be released from browser memory and garbage collected by the clientside OS.

          3. DataSource.setAddGlobalId is described as "an advanced setting." Is this something I shouldn't have to mess around with? Is it 'true' by default?
          It is true by default and you usually would not need to change it. Typically a developer would either not specify an explicit ID (a unique ID would then be automatically assigned when the DS is created), or would specify a unique ID for each DataSource instance within the application.
          There are very few use cases where you'd want to set the same ID on 2 DataSources that exist at the same time.

          Comment


            #6
            I guess I wasn't clear...

            My DataSource represents a result set whose fields can be different with each query result. Imagine this structure: Salespeople as columns, and sales per day as rows. Each row can have a "total for day" attribute as a column, and there's a row that totals each salesperson's activity. The application user chooses a time period and a simple query runs server-side to get the results.

            There are too many salespeople to give each a column in all circumstances (i.e., for when they don't have activity over a given time period). So I dynamically determine which salespeople had activity, sort them alphabetically from left to right, and then show the activity for each day... it can be a sparse matrix if not every salesperson who had SOME activity over the entire period had activity on EVERY day. The resulting ListGrid is as compact as the data allows for. The key-value mechanism of the Record actually works quite well for this data-modeling circumstance... I just manufacture field names on the fly and use them as the attribute keys for my ListGridRecord subclasses.

            Clearly, I can't have a different DataSource descriptor (ds.xml) file for every possible ListGrid configuration.

            My data is coming back to me via a GWT-RPC 'onSuccess' callback, so I'm already client-side when I get my data (POJOs). At that point, I'm constructing a custom subclass of com.smartgwt.client.data.DataSource, creating DataSourceFields and so on, and then ultimately ListGridFields. It's all working very well (with the exception of Filtering, which I'll get to in another post). This DataSource subclass serves as a 'data model' in the traditional sense (MVC) for this part of my app.

            With that as the backdrop, and since I imagine other people have similar ("untypical"?) needs, can you please re-address my first question?

            Thanks for the background on the other two questions.
            Last edited by dzarras; 20 Jun 2012, 17:51.

            Comment


              #7
              There is really no way to change the fields on the DataSource itself once it has been created. Your options would seem to be as follows:

              1) use the approach you are already using - when you have the new dataSet and structure, call "destroy()" on the old DataSource, create a new one with the appropriate fields, and bind it to the ListGrid (no need to destroy and re-create the listGrid). You'll presumably have the DataSource be marked as client-only.

              2) This will not give you persistance automatically - not an issue if your data is read-only of course, but if you're looking for dynamic server side dataSources, you may want to make use of the addDynamicDSGenerator() on the server, which allows dataSources to be generated on the back end without needing separate .ds.xml files etc for each dataSource.

              3) Alternatively - if you have a fixed superset of fields you could create a single dataSource with this full set of field, and simply modify the ListGrid to show only the fields you care about in response to a particular fetch.

              Also - it is worth noting that we do not usually recommend using GWT-RPC for communication with the server. See the FAQs for an explanation of why we do not consider this the best choice.

              Regards
              Isomorphic Software

              Comment


                #8
                Hi,

                I have similar question.
                I have only one datasource in application but when I pass that datasource to ComboBox.setOptionDataSource() I am getting this kind of message with global ID clashing:
                Code:
                17:59:53.594 [ERROR] [tool] 17:59:53.374:MUP8:WARN:Log:ClassFactory.addGlobalID: ID:'product' for object '[DataSource ID:product]' collides with ID of existing object '[DataSource ID:product]'. The global reference to this object will be replaced
                Do you maybe know why?
                Thank you.
                BR
                Marije

                Comment


                  #9
                  Thanks Isomorphic...

                  I'll probably stick with approach #1 for now, as this is indeed read-only data being created by processes external to my web app. Thanks for the instruction on "destroy()"... harkens somewhat back to my C/C++ days, but that's ok :-)

                  I had thought about approach #3 some time ago, but there are "too many salespeople" (as per my example), and it just wouldn't work -- likewise for the DynamicDataSource facility, at least with this data.

                  Points taken on the caveats of GWT-RPC; I've spent a lot of time with that link you mentioned. For what it's worth, I've had a lot of success thus far with being able to factor out re-usable "services" implemented with GWT-RPC, where these services are available to multiple SmartGWT apps, and even with GWT-RPC services that inherit from other GWT-RPC services. We also use a lot of home-grown custom persistence stuff that predates feature-richer Hibernate versions (but which still smokes Hibernate performance-wise with large and complex object graphs). That said, I'm all for getting out of that business, given the right performance. Rest assured, I'm forging ahead with SmartGWT-based DB I/O where I can.

                  Huge point taken on the "avoiding DTO's" issue -- although the loss of compile-time type- and access-checking (no runtime bugs/lost-productivity because of attribute name typos, for example -- but yes, they can be factored into one place in the code...) is a steep price to pay. Looking into further use of beanClassName for that...

                  Comment


                    #10
                    Just a quick note - the compile-time checking benefit you describe is mostly for code that you will be able to simply delete by switching away from GWT-RPC.

                    It's not just the scaffolding and DTOs that go away, but also things like validation and security logic, which tend to become declarative when using .ds.xml files.

                    Likewise any code involve in putting together custom SQL queries - replaced by SQL templating.

                    For the little code that remains, beanClassName can make sense if it's non-trivial, and cannot be captured in a generic way that applies to all DataSources (which is best where possible).

                    Comment

                    Working...
                    X