Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency conflicts on com.fasterxml.jackson.core:jackson-core, leading to inconsistent program behaviors #119

Closed
HelloCoCooo opened this issue Sep 22, 2020 · 1 comment

Comments

@HelloCoCooo
Copy link

HelloCoCooo commented Sep 22, 2020

Hi, in yql-plus/yqlplus_language, there are mulptiple versions of library com.fasterxml.jackson.core:jackson-core. However, according to Maven's dependency management strategy: "first declaration wins", only com.fasterxml.jackson.core:jackson-core:2.7.9 can be loaded, and com.fasterxml.jackson.core:jackson-core:2.9.10 will be shadowed.

In total, there are 29 conflicting API pairs between these two library version.

As shown in the following figure, your project expects to invoke method <com.fasterxml.jackson.core.json.UTF8StreamJsonParser: nextToken()Lcom/fasterxml/jackson/core/JsonToken;> in library com.fasterxml.jackson.core:jackson-core:2.9.10 (along the original dependency path). As it has been shadowed, this method defined in com.fasterxml.jackson.core:jackson-core:2.7.9 is actually forced to be referenced via the following invocation path (along the actual dependency path):

<com.yahoo.yqlplus.language.operator.OperatorNode: toString(Ljava/lang/StringBuilder;)V> /home/wwww/sensor/unzip/yql-plus-1.0.16/yqlplus_language/target/classes
<com.fasterxml.jackson.databind.util.TokenBuffer: toString()Ljava/lang/String;> /home/wwww/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.9.10.3/jackson-databind-2.9.10.3.jar
<com.fasterxml.jackson.core.json.UTF8StreamJsonParser: nextToken()Lcom/fasterxml/jackson/core/JsonToken;>

yql-plus

Although both of these conflicting libraries contain the referenced methods (with the same signature), they have different implementations. This issue will not cause runtime crashes, but it can introduce inconsistent semantic program hehaviors----

Code snippet of <com.fasterxml.jackson.core.json.UTF8StreamJsonParser: nextToken()Lcom/fasterxml/jackson/core/JsonToken;> in com.fasterxml.jackson.core:jackson-core:2.9.10 (shadowed but expected to invoke method):

detailed method body
@Override
    public JsonToken nextToken() throws IOException
    {
        /* First: field names are special -- we will always tokenize
         * (part of) value along with field name to simplify
         * state handling. If so, can and need to use secondary token:
         */
        if (_currToken == JsonToken.FIELD_NAME) {
            return _nextAfterName();
        }
        // But if we didn't already have a name, and (partially?) decode number,
        // need to ensure no numeric information is leaked
        _numTypesValid = NR_UNKNOWN;
        if (_tokenIncomplete) {
            _skipString(); // only strings can be partial
        }
        int i = _skipWSOrEnd();
        if (i < 0) { // end-of-input
            // Close/release things like input source, symbol table and recyclable buffers
            close();
            return (_currToken = null);
        }
        // clear any data retained so far
        _binaryValue = null;

        // Closing scope?
        if (i == INT_RBRACKET) {
            _closeArrayScope();
            return (_currToken = JsonToken.END_ARRAY);
        }
        if (i == INT_RCURLY) {
            _closeObjectScope();
            return (_currToken = JsonToken.END_OBJECT);
        }

        // Nope: do we then expect a comma?
        if (_parsingContext.expectComma()) {
            if (i != INT_COMMA) {
                _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
            }
            i = _skipWS();
            // Was that a trailing comma?
            if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
                if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
                    return _closeScope(i);
                }
            }
        }

        /* And should we now have a name? Always true for Object contexts
         * since the intermediate 'expect-value' state is never retained.
         */
        if (!_parsingContext.inObject()) {
            _updateLocation();
            return _nextTokenNotInObject(i);
        }
        // So first parse the field name itself:
        _updateNameLocation();
        String n = _parseName(i);
        _parsingContext.setCurrentName(n);
        _currToken = JsonToken.FIELD_NAME;

        i = _skipColon();
        _updateLocation();

        // Ok: we must have a value... what is it? Strings are very common, check first:
        if (i == INT_QUOTE) {
            _tokenIncomplete = true;
            _nextToken = JsonToken.VALUE_STRING;
            return _currToken;
        }        
        JsonToken t;

        switch (i) {
        case '-':
            t = _parseNegNumber();
            break;

            // Should we have separate handling for plus? Although it is not allowed per se,
            // it may be erroneously used, and could be indicate by a more specific error message.
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            t = _parsePosNumber(i);
            break;
        case 'f':
            _matchFalse();
             t = JsonToken.VALUE_FALSE;
            break;
        case 'n':
            _matchNull();
            t = JsonToken.VALUE_NULL;
            break;
        case 't':
            _matchTrue();
            t = JsonToken.VALUE_TRUE;
            break;
        case '[':
            t = JsonToken.START_ARRAY;
            break;
        case '{':
            t = JsonToken.START_OBJECT;
            break;

        default:
            t = _handleUnexpectedValue(i);
        }
        _nextToken = t;
        return _currToken;
    }

Code snippet of <com.fasterxml.jackson.core.json.UTF8StreamJsonParser: nextToken()Lcom/fasterxml/jackson/core/JsonToken;> in com.fasterxml.jackson.core:jackson-core:2.7.9 (loaded version):

