Skip to end of metadata
Go to start of metadata

Intent

  1. Allow for external message repositories to be created for serving KRAD messages
  2. Allow for dictionary messages (defined in requirements) to be overridden with an external message
  3. Allow for messages currently specified in a resource file (errors, warnings) or JavaScript to be in an external repository
  4. Deliver property file message repository and database message repository as default options
  5. Cleanup of configuration service and other KRAD property resource infrastructure (from struts to Spring)

Requirements: KRAD - Messages from Central Repository

 

Scope

Icon

The intention of this enhancement is to externalize messages only. At this point, Rice will not provide functionality for managing (add, edit, delete) messages in an external repository (for example an UI).

Design

Participants

Message (org.kuali.rice.krad.messages.Message)

A message object will represent a message string within KRAD and the basic artifact stored in external message repositories. A message has the following properties:

Namespace: The namespace the message belongs to (same as in KIM). Ex. 'KFS-FP', 'KS-COURSE'

Component Code: A component or group the message belongs to. A further grouping of messages within a namespace. Ex. 'InternalBillingDocument', 'DocInfoGroup'

Key: A unique key for the message within the namespace and group. Ex. 'documentNumber.label', 'MissingTitle'

Text: The text for the message. Ex. 'Document Number', 'Title must not be empty"

Description: A description of the messages. Not required but in place for future interfaces

Locale: An identifier representing the language for the message

Note the combination of namespace, component code, and key is needed to uniquely identify a message.

 

Message Service (org.kuali.rice.krad.messages.MessageService)

The message service provides read access to one or more external message repositories. It will be used by KRAD and other modules to acquire the text for a message. The following methods will be provided:

The default message service implementation will delegate to configured Message Providers (one or more may be configured).

Message providers can be enabled/disabled by removing the provider from the message service.

 

Message Provider

A message provider reads from the external message repository and returns representing Message objects. A Message Provider must implement the following methods:

To implement both of these methods, the message provider must support retrieving messages without the namespace and component. In these cases the provider can treat the namespace and component as empty, or a configured default.

 

Message Table Provider

The Messaging Table will be enabled by default as a message provider (out of the box Rice). This will store a message object in a database table.

NMSPC_CD

CMP_CD

KEY

TXT

DESC

LOC

"KFS-FP"

"InternalBilling"

"missingTitle"

"Title {0} is required"

 

"EN"

"KFS"

"All"

"missingDocNumber"

"Document number is required"

 

"EN"

"KFS-FP"

"IBDetailGroup.itemCode"

"Item Code Label"

"Item Code"

 

"EN"

"KFS-FP"

"IBView.instructions"

"IB Document Instructions"

"Please complete the IB document"

 

"EN"

When the namespace and/or component code is missing, the table provider will use a configured default.

 

Message Properties Provider

Message properties will also be enabled by default as a message provider. Here a message object will be stored as a key/value pair within a properties file.

Key Formation

The message key will be formed by placing the component code inside '@cmp{componetCode}'. If the component is not given, just the name will be used.

Ex.
Component: "InternalBilling", Key: "missingTitle"
Properties Key: "@cmp{InternalBilling}missingTitle"

Following standard property file bundle conventions, the locale information will come from the property file name. Furthermore, a namespace can be associated with a group of messages through the module configuration (see below).

Queries to the provider without a namespace and/or group will be treated as null (just the name will be used for matching keys).

 

Module Configuration
The KRAD Module Configuration will contain a property named resourceBundleNames which will hold the full classpath for the message bundle to load. This follows the convention for selecting the properties file based on locale:

http://docs.oracle.com/javase/6/docs/api/java/util/PropertyResourceBundle.html

Bundles from all configured KRAD modules will then be pulled in by the message properties provider.

Module Configuration

UIF Bean Post Processor

The UIF bean post processor (which currently processes expressions in bean definitions) will be enhanced to load external messages. See Collaborations for more details.

