본문 바로가기

Programing/Framework

[spring integration] TCP 연결시간 설정

버전 의존성

Spring boot Spring core Spring integration Jackson core
2.1.6 5.1.8 5.1.6 2.9.0
2.2.0 5.2.0 5.2.0 2.10.0
2.2.4 5.2.3 5.2.3 2.10.2
2.2.5 5.2.4 5.2.4 2.10.2

 

TCP 통신을 하는 것을 만들고 있는데, 아직 상대방 방화벽이 닫혀있는지 긴 타임아웃이 발생하였다.

기본 연결 대기 시간 이전에 timeout이 발생하게 하고 싶었다.

 

AbstractClientConnectionFactory 에 정의되어 있는 기본 연결 타임아웃은 60초이다.

구현체에서 소켓을 만들때 getConnectTimeout() 이라는 메서드를 통해 값을 구해온다.

예) https://github.com/spring-projects/spring-integration/blob/master/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpNetClientConnectionFactory.java#L93

 

이 값은 TcpConnectionFactoryFactoryBean 의 connectTimeout의 프로퍼티가 null일 아닐 경우에 설정이 된다.

TcpClientConnectionFactorySpec 의 connectTimeout 을 통해서도 설정이 가능하다.

 

문제는 내가 사용하고 있는 스프링 부트 버전이 2.1.6인데 spring-integration 5.1.6에 의존을 하고 있었다.

connectTimeout 은 주석에도 써있지만 5.2부터 생겼다.

 

현재 스프링 부트 버전은 2.2.4인데 spring-integration 는 5.2.3로 올라갔다.

스프링부트 버전을 2.1.6에서 2.2.4로 올렸더니 Jackson 도 버전이 올라가서 테스트코드가 깨진다.

1) Jackson - readValue - TypeReference

컴파일 에러: Error:(61, 26) java: incompatible types: java.lang.Object cannot be converted to T

readValue(String content, TypeReference valueTypeRef) 가

readValue(String content, TypeReference<T> valueTypeRef) 로 바뀌었다.

2) Jackson - IllegalArgumentException <- NullPointerException

NullPointerException 예외를 던지던 것이 IllegalArgumentException 로 바뀌었다.

이것은 ObjectMapper의 내부에 _assertNotNull가 추가되었기 때문이다.

public <T> T readValue(String content, Class<T> valueType)
    throws IOException, JsonParseException, JsonMappingException
{
    return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
} 

public <T> T readValue(String content, Class<T> valueType)
    throws IOException, JsonParseException, JsonMappingException
{
    _assertNotNull("content", content);
    DefaultDeserializationContext ctxt = _deserializationContext();
    return (T) _readMapAndClose(ctxt,
            _streamFactory.createParser(ctxt, content), _typeFactory.constructType(valueType));
} 

기존에는 내부의 ByteSourceJsonBootstrapper에서 readUnsignedByte() 메서드가 호출 될때 까지 call stack 안으로 들어갔다.

at com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.skipUTF8BOM(ByteSourceJsonBootstrapper.java:187)
at com.fasterxml.jackson.core.JsonFactory._createParser(JsonFactory.java:1378)
at com.fasterxml.jackson.core.JsonFactory.createParser(JsonFactory.java:937)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3135)

3) Jackson - 인코딩 깨짐 (한글이 깨진다) Issue 

5.1.8에서는 MockMvc의 servletResponse의 characterEncoding이 처음에는 ISO-8859-1였다가 나중에 UTF-8로 바뀌었다.

org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal

사실 이것은 아래 4번의 MediaType을 APPLICATION_JSON_UTF8_* 에서 APPLICATION_JSON 로 바꾸면서 발생하는 이슈이다.

https://namocom.tistory.com/832 참고.

 

이것은 테스트 비교를 할 때 string 이 아닌 byte[]로 비교를 하면 된다.

ContentResultMatchers: string('') -> bytes(''.getBytes(StandardCharsets.UTF_8))

4) Deprecated : MediaType

org.springframework.http.MediaType#APPLICATION_JSON_UTF8_VALUE

	/**
	 * A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}.
	 * @deprecated as of 5.2 in favor of {@link #APPLICATION_JSON_VALUE}
	 * since major browsers like Chrome
	 * <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
	 * now comply with the specification</a> and interpret correctly UTF-8 special
	 * characters without requiring a {@code charset=UTF-8} parameter.
	 */
	@Deprecated
	public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";

5) MockMvc 인코딩 깨짐

org.springframework.test.web.servlet 패키지의 MockMvc 으로 수행하던 테스트 코드가 인코딩 깨진다.

GitHub 에도MockMvc no longer handles UTF-8 characters 와 같이 이슈가 올라온 적이 있다.

답변은 String 대신 bytes 배열을 래핑해서 쓰라는 것..

MockHtpServletResponse: getContentAsString() -> getContentAsString(StandardCharsets.UTF_8)

단점은 ...Sonarqube는 지표가 엄청 깨짐.