portals-jetspeed-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Ate Douma (JIRA)" <jetspeed-...@jakarta.apache.org>
Subject [jira] Closed: (JS2-219) A new message definition and automatic translation facility: KeyedMessage.
Date Mon, 07 Mar 2005 14:18:52 GMT
     [ http://issues.apache.org/jira/browse/JS2-219?page=history ]
Ate Douma closed JS2-219:

    Resolution: Fixed

> A new message definition and automatic translation facility: KeyedMessage.
> --------------------------------------------------------------------------
>          Key: JS2-219
>          URL: http://issues.apache.org/jira/browse/JS2-219
>      Project: Jetspeed 2
>         Type: Improvement
>   Components: i18n and l10n
>     Versions: 2.0-dev/cvs
>     Reporter: Ate Douma
>     Assignee: Ate Douma
>      Fix For: 2.0-dev/cvs, 2.0-M2

> I've been working on enhancements of the Login Portlet to provide better feedback to
the user about login errors.
> Because the exceptions thrown by the Security layer currently use statically defined
Exception messages to distinguish the different conditions, checking which type of error occurred
is very cumbersome to do. Furthermore, in some situations the statically defined messages
> are only used as templates (.e.g. a specific User/Role/Group name is postfixed) making
it even harder to compare the exceptions.
> I've come up with a generic, transparent and very easy to use, i18n supporting, message
definition and automatic translation facility which also provides named constant definitions
(phew, heavy definition).
> For this I created a new class org.apache.jetspeed.i18n.KeyedMessage.
> As I've provided extensive documentation through javadoc I'll provide the class level
description below to explain its purpose and usage including a real ;-) example.
> I've used this KeyedMessage to replace all SecurityException constant String messages
with a KeyedMessage variant. 
> Furthermore, I've enhanced JetspeedException (and JetspeedRuntimeException) to recognize
Exceptions constructed using a KeyMessage, and provided a org.apache.jetspeed.exception.JetspeedExceptionMessages.properties
resource bundle (as well as a Dutch variant) containing SecurityException translations.
> After I've committed these changes, the benefit of this new facility can be easily tested
as follows:
> - login as admin
> - switch the locale to Dutch using the Locale Portlet
> - go to the UserBrowserPortlet
> - select the guest user
> - try to delete the guest user
> - You will see a Dutch error message saying: "De gebruiker guest is beveiligd."
>   (Previously, you would have been presented with: "The anonymous user is protected.")
> I didn't have to change anything in the UserBrowserPorlet or UserDetailPorlet for this
to work.
> A copy of the class level documentation of org.apache.jetspeed.i18n.KeyedMessage follows:
> =========================================================================================
> public class KeyedMessage extends java.lang.Object implements java.io.Serializable
> KeyedMessage provides an automatically derived i18n message key based on its static instance
definition and can be used as comparable constant too.
> Purpose
> -------
> With a KeyedMessage a named constant message (format) can be statically defined which
automatically translate themselves for a specific locale using an automatically derived ResourceBundle
or even a specified one.
> Key derivation
> --------------
> Because KeyedMessages are created with a default message (format), even if no ResourceBundle
or its key is defined or can't be found, message translation is still possible.
> A KeyedMessage automatically derives the ResourceBundle lookup key from its (statically
defined) instance field name using the following format:
>   <containingClass.name>.<staticInstanceField.name>
> The containingClass is derived at construction time by analyzing the StackTraceElements
of a thrown exception. This requires the instance to be defined as a public static field!
> At first access, the key is resolved by inspecting the derived containingClass for the
declared field defining this instance.
> If the KeyedMessage instance wasn't defined as public static field, the key can't be
resolved and message translation using a ResourceBundle won't be possible. Translation using
the default message will still work though. Furthermore, this instance can't be used as comparable
named constant as the equals(Object)method will always return false in this case.
> Default ResourceBundle name derivation
> --------------------------------------
> When the key of a KeyedMessage is resolved, the default ResourceBundle name for message
translation is retrieved from the defined public static String field named "KEYED_MESSAGE_BUNDLE"defined
in its containingClass or one of its superClasses or interfaces.
> If this field cannot be found, the fully qualified name of the containingClass is used.
> ResourceBundle names are cached in a Map for each containingClass and only derived for
the first KeyedMessage defined in a containingClass.
> Again: only resolved instances can use a ResourceBundle for message translation.
> Default Locale lookup
> ---------------------
> When a message is translated without a specified Locale, CurrentLocale.get()is used to
determine the default Locale for the current Thread.
> In Jetspeed, the LocalizationValve initializes the CurrentLocale on each request. KeyedMessages
accessed within the context of an Jetspeed request therefore will always be translated using
the current user Locale with the getMessage()or toString()methods.
> Default ResourceBundle lookup
> If a message translation is done using the default ResourceBundle name the ResourceBundle
is retrieved using the ClassLoader of the containingClass. This means the bundle(s) must be
provided in the same context as from where the containingClass is loaded. Usually (and preferably),
this will be from the shared classpath of the webserver.
> MessageFormat parameters
> ------------------------
> MessageFormat patterns can also be used for a KeyedMessage.
> With the create(Object[])method a specialized copy of a KeyedMessage instance can be
created containing the arguments to be used during message translation.
> This new copy remains equals(Object)to its source and can still be used for named constant
> For simplified usage, three create(Object),create(Object, Object)and create(Object, Object,
Object)methods are provided which delegate to create(Object[])with their argument(s) transformed
into an Object array.
> Extending KeyedMessage
> ----------------------
> An statically defined KeyedMessage can be used as a "simple" named constant.
> If additional metadata is required like some kind of status, level or type indication,
the KeyedMessage class can easily be extended by providing a specialized version of the create(KeyedMessage,
Object[])copy factory.
> Usage
> -----
> KeyedMessage has been used to replace the hardcoded SecurityException String constants.
> The ResourceBundle name used is defined by JetspeedException.KEYED_MESSAGE_BUNDLE which
is the superClass of SecurityException.
> For a different ResourceBundle to be used for SecurityException messages a KEYED_MESSAGE_BUNDLE
field can be defined in SecurityException too, overriding the one in JetspeedException.
> Example:
>        public class JetspeedException extends Exception {
>            public static final String KEYED_MESSAGE_BUNDLE = "org.apache.jetspeed.exception.JetspeedExceptionMessages";
>            ...
>            public String getMessage() {
>                 if ( keyedMessage != null ) {
>                    return keyedMessage.getMessage(); // translated using current Locale
and default ResourceBundle
>                 }
>                 return super.getMessage();
>            }
>        }
>        public class SecurityException extends JetspeedException {
>            public static final KeyedMessage USER_DOES_NOT_EXIST = new KeyedMessage("The
user {0} does not exist.");
>            ...
>        }
>        // resource file: org.apache.jetspeed.exception.JetspeedExceptionMessages_nl.properties
>        org.apache.jetspeed.security.SecurityException.USER_DOES_NOT_EXIST = De gebruiker
{0} bestaat niet.
>        ...
>        public class UserManagerImpl implements UserManager {
>            public User getUser(String username) throws SecurityException {
>                ...
>                if (null == userPrincipal) { 
>                    throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(username));
>                }
>                ...
>            }
>            ...
>        }
>        // example get User
>        try {
>            User user = userManager.getUser(userName);
>        } catch (SecurityException sex) {
>            if ( SecurityException.USER_DOES_NOT_EXISTS.equals(sex.getKeyedMessage())
>                // handle USER_DOES_NOT_EXISTS error
>            }
>        }

This message is automatically generated by JIRA.
If you think it was sent incorrectly contact one of the administrators:
If you want more information on JIRA, or have a bug to report see:

To unsubscribe, e-mail: jetspeed-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jetspeed-dev-help@jakarta.apache.org

View raw message