Question about using Units of Measurement API

111 views
Skip to first unread message

César Martínez Izquierdo

unread,
Mar 9, 2018, 7:49:15 AM3/9/18
to unit...@googlegroups.com
Dear developers,

I am evaluating using JSR363 API in our product, and in particular
whether we should expose JSR363 references in our own API. I am not
sure if this is the right forum to raise this kind of questions,
please point me to the right one if not.

My main concerns are about API stability and also about the
compatibility of different implementations. In particular:

- I've seen that you are now preparing JSR385. Is this an evolution of
JSR363 or is it a complementary API? Will this break the existing API
in some way?

I am particularly concerned about changes on method signatures, or
renaming / removing existing interfaces. Ideally, I'd expect to be
able to use JSR363 API in our product even if we use a JSR385
implementation. I believe this should be possible in runtime, as far
as only method/interface additions happen in JSR385 (if
implementations are carefully coded).

- I would expect different JSR363 implementations to be compatible
among them, but I've done some tests and it seems they are not.
Consider this code snippet (which raises UnconvertibleException) :

ServiceProvider riProvider = null, seProvider = null;
for (ServiceProvider p:ServiceProvider.available()) {
if (p instanceof tec.units.ri.spi.DefaultServiceProvider) {
riProvider = p;
}
else if (p instanceof tec.uom.se.spi.DefaultServiceProvider) {
seProvider = p;
}
}

UnitFormatService formatServiceSe =
seProvider.getUnitFormatService();
UnitFormat defaultFormatSe = formatServiceSe.getUnitFormat();
Unit<Length> metreSe = (Unit<Length>) defaultFormatSe.parse("m");

UnitFormatService formatServiceRi =
riProvider.getUnitFormatService();
UnitFormat defaultFormatRi = formatServiceRi.getUnitFormat();
Unit<Length> metreRi = (Unit<Length>) defaultFormatRi.parse("m");

UnitConverter converter = metreSe.getConverterTo(unit);
// next line raises a javax.measure.UnconvertibleException
double d = converter.convert(101.02);

QuantityFactory<Length> quantityFactory =
seProvider.getQuantityFactory(Length.class);
Quantity<Length> metreSeQuantity =
quantityFactory.create(101.02, metreSe);
// next line raises a javax.measure.UnconvertibleException
Quantity<Length> metreRiQuantity = metreSeQuantity.to(metreRi);

According to "Supported Quantities" section in JSR363 specification,
I'd expect this kind of conversions to work properly for the Quantity
types defined on that section (page 25 table).

The lack of this compatibility makes me question whether we should
expose JSR363 in our API. Since we use 3rd party libraries and they
might use different implementations of the API, what's the goal of
providing this API if we can't trust the units will be comparable and
the quantities convertible?

Maybe the situation could be improved if JSR363/385 would define
constants for the main unit symbols, since the different
implementations could parse a "foreign" unit to a "native" unit that
knows how to convert.

This could also be helpful for other libraries using JSR363/385, since
they could instantiate Units in a generic way, avoiding to be bound to
a particular implementation (at least for common Units).

I hope this thoughts are useful for the JSR363 community, providing
maybe new use cases that were not considered when designed.

Regards,

César Martínez


--
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
César Martínez Izquierdo
GIS developer
- - - - - - - - - - - - - - - - - - - -
SCOLAB: http://www.scolab.es
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Martin Desruisseaux

unread,
Mar 9, 2018, 8:47:04 AM3/9/18
to César Martínez Izquierdo, unit...@googlegroups.com
Hello César, and welcome!

Le 09/03/2018 à 13:49, César Martínez Izquierdo a écrit :

> I am evaluating using JSR363 API in our product, and in particular
> whether we should expose JSR363 references in our own API. I am not
> sure if this is the right forum to raise this kind of questions,
> please point me to the right one if not.
>
Werner could confirm, but I think this is the right place.


