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

Java 9 미만 환경에서 java.lang.NoSuchMethodError 발생 #340

Closed
hjyun328 opened this issue Apr 26, 2021 · 3 comments
Closed

Java 9 미만 환경에서 java.lang.NoSuchMethodError 발생 #340

hjyun328 opened this issue Apr 26, 2021 · 3 comments
Assignees

Comments

@hjyun328
Copy link
Collaborator

hjyun328 commented Apr 26, 2021

이슈

arcus-java-client는 최소 Java 6 이상 버전을 지원한다. 하지만 Java 9 미만 환경에서 TCPMemcachedNodeImpl 클래스의 생성자 호출 시 아래와 같은 ByteBuffer.clear() 메소드의 NoSuchMethodError 현상이 발생한다.

java.lang.NoSuchMethodError: java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer;
	at net.spy.memcached.protocol.TCPMemcachedNodeImpl.<init>(TCPMemcachedNodeImpl.java:147) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.protocol.ascii.AsciiMemcachedNodeImpl.<init>(AsciiMemcachedNodeImpl.java:23) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.DefaultConnectionFactory.createMemcachedNode(DefaultConnectionFactory.java:220) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.MemcachedConnection.attachMemcachedNode(MemcachedConnection.java:542) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.MemcachedConnection.<init>(MemcachedConnection.java:131) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.DefaultConnectionFactory.createConnection(DefaultConnectionFactory.java:243) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.ConnectionFactoryBuilder$1.createConnection(ConnectionFactoryBuilder.java:444) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.MemcachedClient.<init>(MemcachedClient.java:223) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.plugin.FrontCacheMemcachedClient.<init>(FrontCacheMemcachedClient.java:53) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.ArcusClient.<init>(ArcusClient.java:326) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.ArcusClient.getInstance(ArcusClient.java:313) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.CacheManager.createArcusClient(CacheManager.java:432) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.CacheManager.commandCacheListChange(CacheManager.java:375) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at net.spy.memcached.CacheMonitor.processResult(CacheMonitor.java:103) ~[arcus-java-client-9.9.9.jar:9.9.9]
	at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:592) [zookeeper-3.4.14.jar:3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf]
	at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:508) [zookeeper-3.4.14.jar:3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf]

원인

Java 9부터 ByteBuffer의 일부 메소드가 Override 되었다. Override 된 메소드는 리턴 타입이 Buffer에서 ByteBuffer로 변경되었다.

변경된 메소드는 아래와 같다.

  • position(int newPosition)
  • limit(int newLimit)
  • flip()
  • clear()
  • mark()
  • reset()
  • rewind()

Java 9 미만

public abstract class ByteBuffer extends Buffer {
   // clear 메소드는 Buffer에 의해 상속됨
}
​
public abstract class Buffer {
  ...
  public Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
  }
  ...
}

Java 9 이상

public abstract class ByteBuffer extends Buffer {
   ...
   ByteBuffer clear() {
       super.clear();
       return this;
   }
   ...
}

Java 9 이상의 환경에서 arcus-java-client를 배포(source 1.6, target 1.6 설정으로 빌드)하면 ByteBuffer.clear() 메소드 호출의 바이트 코드는 아래와 같이 Java 9 기준으로 생성된다.

$ javap -c TCPMemachedNodeImpl
... (생략) ...
288: invokevirtual #50                 // Method java/nio/ByteBuffer.clear:()Ljava/nio/ByteBuffer;
... (생략) ...

Java 9 미만 환경에서 ByteBuffer 클래스는 Buffer 클래스로부터 상속받은 Buffer.clear()라는 메소드는 존재하지만, ByteBuffer.clear() 라는 메소드는 존재하지 않는다. 따라서 Java 9 이상 환경에서 빌드된 arcus-java-client를 Java 9 미만에서 사용할 경우 메소드의 Signature를 찾을 수 없어 NoSuchMethodError가 발생한 것이다.

참고로 Java 9 미만 환경에서 arcus-java-client를 배포하면, ByteBuffer.clear() 메소드 호출의 바이트 코드는 아래와 같이 생성된다.

$ javap -c TCPMemachedNodeImpl
... (생략) ...
288: invokevirtual #50                 // Method java/nio/Buffer.clear:()Ljava/nio/Buffer;
... (생략) ...

해결 방법

해결 방법은 3가지다.

  1. Java 9 미만 환경에서 빌드한다.

  2. --release 옵션을 사용하여 빌드하여, 해당 버전을 기준으로 바이트코드를 생성한다. 이 옵션으로 빌드하려면 Java 9 이상의 환경을 요구한다.

<properties>
     <maven.compiler.release>6</maven.compiler.release>
</properties>

or

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
        <release>6</release>
    </configuration>
</plugin>
  1. Buffer로 타입 캐스팅하여, Buffer의 clear 메소드를 호출하도록 바이트 코드 변경을 유도한다.
Buffer buffer = ByteBuffer.allocate(1024);
buffer.clear();                    // Method java/nio/Buffer.clear:()Ljava/nio/Buffer;                  

((Buffer) buffer).clear();     // Method java/nio/Buffer.clear:()Ljava/nio/Buffer;

참고

@hjyun328
Copy link
Collaborator Author

hjyun328 commented Apr 26, 2021

@jhpark816
참고 링크의 spring-projects에서도 Buffer 타입으로 강제 형변환을 하여 하위 호환성 버전을 지원하는 것 같습니다.
arcus-java-client의 경우도 모든 Java의 환경을 지원하기 위해 3번 방식을 적용하는 것이 좋을 것 같습니다.

참고로 문제가 되는 메소드의 Java 9 이상의 구현 내용은 부모 메소드를 호출하고, 자기 자신을 리턴하는 것 외엔 없습니다.

    ByteBuffer position(int newPosition) {
        super.position(newPosition);
        return this;
    }

    ByteBuffer limit(int newLimit) {
        super.limit(newLimit);
        return this;
    }

    ByteBuffer clear() {
        super.clear();
        return this;
    }

    ByteBuffer flip() {
        super.flip();
        return this;
    }

    ByteBuffer mark() {
        super.mark();
        return this;
    }

    ByteBuffer reset() {
        super.reset();
        return this;
    }

    ByteBuffer rewind() {
        super.rewind();
        return this;
    }

의견 부탁드리겠습니다.

@hjyun328 hjyun328 self-assigned this Apr 26, 2021
@jhpark816
Copy link
Collaborator

@hjyun328

  • 리턴 값을 가지고 다른 API를 호출하는 것이 없다면,
    모든 java 환경을 위해 type casting 하는 것이 동의합니다.
  • Comment만 잘 기록해 두세요.

@hjyun328
Copy link
Collaborator Author

PR 반영되어 클로즈합니다.

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