Table of Contents

MessageContext

The MessageContext allows using a fluent API for

  • Creating messages
  • Creating message text
  • Processing messages
  • Changing the configuration of the current context
  • Using the current context as template for a new context

Furthermore, the context provides the current locale.

Message

The default implementation provides

  • Descriptor (Key or inline-message)
  • Arguments
    • Numbered arguments
    • Named arguments
  • Payload

The Message interface extends Localizable . Therefore it's possible to use a MessageContext to create the final message text based on the message and the current configuration of the MessageContext .

Creating messages

Instead of instantiating messages manually, the fluent API allows to create messages with (named-)arguments and message payload.

Example 1

Creating messages with inline-text
Message message = messageContext.message().text("Hello MyFaces").create();

... creates a message with the hardcoded text 'Hello MyFaces' as message descriptor.

Example 2

Creating messages with a key
Message message = messageContext.message().text("msgKey").create();

... creates a message with the key 'msgKey' as message descriptor.
(The concrete syntax of a key depends on the used MessageResolver. Further details are available in the DevDoc.)

Example 3

Creating messages with arguments
Message message = messageContext.message().text("Hello {0}").argument("MyFaces").create();

... creates a message with 'Hello {0}' as message descriptor and 'MyFaces' as argument.
An argument has to be Serializable .
(By default numbered arguments are formatted by MessageFormat based on the current locale.)

Example 4

Creating messages with named arguments
Message message = messageContext.message().text("Hello {name}").namedArgument("name", "MyFaces").create();

... creates a message with 'Hello {name}' as message descriptor and 'MyFaces' as argument.
Values of named arguments are formatted by Formatter s created by the FormatterFactory

Example 5

Creating messages with payload
Message message = messageContext.message().text("Hello CODI").payload(MessageSeverity.ERROR).create();

... creates a message with 'Hello CODI' as message descriptor and error as message-severity.
Payload is similar to the validation payload of Bean-Validation. Via payload it's possible to provide additional information via a typesafe mechanism. It's possible to use the provided types of payload ( MessageSeverity or InternalMessage ) or to create custom payload like Label or to create something like serializable attatchments. It's possible to handle messages depending on the payload of the message. By default a message is created with MessageSeverity.INFO .

Creating message text

Example 1

Creating the message-text (immediately)
String messageText = messageContext.message().text("msgKey").toText();

... creates the message-text based on the constructed message.

To avoid side-effects with other libs as well as IDEs there is no #toString().

Example 2

Creating the message-text of a given message
//Message message = messageContext.message().text("Hello {name}").namedArgument("name", "MyFaces").create();
String messageText = message.toString(messageContext);

... creates the message-text based on the given message and MessageContext . That's essential if you would like to create the final text with a different/new MessageContext e.g. with a different configuration or with the current MessageContext if you receive a de-serialized message instance.

Processing messages

It's possible to register MessageHandler s which process or forward messages added to the MessageContext .

E.g. a MessageHandler for JSF would convert messages to FacesMessage s and add them to the current FacesContext .

Example 1

Creating the message-text (immediately)
messageContext.message().text("Hello MyFaces").add();

... adds the constructed message to the MessageHandler (s) of the current MessageContext .

Example 2

Creating the message-text of a given message
//Message message = messageContext.message().text("Hello {name}").namedArgument("name", "MyFaces").create();
messageContext.addMessage(message);

... adds the given message to the MessageHandler (s) of the current MessageContext .

Message Builder

The MessageContext uses a (default) MessageBuilder to create Message s via a fluent API.
If you don't have access to the MessageContext it's possible to use a stand-alone MessageBuilder (SimpleMessageBuilder ).

Attention!

Keep in mind that a stand-alone MessageBuilder is able to create Message objects, but it needs a MessageContext to create the final text or to add the message to the context.

Without direct access to the MessageContext you can use a MessageBuilder to create a message via:
Message message = SimpleMessageBuilder.message().text("msgKey").create();
As soon as you have a MessageContext you have access to the final message-text via:
message.toString(messageContext);
And/or you can add it to the context via:
messageContext.addMessage(message);

Advanced Use-Cases

Custom Message Types

A custom MessageBuilder also allows to introduce custom message types easily:

