Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Table of Contents

Table of Contents
indent10px
styledisc
printablefalse

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

Code Block
titleCreating messages with inline-text
borderStylesolid
Message message = messageContext.message().text("Hello MyFaces").create();

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

Example 2

Code Block
titleCreating messages with a key
borderStylesolid
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

Code Block
titleCreating messages with arguments
borderStylesolid
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

Code Block
titleCreating messages with named arguments
borderStylesolid
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

Code Block
titleCreating messages with payload
borderStylesolid
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

Code Block
titleCreating the message-text (immediately)
borderStylesolid
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

Code Block
titleCreating the message-text of a given message
borderStylesolid
//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

Code Block
titleCreating the message-text (immediately)
borderStylesolid
messageContext.message().text("Hello MyFaces").add();

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

Example 2

Code Block
titleCreating the message-text of a given message
borderStylesolid
//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 ).

Note
titleAttention!

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:

Code Block
titleCreating custom message types via a custom message-builder
borderStylesolid
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);
Code Block
titleImplementation of a custom message-builder
borderStylesolid
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.

Code Block
titleHandling different message types
borderStylesolid
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.

Code Block
titleProvider of the current business client
borderStylesolid
    @Produces
    @RequestScoped
    public Client getCurrentClient(ClientService clientService)
    {
        return clientService.loadClient(this.currentClientId);
    }
Code Block
titleBusiness client specific messages
borderStylesolid
    @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.

Code Block
titleInjecting the business client aware MessageContext
borderStylesolid
    @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.

Code Block
titleExpose business client aware messages to XHTML pages
borderStylesolid
@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();
    }
}
Code Block
titleUsage in XHTML pages
borderStylesolid
#{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 .

Code Block
titleCreating a message manually
borderStylesolid
Message message = new DefaultMessage("inline message");