> - I've seen that you are now preparing JSR385. Is this an evolution of
> JSR363 or is it a complementary API? Will this break the existing API
> in some way?
>
This is an evolution of JSR-363. We are strongly committed to API
compatibility, not only in this group but also because JSR-363 is part
of Java Community Process, which has strong rules. If I'm understanding
right, we expect a level of compatibility close to the one we expect
from Java Standard Edition.


> I am particularly concerned about changes on method signatures, or
> renaming / removing existing interfaces. Ideally, I'd expect to be
> able to use JSR363 API in our product even if we use a JSR385
> implementation. I believe this should be possible in runtime, as far
> as only method/interface additions happen in JSR385 (if
> implementations are carefully coded).
>
Yes, this is the goal.


> - I would expect different JSR363 implementations to be compatible
> among them, but I've done some tests and it seems they are not.
> Consider this code snippet (…snip…)
>
Thanks for the code snippet. I think that this is an implementation
issue rather than a JSR-363 issue. Alternatively you can also tests with
Apache SIS, which provides another JSR-363 implementation. It may fail
too, but with such bug report we could try to fix those issues.


> The lack of this compatibility makes me question whether we should
> expose JSR363 in our API. Since we use 3rd party libraries and they
> might use different implementations of the API, what's the goal of
> providing this API if we can't trust the units will be comparable and
> the quantities convertible?
>
I agree this need to be improved. Note however that the API does not
become useless; it still possible to perform some other operations on
them regardless their implementation (e.g. determine if a unit is
angular or linear, get its symbol).


> Maybe the situation could be improved if JSR363/385 would define
> constants for the main unit symbols, since the different
> implementations could parse a "foreign" unit to a "native" unit that
> knows how to convert.
>
I'm not sure that we need String constants if we specify that SI symbols
should be used. But the strategy of parsing the string representation of
a "foreign" unit could indeed work in many cases.

Note (since we talked about geospatial libraries previously) that if
using GeoAPI together with JSR-363, this requires GeoAPI 3.0.1 which was
released in September 2017. GeoAPI is an OGC standard; GeoTools has its
own fork of GeoAPI, but this is non-standard and conflict with the
official standard.

Regards,

    Martin


César Martínez Izquierdo

unread,
Mar 9, 2018, 11:05:34 AM3/9/18
to Martin Desruisseaux, unit...@googlegroups.com
Hi Martin, thanks for the quick response,

On 9 March 2018 at 14:47, Martin Desruisseaux
<martin.de...@geomatys.com> wrote:
[snip]
>>
> This is an evolution of JSR-363. We are strongly committed to API
> compatibility, not only in this group but also because JSR-363 is part
> of Java Community Process, which has strong rules. If I'm understanding
> right, we expect a level of compatibility close to the one we expect
> from Java Standard Edition.
>
>
>> I am particularly concerned about changes on method signatures, or
>> renaming / removing existing interfaces. Ideally, I'd expect to be
>> able to use JSR363 API in our product even if we use a JSR385
>> implementation. I believe this should be possible in runtime, as far
>> as only method/interface additions happen in JSR385 (if
>> implementations are carefully coded).
>>
> Yes, this is the goal.

Good to know this.


>
>
>> - I would expect different JSR363 implementations to be compatible
>> among them, but I've done some tests and it seems they are not.
>> Consider this code snippet (…snip…)
>>
> Thanks for the code snippet. I think that this is an implementation
> issue rather than a JSR-363 issue. Alternatively you can also tests with
> Apache SIS, which provides another JSR-363 implementation. It may fail
> too, but with such bug report we could try to fix those issues.

I agree it is an implementation issue (according to the specification)

>
>> The lack of this compatibility makes me question whether we should
>> expose JSR363 in our API. Since we use 3rd party libraries and they
>> might use different implementations of the API, what's the goal of
>> providing this API if we can't trust the units will be comparable and
>> the quantities convertible?
>>
> I agree this need to be improved. Note however that the API does not
> become useless; it still possible to perform some other operations on
> them regardless their implementation (e.g. determine if a unit is
> angular or linear, get its symbol).

