You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Peter Donald <do...@apache.org> on 2001/02/27 14:54:17 UTC

[Phoenix] Exceptions, Events and i18n

Hi,

I have been thinking about how we should handle notification of exceptions
and events in the kernel and this relates to i18n. First i18n.

There is a number of ways of doing it. A standard way in the java world is
to associate each message with a pair. The pair being the name of bundle
and name of message in bundle. So you would have "kernel.state", "startup"
as one message and for english it may extract out a string "Entry %s has
started up". This is stored in separate text files.

The other approach is to use numeric values ie event 53124 is "Entry %s has
started up" and these may be held in a constants interface.

Approach 1 is very good for small systems and is very extensible. It is
however very difficult to validate it - you end up having to virtually run
through the code to see if the bundle/message pair actually exists for any
particular language.

Approach 2 is much easier to validate but is much harder to extend. It
scales to massive architectures and many languages.

Votes/Opinions on either one ?

The next issue is how to deal with logging/notification/exception. I looked
at a few architectures and the best of them followed an Aspect Orientated
Programming approach. They had interface to listeners for each slice
through the system. So in the end you may have a collection of interfaces
such as 

ApplicationErrorHandler
KernelErrorHandler
BlockErrorHandler
KernelStatusListener
ApplicationStatusListener
BlockStatusListener

These can be as fine grain or as coarse as is deemed necessary. Now where
we previously had

try
{
}
catch( final Exception e )
{
  final String message = ...;
  getLogger().warn( message, e );
  throw new MyException( message, e );
}

it would now be either
try
{
}
catch( final Exception e )
{
  m_errorListener.errorDoingX( ..., e );
}

or

try
{
}
catch( final Exception e )
{
  m_errorListener.errorDoingX( ..., e );
  throw e;
}

The second form would force errors to be propogated while the first would
allow the actual handler to decide the policy. This may be useful if you
wanted to plough ahead with kernel running even though an application
failed etc. Policy when responding to such execptions is taken from Fedes
wishes and is similar to SAX model and discussed Ant model.

A lot of the logging throughout the kernel/application would also be
replaced my calling into event listeners. ie instead of 

getLogger().info( "Entry " + name + " did something " );

It would be

m_eventListener.somethingOccuredTo( name );

It would be inside each of these listeners/handlers that the events would
use a i18n scheme outlined above.


Cheers,

Pete

*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof."                   |
|              - John Kenneth Galbraith               |
*-----------------------------------------------------*


Re: [Phoenix] Exceptions, Events and i18n

Posted by Rob Walker <ro...@softsell.com>.
Woops, I'm unintentionally "noisy" on the email list for a newbie. 
Internationalisation was the one piece I left off my questions email 
for fear of throwing out too much garbage. It's a key area for us tho. 
so here goes.

> Lets assume there are N different types of status/error 
messages in the
> kernel/applications. Each different message may have 0, 1 or 
more
> parameters. Some of these messages belong to a "family" where 
each member
> of family takes same parameters. (ie they may all take name of 
block or
> numeric). 

* Let me check my assumptions - actually Internatilising the 
messages is well covered using the standard JDK combination of:

- MessageFormat & ChoiceFormat classes
- Locale specific .properties files and ResourceBundles

So what I think you're looking for is some higher abstraction i.e. if 
we have 100 different error/status messages, all with different 
numbers and kinds of placeholder replacement arguments, how 
best to group and collect them?

> There is a few different ways of doing this
> 
> (1) Each message has a different method. ie
> 
> listener.componentAdded( "myBlock" );
> listener.errorLoadingComponent( "myBlock", myException );
> listener.componentUnloaded( "myBlock" );

Taking an example of 3 messages, all of which take a single 
argument that is a String containing a filename e.g. 

	File {0} is full
	File {0} cannot be found
	File {0} is locked 

Whether to group them as "FileMessages" or even 
"FileMessagesSingleArg" (both with a defined/common array of 
parameters). Or whether to say each is separate e.g.

	Message.FileFull (... )
	Message.FileNotFound (...)
	Message.FileLocked (... )

Pain that it might be, I'd say the latter. Any change to one of the 
message's construction or argument lists will not pollute the others 
unintentionally. 

> (2) Generic code + generic parameter list method
> listener.status( Status.COMPONENT_ADDED, new Object[] { "myBlock" } );
> listener.error( Status.ERROR_LOADING, 
>                 new Object[] { "myBlock", myException } );
> listener.status( Status.COMPONENT_UNLOADED, new Object[] { "myBlock" } );