Collaborations

The following collaborations with the message service will take place:

  • Overriding of dictionary text
  • Pulling of error/info/warning messages
  • Framework messages given through JavaScript
  • Other framework messages added to the view (for example: window title prefix)
  • Custom application code

Overriding of Dictionary Text

An additional step will be added to the dictionary loading process to pull in messages from the message service. There is no configuration needed to enable this functionality. These messages can be given to populate property values for common objects defined in the dictionary, including:

  • Attribute Definition
  • UIF Components

Any value can be externalized. This includes labels, headers, messages, URLs, etc. 

Note: Values for properties that can be externalized may still have values in this XML (but not required). These values provide defaults. If an external message value is found, it will override the XML value, else the XML value will be used. This allows text in the dictionary to be overridden as needed, or completely configured externally.

Associating Messages with Property Values

1) After the bean definitions have been populated, the message service will be invoked to retrieve any messages for a bean. Messages will be associated with a bean by the component code. The component code can tie back to a bean in one of three ways:

  • Bean Id - If the bean definition has an id, then messages with component code equal to the bean id (within the same namespace) will be associated with the bean definition
  • Bean Message Component Code Property - All dictionary objects that can have external messages will have a new property added named componentCode. This is a property that can be set with a bean definition to specify the component code for messages that are associated with the bean
  • Nested Beans -  Some beans that are nested (and do not have an id) can have messages associated by specifying the component code as the top level bean id plus an identifier for the nested bean. This will mostly be used for data fields that have the property name attribute.

2) Once messages are retrieved for a bean, the key for the message is used to map to the specific property. One way the key can be specified is by using the property name itself (possibly nested), prefixed with '#'. 

Ex.

  • #label would map to the property named 'label'
  • #headerText would map to the property name 'headerText'
  • #layoutManager.sequenceFieldPrototype.label would map to the property 'layoutManager.sequenceFieldPrototype.label'

 

Example Dictionary Message Mappings Ex 1

Message Inheritence

Icon

Since the bean property values are being replaced by external messages, standard bean inheritance will apply. This means if you override the label for an attribute definition, all attribute definitions that extend will get the external message as well (unless they have overridden the property value in the child bean definition).

 

Using the key to identify the property has limitations. Even though it can reach nested properties, it cannot go through nested collections (list, map). The only exception to this is a few built in cases:

  1. One of those is when the collection is a list of data or input fields. In this case the framework allows you to identify the element of the collection using the property name of the data/input field. 
  2. Another case is when the collection contains KeyValue pairs. In this case you can reference the collection element using the key of the KeyValue pair.
  3. Finally when the collection contains Action component, you can reference the element by the methodToCall property.
  4. For other types you must give the nested component an id or set the component code property.

Ex.

  • #items.name.label would refer to the label property for the data/input field with property name 'name' inside the items property list
  • #items.account.label would refer to the label property for the data/input field with property name 'account' inside the items property list
  • #options.ba.value would refer to the value property for the KeyValue object with key 'ba' inside the options property list
  • #addLineActions.save.actionLabel would refer to the actionLabel property of the Action component with methodToCall equal to 'save' within the addLineActions property list
Example Dictionary Message Mappings Ex 2

Explicit Message Association (By Name)

You may also explicitly reference a message within a value. This allows you to partially complete a value or combine multiple resources.

This is given in the following format:

value="@msg{namespace:component:key}"

Note, the external message does not have to exist. This is just to allow the external message to be given. Also the namespace and component values are optional. If not given they will be taken from the parent component of the property.

Handling Messages with Expressions

Property values may contain a mix of static text with expressions. Thus we want to be able to externalize parts of the message that are static, but keep the expressions.

Sample Expression Value

In these cases the external message can be specified with placeholders. These placeholders represent the positions of the expressions (similar to variable replacement in validation messages).

Sample Messages with Expression Placeholders

Priority for applying Messages

