11 Replies Latest reply on Jan 28, 2014 11:55 AM by datsunhls30

    Re-writing an existing GWT app in Errai

    datsunhls30

      So I've spent the better part of the last 40 hours getting a "HelloErrai" implemented in my existing GWT application.  The plan is to transition new development into Errai and eventually backfill as possible.  I found it to be pretty frustrating partly because if there's a guide on how to do this, I couldn't find it, and partly because I made a lot of my own dumb mistakes in the process. 

       

      Anyway, so I can click a button and dispatch/observe an event, have an @Templated page with some @DataField, and all seems to be really snazzy.  I am pretty excited to go forward with Errai.  I'm somewhat concerned about the performance I'm seeing in DevMode, so I'm hoping it's something dumb I'm doing.

       

      I have ErraiApp.properties at the top of my classpath that contains a couple thousand Widgets(the existing application), and I'm seeing DevMode take ~22 seconds to refresh on an Errai UI that only consists of 3 files.  I can refresh my non-Errai application in ~2 seconds.  I've tried to be more selective in where ErraiApp.properties lives (like putting it only in the package that contains my 3 Errai-specific classes) but I'm pretty sure it's still scanning the entire class structure.  Do I need to do some work to break Errai out into a separate module so that it only scans/generates code for those classes?  Do you know of any "large" apps implemented in Errai and have any anecdotal information about how well it scales in DevMode and/or an actual deployed site?

       

      Thanks!

        • 1. Re: Re-writing an existing GWT app in Errai
          csa

          Hi Mike,

           

          Thanks for the feedback about the missing tutorial/guide. I've added a JIRA for a new tutorial that shows how to add Errai to an existing GWT application:

          https://issues.jboss.org/browse/ERRAI-684

           

          I am glad you had success getting up and running!

           

          Regarding DevMode performance:

          - Breaking down your app into separate GWT modules will definitely improve the startup and refresh time!

           

          - A simpler way to achieve the same (in case modularization is a lot of work) is to configure white or black lists of types. So, basically you can tell Errai IOC which types or packages to ignore when generating code: ErraiApp.properties - Errai - Project Documentation Editor

           

          - DevMode performance is a main area of focus for us in Errai 3 (and in GWT 3 as well). We've already made progress on speeding it up for marshalling on a separate branch that will hopefully be ready and merged into master in the next couple of days.

           

          Cheers,

          Christian

          1 of 1 people found this helpful
          • 2. Re: Re-writing an existing GWT app in Errai
            datsunhls30

            So a few steps forward:  I was working on 2.4.3 Final as I was hesitant to work off of an in-progress release, but the white/black lists are 3.0.  I upgraded to3.0.0.20131205-M3 and started by configuring the whitelist like:

             

            errai.ioc.whitelist=com.mycompany.app.myapp.client.local.* \

                                com.mycompany.app.myapp.client.shared.* \

                                com.mycompany.app.myapp.server.*

             

            I kept getting NullPointerExceptions(pasted below for reference).  Running the debugger a bit, I learned that I needed to be whitelisting com.google.gwt.*  It might be helpful on TypeInjector.getBeanInstance on or about line 219 to do something like:

            if(creationalCallbackVarName == null)

              throw new InjectionFailure("Failed to instantiate field: " + injectableInstance.getField().getName() + " type: " + injectableInstance.getField().getGenericType())

             

            Also, it might not fit, but adding com.google.gwt.* to the implicitWhiteList in InjectionContext seems like a reasonable sanity measure too.

             

            Unfortunately, this didn't really bring the refresh time down in development mode, it hovers right at 20 seconds per refresh :-/

             

            NPE for not whitelisting com.google.gwt.*

            -----------------------------------------------------------------------

            java.util.concurrent.ExecutionException: java.lang.NullPointerException

                 at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)

                 at java.util.concurrent.FutureTask.get(FutureTask.java:83)

                 at org.jboss.errai.config.rebind.AsyncGenerators$FutureWrapper.get(AsyncGenerators.java:112)

                 at org.jboss.errai.config.rebind.AsyncGenerators$FutureWrapper.get(AsyncGenerators.java:86)

                 at org.jboss.errai.config.rebind.AbstractAsyncGenerator.startAsyncGeneratorsAndWaitFor(AbstractAsyncGenerator.java:100)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCGenerator.generate(IOCGenerator.java:58)

                 at com.google.gwt.core.ext.IncrementalGenerator.generateNonIncrementally(IncrementalGenerator.java:40)

                 at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:657)

                 at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:41)

                 at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:79)

                 at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:276)

                 at com.google.gwt.dev.shell.ShellModuleSpaceHost.rebind(ShellModuleSpaceHost.java:141)

                 at com.google.gwt.dev.shell.ModuleSpace.rebind(ModuleSpace.java:595)

                 at com.google.gwt.dev.shell.ModuleSpace.rebindAndCreate(ModuleSpace.java:465)

                 at com.google.gwt.dev.shell.GWTBridgeImpl.create(GWTBridgeImpl.java:49)

                 at com.google.gwt.core.shared.GWT.create(GWT.java:57)

                 at com.google.gwt.core.client.GWT.create(GWT.java:85)

                 at org.jboss.errai.ioc.client.Container.bootstrapContainer(Container.java:62)

                 at org.jboss.errai.ioc.client.Container.onModuleLoad(Container.java:39)

                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

                 at java.lang.reflect.Method.invoke(Method.java:597)

                 at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:406)

                 at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:200)

                 at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:526)

                 at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)

                 at java.lang.Thread.run(Thread.java:662)

            Caused by: java.lang.NullPointerException

                 at org.jboss.errai.codegen.builder.impl.StatementBuilder.loadVariable(StatementBuilder.java:198)

                 at org.jboss.errai.codegen.util.Stmt.loadVariable(Stmt.java:202)

                 at org.jboss.errai.ioc.rebind.ioc.injector.basic.TypeInjector.getBeanInstance(TypeInjector.java:220)

                 at org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil.getInjectorOrProxy(InjectUtil.java:526)

                 at org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil.getInjectorOrProxy(InjectUtil.java:440)

                 at org.jboss.errai.ioc.rebind.ioc.injector.api.InjectionTask.doTask(InjectionTask.java:116)

                 at org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil.handleInjectionTasks(InjectUtil.java:163)

                 at org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil.access$000(InjectUtil.java:72)

                 at org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil$2.generateConstructor(InjectUtil.java:150)

                 at org.jboss.errai.ioc.rebind.ioc.injector.basic.TypeInjector.renderProvider(TypeInjector.java:138)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCConfigProcessor$3.handle(IOCConfigProcessor.java:414)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCConfigProcessor$7.process(IOCConfigProcessor.java:679)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCConfigProcessor.process(IOCConfigProcessor.java:625)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCBootstrapGenerator.generateBootstrappingClassSource(IOCBootstrapGenerator.java:292)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCBootstrapGenerator.generate(IOCBootstrapGenerator.java:142)

                 at org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCGenerator.generate(IOCGenerator.java:68)

                 at org.jboss.errai.config.rebind.AbstractAsyncGenerator$1.call(AbstractAsyncGenerator.java:43)

                 at org.jboss.errai.config.rebind.AbstractAsyncGenerator$1.call(AbstractAsyncGenerator.java:40)

                 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)

                 at java.util.concurrent.FutureTask.run(FutureTask.java:138)

                 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

                 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

            • 3. Re: Re-writing an existing GWT app in Errai
              csa

              Thanks, I've added com.google.gwt.* to the implicit type whitelist. I am definitely eager to find out why refreshing takes 20s in your case. Any diagnostic information you can provide would really be helpful.

               

              For instance your console should log statements like this after refreshing: "generated marshalling class in ..ms", "generated IOC boostrapping class in ..ms". You can find those generated classes in your .errai folder (they should also point us to where the time is spent).

               

              Cheers,

              Christian

              • 4. Re: Re-writing an existing GWT app in Errai
                datsunhls30

                I don't see exactly the log statements you're looking for in the devmode gui logs (which I set to -logLevel DEBUG).  If I need to increase logging elsewhere, let me know.  Also, the increased logging significantly slows it down too, adding about 10s it seems.  In case you're wondering, I have a pretty quick development workstation with SSDs -- non-errai devmode is plenty fast.

                 

                00:00:38.897 [INFO] Loading module erraitest

                ... ( stuff that takes about 3.3s)

                00:00:42.279 [DEBUG] Rebinding org.jboss.errai.marshalling.client.api.MarshallerFactory

                00:00:46.600 [INFO] Checking ErraiApp.properties for configured types ...

                00:00:53.700 [DEBUG] Generator returned type 'org.jboss.errai.marshalling.client.api.MarshallerFactoryImpl; mode USE_ALL_NEW_WITH_NO_CACHING; in 10885 ms

                00:00:56.068 [DEBUG] Rebinding org.jboss.errai.marshalling.client.api.MarshallerFramework

                00:00:56.101 [DEBUG] Rebinding org.jboss.errai.bus.client.api.messaging.MessageBus

                ... (rebinding standard GWT packages) ...

                00:01:05.850 [DEBUG] Rebinding org.jboss.errai.ioc.client.QualifierEqualityFactory

                00:01:06.355 [DEBUG] Rebinding org.jboss.errai.ioc.client.container.IOCEnvironment

                00:01:06.366 [DEBUG] Rebinding org.jboss.errai.ioc.client.Bootstrapper

                00:01:06.881 [DEBUG] Rebinding org.jboss.errai.databinding.client.BindableProxyLoader

                ...

                00:01:07.155 [INFO] Module erraitest has been loaded

                • 5. Re: Re-writing an existing GWT app in Errai
                  csa

                  Hi Mike,

                   

                  From the log it looks like generating the marshallers took almost 11 seconds. Do you have a lot of @Portable types in your project?

                   

                  I have just pushed the marshaller generator improvements which speed up marshaller generation quite a bit. Can you try using the latest 3.0-SNAPSHOTs and see if it improves this situation? For our test suite and a community user (who built a huge application) this change made a big difference.

                   

                  Cheers,

                  Christian

                  • 6. Re: Re-writing an existing GWT app in Errai
                    datsunhls30

                    Still taking roughly 20 seconds to refresh on 3.0-SNAPSHOT.  I do see that the timing from before that was 11 seconds is down to 6 seconds, looks like a big improvement!

                     

                    To answer your other question, we currently have 2 classes defined as @Portable, 1 class is @EntryPoint, and 1 class is @Templated as we're still evaluating this as a new platform. For the fun of it, I deleted everything that wasn't Errai from our project, and I can refresh DevMode in about 2 seconds.  I've also tried pretty exhaustively to white/black-list out everything except the Errai code.  My guess is that something is still scanning the non-Errai code we have.

                     

                    Unless you know of other things I should try, I'm going to try to re-structure some to lower the class-scanning overhead.  We have about 1,500 java/GWT UI files and (unfortunately) that expands out to around 10,000 .class files since we have a ton of anonymously implemented callbacks, handlers, etc. 

                     

                    Thanks,

                    Mike

                    • 7. Re: Re-writing an existing GWT app in Errai
                      csa

                      OK thanks for the update. You can take a look at all the generated Errai files in your .errai folder, especially the generated BootstrapperImpl.java. It should NOT contain the classes you blacklisted. Is that the case for you?

                       

                      Looking through our scanning logic I see that our GwtValidatorScanner that doesn't limit itself to Errai modules (GWT modules that have an ErraiApp.properties). So, that's a scanner that would run through the whole classpath. Do you inherit from org.jboss.errai.validation.Validation? If so, there's a special blacklist property you could use (errai.validation.blacklist).

                       

                      We are actively working on improving the DevMode start up and refresh performance. Any feedback you have is really appreciated!

                       

                      Thanks,

                      Christian

                      • 8. Re: Re-writing an existing GWT app in Errai
                        datsunhls30

                        BootstrapperImpl.java is ~600 lines long and only contains references to the 4 classes that I would expect. 

                         

                        I was not inheriting org.jboss.errai.validation.Validation, but I added it and the blacklist property, and had no discernible difference.  I think the net result of adding it and blacklisting it was to tell it to do nothing though

                         

                        Could you inspect all matching annotated classes/templates and if the sum-total hash of all the files was unchanged from the previous generate operation, you skip that phase and just let it use the old copies?  I don't know much about the intricacies of your libraries and may be over-simplifying what you need to do, but that's what we do in some of the in-house model/view/controller generator code we have to keep from constantly re-generating the same code.

                        • 9. Re: Re-writing an existing GWT app in Errai
                          datsunhls30

                          So I've spent the last few days on 3.0-SNAPSHOT with some successes and some failures, one thing I'm confused about is the (seemingly) rigorous package structure needed for Errai to function properly.  In the sample archetype, there are 3 main packages:

                           

                          client.local

                          client.shared

                          server

                           

                          It was my understanding that the package structure was somewhat flexible as long as an ErraiApp.properties existed in the classpath, but when I attempt to refactor "client.shared" to "shared" I get no errors, but the frontend javascript silently fails afterward.  I can reproduce this with the simple-cdi archetype as well.  Is this by design or a bug?  If it's by design, is there a way to detect that the class structure isn't correct and throw an Exception rather than silently failing?

                           

                          Thanks,

                          Mike

                          • 10. Re: Re-writing an existing GWT app in Errai
                            csa

                            Hi,

                             

                            It's a convention by GWT that everything under .client will be considered client-side code and compiled to JavaScript. If you just refactor from .client to .shared (as opposed to client.shared) you effectively have no more client-side code.

                             

                            If you want to change this default you can specify a source path in your gwt.xml file (i.e. <source path="shared" />):

                            http://www.gwtproject.org/doc/latest/DevGuideOrganizingProjects.html#DevGuideModuleXml

                             

                            Cheers,

                            Christian

                            1 of 1 people found this helpful
                            • 11. Re: Re-writing an existing GWT app in Errai
                              datsunhls30

                              Ha, shoot...you're right.  Apologies for throwing blame on that one