I think the standard MessageFormat stuff will take care of this 
approach for you ... albeit, the arguments passed in code must 
match the text patterns and there's only programmer integrity to 
police this.

> 
> (3) Semi-stateful listeners
> 
> listener.componentAdded( "myBlock" );
> 
> // Note we know that the next message pertains to "myBlock" because
> // it was last named in call to componentAdded()
> listener.errorLoadingComponent( myException );
> 
> listener.componentUnloaded( "myBlock" );
> 
> (4) Force errors in same "family" into same method
> 
> listener.lifecycleStatus( Status.COMPONENT_ADDED, "myBlock" );
> listener.lifecycleError( Status.ERROR_LOADING, "myBlock", myException );
> listener.lifecycleStatus( Status.COMPONENT_UNLOADED, "myBlock" );
> 
> Or something else ?
> 
> Which method is the most correct? Probably (1) - however it can get
> excessive as there will likely be close to 30 messages. Anyone else have
> ideas/opinions etc?

Trouble comes (and this is a big headache for us) when you have 
1000 messages and each needs a locale specific text, and a 
separate method (or worse) to represent it.

I suspect we'll going to tackle this by a combination of (1) and (4) e.g. a
way for individual messages to delegate their argument handling to 
a common method e.g. StandardFileMessageParams

FYI (at the risk of teaching you to suck eggs) there's an 
internatilization toolkit at:

	http://java.sun.com/products/jilkit

-- Rob --


SoftSell Business Systems, Ltd.
' testing solutions for a changing world '
 
+44 (20) 7488 3470
www.softsell.com
robw@softsell.com

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


Re: [Phoenix] Exceptions, Events and i18n

Posted by Peter Donald <do...@apache.org>.
Hi,

I haven't been successful in doing this and there is a few different models
that I am unsure which way to go. 

Lets assume there are N different types of status/error messages in the
kernel/applications. Each different message may have 0, 1 or more
parameters. Some of these messages belong to a "family" where each member
of family takes same parameters. (ie they may all take name of block or
numeric). 

There is a few different ways of doing this

(1) Each message has a different method. ie

listener.componentAdded( "myBlock" );
listener.errorLoadingComponent( "myBlock", myException );
listener.componentUnloaded( "myBlock" );
 
(2) Generic code + generic parameter list method
listener.status( Status.COMPONENT_ADDED, new Object[] { "myBlock" } );
listener.error( Status.ERROR_LOADING, 
                new Object[] { "myBlock", myException } );
listener.status( Status.COMPONENT_UNLOADED, new Object[] { "myBlock" } );

(3) Semi-stateful listeners

listener.componentAdded( "myBlock" );

// Note we know that the next message pertains to "myBlock" because
// it was last named in call to componentAdded()
listener.errorLoadingComponent( myException );

listener.componentUnloaded( "myBlock" );

(4) Force errors in same "family" into same method

listener.lifecycleStatus( Status.COMPONENT_ADDED, "myBlock" );
listener.lifecycleError( Status.ERROR_LOADING, "myBlock", myException );
listener.lifecycleStatus( Status.COMPONENT_UNLOADED, "myBlock" );

Or something else ?

Which method is the most correct? Probably (1) - however it can get
excessive as there will likely be close to 30 messages. Anyone else have
ideas/opinions etc?

Cheers,

Pete

*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof."                   |
|              - John Kenneth Galbraith               |
*-----------------------------------------------------*


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


Re: [Phoenix] Exceptions, Events and i18n

Posted by Peter Donald <do...@apache.org>.
At 09:50  27/2/01 -0500, Berin Loritsch wrote:
>Having some history with the GNU GetText program, the way they do the change
>is similar to #1.  By saying that "Entry %s has ..." is flawed because of
>languages that change the form of the word based on its position in the
sentence

I agree but in our case these inserted things won't be things that alter
gramatically as they are usually names, numbers or boolean. The underling
java i18n handles boolean/numbers and we don't need names altered. ie if a
block is called "socketserver" we don't want it to be "translated" into
another language as then it would not match the name in config file.

Cheers,

Pete

*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof."                   |
|              - John Kenneth Galbraith               |
*-----------------------------------------------------*


Re: [Phoenix] Exceptions, Events and i18n

