[ 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 comparison.
> 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:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
If you want more information on JIRA, or have a bug to report see:
   http://www.atlassian.com/software/jira


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to