6 Replies Latest reply on Sep 25, 2009 12:05 PM by tagrossr

    Problem with Seam Mail and FacesContext

    tagrossr

      Hi there,


      I'm working with Seam 2.2.1-SNAPSHOT. The goal is to send a message to multiple recipients with seam mail. When I send one email it will work most of the time (after 3 up to five mails it will fail although). But when I'm working through a list of recipients it will fail on the second rendering. I always get a:




      19:20:59,437 ERROR [STDERR] java.lang.IllegalStateException |@LOCATION: org.jboss.logging.util.LoggerStream.write(LoggerStream.java:152) 
      19:20:59,453 ERROR [STDERR]     at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:395) |@LOCATION: org.jboss.logging.util.LoggerStream.write(LoggerStream.java:152) 
      19:20:59,484 ERROR [STDERR]     at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:147) |@LOCATION: org.jboss.logging.util.LoggerStream.write(LoggerStream.java:152) 
      19:20:59,484 ERROR [STDERR]     at com.sun.faces.util.RequestStateManager.getStateMap(RequestStateManager.java:276) |@LOCATION: org.jboss.logging.util.LoggerStream.write(LoggerStream.java:152)
      ...





      I hope someone know this problem or have a clue how to fix.


      I have a Gateway which will invoke the send method of the EmailOutAdapter. The EmailOutAdapter will invoke the render method of the FaceletsRenderer.


      My classes


      @Name(MessageGateway.COMPONENT_NAME)
      @Scope(ScopeType.APPLICATION)
      public class MessageGateway {
              ...
              @Asynchronous
              public void sendMessage(List<Transmission> transmissions) {
                      for (Transmission transmission : transmissions) {
                          EmailOutAdapter eoa = (EmailOutAdapter) Component.getInstance(EmailOutAdapter.class);
                          eoa.send(transmission);
                      }
              }
              ...
      }




      @Name(EmailOutAdapter.COMPONENT_NAME)
      @Scope(ScopeType.EVENT)
      public class EmailOutAdapter implements Adapter {
              ...
              public void send(Transmission transmission) {
                      Contexts.getEventContext().set("transmissionToSend", transmission);
                      try {
                              Renderer renderer = (Renderer) Component.getInstance(Renderer.class);
                              renderer.render(TEMPLATE);
                      } catch (IllegalStateException ise) {
                              ise.printStackTrace();
                      }
              ...
      }



      I debugged it and found out that the problem is in the RenderRequest.class. In the first round there is no FacesContext available so it will create one and all is fine (1). In the second the FacesContext.getCurrentInstance() will not return null and there's the problem. It gets this FacesContext but the state of the Context is released = true (2) and will end up in the IllegalStateException when it tries to get the Application (3).



      package org.jboss.seam.ui.facelet;
      ...
      public class RendererRequest{
      ...
      private void init()
         {
            request = new MockHttpServletRequest(HttpSessionManager.instance());
            response = new MockHttpServletResponse();
      
            setContextClassLoader();
            
            // If a FacesContext isn't available, set one up
            if (FacesContext.getCurrentInstance() == null)
            {
               // (1) good case
               facesContext = RendererFacesContextFactory.instance().getFacesContext(request, response);
            }
            else
            {
               // (2) bad case
               facesContext = FacesContext.getCurrentInstance();
            }
      
            // Generate the FacesContext from the JSF FacesContextFactory
            originalFacesContext = FacesContext.getCurrentInstance();
            DelegatingFacesContext.setCurrentInstance(facesContext);
      
            // Create the viewRoot
            // (3) ends up in a mess when it tries to get the application because the context state is released
            UIViewRoot newRoot = facesContext.getApplication().getViewHandler().createView(facesContext, viewId);
            facesContext.setViewRoot(newRoot);
      
            // Set the responseWriter to write to a buffer
            writer = new StringWriter();
            facesContext.setResponseWriter(facesContext.getRenderKit().createResponseWriter(writer,
            null, null));
         }
      }
      ...
      }



      Do I've lost sight of something? I'm thankful for every clue.


      kind regards
      Thomas




        • 1. Re: Problem with Seam Mail and FacesContext
          kapitanpetko

          Save yourself some trouble, use JavaMail directly in asynchronous methods.


          There might be a way to fix it, but IMHO Seam mail is not reliable in async methods. So better skip it
          and use plain JavaMail.


          HTH

          • 2. Re: Problem with Seam Mail and FacesContext
            tagrossr

            Thank you for the advice. I will consider take this way if I don't get it soon with seam mail.


            So, is there a possibility to destroy this FacesContext or initialize a new one? In fact that there is always a new FacesContext by rendering?


            kind regards Thomas

            • 3. Re: Problem with Seam Mail and FacesContext
              kapitanpetko

              Don't know. You may try to patch RendererRequest, but look here JBSEAM-3555 first
              for what others have tried.

              • 4. Re: Problem with Seam Mail and FacesContext
                tagrossr

                I have a solution to get it work. To assure that RenderRequest.class will create a new FacesContext each time I'm setting it to null after every rendering.


                DelegatingFacesContext.setCurrentInstance(null);



                For now it works. But it could be that there will appear some other troubles.


                Thanks
                Thomas

                • 5. Re: Problem with Seam Mail and FacesContext

                  Hello Thomas,


                  I have a similar problem:


                  I have a save button that calls the save method via ajax, and I need to send a mail notification of the save action. I want this notification to be sent in an asynchronous way, but seam mail doesn't work correctly with asychronous. I try your solution, but this doesn't work ...




                  @Name("mailSender")
                  public class MailSender {  
                       
                       @Asynchronous
                       public void sendNewOutNoteNotification(OutNote outNote) {
                           try {
                                DelegatingFacesContext.setCurrentInstance(null);
                                Renderer renderer = (Renderer) Component.getInstance(Renderer.class);
                              renderer.render("/mail/newOutNote.xhtml");
                          } catch (Exception e) {
                               e.printStackTrace();
                          }
                       }
                       
                  }




                  Could you help me??


                  here my original post: postk




                  • 6. Re: Problem with Seam Mail and FacesContext
                    tagrossr

                    Hi Juan,


                    Perhaps you could precise your problem...


                    But when I look to your code, I would try the following:



                    @Name("mailSender")
                    public class MailSender {  
                         
                         @Asynchronous
                         public void sendNewOutNoteNotification(OutNote outNote) {
                                // XYZ -> the name on which you refer in your newOutNote.xhtml
                                Contexts.getEventContext().set("XYZ", outNote);
                             try {
                                  DelegatingFacesContext.setCurrentInstance(null);
                                  Renderer renderer = (Renderer) Component.getInstance(Renderer.class);
                                 renderer.render("/mail/newOutNote.xhtml");
                                    // I had some problems when I dont set the FacesContext to null AFTER rendering
                                    DelegatingFacesContext.setCurrentInstance(null);
                            } catch (Exception e) {
                                 e.printStackTrace();
                            }
                         }
                         
                    }