With the release of the CDI portlet integration library, we can now take advantage of the vast benefits to using CDI with JSF inside our portlets with the Portlet Bridge!
How to use it?
In addition to the usual Portlet Bridge dependencies, to enable your portlet for CDI we need to:
- Add the following dependency to your project:
<dependency> <groupId>org.gatein</groupId> <artifactId>cdi-portlet-integration</artifactId> <version>1.0.2.Final</version> </dependency>
- Create an empty beans.xml file within WEB-INF of our WAR.
- Add the following filter definition to our portlet.xml:
<filter> <filter-name>PortletCDIFilter</filter-name> <filter-class>org.gatein.cdi.PortletCDIFilter</filter-class> <lifecycle>ACTION_PHASE</lifecycle> <lifecycle>EVENT_PHASE</lifecycle> <lifecycle>RENDER_PHASE</lifecycle> <lifecycle>RESOURCE_PHASE</lifecycle> </filter> <filter-mapping> <filter-name>PortletCDIFilter</filter-name> <portlet-name>yourPortletName</portlet-name> </filter-mapping>
The PortletCDIFilter only wraps the incoming portlet request for CDI to be able to use it. If your application code utilizes frameworks that need to act on the portlet response, you may need to use the PortletCDIResponseFilter instead, which wraps both the request and response.
Beans with this scope do not behave the same in a portlet as regular JSF. In a portlet, any modified content of a Bean that is set during an ActionRequest will not be available in any other phase of the portlet lifecycle, including RenderRequest. Therefore its recommended to not use this scope for your CDI beans within a JSF portlet, instead we recommend using the new @PortletLifecycleScoped that is provided by GateIn 3.6.0.Final. This new scope retains the Bean instance from ActionRequest to RenderRequest, allowing you to set data you want to use for rendering the portlet within an action.
Transient conversations act the same as if the Bean was annotated with @RequestScoped. Therefore they are also not recommended for use in making data available for the rendering of a JSF portlet.
Long running conversations do work as expected within a JSF portlet, though there are a couple of caveats to be aware of:
- Ending a long running conversation during a RenderRequest will result in subsequent portlet renders encountering a ConversationNotFoundException. This is because the url that triggers the rendering of the portlet contains the cid parameter that CDI uses to know whether to retrieve a long running conversation or create a new transient one. If that parameter still exists when a conversation has been terminated, then it will attempt to restart that ended conversation the next time the portlet is rendered.
- Starting or ending a long running conversation within an Ajax request is fine, but we need to re render all the components that contain URLs for our portlet so that CDI knows whenever we click a link or button which conversation to use, if any. Without updating the URLs, the links could be set with an old cid, causing ConversationNotFoundException, or no cid at all, making any call run in a transient conversation instead.
Lastly there are the two new scopes provided within GateIn 3.6.0.Final, @PortletLifecycleScoped and @PortletRedisplayScoped. Both of these scopes are usable by JSF portlets and are able to interact with other CDI scoped beans without problem. These scopes will be discussed in upcoming blog posts on GateIn.
Hopefully this post has helped point out some common pitfalls to watch out for when developing a JSF portlet that uses CDI!