Overview
Other MyFaces Extensions
- ExtVal
- Ext-Script
- [Orchestra]
- [Portlet Bridge]
Community
Development
Sponsorship
Your browser does not support iframes
The Intro page provides an overview and describes the motivation for the features described below.
The page CODI Modules provides an overview about CODI modules and how to add them to your project.
For using the features described in this page, you have to add the core, the message module and the JSF module for the JSF version you are using.
First of all it's important to mention that CODI starts (grouped) conversations automatically as soon as you access conversation scoped beans. Furthermore, the invocation of Conversation#end
* leads to an immediate termination of the conversation.
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ConversationGroup; @ConversationScoped public class DemoBean1 implements Serializable { //... }
... leads to a conversation which contains just one bean with the group DemoBean1.
(In case of CDI std. conversations there is just one big conversation which contains all conversation scoped beans.)
The grouped conversations provided by CODI are completely different. By default every conversation scoped bean exists in an "isolated" conversation. That means there are several parallel conversations within the same window.
Example:
@ConversationScoped public class DemoBean2 implements Serializable { //... } @ConversationScoped public class DemoBean3 implements Serializable { //... }
... leads to two independent conversations in the same window (context).
If you end the conversation of DemoBean2
, the conversation of DemoBean3
is still active.
If you have an use-case (e.g. a wizard) which uses multiple beans which are linked together very tightly, you can create a type-safe conversation group.
interface Wizard1 {} @ConversationScoped @ConversationGroup(Wizard1.class) public class DemoBean4 implements Serializable { //... } @ConversationScoped @ConversationGroup(Wizard1.class) public class DemoBean5 implements Serializable { //... }
You can use @ConversationGroup
to tell CODI that there is a logical group of beans. Technically @ConversationGroup
is just a CDI qualifier. Internally CODI uses this information to identify a conversation. In the previous example both beans exist in the same conversation (group). If you terminate the conversation group, both beans will be destroyed. If you don't use @ConversationGroup
explicitly, CODI uses the class of the bean as conversation group.
//... public class CustomBean1 { @Inject @ConversationGroup(Group1.class) private CustomBean2 demoBean; @Inject @ConversationGroup(Group2.class) private CustomBean2 demoBean; }
Since @ConversationGroup
is a std. CDI qualifier you have to use it at the injection point. You have to do that esp. because it's possible to create beans of the same type which exist in different groups (e.g. via producer methods).
Example:
interface Group1 {} interface Group2 {} public class CustomBean2 { @Produces @ConversationScoped @ConversationGroup(Group1.class) public CustomBean2 createInstanceForGroup1() { return new CustomBean2(); } @Produces @ConversationScoped @ConversationGroup(Group2.class) public CustomBean2 createInstanceForGroup2() { return new CustomBean2(); } }
You can inject the conversation via @Inject
and use it to terminate the conversation immediately (see *) or you inject the current WindowContext
which can be used to terminate a given conversation group.
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ConversationScoped; import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.Conversation; @ConversationScoped public class DemoBean6 implements Serializable { @Inject private Conversation conversation; //injects the conversation of DemoBean6 (!= conversation of DemoBean7) //... public void finish() { this.conversation.end(); } } @ConversationScoped public class DemoBean7 implements Serializable { @Inject private Conversation conversation; //injects the conversation of DemoBean7 (!= conversation of DemoBean6) //... public void finish() { this.conversation.end(); } }
interface Wizard2 {} @ConversationScoped @ConversationGroup(Wizard2.class) public class DemoBean8 implements Serializable { @Inject private Conversation conversation; //injects the conversation of Wizard2 (contains DemoBean8 and DemoBean9) //... public void finish() { this.conversation.end(); } } @ConversationScoped @ConversationGroup(Wizard2.class) public class DemoBean9 implements Serializable { @Inject private Conversation conversation; //injects the conversation of Wizard2 (contains DemoBean8 and DemoBean9) //... public void finish() { this.conversation.end(); } }
//... public class DemoBean10 implements Serializable { @Inject private WindowContext windowContext; //injects the whole window context (of the current window) //... public void finish() { this.windowContext.endConversationGroup(Wizard2.class); //ends the conversation of group Wizard2.class } }
//... public class DemoBean11 implements Serializable { @Inject private WindowContext windowContext; //... public void finish() { this.windowContext.endConversations(); //ends all existing conversations within the current window (context) } }
Hint
Since the View-Access scope is just a different kind of a conversation #endConversations
also terminates all view-access scoped beans.
There will be a SPI to customize this behavior. Usually you will need #endConversations
e.g. if the user triggers a navigation via the main-menu and in such a case you usually exit the current use-case. So it makes sense that all kinds of conversations will be ended.
Hint
* Please note that all methods which starts with #end might change until alpha1 - see EXTCDI-57
Instead of destroying the whole conversation the conversation stays active and only the scoped instances are destroyed. (The conversation will be marked as accessed.) As soon as an instance of a bean is requested, the instance will be created based on the original bean descriptor. This approach allows a better performance, if the conversation is needed immediately (e.g. if you know in your action method that the next page will/might use the same conversation again).
@ConversationScoped public class DemoBean12 implements Serializable { @Inject private Conversation conversation; //... public void finish() { this.conversation.restart(); } }
Hint
Compared to std. CDI conversations CODI provides completely different conversation concepts. "Just the name is the same."
So please don't try to use the same implementation patterns which you might have learned for std. conversations.
CDI conversations are comparable to MyFaces Orchestra conversations.
In case of conversations you have to un-scope beans manually (or they we be terminated automatically after a timeout). However, sometimes you need beans with a lifetime which is as long as needed and as short as possible - which are terminated automatically (as soon as possible). In such an use-case you can use this scope. The simple rule is, as long as the bean is referenced by a page - the bean will be available for the next page (if it's used again the bean will be forwarded again). It is important that it's based on the view-id of a page (it isn't based on the request) so e.g. Ajax requests don't trigger a cleanup if the request doesn't access all view-access scoped beans of the page. That's also the reason for the name @*View*AccessScoped.
@ViewAccessScoped public class WizardBean implements Serializable { //... }
The usage of this scope is very similar to normal conversations. Only the cleanup strategy is different and the concept itself doesn't need/support the usage of @ConversationGroup
.
The usage of this scope is very similar to normal conversations. Only the cleanup strategy is different and the concept itself doesn't need/support the usage of @ConversationGroup
.
@WindowScoped public class PreferencesBean implements Serializable { //... }
Since WindowContext#endConversations
doesn't affect window scoped beans we need a special API for terminating all window scoped beans.
If you don't use qualifiers for your window scoped beans, you can just inject the conversation into a window scoped bean and invoke the methods discussed above. If you don't have this constellation, you can use the WindowContext
to terminate the window scoped beans or the whole window context. If you terminate the whole window, you also destroy all conversation and view-access scoped beans automatically.
//... public class CustomWindowControllerBean1 { @Inject private WindowContext windowContext; //... public void closeWindow() { this.windowContext.end(); } }
//... public class CustomWindowControllerBean2 { @Inject private WindowContext windowContext; //... public void finish() { this.windowContext.endConversationGroup(WindowScoped.class); } }
CODI allows using @Inject within the following JSF (1.2 and 2.0) artifacts:
As soon as a converter or a validator is annotated with @Advanced it's possible to use @Inject.
Example for a validator:
@Advanced //@FacesValidator("...") //in case of JSF 2.0 public class DependencyInjectionAwareValidator implements Validator { @Inject private CustomValidationService customValidationService; public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException { Violation violation = this.customValidationService.validate(value); //... } }
Example for a converter:
@Advanced //@FacesConverter("...") //in case of JSF 2.0 public class DependencyInjectionAwareConverter implements Converter { @Inject private OrderService orderService; public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) throws ConverterException { if (value != null && value.length() > 0) { return this.orderService.loadByOrderNumber(value); } return null; } //... }
As an alternative to a full phase-listener CODI allows to use observers as phase-listener methods.
Example:
protected void observePreRenderView(@Observes @BeforePhase(PhaseId.RENDER_RESPONSE) PhaseEvent phaseEvent) { //... }
If you would like to restrict the invocation to a specific view, it's possible to use the optional @View
annotation.
@View(DemoPage.class) public void observePostInvokeApplication(@Observes @AfterPhase(PhaseId.INVOKE_APPLICATION) PhaseEvent event) { //... }
For further details about DemoPage.class
please have a look at the view-config section.
Hint
@View
is an interceptor. The disadvantage is that intercepted beans introduce an overhead.
If you would like to use this mechanism for implementing pre-render view logic, you should think about using the @PageBean
annotation.
Further details are available in the view-config section.
CODI provides an annotation for phase-listeners:
@JsfPhaseListener public class DebugPhaseListener implements PhaseListener { private static final Log LOG = LogFactory.getLog(DebugPhaseListener.class); private static final long serialVersionUID = -3128296286005877801L; public void beforePhase(PhaseEvent phaseEvent) { if(LOG.isDebugEnabled()) { LOG.debug("before: " + phaseEvent.getPhaseId()); } } public void afterPhase(PhaseEvent phaseEvent) { if(LOG.isDebugEnabled()) { LOG.debug("after: " + phaseEvent.getPhaseId()); } } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
If you have to specify the order of phase-listeners you can us the optional @InvocationOrder
annotation.
In combination with @Advanced
it's possible to use dependency injection.
Example:
@Advanced @JsfPhaseListener @InvocationOrder(1) //optional public class DebugPhaseListener implements PhaseListener { @Inject private DebugService debugService; public void beforePhase(PhaseEvent phaseEvent) { this.debugService.log(phaseEvent); } public void afterPhase(PhaseEvent phaseEvent) { this.debugService.log(phaseEvent); } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
Why @JsfPhaseListener instead of @PhaseListener?
It's easier to use the annotation because there isn't an overlap with the name of the interface. So it isn't required to use the fully qualified name for one of them.
Sometimes it's essential to perform logic directly after the FacesContext
started and/or directly before the FacesContext
gets destroyed. In such a case CODI provides the annotations @BeforeFacesRequest
and @AfterFacesRequest
.
Example:
protected void initFacesRequest(@Observes @BeforeFacesRequest FacesContext facesContext) { //... }
CODI offers a bunch of producers for JSF artifacts.
Example:
@Inject private FacesContext facesContext;