Creating custom message types via a custom message-builder
Message label = CustomMessageBuilder.label().text("myMsgKey").create();
Message technicalMessage = CustomMessageBuilder.technicalMessage().text("myMsgKey").create();

//the context needs a message-resolver which knows how to handle the different message types.
label.toString(messageContext);
technicalMessage.toString(messageContext);
Implementation of a custom message-builder
public class CustomMessageBuilder extends SimpleMessageBuilder
{
    private CustomMessageBuilder ()
    {
    }

    public static MessageBuilder message()
    {
        return new CustomMessageBuilder();
    }

    public static MessageBuilder technicalMessage()
    {
        return new CustomMessageBuilder().payload(TechnicalMessage.PAYLOAD);
    }

    public static MessageBuilder label()
    {
        return new CustomMessageBuilder().payload(Label.PAYLOAD);
    }
}

A custom MessageResolver has to know how to handle the different message types. If a payload type just allows one possible value (as marker), we can use the class of the payload to check if the payload-map contains such a marker entry.

Handling different message types
public class TestMessageResolver implements MessageResolver
{
    public String getMessage(String key, Locale locale, Map<Class, MessagePayload> messagePayload)
    {
        if (!isKey(key))
        {
            return key;
        }

        try
        {
            key = extractKey(key);

            if(messagePayload.containsKey(Label.class))
            {
                return ResourceBundle.getBundle(TEST_LABELS, locale, getClassLoader()).getString(key);
            }
            else if(messagePayload.containsKey(TechnicalMessage.class))
            {
                return ResourceBundle.getBundle(TEST_TECHNICAL_MESSAGES, locale, getClassLoader()).getString(key);
            }
            return ResourceBundle.getBundle(TEST_MESSAGES, locale, getClassLoader()).getString(key);
        }
        catch (MissingResourceException e)
        {
            return key;
        }
    }
    //The full example is available in the code-base (see: TestMessageResolver)
}

Business client specific messages

As soon as an application has to support different business clients, it's required to display messages which are specific to each business client. The message module allows to use one MessageResolver per client. So it's possible to use the same message-keys for all clients and the concrete MessageResolver provides the correct message.

In combination with CDI a possible implementation is illustrated in the following listings.

Provider of the current business client
    @Produces
    @RequestScoped
    public Client getCurrentClient(ClientService clientService)
    {
        return clientService.loadClient(this.currentClientId);
    }
Business client specific messages
    @Produces
    @RequestScoped
    @ClientQualifier
    public MessageContext createClientAwareContext(@Jsf MessageContext messageContext,
                                                   Client client)
    {
        MessageResolver clientAwareMessageResolver = createMessageResolverForClient(client.getId());

        return messageContext.config().use().messageResolver(clientAwareMessageResolver).create();
    }

    private MessageResolver createMessageResolverForClient(final String currentClientId)
    {
        return new MessageResolver()
        {
            public String getMessage(String messageDescriptor,
                                     Locale locale,
                                     Map<Class, MessagePayload> messagePayload)
            {
                FacesContext facesContext = FacesContext.getCurrentInstance();

                try
                {
                    return facesContext.getApplication()
                            .getResourceBundle(facesContext, currentClientId).getString(messageDescriptor);
                }
                catch (MissingResourceException e)
                {
                    return "???" + messageDescriptor + "???";
                }
            }
        };
    }

This example uses the existing message-context as template and creates a new context with a different message resolver.
In this example the client-id is used as resource-bundle name.

Injecting the business client aware MessageContext
    @Inject
    @ClientQualifier
    private MessageContext clientAwareMessageContext;

To use such messages in a XHTML page, it's required to provide a bean which in-/directly implements the Map interface.

Expose business client aware messages to XHTML pages
@Named("clientMessages")
@Singleton
public class ClientAwareMessages extends MapHelper<String, String>
{
    @Inject
    @ClientQualifier
    private MessageContext clientAwareMessageContext;

    protected String getValue(String key)
    {
        return this.clientAwareMessageContext.message().text(key).toText();
    }
}
Usage in XHTML pages
#{clientMessages.sample_message}

The full example is available in the current MyFaces CODI example application.

Manual Usage

If you don't like the fluent API you can create your own message (e.g. based on DefaultMessage ) or you can just instantiate DefaultMessage .

Creating a message manually
Message message = new DefaultMessage("inline message");
  • No labels