commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Luc Maisonobe <...@spaceroots.org>
Subject Re: [math] Exception Design
Date Fri, 25 Dec 2015 09:31:43 GMT
Le 25/12/2015 04:46, Gilles a écrit :
> On Thu, 24 Dec 2015 16:56:54 +0100, Luc Maisonobe wrote:
>>>> [...]
>>>>
>>>> When our users build a large application, they rely on numerous
>>>> different libraries and tools, both open-source and proprietary.
>>>> These libraries do have standard interfaces so they can be used
>>>> together. The Java standard library is one of them. the
>>>> getLocalizedMessage method is specified there. Many of the libraries
>>>> and tolls user will assemble rely on it. Deciding that for the sake
>>>> of Apache Commons Math we do not abide to this and decide that all
>>>> other existing code should adapt to a different design is a clear
>>>> no go for me.
>>>
>>> Does the JVM abide by this?
>>
>> Yes,
> 
> Then, how to explain that the following code
> 
> ---CUT---
>         for (Locale loc : new Locale[] { Locale.ENGLISH, Locale.FRENCH }) {
>             Locale.setDefault(loc);
>             System.out.println("Locale=" + loc);
> 
>             try {
>                 final int a = 2;
>                 double r = a / 0;
>             } catch (Exception e) {
>                 System.out.println("JVM toString(): " + e);
>                 System.out.println("JVM getLocalizedMessage(): " +
> e.getLocalizedMessage());
>             }
> 
>             try {
>                 throw new NotStrictlyPositiveException(-1);
>             } catch (Exception e) {
>                 System.out.println("CM -> toString(): " + e);
>                 System.out.println("CM -> getLocalizedMessage(): " +
> e.getLocalizedMessage());
>             }
>         }
> ---CUT---
> 
> produces this output:
> 
> ---CUT---
> Locale=en
> JVM toString(): java.lang.ArithmeticException: / by zero
> JVM getLocalizedMessage(): / by zero
> CM -> toString():
> org.apache.commons.math4.exception.NotStrictlyPositiveException: -1 is
> smaller than, or equal to, the minimum (0)
> CM -> getLocalizedMessage(): -1 is smaller than, or equal to, the
> minimum (0)
> Locale=fr
> JVM toString(): java.lang.ArithmeticException: / by zero
> JVM getLocalizedMessage(): / by zero
> CM -> toString():
> org.apache.commons.math4.exception.NotStrictlyPositiveException: -1
> n'est pas strictement plus grand que le minimum (0)
> CM -> getLocalizedMessage(): -1 n'est pas strictement plus grand que le
> minimum (0)
> ---CUT---
> 
> ?

I'm confused. If instead of the division by zero I try to open an
inexistent file, the message is translated in French. This is why I
assumed it was properly handled. However, it is also translated using
the regular getMessage(). So I guess the translated message is provided
at lower level by the operating system (Linux in my case) which uses
the LANG environment variable and does not follow the setDefaultLocale
setting.

best regards,
Luc


> 
>> as well as JUnit, Eclipse, Maven, and all tools I use.
>>
>>>
>>> The point is that CM code is not user-level code: requirements that
>>> have nothing to do with mathematical algorithms should not have
>>> top-level priority here.  This is what has always biased this
>>> discussion.
>>>
>>> This issue is not one of a design that would not use
>>> "getLocalizedMessage"
>>> just that CM is not the place to do so. CM throws exception; caller
>>> handle them in any way they want.
>>> For example:
>>>
>>> try {
>>>  // ...
>>>
>>>  // Use a low-level library: do not let foreign exceptions bubble up.
>>>  try {
>>>    CMAlgo algo = new CMAlgo();
>>>    algo.run();
>>>  } catch(RuntimeException e) {
>>>    if (e instanceof CMAlgoException)
>>>      throw new MyCMAlgoException(e);
>>>    } else {
>>>      throw new MyUnexpectedException(e);
>>>    }
>>>  }
>>>
>>>  // ...
>>> } catch (MyRuntimeException e) {
>>>   LOG.error(e.getLocalizedMessage()); //
>>> }
>>
>> Times thousands times as CM call in complex applications that do
>> a lot of math occur everywhere.
> 
> But you do not have to do the above a thousand times only at the point
> where you need to extract the message.
> 
> public class MyApp {
> 
>   public static void main(String[] args) {
>     final Locale loc = new locale(args[0]);
>     try {
>       final MyApp app = new MyApp(args[1], args[2]);
>       app.run();
>     } catch (RuntimeException e) {
>       if (e instanceof MathException) {
>         LOG.error(translate(e, loc));
>         exit(1);
>       }
>     }
>   }
> 
>   private void run() {
>     // ...
>   }
> }
> 
>>>
>>>> Look at it, it is *only* one field with its setter and two one line
>>>> methods defined in the top level MathException class in order to
>>>> abide to a standardized interface widely used.
>>>
>>> OK, and I also worked a lot to make it less of a duplication mess.
>>> I don't think that I can oppose you on these 3 lines given all the
>>> work you do. ;-)
>>
>> Thanks.
>>
>>>
>>> But once and for all, I'd like that we acknowledge that this decision
>>> has nothing to do with good design.
>>
>> I don't claim this to be good design. It is only meant to be practical
>> and not cumbersome for users.
> 
> If it is not practical or cumbersome, then it is not good design. :-)
> 
>>>>>
>>>>> The list of translated type could still be maintained here, in the
>>>>> same
>>>>> way the unit tests and user guide are.
>>>>
>>>> I'm not sure. If we decide to delegate the localization to the user
>>>> and simply provide the two one-liners hook to call it, then we can
>>>> remove the list of translated types and let the user handle it. It
>>>> would also allow them to support additional languages. For now,
>>>> we support only one, we could drop it using this delegation mechanism.
>>>
>>> I just meant it as a service to the international of users. :-)
>>> We could centralize the translations as is done for other resources like
>>> the userguide.
>>
>> This is fine with me.
> 
> I'm also fine if we don't provide this service!
> 
>>>>> [...]
>>>>
>>>> The setLocalizer method is not a computation algorithm expected
>>>> to be called millions of times and that therefore could experience
>>>> clashes in case of concurrency.
>>>
>>> I was not considering this case.
>>> Just the inherent potential problem of a global setter for a
>>> low-level library.
>>> Do you know of other libraries that do such a thing?
>>
>> At least Orekit (for something called DataProvidersManager, which
>> is a singleton and uses initialization-on-demand holder idiom),
>> but this example is biased ;-)
>>
>> Another example would be log4j2, with the MainMapLookup singleton
>> and its setMainArguments method that is intended to be called once
>> with the main arguments, but nothing prevents it to be called
>> at will (and there are no protections against concurrent accesses).
>>
>> There is also the JSCH library which uses a private static map for
>> config and has setters and getters allowing to change its content
>> (here, there are some protections agains concurrent access).
>>
>> In many case, as long as you need to store some configuration,
>> you end up with either a static field (often private and
>> with getters/setters) or an instance field in a singleton.
>> It happens in the best libraries.
> 
> IMHO, this (mis)feature cannot be what makes them "best".
> Why is "slf4j" configured the way it is?
> 
> Best regards,
> Gilles
> 
>>>> [...]
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
> 
> 


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


Mime
View raw message