6 Replies Latest reply on Jul 21, 2013 1:59 PM by marius.gerwinn

    force downloading of @LoadAsync annotated classes

    marius.gerwinn

      Hello together

       

      Is there a simple approach to force downloading all (missing) split points annotated with @LoadAsync at once?


      Split Points are great to reduce the initial startup time. But for offline-support it would be useful to load all missing code fragments at once, after the app has started.

       

      Our current pattern is to create an instance of each annotated object. Obviously this isn't so elegant regarding performance and memory.

       

      Thanks for your help,

       

      marius

        • 1. Re: force downloading of @LoadAsync annotated classes
          jfuerth

          Hi Marius,

           

          One thing you could do is compile your app twice: once with async enabled and once with it disabled. Put each in a different host page. Then you could direct clients who may be interested in offline support to use the non-async build.

           

          If your main concern is to get a fast startup time when a user first visits the app, another strategy would be not to use async bean management at all, and simply hide the load time of the app behind a login screen. I described this technique in an earlier message, but I can't find it now. So here it goes again:

           

          1. Make a monolithic app (no split points; disable errai async bean management)
          2. Create a login page (for example /contextRoot/login.html) which is not a GWT application
          3. Include the <script> tag for your GWT app at the bottom of the login page (so the app loads with the login page already displayed)
          4. In your GWT app's entry point, check if the current window.location ends with "login.html" and if so, return immediately
          5. Make the submit button on the login form actually submit the form in the traditional way (which will lead to a page refresh)
          6. Upon successful login, redirect the user to the "real" host page for the GWT app. All the appropriate *.cache.js files will already be in the browser's local cache!

           

          Let me know if this technique is of interest to you.

           

          -Jonathan

          1 of 1 people found this helpful
          • 2. Re: force downloading of @LoadAsync annotated classes
            marius.gerwinn

            Thanks for your answer Jonathan.

             

            Your first solution is quite interesting in the case someone want's to provide an real offline app, like a chrome webapp.

            I didn't had in mind, that you can easily disable async loading in errai.Anyhow it's not an good option for us right now.

             

            The second described approach is quite familar to me. We planned to do so a long time ago. But since you have to manage this single entry page(s) by yourself (localization, validation, error handling, etc.) we didn't do so yet. An other problem in our case is, that we have at least 3 entry points for the user so we have to manage 3 single pages. Anyhow we will implement this technique at least for signup & login page!

             

            But I'm curious if there isn't an even better solution in the case your app is growing quite big like in our case. We like the way Google+ is handling it. They seem to have implemented async loading for their top navigation points like Games, Communities, etc. So we adapted this approach in our app using @LoadAsync.

            When I asked the question I had in mind, that there might be an easy solution to simply iterate over the @LoadAsync annotated classes and force to load them if needed. Actually it also would be totally fine to specify the classes which should be forced to load. We just wan't to avoid creating an instance of each class which should be (pre)loaded. Something like this would be nice:

             

            @Inject
             protected AsyncBeanManager bm;
            
            void onPreloadEvent(@Observes preloadEvent){
                 bm.lookupBean(MyClass.class).doLoad(new AsyncCallback()));
            }
            
            

             

            An other thing with @LoadAsync is the missing grouping/package functionality which would be very help in the described case were you wan't to preload a whole area of a page like "communities".

            But I know that is pretty tricky and don't expect that this could be done so easily. Anyhow here's an easy code snipped what it could look like:

             

            @LoadAsync(group="splitPoint1")
            class MyClassPresenter{
            ...
            }
            
            @LoadAsync(group="splitPoint1")
            class MyClassViewImpl{
            ...
            }
            
            • 3. Re: force downloading of @LoadAsync annotated classes
              marius.gerwinn

              Any chance to get a comment on my last described approach regarding code splitting (see the two code snippets above)? Does it sound reasonable to you jboss guys? Or do i miss something important here?

              • 4. Re: force downloading of @LoadAsync annotated classes
                jfuerth

                Your first solution is quite interesting in the case someone want's to provide an real offline app, like a chrome webapp.

                I didn't had in mind, that you can easily disable async loading in errai.Anyhow it's not an good option for us right now.

                 

                Okay, that's too bad because it's probably the easiest solution

                 

                One other thing you may consider: why not use an HTML5 application manifest? This way, the async loading can still happen, but the resources will all come from the browser's local cache.

                 

                 

                The second described approach is quite familar to me. We planned to do so a long time ago. But since you have to manage this single entry page(s) by yourself (localization, validation, error handling, etc.) we didn't do so yet. An other problem in our case is, that we have at least 3 entry points for the user so we have to manage 3 single pages. Anyhow we will implement this technique at least for signup & login page!

                 

                It should only be necessary for the entry point that new users see (a page that prompts "log in here or sign up" would be ideal). Once someone has visited the app, if you have your webserver set up properly for the "*.cache.js" files, the browser should cache the app forever. Entry points for users who have logged in before do not need to use this "background loading" trick since the app will be precached.

                 

                 

                But I'm curious if there isn't an even better solution in the case your app is growing quite big like in our case. We like the way Google+ is handling it. They seem to have implemented async loading for their top navigation points like Games, Communities, etc. So we adapted this approach in our app using @LoadAsync.

                When I asked the question I had in mind, that there might be an easy solution to simply iterate over the @LoadAsync annotated classes and force to load them if needed. Actually it also would be totally fine to specify the classes which should be forced to load. We just wan't to avoid creating an instance of each class which should be (pre)loaded. Something like this would be nice:

                 

                @Inject
                 protected AsyncBeanManager bm;
                 
                void onPreloadEvent(@Observes preloadEvent){
                     bm.lookupBean(MyClass.class).doLoad(new AsyncCallback()));
                }
                 
                

                 

                An other thing with @LoadAsync is the missing grouping/package functionality which would be very help in the described case were you wan't to preload a whole area of a page like "communities".

                But I know that is pretty tricky and don't expect that this could be done so easily. Anyhow here's an easy code snipped what it could look like:

                 

                @LoadAsync(group="splitPoint1")
                class MyClassPresenter{
                ...
                }
                 
                @LoadAsync(group="splitPoint1")
                class MyClassViewImpl{
                ...
                }
                

                 

                In our experience, even when each @LoadAsync bean is its own split point, the compiled app often ends up with 98% of its code in a single fragment anyway. By grouping multiple beans together, you would be increasing the number of references to other types, and this could only aggravate the "one huge fragment" problem further.

                 

                Have you done a SOYC report of your compiled app? Are you getting evenly balanced fragments when you compile? If so, we'd be interested to hear your technique!

                 

                Cheers,

                Jonathan

                • 5. Re: force downloading of @LoadAsync annotated classes
                  jfuerth

                  Oh, I forgot to respond to this part:

                  But I'm curious if there isn't an even better solution in the case your app is growing quite big like in our case. We like the way Google+ is handling it. They seem to have implemented async loading for their top navigation points like Games, Communities, etc. So we adapted this approach in our app using @LoadAsync.

                  The thing Google+ does (or at least did when they first launched) is a very good technique in my opinion. They use Closure Templates, a templating system that can render its templates on either the server side or the client side. When you first request a page, the *server* renders the template and sends out finished HTML. The <script> tags are at the bottom of that pre-rendered page, and the scripts get loaded and parsed when the page is already visible. They look up elements on the page and attach event handlers while the user is busy looking at the pretty pictures.

                   

                  Server-side prerendering is something I'm keen to see implemented in Errai UI's own templating system. It's essentially the same thing as the handcoded login page that loads your app in the background, but it works for any page, and it doesn't involve hand coding. Unfortunately, nobody has yet taken a stab at implementing this feature.

                   

                  -Jonathan

                  • 6. Re: force downloading of @LoadAsync annotated classes
                    marius.gerwinn

                    Sorry for my delayed reply! But here it is:

                     

                    Have you done a SOYC report of your compiled app? Are you getting evenly balanced fragments when you compile? If so, we'd be interested to hear your technique!

                    Actually you are right. Its pretty hard to get balanced fragments Anyhow with our setup (MVP with  Errai UI) we have reduced our initial download size by about 30%.

                    But with 99 split points to much server calls are required to get a real benefit.

                    compile report.PNG

                    The Split points are very much a result of a missing grouping functionality because we added @Async to all of our "templated ui classes".

                    These classes seems to have the biggest effect regarding code size. Presenters are currently left out because they have less effect and creating just more small code fragments (more server calls).

                     

                    For example our account menu has about 6 subpages which also have some subpages and so on. This results in total in about 24 @Async annoted classes.

                    But the account menu is (most of the time) not needed. Especially not at startup. So it makes much sence for us to load it defered.

                    With grouping we could bundle all of this 24 classes and even more. We would also annotate the relevant presenters and further reduce the code size.

                     

                    Server-side prerendering is something I'm keen to see implemented in Errai UI's own templating system. It's essentially the same thing as the handcoded login page that loads your app in the background, but it works for any page, and it doesn't involve hand coding. Unfortunately, nobody has yet taken a stab at implementing this feature.

                    We are also looking forward to see this implemented. I totally agree to your arguments and assume that this could make some of the async loading we discussed above obsolete.