There are a couple of steps to enabling CDI for JSF2 portlets that use Portlet Bridge.
|This is only required when deploying to servers that use CDI 1.0|
First is to add the following dependency to your pom.xml to add the portlet integration library for CDI:
When deploying to GateIn, the portlet integration library for CDI is automatically included for JSF2 portlets that use CDI.
So we need to ensure the dependency is marked as provided in our pom.xml:
If you still want to include the CDI integration library in your application you can disable the automatic inclusion of it. This could, for example, be useful if you need to use a different version of the CDI integration library.
To do that, just specify the org.gatein.cdi.DISABLE_CDI_INTEGRATION context param in your web.xml file like this:
Add the following portlet filter definition into your portlet.xml so that Portlet Request objects are wrapped appropriately for CDI usage:
Then add to portlet.xml the activation of the above filter for each portlet that requires CDI:
|If you need to also wrap Portlet Response objects, then use the org.gatein.cdi.PortletCDIResponseFilter instead.|
The default is now to not wrap multipart requests with any Http wrapper. If you need to have multipart requests wrapped so that they can be used with CDI, you can re-enable the wrapping by adding an init-param for org.gatein.cdi.ENABLE_MULTIPART_REQUEST_WRAPPING to the portlet filter definition in portlet.xml, such as:
For CDI 1.0 the following scopes are available for use within JSF2 portlets:
There are some possible pitfalls with CDI that are explained below.
Any beans that are defined as @ConversationScoped, but which remain transient, that store data during the ActionRequest will not have that data accessible within the bean during a subsequent RenderRequest. As the Portlet lifecycle uses separate servlet requests to represent ActionRequest and RenderRequest, it results in the conversation context being deactivated at the end of ActionRequest and all transient conversations are destroyed.
The only difference in behavior between a JSF2 application and a JSF2 portlet is that there can be problems if a long running conversation is ended within a RenderRequest.
For example, when a long running conversation is ended during RenderRequest CDI will destroy the conversation at the end of the JSF render response phase, but the id of the conversation will still be present within the Portlet Bridge scope. As the id of the conversation is retained by the Portlet Bridge, if the user refreshes the portlet page and a RenderRequest is sent to the portlet, the previously saved conversation id will be attempted to be activated by CDI but the associated conversation context does not exist.
One work around for the above problem is to not end a long running conversation. Instead of always calling begin() on the injected conversation, only call it when isTransient() is true. This will prevent a failure in converting a transient to long running conversation.
If it's possible for your portlet to only end a long running conversation within an ActionRequest, then you won't experience the above problem.
As the conversation id parameter, cid, is set as a request parameter on the url, each time we convert a conversation from long running to transient, or vice versa, we need to ensure that any urls that our portlet uses are updated. This ensures that the latest value for the conversation id parameter, or lack of one if we converted a conversation to transient, are present on any links the user clicks. Not doing so can result in problems such as:
- Long running conversations being lost because the conversation id parameter was not present on subsequent requests to the portlet
- A CDI container throwing ContextNotActiveException because the conversation id parameter that was present on the link is no longer present as a valid conversation for use.
In most cases its simply a matter of adding more ids to the render attribute of any ajax component that will start or end a long running conversation, so that the ids of all components that contain url links to actions using a conversation or links to pages of the portlet are included in the list to be re rendered.
@RequestScoped will work within a CDI and JSF2 portlet, but due to the portlet lifecycle you need to be aware that data from an ActionRequest will not be retained for use in a RenderRequest.
There are plans to implement a replacement for @RequestScoped specifically for use within portlets, but it is not currently available.
Using DeltaSpike within your CDI/JSF portlet is a very straightforward process. The most important thing to note about using DeltaSpike in a portlet, is to disable the client window render mode by setting ClientWindowRenderMode.NONE.
To see DeltaSpike in action in a portlet, first we need to get the JSF/CDI example application for DeltaSpike from https://github.com/apache/deltaspike/tree/master/deltaspike/examples/jsf-examples.
We then need to add the following Maven dependencies to the project:
Modify SampleClientWindowConfig to return ClientWindowRenderMode.NONE instead of ClientWindowRenderMode.CLIENTWINDOW.
Finally add the portlet.xml from https://github.com/portletbridge/portletbridge/tree/master/examples/deltaspike-portlet to make it accessible as a portlet.