Posted by Berin Loritsch <bl...@apache.org>.
Peter Donald wrote:
> 
> Hi,
> 
> I have been thinking about how we should handle notification of exceptions
> and events in the kernel and this relates to i18n. First i18n.
> 
> There is a number of ways of doing it. A standard way in the java world is
> to associate each message with a pair. The pair being the name of bundle
> and name of message in bundle. So you would have "kernel.state", "startup"
> as one message and for english it may extract out a string "Entry %s has
> started up". This is stored in separate text files.
> 
> The other approach is to use numeric values ie event 53124 is "Entry %s has
> started up" and these may be held in a constants interface.
> 
> Approach 1 is very good for small systems and is very extensible. It is
> however very difficult to validate it - you end up having to virtually run
> through the code to see if the bundle/message pair actually exists for any
> particular language.
> 
> Approach 2 is much easier to validate but is much harder to extend. It
> scales to massive architectures and many languages.
> 
> Votes/Opinions on either one ?

Having some history with the GNU GetText program, the way they do the change
is similar to #1.  By saying that "Entry %s has ..." is flawed because of
languages that change the form of the word based on its position in the sentence
or plurality is expressed differently.  The GNU coding standards (which say
that for any official product, they have to use GetText) state that the entire
sentence should be keyed.  For example:  We have a resource file with all the
entries for english text, and our code.

// The GetText equivalent way (actually I think the macro converts it to a
// resource number
log.info(resource.get("Entry foo has started up"));

// The unrolled method
log.info(resource.get(Resources.FOO_START));

In the resource, if the foreign lanuage resource does not exist, it falls back
to the english message.  This allows people to contribute by providing translations,
and knowing they won't miss one.

Resource.en
-----------
Entry FOO has started up
Entry FOO has shut down
Added extra FOOs
Removed old FOO

Resource.es
-----------
Elemento FOO comence'
Elemento FOO termino'
Incluimo' ma's objectos de FOO
departido FOO viejo

Note: I appologize for the incorrect method of accents, just pretend that
the trailing single quote is over the vowel.  Also notice the last entry:
the ordering of words is different.

This gets even trickier with Russian and Greek where words change based on
grammatical structure.

I vote for a variation of 2 where the whole phrase is included instead of
something that substitute values--unless you want to build a whole inference
engine that will find the right form of the word based on gramatical structure
and plurality.

> The next issue is how to deal with logging/notification/exception. I looked
> at a few architectures and the best of them followed an Aspect Orientated
> Programming approach. They had interface to listeners for each slice
> through the system. So in the end you may have a collection of interfaces
> such as
> 
> ApplicationErrorHandler
> KernelErrorHandler
> BlockErrorHandler
> KernelStatusListener
> ApplicationStatusListener
> BlockStatusListener
> 
> These can be as fine grain or as coarse as is deemed necessary. Now where
> we previously had
> 
> try
> {
> }
> catch( final Exception e )
> {
>   final String message = ...;
>   getLogger().warn( message, e );
>   throw new MyException( message, e );
> }
> 
> it would now be either
> try
> {
> }
> catch( final Exception e )
> {
>   m_errorListener.errorDoingX( ..., e );
> }
> 
> or
> 
> try
> {
> }
> catch( final Exception e )
> {
>   m_errorListener.errorDoingX( ..., e );
>   throw e;
> }
> 
> The second form would force errors to be propogated while the first would
> allow the actual handler to decide the policy. This may be useful if you
> wanted to plough ahead with kernel running even though an application
> failed etc. Policy when responding to such execptions is taken from Fedes
> wishes and is similar to SAX model and discussed Ant model.
> 
> A lot of the logging throughout the kernel/application would also be
> replaced my calling into event listeners. ie instead of
> 
> getLogger().info( "Entry " + name + " did something " );
> 
> It would be
> 
> m_eventListener.somethingOccuredTo( name );
> 
> It would be inside each of these listeners/handlers that the events would
> use a i18n scheme outlined above.

I like that approach.  I would make the listener implement the method like
ErrorListener.error(ErrorListener.ACTION_CONSTANT, Exception e)

so that we can keep the second version of i18n working properly.  This will
also help frameworks like Cocoon in the performance department.  Basically,
I could have a background thread that is the ErrorListener, and asynchronously
log messages without slowing down the performance of the Servlet.  The
integer constant approach will make these evaluations even faster!

+1