Wednesday 30 March 2011

CDI Specializes

Introduction

CDI is what I call a modern specification / framework. The emphasis
is placed on a few concepts and their usage and not on a limited list of
functionalities that should be implemented and covered. And it is easy to
extend it or change a few internal implementations. In the specification,
there is explicitly noted that portable extensions should be possible. The
most known extensions at this moment are CODI, maintained by the MyFaces
project, and Seam 3, the JBoss lead project.

And the specializes in the title could refer to this; a blog entry
about the additional functionalities that can be found in CODI and Seam 3
on top of the standard. Because, once you are familiar with the concepts,
you have a complete new set of tools at hand to handle dependencies and
managed beans in a type safe way. And those two projects have defined for
you some very common additions that you will use very often.

But the goal of this text is different. I'll try to explain you one
of the concepts in CDI offered by the annotation
javax.enterprise.inject.Specializes. The goodies of those CDI extensions
might be a subject of another entry. In the mean time, you can read their
documentation on how to use the provided functionality.


CODI messages

I would like to explain the purpose of @Specializes based on a
example, that makes it probably a bit easier. If you look in the
specification document, you find various paragraphs about the
@Specializes annotations. But they are sometimes very 'scientific' like
'bean Z exists, such that X directly specializes Z and Z specializes
Y'

As an example, I would like to take the messages functionality
that you can find within CODI. The idea of it is that you can have a
message module that you can use within JSF and Java SE (if you use it in
that environment) So there must be a decoupling of the JSF
infrastructure. For that purpose they have created various resolvers
(MessageResolver, LocaleResolver, etc ..) and a fluent API to create and
add message to the context. A short example to show the usage in a JSF
environment:

@RequestScoped
@Named
public class LoggedIn extends AbstractAccessDecisionVoter
{

    @Inject @Jsf
    private MessageContext messageContext;

    public void addTestMsg(ActionEvent actionEvent) {
        messageContext.message().text("{msgKey}").add();
    }
}

When the method addTestMsg is executed, for instance linked to a
button on the screen, the message with the key 'msgKey', is looked up in
the properties resource bundle and is placed as Faces Message with
severity Info on the screen. The properties resource bundle that is used
to lookup the message key is taken from the faces configuration file
message-bundle tag.

There is also support for parameters, payloads to change for
instance the severity of the message, retrieving the text message
(without adding it to the context) and so on. For a complete list of
functionalities, you can look at the CODI wiki site for the message
module (is listed under java-se support)

MessageContext Specializes

The provided functionality is sufficient for some projects, but some
requirements are missing for more special cases. But as with CDI itself,
CODI is also made with extensibility in mind. So, let see how we can
achieve the following requirement.

In case there is a message with severity Fatal displayed on the
screen, it needs to be persisted in the database to keep track of the
'infrastructure' problems.

Suppose you have developed an application that, in case of
infrastructure problems (database or other remote resource not available,
unexpected exceptions, etc) shows a JSF FacesMessage of type Fatal. It
could be that, although the functionality to the user is severely
impacted; you don't always get a feedback of him. So you decide to log
those incidents, so that you know when and in the case you can assemble
all the events that lead to the problem, why those failures
happened.

And browsing through the documentation, you see that the CODI
message module uses a MessageHandler. This handler receives the messages
and processes them. They are first handed over to a filter (interface
MessageFilter), that can decide that the message should or shouldn't be
treated and then, in case of JSF, added to the JSF message Context. So a
new MessageHandler with a MessageFilter that keeps only the messages of
type fatal is what we need.

The beans for use in the injection points that need a
MessageContext, like in the example above, are created by a producer
method (those methods have a @Produces annotation). The return value of a
method is used to inject into the property. Within extensions, a lot of
CDI artifacts are generated through producer methods because you can apply
complex functionality to the produced bean. You can probably use the 'find
Usage' of your IDE to find those methods that return, in our case an
instance of the MessageContext class.

Now you have found the location where the MessageContext is
produced. But how should you proceed now?

You can create your own method producers, but in that case you have
to use another qualifier. Within CDI the combination class - Qualifier
(maybe a bit simplified, but easy to understand) must be unique. So if you
create a producer method which is annotated with the same qualifier @Jsf,
CDI will complain during startup. There are 2 places that creates the same
bean, which one should he take? But having another qualifier is not a good
solution. Developers can inject the 'wrong' version and what if you decide
in the middle of the development phase to add this new functionality. Are
you sure that you didn't miss a usage somewhere?

And this is the moment where the Specializes annotation comes into
the picture. You can use it to tag a CDI bean as a more specialized
version of the default one. CDI will ignore the default version and use
your specialized one.

The following class is the solution to our problem.

@Specializes
public class CustomMessageProducer extends JsfAwareMessageContextProducer
{

    @Inject
    private FatalMessageHandler fatalMessageHandler;

    @Override
    public MessageContext createContext(MessageContext defaultMessageContext,
                                        Instance<MessageFactory> messageFactoryInstance,
                                        Instance<ELProvider> elProviderInstance,
                                        Instance<ArgumentFilter> argumentFilterInstance)
    {
        MessageContext result = super.createContext(defaultMessageContext, messageFactoryInstance,
            elProviderInstance, argumentFilterInstance);
        return result.config().use().addMessageHandler(fatalMessageHandler).create();
    }
}

With the Specializes annotation on the class we tell CDI to use this
class instead of the parent. Therefore, when a MessageContext is requested
for injection, the overridden method createContext is called. First we get
the default behaviors by calling the super method, then we add our
FatalMessageHandler. We don't use the new keyword to instantiate the
object FatalMessageHandler. Since every class is automatically a CDI bean,
you can inject any 'bean' (even a class without any annotations). In the
test that I made for this entry, the MessageHandler does just a
System.out. The real version in the project could then access the database
to write the problem, with some injected service (CDI or even EJB) that
can access those resources.

Conclusion

With the @Specializes annotation, you can override the default
implementation with your own version. It is a specialized version of the
original, where you keep the qualifiers, name and so on. It gives you the
ability to customize the default implementation to you needs. As long as
the default is implemented with this kind of extensibility in mind.

No comments:

Post a Comment