Well, don't misunderstand me, the API is great for a number of use cases.

I was just considering our own use case, in which different 3rd party
libraries can be integrated in our product, each using different
implementations. If we receive a Unit from one of those libs, it is
quite limiting if we can't consider "safe" to provide such instance to
one of the other libraries. I guess in this case we would need to
build a kind of bridge to convert instances from one implementation to
a different one.

It would be great if each library using the API would create the Unit
instances only using generic methods such as:

ServiceProvider provider = ServiceProvider.current();
UnitFormatService formatService = provider.getUnitFormatService();
UnitFormat defaultFormat = formatService.getUnitFormat();
METRE = (Unit<Length>) defaultFormat.parse("m");

In this way, we could use setCurrent to choose an implementation in
runtime for all those 3rd party libraries.
Of course this ball lays on the libraries' court and not on the JSR363 one.
But still, it would be simpler to achieve if the main units symbols
were defined on the API (the ones that are defined on page 25 of the
specification).

Best regards,

César

Martin Desruisseaux

unread,
Mar 9, 2018, 4:48:42 PM3/9/18
to César Martínez Izquierdo, unit...@googlegroups.com

Le 09/03/2018 à 17:05, César Martínez Izquierdo a écrit :

I was just considering our own use case, in which different 3rd party libraries can be integrated in our product, each using different implementations. If we receive a Unit from one of those libs, it is quite limiting if we can't consider "safe" to provide such instance to one of the other libraries. I guess in this case we would need to build a kind of bridge to convert instances from one implementation to a different one.

We could emit recommendation to implementors, along the lines discussed in previous email (formatting then parsing standard unit symbols).


It would be great if each library using the API would create the Unit
instances only using generic methods such as:

  ServiceProvider provider = ServiceProvider.current();
  UnitFormatService formatService = provider.getUnitFormatService();
  UnitFormat defaultFormat = formatService.getUnitFormat();
  METRE = (Unit<Length>) defaultFormat.parse("m");

In this way, we could use setCurrent to choose an implementation in
runtime for all those 3rd party libraries.

This is different than the previous proposal, to encourage implementations to accept "foreigner" units. In this second approach, a single implementation would be used.

