8 Replies Latest reply on Jan 4, 2010 9:24 AM by Nicklas Karlsson

    No active context exception

    John Ament Master

      So I'm not sure if there's other reasons for me not realizing this, but I just noticed these exceptiosn while playing with my app this morning.




      Caused by: javax.enterprise.context.ContextNotActiveException: No active contexts for scope type javax.enterprise.context.SessionScoped
           at org.jboss.weld.BeanManagerImpl.getContext(BeanManagerImpl.java:928)
           at org.jboss.weld.bean.proxy.ClientProxyMethodHandler.getProxiedInstance(ClientProxyMethodHandler.java:140)
           at org.jboss.weld.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:101)
           at security.MySecurityContext_$$_javassist_112.getUserName(MySecurityContext_$$_javassist_112.java)
           at org.jboss.dna.graph.request.processor.RequestProcessor.notifyObserverOfChanges(RequestProcessor.java:979)
           at org.jboss.dna.connector.store.jpa.model.simple.SimpleJpaConnection.execute(SimpleJpaConnection.java:138)
           at org.jboss.dna.graph.request.CompositeRequestChannel$2.call(CompositeRequestChannel.java:193)
           at org.jboss.dna.graph.request.CompositeRequestChannel$2.call(CompositeRequestChannel.java:183)
           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)
           ... 1 more



      The way it works, when I am closing a session (via a disposer, since I don't own the Session object), I get this exception.  From what I can tell, DNA is using a wrapper of my managed bean with the security context in a FutureTask. 




      String userName = context.getSecurityContext() != null ? context.getSecurityContext().getUserName() : null;





      where context.getSecurityContext() is returning the instance of MySecurityContext that is a managed bean/CDI enabled bean.  Since this happens in a FutureTask, I'm not sure what exactly should be happening.  An interesting note, I can make it go away by creating a method in MySecurityContext:




      public SecurityContext getSimpleSession() {
                  return new SecurityContext() {
      
                  @Override
                  public String getUserName() {
                      return username;
                  }
      
                  @Override
                  public boolean hasRole(String roleName) {
                      return MySecurityContext.this.hasRole(roleName);
                  }
      
                  @Override
                  public void logout() {
                      
                  }
      
                  };
              }



      and it works perfectly.  I assume because this forces it out of the weld managed bean lifecycle.  The question though, why won't it work with a FutureTask?  I could have sworn that EJB Async was done via FutureTask, so it stands to reason that weld would work here as well.  Should this be working?

        • 1. Re: No active context exception
          Matt Drees Master

          It appears as though your MySecurityContext bean is @SessionScoped.  So, when DNA accesses MySecurityContext, the CDI impl will look up the component from the session context for the current thread.  When the thread isn't running in the context of a HTTP request, as is happening here, there is no active session context. 


          I'm assuming that at some point, you're giving DNA your MySecurityContext via a setter or something.  Is this an application-wide setting, or is it done per-DNA-session?


          It'd probably be helpful to give more context information.




          I could have sworn that EJB Async was done via FutureTask

          FutureTask is a representation of a task, not a framework for executing tasks (this is the job of an Executor).  By the way, the session context is not propagated in EJB asynchronous invocations.  The session context is tied to the thread servicing the servlet request.


          So what you're trying to do should not work, no.

          • 2. Re: No active context exception
            John Ament Master

            So that's pretty much what I figured was happening.  I'm a bit surprised by one note, that the HTTP Session is not active in a thread outside of the HTTP Request.  This seems odd to be honest, I could see it going either way to be perfectly honest.  From a high level, how would that work in an asynch EJB?  Or is it if you pass in the HttpSession object, it just works against that?  In the same way that I can pass the HttpSession object to the EJB simply via method call, couldn't I do something similar by unproxying the SecurityContext and marking it @Dependent ?


            MySecurityContext is SessionScoped, has to be (by design at least).  Tried RequestScoped, application still functioned (mostly because on PostConstruct, it reads the username from the HTTP request) and error continued as expected.  The setting of the security context is done per HTTP request, at least with the way I have it working.  I'm sure you're familiar with hibernate, the handing of the security context to DNA creates a Session-per-http-request scheme (like hibernate would do via ThreadLocal).  It's a bit weird to be honest - the firing of the event happens when I do session.save().  I immediatley do a session.logout(); to me it stands to reason that session.logout() may complete before the FutureTask completes.  I'm not saying that's what I'm seeing, I'm just babbling a little at 1am.

            • 3. Re: No active context exception
              Matt Drees Master

              From a high level, how would that work in an asynch EJB? Or is it if you pass in the HttpSession object, it just works against that?

              The session context would not be available in asynch EJB.  There's no built-in way to pass an HttpSession, either.  I'm guessing a weld-specific extension could be made that would propagate the session context to asynch EJB invocations, but there's no portable way, and I'm not sure it's a great idea anyway (like you said, what happens when the session expires before the invocation returns?).



              So, I think what I might do in your situation is create a new SecurityContext bean that is @Dependent scoped, in addition to your @SessionScoped MySecurityContext bean.  I'd probably create it as a producer bean on MySecurityContext, whose implementation would look a lot like your getSimpleSession() method. Something like:


              @SessionScoped
              class MySecurityContext implements SecurityContext {
                 private String username;
                 ...
                 @Produces @Dependent @Snapshot SecurityContext produceSnapshotSecurityContext() {
                    return new SecurityContext {
                       String snapshotUsername = username;
                       Set<String> roles = getRolesFromMySecurityContext(); 
                       // I think you're better off copying the roles rather than calling back for hasRole()
              
                       public String getUserName() {return username;}
                       public boolean hasRole(String role) {return roles.contains(role);}
                       public void logout() {throw new UnsupportedOperationException();}
                    }
                 }
              }
              



              and then when setting the security context on the DNA session, use @Snapshot SecurityContext insead of MySecurityContext.


              (I'm not sure 'Snapshot' is the best word for the qualifier here; I couldn't think of anything better off the top of my head.)


              What do you think?

              • 4. Re: No active context exception
                John Ament Master

                Well, it works; and it's pretty much where I was heading too.  I have a role cache already in my implementation so I end up just copying that.  Now, if my underlying implementation of hasRole is talking to an IDM that i've exposed via CDI, (it's an EJB that invokes a stored procedure) is it recommended to pass that in as a reference by looking up the object in JNDI or pass in the local copy?

                • 5. Re: No active context exception
                  Matt Drees Master

                  Sorry, I don't quite follow.  Could you be more explicit?

                  • 6. Re: No active context exception
                    John Ament Master

                    It's more of a best practices question.  Let say I'm producing the instance of this class.  The real implementation of hasRole uses an EJB, it'd be best to just lookup the instance of the EJB (stateless) over JNDI than to use the existing CDI wrapper?

                    • 7. Re: No active context exception
                      Matt Drees Master

                      I don't think I have an answer for that.


                      If it's a stateless EJB, then I think it wouldn't matter to too much whether you passed an injected reference or a jndi-lookup reference to the @Dependent bean.  I suppose I'd go with the injected reference, because I think less ugly code would be involved.

                      • 8. Re: No active context exception
                        Nicklas Karlsson Master

                        And with the injected reference you can later hook on CDI interceptors and decorators which are not available on the JNDI-vanilla-EJB