7 Replies Latest reply on Oct 20, 2007 6:03 AM by jjarkko

    Seam2 as portlet

      Hello,
      Previously we have had a fully working aplication which consist of JSR-168 portlets implemented with Seam 1.2, Facelets. The portal we're using is Liferay.

      Now, we're planning to move with the flow and upgrade versions of different things. As with most cases you just cannot upgrade one component to a new version but you'll have to upgrade the depencies also.

      Currently, we're using Seam 1.2-custom (for portlets,see below), JBoss 4.0.5.GA (patched almost to a breaking point), Liferay 4.2.2 (2 patches). And now we see Seam 2 coming out of the oven we'll be upgrading everything in one big effort. So Seam for 2.0, JBoss AS for 4.2.1, Liferay to 4.3.3. Every major component is more or less different than their previous version.

      We've identified couple of critical changes, one is the change JSF implementation from MyFaces to Sun's RI (this is where some of the black magic happens ;).

      Previously we had a heavily customized MyFacesGenericPortlet with all sorts of tricks for various bugs/things/ related to MyFaces, Facelets. Now this portlet is empty and simply extending com.sun.faces.portlet.FacesPortlet.

      For facelets we've (always had) custom FaceletPortletViewHandler which used to extend SeamFaceletViewHandler. Now it just extends com.sun.facelets.FaceletViewHandler. So, we managed to get rid some of the hacks, but it seems we need some new ones ;)

      Also, we had a customized org.jboss.seam.portlet.PortletSessionImpl which used PortletSession.APPLICATION_SCOPE rather than PortletSession.PORTLET_SCOPE to access the portlet session.

      Now PortletSessionImpl has been removed from Seam and I'm trying to figure it ouf if this hack is still needed.
      So can you point me what structure replaced the servlet/portal/SessionImpl way of doing things?

      Is org.jboss.seam.contexts.SessionContext part of this new way?


      So, what we currently have is a JBoss 4.2.1, Liferay 4.3.3, Seam2 and our applocation which is deploying just nicely! So we've managed to fix some of the integration issues.

      However, when actually using the portlet you get an exception ( http://pastebin.com/f6da360a9 ) telling there's no active application context (one was created on deploy), and from debugger I can tell that's true, there's no active application context.


      15:42:13,954 ERROR [STDERR] 18.10.2007 15:42:13 com.sun.faces.portlet.LifecycleImpl phase
      INFO: PS_CSFP0032
      java.lang.IllegalStateException: No active application scope
      at org.jboss.seam.core.Init.instance(Init.java:75)
      at org.jboss.seam.navigation.Pages.isDebugPage(Pages.java:1508)
      at org.jboss.seam.debug.jsf.SeamDebugPhaseListener.beforePhase(SeamDebugPhaseListener.java:36)
      at com.sun.faces.portlet.LifecycleImpl.phase(LifecycleImpl.java:308)
      at com.sun.faces.portlet.LifecycleImpl.render(LifecycleImpl.java:266)
      at com.sun.faces.portlet.FacesPortlet.renderFaces(FacesPortlet.java:352)
      at com.sun.faces.portlet.FacesPortlet.doView(FacesPortlet.java:280)
      at javax.portlet.GenericPortlet.doDispatch(GenericPortlet.java:235)
      at fi.jab.jsf.MyFacesGenericPortlet.doDispatch(MyFacesGenericPortlet.java:49)
      at javax.portlet.GenericPortlet.render(GenericPortlet.java:163)
      at fi.jab.jsf.MyFacesGenericPortlet.render(MyFacesGenericPortlet.java:56)
      at com.liferay.portal.kernel.servlet.PortletServlet.service(PortletServlet.java:107)


      So, my reasoning is that somehow Seam is not being "kicked in" because of the different lifecycle you've on portlets. Not even the JSF phase listener has a change to do it's magic and active seam for the current request.

      So maybe we still need some magic in the Portlet class or either in the FaceletViewHandler. I found couple of nice candicates for kickstarting Seam request, one from ServletLifecycle and the other from FacesLifecycle. I guess the FacesLifecycle should be called from SeamPhaseListener but it's doesn't get called early(?) enough for things to work.

      SeamPhaseListener has some support for portlets because there're methods like afterPortletRequest but simply having your portlet class (extending) FacesPortlet doesn't seem to be enough to get it up and running.

      Let's see if we can get this any further.













        • 1. Re: Seam2 as portlet

          Maybe in SeamPhaseListener.beforePortletPhase (in the very first phase, before anything else) we could do something similar to FacesLifecycle.beginRequest and kickstart seam?

          public static void beginRequest(ExternalContext externalContext)
          {
          log.debug( ">>> Begin JSF request" );
          Contexts.eventContext.set( new EventContext( externalContext.getRequestMap() ) );
          Contexts.applicationContext.set( new ApplicationContext( externalContext.getApplicationMap() ) );
          Contexts.sessionContext.set( new SessionContext( externalContext.getSessionMap() ) );
          Session session = Session.getInstance();
          if ( session!=null && session.isInvalidDueToNewScheme( Pages.getRequestScheme( FacesContext.getCurrentInstance() ) ) )
          {
          invalidateSession(externalContext);
          }
          Contexts.conversationContext.set(null); //in case endRequest() was never called
          //Events.instance(); //TODO: only for now, until we have a way to do EL outside of JSF!
          }

          • 2. Re: Seam2 as portlet

            Except, there seems to be problem that SeamPhaseListener.beforePhase is never called, SeamPhaseListener.afterPhase is called several times (by com.sun.faces.portlet.LifecycleImpl.render and com.sun.faces.portlet.LifecycleImpl.phase ).

            • 3. Re: Seam2 as portlet
              pmuir

              Hopefully we will see improvements in portal integration in 2.0.1 - sorry, they didn't make it into 2.0.0

              • 4. Re: Seam2 as portlet

                Ok, thanks for the information. In the mean time, is there a place where I could find additional information and try to implement those by myself?

                • 5. Re: Seam2 as portlet
                  pmuir

                  The best starting point is the portal example in Seam CVS. I've also asked Alex, who said he had some patches, to create a jira issue and attach them.

                  • 6. Re: Seam2 as portlet

                    Ok,
                    Unfortunately the portal example in examples/portal is JSP based, and still uses MyFaces.
                    I downloaded sources for the jsf-portlet.jar (aka portletbridge ) to see if there's something there.

                    • 7. Re: Seam2 as portlet

                      Ok,
                      After getting the sources for jsf-portet.jar (com.sun.faces.portlet) I managed to make same progress. I've get output from XHTML!

                      There's couple issues with com.sun.faces.portlet.FacesPortlet. First the method getFacesContextFactory() always uses com.sun.faces.context.FacesContextFactoryImpl instead of com.sun.faces.portlet.FacesContextFactory. This causes issues and has to be fixed.

                      Broken getFacesContextFactory() from com.sun.faces.portlet.FacesPortlet. I just don't know to to wire FactoryFinder to use com.sun.faces.portlet.FacesContextFactory.

                       public FacesContextFactory getFacesContextFactory() throws PortletException{
                       if (facesContextFactory != null) {
                       return facesContextFactory;
                       }
                       // Acquire our FacesContextFactory instance
                       try {
                       facesContextFactory = (FacesContextFactory)
                       FactoryFinder.getFactory
                       (FactoryFinder.FACES_CONTEXT_FACTORY);
                       logger.log(Level.FINEST, "PS_CSFP0010", facesContextFactory);
                       } catch (FacesException e) {
                       Throwable rootCause = e.getCause();
                       if (rootCause == null) {
                       throw e;
                       } else {
                       throw new PortletException(e.getMessage(), rootCause);
                       }
                       }
                       return facesContextFactory;
                       }
                      


                      So I overrided the methodin custom portlet class:
                       @Override
                       public FacesContextFactory getFacesContextFactory() throws PortletException {
                      
                       if (facesContextFactory != null) {
                       return facesContextFactory;
                       }
                      
                       facesContextFactory = new com.sun.faces.portlet.FacesContextFactoryImpl();
                      
                       return facesContextFactory;
                       }
                      




                      Also FacesPortlet.getLifecycle() uses the same javax.faces.FactoryFinder which has hardcoded/or just I known know how names for factories.

                      I also override getLifecycleFactory() and getLifecycle() to return classes from com.sun.faces.portlet.

                      In getLifecycle() copy the phaselisteners from super.getLifecycle() to here. super.lifecycle has all the phaselisteners EL,Seam, and our custom phaselister from faces-config.

                      Lifecycle superLifecycle = super.getLifecycle();
                       PhaseListener phs[] = superLifecycle.getPhaseListeners();
                       for (PhaseListener phaseListener : phs) {
                       lifecycle.addPhaseListener(phaseListener);
                       }
                      


                      Then I simply make kick Seam with this new portlet lifecycle stuff in my portlet class:
                       @Override
                       protected void doDispatch(RenderRequest request, RenderResponse response)
                       throws PortletException, PortletSecurityException, IOException {
                      
                       FacesContext context = getFacesContextFactory().getFacesContext(
                       getPortletConfig().getPortletContext(), request, response,
                       getLifecycle());
                      
                       ExternalContext externalContext = context.getExternalContext();
                      
                       // Kick start Seam Contextes
                       FacesLifecycle.beginRequest(externalContext);
                      
                       super.doDispatch(request, response);
                      
                       // Tell Seam Lifecycle manager that're about to end request
                       FacesLifecycle.endRequest(externalContext);
                      
                       }
                      



                      I also've custom FaceletViewHandler with modified createResponseWriter, which is related to facelets and available from Facelets site.

                      So, basic stuff seems to work now. Let's see how conversations and others work.