A possible issue is that some libraries may have reasons for using their own unit implementation. I can not tell for other libraries, but taking Apache SIS as an example, some reasons are:

  • SIS implementation of Unit have additional information very specific to the geospatial domain, namely EPSG code for each unit (admittedly, this information could also be stored outside Unit).
  • Some libraries may want to implement additional interfaces. SIS Unit would have implemented IdentifiedObject interface if we didn't had a conflict on getName() method.
  • The library may need to support unusual units which are not guaranteed to be supported by other implementations. In particular SIS needs sexagesimal degrees (e.g. 10°30' stored as 10.3 instead than 10.5) because it is used in EPSG database for map projection parameters. A unit library not supporting sexagesimal units, or having a broken support of it, would have (indirectly) a sever impact on map projections created from EPSG database.
  • The library may need to support unusual unit names. For example netCDF uses "degrees east" and "degrees north" instead of just "degrees".

For safety, SIS uses its own Unit implementation without interfering with ServiceProvider.current(), so users can set their own implementation for other libraries.

    Martin


Werner Keil

unread,
Mar 9, 2018, 6:33:44 PM3/9/18
to Units Developers
Cesar/Martin,

Thanks a lot for the constructive discussion.

Martin answered most questions already, but let me confirm (having been in various positions in the JCP, while in fact, all 3 Spec Leads of the new JSR 385 served at least one term in the JCP Executive Committee ;-) that backward compatibility is extremely important for Java standards like JSR 363 and its next version (due around the Metric Standard redefinition in Spring 2019) JSR 385.

You are right, that different implementations like uom-se, unit-ri, SIS or Eclipse UOMo 0.7 (all are compatible with the JSR 363 TCK) are not compatible with each other and trying to mix them leads to exceptions like the one you mentioned or others including ClassCastException.

However, that is not uncommon. A JSR is meant to allow and best support multiple different implementations, but nobody in a sane mind would try to mix Weblogic Portal, Websphere Portal, Apache Pluto (the RI) or Liferay Portal in a single classpath and Java Runtime environment. Taking the next door neighbor JSR 362. I know others like JSF behave equally bad and that even comes up in more real life situations like trying to run a WAR with JSF implementations by one vendor, say Apache or Sun in a container based on Wildfly that includes the JBoss implementation of Java Server Faces. 
I am working on a Java EE Performance tutorial for Packt where JCache was a recent chapter. And I used this modified and improved example:
If you have more than one JSR 107 implementation in the classpath and call Caching.getCachingProvider() this throws an exception. You have to call one with Caching.getCachingProvider("myprovider")Caching is somewhat similar to the Unit API ServiceProvider. We had a different entry point like Caching named Bootstrap first (inspired by JavaMoney) but simplified it. Martin will remember because he was influential and helpful to this refactoring.

If you call Caching.getCachingProvider("myprovider1") and Caching.getCachingProvider("myprovider2") from JCache you may easily run into problems trying to retrieve a cache from Hazelcast and then put it into Ehcache. Putting simple variables like a String or Number from one to the other should work, but you also should not have a problem taking a numerical value from one JSR 363 implementation and applying it to another implementation in the same classpath. As long as you don't try to mix different implementation classes.

The JSR 385 implementation Indriya will reduce the number of implementations because it's the new Reference Implementation. And requires Java SE 8 as the minimum version. With Java 9+ Jigsaw and trying to leverage features like multi-release JARs (which run from Java 8 on but allow extra features of JDK 10 or 11 if you are using that Java version) we plan to support many Java versions with a single implementation. There will of course be others, SIS, potentially UOMo (unless it reuses Indriya, we think about that) and more, but whatever Jigsaw or OSGi (JSR 363 supports that already in every module) can do to isolate different libraries and implementation from each other, the behavior should improve, but you will likely have to chose between different modules and need to be aware if those modules are based on different implementations in the same module path.

It sounds like you are suggesting a more neutral accessor to something like METRE or other constants?
Please propose one or the other idea into a feature request for the API:

UnitFormat or QuantityFactory are already available. A Prefix as first class citizen of the API is also a step towards making prefixes like MILLI or GIGA available via he API. We are flexible to other suggestions, but one place I could imagine those is the SystemOfUnitsService  while e.g. a getter by name not only a parser via the format could be a new method in SystemOfUnits. In theory the service would also work, e.g. if one wanted to search more than one available unit system for a unit named "metre", "meter" or similar (what about aliases?;-)
Since the service provider is bound to a single implementation it would not be a problem doing that for a service, it is a question of what makes more sense, the getUnit(quantity class) method would suggest adding it to SystemOfUnits rather than the service. Of course queries returning a collection could also be worth a thought. Please propose a feature in the GitHub tracker to help us define some of those to best meet your needs.

Werner

Jean-Marie Dautelle

unread,
Mar 10, 2018, 5:22:37 AM3/10/18
to unit...@googlegroups.com
Hi All,

I agree with Werner and Martin. The purpose of the "abstract factory pattern" is to ensure that all objects are compatible together / work together since they are all coming from the same factory (typically instantiated at start-up based on platform capabilities). 
Having multiple factories challenges this purpose.

Cheers,
Jean-Marie

--
You received this message because you are subscribed to the Google Groups "Units Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to units-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

Our greatest glory is not in never falling, but in rising every time we fall.
– Confucius
Reply all
Reply to author
Forward
0 new messages