detailed method body
@Override
    public JsonToken nextToken() throws IOException
    {
        /* First: field names are special -- we will always tokenize
         * (part of) value along with field name to simplify
         * state handling. If so, can and need to use secondary token:
         */
        if (_currToken == JsonToken.FIELD_NAME) {
            return _nextAfterName();
        }
        // But if we didn't already have a name, and (partially?) decode number,
        // need to ensure no numeric information is leaked
        _numTypesValid = NR_UNKNOWN;
        if (_tokenIncomplete) {
            _skipString(); // only strings can be partial
        }
        int i = _skipWSOrEnd();
        if (i < 0) { // end-of-input
            // Close/release things like input source, symbol table and recyclable buffers
            close();
            return (_currToken = null);
        }
        // clear any data retained so far
        _binaryValue = null;

        // Closing scope?
        if (i == INT_RBRACKET) {
            _updateLocation();
            if (!_parsingContext.inArray()) {
                _reportMismatchedEndMarker(i, '}');
            }
            _parsingContext = _parsingContext.clearAndGetParent();
            return (_currToken = JsonToken.END_ARRAY);
        }
        if (i == INT_RCURLY) {
            _updateLocation();
            if (!_parsingContext.inObject()) {
                _reportMismatchedEndMarker(i, ']');
            }
            _parsingContext = _parsingContext.clearAndGetParent();
            return (_currToken = JsonToken.END_OBJECT);
        }

        // Nope: do we then expect a comma?
        if (_parsingContext.expectComma()) {
            if (i != INT_COMMA) {
                _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
            }
            i = _skipWS();
        }

        /* And should we now have a name? Always true for
         * Object contexts, since the intermediate 'expect-value'
         * state is never retained.
         */
        if (!_parsingContext.inObject()) {
            _updateLocation();
            return _nextTokenNotInObject(i);
        }
        // So first parse the field name itself:
        _updateNameLocation();
        String n = _parseName(i);
        _parsingContext.setCurrentName(n);
        _currToken = JsonToken.FIELD_NAME;

        i = _skipColon();
        _updateLocation();

        // Ok: we must have a value... what is it? Strings are very common, check first:
        if (i == INT_QUOTE) {
            _tokenIncomplete = true;
            _nextToken = JsonToken.VALUE_STRING;
            return _currToken;
        }        
        JsonToken t;

        switch (i) {
        case '-':
            t = _parseNegNumber();
            break;

            /* Should we have separate handling for plus? Although
             * it is not allowed per se, it may be erroneously used,
             * and could be indicate by a more specific error message.
             */
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            t = _parsePosNumber(i);
            break;
        case 'f':
            _matchToken("false", 1);
             t = JsonToken.VALUE_FALSE;
            break;
        case 'n':
            _matchToken("null", 1);
            t = JsonToken.VALUE_NULL;
            break;
        case 't':
            _matchToken("true", 1);
            t = JsonToken.VALUE_TRUE;
            break;
        case '[':
            t = JsonToken.START_ARRAY;
            break;
        case '{':
            t = JsonToken.START_OBJECT;
            break;

        default:
            t = _handleUnexpectedValue(i);
        }
        _nextToken = t;
        return _currToken;
    }

The detailed informantion of the remaining 28 conflicting API pairs can be found in the following attachment.
29 conflicting API pairs in project yqlplus_language.txt

Dependency tree--

[INFO] com.yahoo.yqlplus:yqlplus_language:jar:1.0.16
[INFO] +- org.testng:testng:jar:6.8.5:test
[INFO] | +- org.beanshell:bsh:jar:2.0b4:test
[INFO] | +- com.beust:jcommander:jar:1.27:test
[INFO] | - org.yaml:snakeyaml:jar:1.6:test
[INFO] +- org.antlr:antlr4-runtime:jar:4.5:compile
[INFO] | - org.abego.treelayout:org.abego.treelayout.core:jar:1.0.1:compile
[INFO] +- com.google.guava:guava:jar:24.1.1-jre:compile
[INFO] | +- (com.google.code.findbugs:jsr305:jar:1.3.9:compile - omitted for conflict with 2.0.1)
[INFO] | +- org.checkerframework:checker-compat-qual:jar:2.0.0:compile
[INFO] | +- com.google.errorprone:error_prone_annotations:jar:2.1.3:compile
[INFO] | +- com.google.j2objc:j2objc-annotations:jar:1.1:compile
[INFO] | - org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile
[INFO] +- com.google.code.findbugs:jsr305:jar:2.0.1:provided (scope not updated to compile)
[INFO] +- com.google.inject:guice:jar:4.0:compile
[INFO] | +- javax.inject:javax.inject:jar:1:compile
[INFO] | +- aopalliance:aopalliance:jar:1.0:compile
[INFO] | - (com.google.guava:guava:jar:24.1.1-jre:compile - version managed from 16.0.1; omitted for duplicate)
[INFO] +- com.fasterxml.jackson.core:jackson-core:jar:2.7.9:compile
[INFO] - com.fasterxml.jackson.core:jackson-databind:jar:2.9.10.3:compile
[INFO] +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.10:compile
[INFO] - (com.fasterxml.jackson.core:jackson-core:jar:2.7.9:compile - version managed from 2.9.10; omitted for duplicate)

Suggested solutions:

Solution: Declare version com.fasterxml.jackson.core:jackson-core:2.9.10 as a direct dependency, to override the version 2.7.9 (based on Maven's nearest wins loading strategy).

Thanks.
Best regards,
Coco

@HelloCoCooo
Copy link
Author

Could please help me check this issue?
May I pull a request to fix it?
Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants