K P created TEXT-233:
------------------------

             Summary: Missing OSGi import-package commons-lang3 version can 
make commons-text unusable in OSGi
                 Key: TEXT-233
                 URL: https://issues.apache.org/jira/browse/TEXT-233
             Project: Commons Text
          Issue Type: Bug
    Affects Versions: 1.11.0
            Reporter: K P


Since commons-text 1.11.0, there's commons-text codes that requires the 
presence of commons-lang3 3.13.0 or higher. However, in an OSGi environment, 
this requirement is not enforced due to shortcoming an the commons-text 1.11.0 
MANIFEST.MF, which may lead to unpredictable, uncontrollable failure when 
trying to use commons-text 1.11.0.

Some more detail:

Since commons-text 1.11.0, the NumericEntityEscaper makes uses of the method 
*Range.{_}+of+{_}(...)* in commons-lang3 :

 
{code:java}
private NumericEntityEscaper(final int below, final int above, final boolean 
between) {
    this.range = Range.of(below, above);
    this.between = between;
}{code}
 

Method Range.of(...) was added in *commons-lang3 3.13.0* :

 
{code:java}
 * @since 3.13.0
 */
public static <T extends Comparable<? super T>> Range<T> of(final T 
fromInclusive, final T toInclusive) {{code}
 

Previously, in commons-text 1.10.0 or earlier, NumericEntityEscaper made use of 
the the commons-lang3 method *Range.{_}+between+{_}(...)*  (which is now marked 
as deprecated since commons-lang3 3.13.0)

 
{code:java}
private NumericEntityEscaper(final int below, final int above, final boolean 
between) {
    this.range = Range.between(below, above);
    this.between = between;
}{code}
 

This is all fine in a non-OSGi environment.

 

However, to always resolve this correctly in an OSGi environment, some 
essential information is missing in the commons-text 1.11.0 {*}MANIFEST.MF{*}.

The commons-text 1.11.0 declares the following *Import-Package* in its manifest:

 
{noformat}
Import-Package: javax.script,javax.xml.xpath,org.apache.commons.lang3,or
 g.apache.commons.lang3.time,org.xml.sax
{noformat}
Notice how this indeed imports the required package 
"{_}org.apache.commons.lang3{_}", but this import {+}is not qualified with a 
version number{+}.

When an OSGi package is not qualified by a version number or version range, 
then during package resolution, OSGi may resolve this import against whatever 
version of commons-lang3 it can find and that exports this package.

In other words, because there's no version restriction in this import as far as 
OSGi is concerned : if you have an OSGi environment where also an older version 
older version of commons-lang3 is already present, e.g. commons-lang "3.12.0", 
then the import-package of commons-text 1.11.0 will happily be satisfied by 
that package and resolve and link its import-package requirement against the 
3.12.0 version.

So when loading these OSGi bundles, all will look fine. The OSGi will nicely 
resolve everything without complaining at this point.

However in the above example environment, maybe much much later, when at a 
given moment a user of the 1.11.0 commons-text NumericEntityEscaper (e.g. some 
of the StringEscapeUtils method being called by user code) is invoked,
then that commons-text 1.11.0 NumericEntityEscaper will see the 3.12.0 version 
of the Range class in its classpath. It will try to invoke the method 
Range.of(...) ... but this method didn't exist yet in this 3.12.0 version of 
the Range class.

Which will fail with a NoSuchMethodError:

 
{noformat}
java.lang.NoSuchMethodError: 
org.apache.commons.lang3.Range.of(Ljava/lang/Comparable;Ljava/lang/Comparable;)Lorg/apache/commons/lang3/Range;
    at 
org.apache.commons.text.translate.NumericEntityEscaper.<init>(NumericEntityEscaper.java:97)
 ~[?:?]{noformat}
 

To avoid this, it is +required+ that the commons-text 1.11.0 *MANIFEST.MF* 
correctly specifies the minimally required version of the commons-lang3 package 
in its Import-Package:

E.g. by adding a version directive: *{{;version="3.13"}}* : in OSGi, 1 single 
version number means a minimal version number, i.e. an open range 3.13.0 or 
higher. 
Example:
{code:java}
Import-Package: 
javax.script,javax.xml.xpath,org.apache.commons.lang3;version="3.13",or
 g.apache.commons.lang3.time;version="3.13",org.xml.sax{code}
That way, when OSGi resolves commons-text 1.11.0, it will now only be satisifed 
by version 3.13 of those package, or higher, and therefore be guaranteed to 
link its org.apache.commons.lang3 Import-Package against the package from 
commons-lang3 3.13.0 or higher. 
Then at runtime, whenever the NumericEntityEscaper calls a method on the Range 
class, it sees a 3.13.0 (or higher) version of the Range class, where the 
method Range.of(...) exists.

(Note: a manifst example from another Apache Project: 
this snippet is from the MANIFEST.MF of the Apache Velocity Engine jar:

 
{code:java}
Import-Package: javax.naming,javax.sql,org.apache.commons.lang3;versio
 n="[3.11,4)",
{code}
Notice how they've specified a version range, which is also a possibility in 
OSGi: range _"[3.11,4)"_ means: import from any version from 3.11 (inclusive) 
to version 4 (exclusive). )

 

I know that the commons-text OSGi MANIFEST.MF headers are generated by the 
"maven-bundle-plugin", which is defined somewhere in the "commons-parent" 
pom.xml I believe? But I assume that it should be possible to overrule some of 
the default (wildcard) generic maven-bundle-plugin settings with 
project-specific Import-Package directives, so that a version number (3.13) can 
be specified explicitly org.apache.commons.lang3 packages?

 

{color:#000000} In the current state, as shown above, the commons-text 1.11.0 
code may fail unpredictably in an OSGi environment. Due to the dynamic nature 
of OSGi, bundles (jars) may come and go. Older versions of commons-lang3 may be 
present or not. So when the commons-text import-package doesn't impose a 
minimal version number in its Import-Package, it may unpredictably resolve 
against a commons-lang3 3.13. or 3.14.0, but just as well against an older 
version where the required Range.of() method is missing, causing the failure. 
In a dynamic environment, such application may sometimes run successfully by 
chance, but may just unpredictably fail later, outside the control of the 
developer or user.{color}{color:#7f0055}
{color}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to