With the above approaches it is possible a message can be matched to a property in more than one ways (thus multiple messages may apply to the same property). When this occurs the priority for messages is:

  1. Any message that matches an explicitly given message (by name)
  2. Any message that matches an explicitly given message group code, and whose message type matches the property
  3. Any message that matches the bean id, and whose message type matches the property

Note in 2 and 3, the message name is not used at all in matching. Therefore this can be set to any text.

Validation Messages

Validation messaging will continue to behave as it does currently with two exceptions:

1) Resolving message text will be done through Message service instead of configuration service (ValidationMessages component)

2) Namespace and group code will be added to ErrorMessage class and setters added to MessageMap. This will allow using validation messages tied to a namespace and/or group.

 

JavaScript Messages

Text for messages given through JavaScript will be pulled from the message service and exposed through a JS variable. Considerations are:

  • Not having to send back all the messages to the client each time (could be many with the validation framework)
  • Not having the script make a callback each time a message is needed

KRAD provides a utility method named getMessage(key, namespace, componentCode) that can be used to retrieve a message from the message service in JavaScript. The first time a message is requested a server request will be made to pull the value. It will then be cached in the user's session using sessionStorage:

http://www.html5rocks.com/en/tutorials/offline/storage/

Note the namespace and componentCode arguments are optional

Default Locale

KRAD currently doesn't support switching locales at runtime (although the MessageService itself can take the locale parameter). 

The locale used is given by the system parameter with namespace: 'KR-NS', component: 'All', and name: 'DEFAULT_LOCALE_CODE'.

Other Tasks

Cleanup of Configuration Service

ConfigurationService currently uses KualiPropertyMessageResourcesFactory which extends a struts class and load resources from property 'rice.struts.message.resources'

  • Replace with Spring loading
  • Rename configuration parameter

Initial Message Load

One difficulty here is the initial loading of the external repository from the current XML text. Without examples, it would be difficult to know what all can be overridden.

To help with this we could write a utility that writes out messages keys and values based on the current XML. We will have all the logic in place, so likely would only require throwing a main in front to load the dictionary (and current configuration service) then add some sort of logging or printing to the post processor.

This would allow us to deliver one of the external repository options with all of the current Rice text. Apps could use this as well to pull new text from XML.

Constraint labelKey Property

For convenience, this property will remain in place but be renamed to messageName. Message namespace and group code will be added as well to specify a message not in the default namespace/group.

Issues

1) Do we need to deliver the property file message repo out of the box or just the table? Will anyone use resources now?

2) When namespace and/or group is not given, should we use a configured default or have a standard default (like 'Kuali', 'All')? Could cause problems if Rice has some messages (in KRAD) using a default which is bootstrapped as 'KR', 'All', but then the apps like the KFS configures the default as 'KFS', 'All'. 

3) Should we add description to this message object? Might be helpful for cases where the message contains placeholders (to explain what those placeholders will be filled with)

4) Should the message table be a Rice server or client table? Similarly what about the MessageService (if resources are involved it would need to be local)? 

5) Loading messages for the dictionary during bean post processing means they will be loaded at application startup only. So changes to the messages will require an application restart. Likewise for the proposed JS solution. This might be an issue down the road if UIs are built to allow users to update the messages runtime, and require some cache clearing mechanism.

6) Should we rely on the system locale or have a mechanism for setting it within the app? Note this task is not to fully support localization, but we don't want to do anything that will make this more difficult down the road

  • No labels

2 Comments

  1. Peri mentioned creating UI for users to modify messages at runtime during the KTI and it sparked a question.  Jerry talked about how many of the messages are culled from the DD and from properties files.  If someone were to update any of those messages were changed those changes would be overwritten when the server starts up again.  Could mark each of the externally sourced messages like these as read/only.

    1. Hey Larry,

        I don't quite follow the scenario. Could you give an example?

      thanks,

      Jerry