13 Replies Latest reply on Jul 5, 2012 7:17 AM by navigateur

    Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?

    navigateur

      I used to be able to use the same servlet instance for RPC and normal messaging in Errai 1.3.2.

       

      Now in 2.0.0-final I'm getting 2 separate servlet instances which means I cannot use my in-memory stuff from both any more, which is critical to my app!

       

      What is the current simplest workaround for this?

        • 1. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
          jfuerth

          I'm not aware of a situation where Errai would create two servlet instances. As far as I know, we just use the one servlet instance created by the server during startup.

           

          Can you show us your web.xml?

          • 2. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
            navigateur

            Yep, my web.xml looks like this:

             

            {code:xml}

            <?xml version="1.0" encoding="UTF-8"?>

            <!DOCTYPE web-app

                PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

                "http://java.sun.com/dtd/web-app_2_3.dtd">

             

            <web-app>

             

                     <context-param>

                         <param-name>login.config</param-name>

                         <param-value>/WEB-INF/login.config</param-value>

                     </context-param>

             

                     <context-param>

                         <param-name>users.properties</param-name>

                         <param-value>/WEB-INF/users.properties</param-value>

                     </context-param>

             

                     <context-param>

                          <param-name>errai.properties</param-name>

                          <param-value>/WEB-INF/errai.properties</param-value>

                     </context-param>

             

                        <!-- Servlets -->

                        <servlet>

                          <servlet-name>myApp</servlet-name>

                          <servlet-class>com.me.server.MyAppServlet</servlet-class>

                        </servlet>

             

                      <servlet>

                          <servlet-name>ErraiServlet</servlet-name>

                          <servlet-class>org.jboss.errai.bus.server.servlet.DefaultBlockingServlet</servlet-class>

                          <init-param>

                               <param-name>auto-discover-services</param-name>

                                  <param-value>true</param-value>

                          </init-param>

                          <load-on-startup>1</load-on-startup>

                      </servlet>

             

                        <!-- Maybe change these "RemoteServiceRelativePath"s -->

                        <servlet-mapping>

                          <servlet-name>myApp</servlet-name>

                          <url-pattern>/myApp/myapp</url-pattern>

                        </servlet-mapping>

             

                      <servlet-mapping>

                              <servlet-name>ErraiServlet</servlet-name>

                              <url-pattern>*.erraiBus</url-pattern>

                     </servlet-mapping>

             

                        <!-- Default page to serve -->

                        <welcome-file-list>

                          <welcome-file>MyApp.html</welcome-file>

                        </welcome-file-list>

             

            </web-app>

             

            {code}

             

            I should say that previously I had been using the Errai version which had the issue of Errai RPC subscription replacing the normal one: https://issues.jboss.org/browse/ERRAI-207 and was solving it using the answer of subscribing manually within the constructor, from https://community.jboss.org/thread/177723. Under these conditions, I was getting only one servlet instance serving both Errai RPC and Errai normal messaging calls, as I wanted (same web.xml, just minus "auto-discover-services true"). As an aside, I was always getting a second servlet instance fired up whenever I used GWT-RPC, which is why I stopped using GWT-RPC (and don't use it any more). Is there a workaround for this too? As I say, I'm getting a separate servlet instance which serves the Errai RPC calls, and a separate one which serves normal messages - verifiable by console-outputting a simple "double" instance variable initialized to Math.rand() - which was the same number for both previously - different numbers now (plus the inability to access the same in-memory data). Looking for the simplest possible workaround to go back to the way things were in this respect...

            • 3. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
              jfuerth

              Okay, the web.xml looks fine.

               

              Thanks for the background information. It's helpful.

               

              Is it a Servlet class or an @Service class that you're getting multiple instances of? From the background info you've provided, I think it's the latter... which means I may have misunderstood your question.

               

              Can you put "new Exception().printStackTrace();" into the constructor of the class you only want a single instance of? Examining the two stack traces should show us which part of the framework is creating each instance.

               

              -Jonathan

              • 4. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                navigateur

                It's a @Service class. Console output is as follows. In 3 test runs (according to subsequent System.out.printlns, using the "unique id"), the one which serves Errai RPC was instantiated first, and the one which serves normal Errai messaging was instantiated second:

                 

                {code}

                SERVLET INSTANCE CREATED, unique id: 0.9780463514379282

                java.lang.Exception

                          at com.me.server.MyAppServlet.<init>(MyAppServlet.java:155)

                          at com.me.server.MyAppServlet$$FastClassByGuice$$1e7f4783.newInstance(<generated>)

                          at com.google.inject.internal.cglib.reflect.$FastConstructor.newInstance(FastConstructor.java:40)

                          at com.google.inject.internal.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:60)

                          at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:85)

                          at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254)

                          at com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)

                          at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)

                          at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)

                          at org.jboss.errai.bus.server.service.ServiceProcessor.createRPCScaffolding(ServiceProcessor.java:215)

                          at org.jboss.errai.bus.server.service.ServiceProcessor.process(ServiceProcessor.java:108)

                          at org.jboss.errai.bus.server.service.ServiceProcessor.process(ServiceProcessor.java:61)

                          at org.jboss.errai.bus.server.service.bootstrap.DiscoverServices.execute(DiscoverServices.java:52)

                          at org.jboss.errai.bus.server.service.bootstrap.OrderedBootstrap.execute(OrderedBootstrap.java:55)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl.boostrap(ErraiServiceImpl.java:70)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl.<init>(ErraiServiceImpl.java:65)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl$$FastClassByGuice$$7879947c.newInstance(<generated>)

                          at com.google.inject.internal.cglib.reflect.$FastConstructor.newInstance(FastConstructor.java:40)

                          at com.google.inject.internal.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:60)

                          at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:85)

                          at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254)

                          at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1031)

                          at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)

                          at com.google.inject.Scopes$1$1.get(Scopes.java:65)

                          at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40)

                          at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:54)

                          at com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)

                          at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)

                          at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)

                          at org.jboss.errai.bus.server.service.ErraiServiceFactory.create(ErraiServiceFactory.java:14)

                          at org.jboss.errai.bus.server.service.ErraiServiceSingleton.initSingleton(ErraiServiceSingleton.java:15)

                          at org.jboss.errai.bus.server.servlet.ServletBootstrapUtil.initService(ServletBootstrapUtil.java:104)

                          at org.jboss.errai.bus.server.servlet.ServletBootstrapUtil.getService(ServletBootstrapUtil.java:54)

                          at org.jboss.errai.bus.server.servlet.AbstractErraiServlet.init(AbstractErraiServlet.java:71)

                          at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:433)

                          at org.mortbay.jetty.servlet.ServletHolder.doStart(ServletHolder.java:256)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.servlet.ServletHandler.initialize(ServletHandler.java:616)

                          at org.mortbay.jetty.servlet.Context.startContext(Context.java:140)

                          at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1220)

                          at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)

                          at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)

                          at com.google.gwt.dev.shell.jetty.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:468)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

                          at org.mortbay.jetty.handler.RequestLogHandler.doStart(RequestLogHandler.java:115)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

                          at org.mortbay.jetty.Server.doStart(Server.java:222)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at com.google.gwt.dev.shell.jetty.JettyLauncher.start(JettyLauncher.java:672)

                          at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:509)

                          at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1068)

                          at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:811)

                          at com.google.gwt.dev.DevMode.main(DevMode.java:311)

                SERVLET INSTANCE CREATED, unique id: 0.47596359092772533

                java.lang.Exception

                          at com.me.server.MyAppServlet.<init>(MyAppServlet.java:155)

                          at com.me.server.MyAppServlet$$FastClassByGuice$$1e7f4783.newInstance(<generated>)

                          at com.google.inject.internal.cglib.reflect.$FastConstructor.newInstance(FastConstructor.java:40)

                          at com.google.inject.internal.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:60)

                          at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:85)

                          at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254)

                          at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:54)

                          at com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)

                          at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)

                          at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)

                          at org.jboss.errai.bus.server.service.ServiceProcessor.process(ServiceProcessor.java:115)

                          at org.jboss.errai.bus.server.service.ServiceProcessor.process(ServiceProcessor.java:61)

                          at org.jboss.errai.bus.server.service.bootstrap.DiscoverServices.execute(DiscoverServices.java:52)

                          at org.jboss.errai.bus.server.service.bootstrap.OrderedBootstrap.execute(OrderedBootstrap.java:55)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl.boostrap(ErraiServiceImpl.java:70)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl.<init>(ErraiServiceImpl.java:65)

                          at org.jboss.errai.bus.server.service.ErraiServiceImpl$$FastClassByGuice$$7879947c.newInstance(<generated>)

                          at com.google.inject.internal.cglib.reflect.$FastConstructor.newInstance(FastConstructor.java:40)

                          at com.google.inject.internal.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:60)

                          at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:85)

                          at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254)

                          at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1031)

                          at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)

                          at com.google.inject.Scopes$1$1.get(Scopes.java:65)

                          at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40)

                          at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:54)

                          at com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)

                          at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)

                          at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)

                          at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)

                          at org.jboss.errai.bus.server.service.ErraiServiceFactory.create(ErraiServiceFactory.java:14)

                          at org.jboss.errai.bus.server.service.ErraiServiceSingleton.initSingleton(ErraiServiceSingleton.java:15)

                          at org.jboss.errai.bus.server.servlet.ServletBootstrapUtil.initService(ServletBootstrapUtil.java:104)

                          at org.jboss.errai.bus.server.servlet.ServletBootstrapUtil.getService(ServletBootstrapUtil.java:54)

                          at org.jboss.errai.bus.server.servlet.AbstractErraiServlet.init(AbstractErraiServlet.java:71)

                          at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:433)

                          at org.mortbay.jetty.servlet.ServletHolder.doStart(ServletHolder.java:256)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.servlet.ServletHandler.initialize(ServletHandler.java:616)

                          at org.mortbay.jetty.servlet.Context.startContext(Context.java:140)

                          at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1220)

                          at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)

                          at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)

                          at com.google.gwt.dev.shell.jetty.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:468)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

                          at org.mortbay.jetty.handler.RequestLogHandler.doStart(RequestLogHandler.java:115)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

                          at org.mortbay.jetty.Server.doStart(Server.java:222)

                          at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)

                          at com.google.gwt.dev.shell.jetty.JettyLauncher.start(JettyLauncher.java:672)

                          at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:509)

                          at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1068)

                          at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:811)

                          at com.google.gwt.dev.DevMode.main(DevMode.java:311)

                {code}

                • 5. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                  navigateur

                  Anyone have a workaround for this? It's a "show stopper" for me right now...

                  • 6. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                    nva

                    As a workaround I implemented all the logic of the service layer in separate singleton beans. The Errai service layer is then injected with these beans and delegates all functionality to them. This way the multiple requests still end up calling a single bean instance. It is an additional layer and needs some careful architecting of the beans, but it worked for me.

                    • 7. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                      nva

                      The only problem I did not manage to solve is using @AuthenticationRequired in Errai RPC endpoints. I started a separate thread on this, but so far no results.

                      • 8. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                        navigateur

                        Is there no one-liner workaround? I wouldn't know where to begin with that. I've created a bug report here: https://issues.jboss.org/browse/ERRAI-337. I hope it's fixed ASAP.

                        • 9. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                          csa

                          We have just discussed this and we are hesitant to introduce a default singleton guarantee for @Services as we might have to break that guarantee in a future release. In errai-cdi you can have @ApplicationScoped, @SessionScoped and @RequestScoped services.

                           

                          In general, holding shared state in servlet or @Service instances is strongly discouraged (considering concurrency and/or clustering). Can you describe your use case that makes this necessary? Creating a singleton holder object for your state (as Valentin described) should be easy and a better design. Alternatively, and if you don't worry about concurrent access to the state you hold in your @Service, can you not just make that fields static?

                          • 10. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                            nva

                            A quick example of the pattern:

                             

                            1. Define a singleton service

                             

                            @Singleton

                            public class MyService {

                             

                                public String doSomething(String with) {

                               

                                    // do it

                                }

                            }

                             

                            2. Your Errai Service endpoint. It's constructor is injected with the singleton service and the service endpoint delegates to it.

                             

                            @Service

                            public class MyServiceEndpointImpl implements ServiceEndpoint {

                             

                                private final MyService myService;

                             

                                @Inject

                                public MyServiceEndpointImpl(final MyService myService) {

                             

                                    this.myService = myService;

                                }

                             

                                public String doSomething(String with) {

                             

                                    myService.doSomething(with);

                                }

                            }

                            • 11. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                              navigateur

                              Making the fields static would seem best. Christian, are you saying that a static field (and changes to it) would be fully replicated in a clustered environment, but an instance field and changes to it would not necessarily be? Because yeah I intend to run in a (new) clustered environment. Is there anything else I should know about using Errai in a cluster (Glassfish 3.1.2 cluster in my case), against ordinary long-running static Java variables on the server side, for holding state? Reason I'm keeping state in memory is performance.

                              • 12. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                                csa

                                If you want this state to be replicated in your cluster you will need to store it as part of the session. I don't know what this state represents in your app but I would be more worried about concurrent access in case you're holding it in instance or static fields of your @Service. Multiple threads will simultaneously invoke methods on a service instance....

                                • 13. Re: Serious problem, how to prevent Errai firing up a second servlet instance when adding RPC?
                                  navigateur

                                  No I mean across sessions, like a kind of in-memory database, except as ordinary Java object variables. I believe I have the concurrency angle covered. But I'd need inter-node communication to keep each copy of the static variables up to date. I wonder if this is even possible in a Glassfish cluster.