Spring Boot 2.0 출시

사용자 삽입 이미지

스프링 부트 2.0이 출시되었습니다.

가장 큰 변화는 Java 8을 최소 조건으로 요구하며 Java 9 지원을 확대하는것과
Spring 5.0의 새로운 기능을 사용할 수 있고
reactive 방식의 개발을 지향한다는 점입니다.

Spring 1.5를 2.0으로 마이그레이션 하기위한 가이드 문서는 이쪽( https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide )입니다.

저는 개인적으로 reactive 방식의 개발을 지원하는것에 관심이 있습니다.
스트림 프로세싱, non-blocking, 함수형(불변상태) 코드 작성이 주목을 받으며, 자바 진영도 RxJAVA 등을 통해 언어, 프레임웍 수준에서 리엑티브 방식의 구현을 위한 노력이 있어왔고 java 9는 RxJAVA 스타일의 리액티브 개발 언어가 될것이라고 한 흐름에서의 변화이기 때문입니다.

이를 위해 Spring에서는 다음 영역에 리엑티브를 지원합니다.
Spring WebFlux ( https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 
Reactive Spring Data ( 카산드라, 몽고DB, 레디스, 카우치베이스 등의  NoSQL 류에 리엑티브 API 지원 )
Reactive Spring Security ( https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#jc-webflux )

상기와 같은 변화에 따라 WebFlux 와 같이 기존 Servlet 구현체로는 대응을 할 수 없는 부분은 Embedded Netty Server 를 리엑티브 서버로 사용하며 Blocking servlet API는 제공되지 않습니다.

이외에도 3rd-party 라이브러리 업데이트 ( tomcat 8.5 , flyway 5, hibernate 5.2, thymeleaf 3 ), http/2 지원, 어플리케이션 모니터링에 자체 metric 대신 micrometer ( micrometer.io ) 적용, 기본 database pool로 HikariCP 적용 등의 변화가 있습니다.



2018/03/05 14:59 2018/03/05 14:59
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

MSSQL jdbcType javaType 매핑 정보

myBatis 로 개발 시 간혹 입출력 변수의 javaType 이나 jdbcType을 명시해 줘야 할 때가 있다.
이럴 때 참고하기위해 기록 해 둠.
참고로, myBatis의 jdbcType enum 정의는 http://www.mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/JdbcType.html 에서 확인 가능.

https://msdn.microsoft.com/ko-kr/library/ms378878(v=sql.110).aspx
SQL Server 형식 JDBC 형식(java.sql.Types) Java 언어 형식
bigint BIGINT long
binary BINARY byte[]
bit BIT boolean
char CHAR String
date DATE java.sql.Date
datetime TIMESTAMP java.sql.Timestamp
datetime2 TIMESTAMP java.sql.Timestamp
datetimeoffset(2) microsoft.sql.Types.DATETIMEOFFSET microsoft.sql.DateTimeOffset
decimal DECIMAL java.math.BigDecimal
float DOUBLE double
image LONGVARBINARY byte[]
int INTEGER int
money DECIMAL java.math.BigDecimal
nchar CHAR

NCHAR(Java SE 6.0)
String
ntext LONGVARCHAR

LONGNVARCHAR(Java SE 6.0)
String
numeric NUMERIC java.math.BigDecimal
nvarchar VARCHAR

NVARCHAR(Java SE 6.0)
String
nvarchar(max) VARCHAR

NVARCHAR(Java SE 6.0)
String
real REAL float
smalldatetime TIMESTAMP java.sql.Timestamp
smallint SMALLINT short
smallmoney DECIMAL java.math.BigDecimal
text LONGVARCHAR String
time TIME(1) java.sql.Time(1)
timestamp BINARY byte[]
tinyint TINYINT short
udt VARBINARY byte[]
uniqueidentifier CHAR String
varbinary VARBINARY byte[]
varbinary(max) VARBINARY byte[]
     
varchar VARCHAR String
varchar(max) VARCHAR String
xml LONGVARCHAR

LONGNVARCHAR(Java SE 6.0)
String

SQLXML
2016/07/25 09:26 2016/07/25 09:26
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

output parameter not allowed as argument list prevents use of rpc

2016/06/29 14:50

서비 JAVA

jTDS Driver + OUTPUT 파라메터가 있는 + Stored Procedure 를 호출하는 경우 발생할 수 있는 데이터 바인딩 이슈.
jTDS Driver 의 경우 OUTPUT 파라메터가 있는 SP 를 수행할 때 RPC 방식을 이용하여 실행 성능 향상을 꾀하는데…
이 때는 SP 의 모든 변수는 바인딩 변수를 사용해야 하며, { call sp_name ( ?, ?, ‘value’, ? , ? OUTPUT ) } 처럼 
바인딩 변수와 값 직접 할당을 동시에 기술 할 수 없습니다.

공식 문서에는 혼용해서 사용은 가능하지만 그렇게 할 경우 RPC모드가 아닌 프로시저 일반실행 모드로 동작한다고 기술되어 있지만, 실제는 output parameter not allowed as argument list prevents use of rpc 익셉션이 발생하는 경우가 있음...
이건 DBMS나 JDBC 접속 설정에 따라 달라지는것 같긴한데... 거기까진 확인 못했음.
2016/06/29 14:50 2016/06/29 14:50
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

PoolingHttpClientConnectionManager 를 xml기반 Spring bean으로 등록하기.

개발한 서비스 중 commons httpclient 로 서버 <-> 서버 간 rest api 호출하는 부분에  부분에서 간헐적으로 NoHttpResponseException : The target server failed to respond 예외가 발생하기 시작했다. 이를 해결하기위해 자료를 좀 찾아봤더니 HttpClient 4.4에서 존재하던 버그였고 4.4.1에서 해결된 문제( https://issues.apache.org/jira/browse/HTTPCLIENT-1610 )라고 하는데... 개발한 서비스에는 4.5.1을 쓰는데?

현상의 이유는 HTTP/1.1의 Keep-Alive로 인해 httpclient는 통신이 끝난 connection을 종료하지 않고 동일host:port에 대해 동일한 커넥션을 이용하려하기 때문이다.

비록 서버측은 통신이 완료되어 해당 연결을 close 할지라도 client 측은 커넥션 객체가 여전히 열여있고 데이터가 인입되길 기다리고 있게된다.  ( close()의 실제 의미는 소켓의 단절이 아닌 "나는 더 이상 보낼 데이터가 없습니다."로 상대측에서는 해당 커넥션을 단절하지 않는 이상 여전히 데이터가 인입될 수 있음을 의미한다. )
이때를 half-closed connection 으로 표현하며 이는 TCP가 그렇게 동작하게끔 설계되었기 때문으로 버그가 아니다. 이런 상황이 되면 JVM상의 connection 객체는 당연히 살아있지만 내부 소켓은 CLOSE_WAIT 상태가 된다.

문제는 httpclient가 이 CLOSE_WAIT 상태에있는 connection 객체를 다시 사용하려고 할때 앞에서 설명한것과 같이 서버 측은 이미 연결을 끊어버렸기 때문에 NoHttpResponseException - The target server failed to respond 예외를 발생한다.
이를 해결하기위해서는 httpclient의 connectionManager에서 통신이 완료된 connection을 적절하게 제거할 필요가 있다.

Spring 4.X 환경에서 commons HttpClient 4.5.x 를 소켓 설정과 KeepAlive 설정등을 포함하여 xml로 설정하는 방법.

PoolingHttpClientConnectionManager를 통하여 CloseableHttpClient 를 사용하는 과정에 ConnectionManager의 closeIdleConnections 설정 통해 특정시간 idle인 커넥션을 종료하고 싶은 경우 java 코드가 아닌 spring xml 설정정으로 bean을 등록하려 할 때. 

사용은 당연히
@Autowired
private CloseableHttpClient httpClient;

[code]
<!-- ===================================================================== -->
<!-- =======================   HttpClient 4.5.X   ======================== -->
<!-- ===================================================================== -->        
<bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig" factory-method="custom">
        <property name="socketTimeout" value="10000" /> 
        <property name="connectTimeout" value="12000" /> 
        <property name="connectionRequestTimeout" value="12000" />
</bean>

<bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build" />

<bean id="socketConfigBuilder" class="org.apache.http.config.SocketConfig" factory-method="custom">
    <!-- 소켓이 연결된후 InputStream에서 읽을때 timeout -->
    <property name="soTimeout" value="10000" /> 
    <!-- SO_KEEPALIVE를 활성화 할 경우 소켓 내부적으로 일정시간 간격으로 heartbeat을 전송하여, 비정상적인 세션 종료에 대해 감지.
    unix 계열 : /etc/sysctl.conf
    windows : \HKEY_LOCAL_MACHINE\SystemCurrentControlSet\Services\TCPIP\Parameters
    -->
    <property name="soKeepAlive" value="true" /> 
    <!-- 비정상종료된 상태에서 아직 커널이 소켓의 bind정보를 유지하고 있을 때 해당 소켓을 재사용 할 수 있도록 -->
    <property name="soReuseAddress" value="true" /> 
    <!-- nagle 알고리즘 적용 여부 -->
    <property name="tcpNoDelay" value="true" /> 
    <!-- socket이 close 될 때 버퍼에 남아 있는 데이터를 보내는데 기다려주는 시간(blocked)-->
    <property name="soLinger" value="100" /> 
</bean>

<bean id="poolingHttpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" destroy-method="shutdown">
    <constructor-arg value="2000" type="long" index="0" /> <!-- pool에 있는 커넥션 제거 idle time -->
    <constructor-arg value="MILLISECONDS" type="java.util.concurrent.TimeUnit" index="1" />
    <property name="maxTotal" value="60" />
    <property name="defaultMaxPerRoute" value="15" />
    <property name="defaultSocketConfig"><bean factory-bean="socketConfigBuilder" factory-method="build" /></property>
</bean>

<bean id="connectionKeepAliveStrategy" class="com.http.client.HttpShortKeepAliveStrategy" />

<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">            
    <property name="defaultRequestConfig" ref="requestConfig" />
    <property name="connectionManager" ref="poolingHttpClientConnectionManager" />
    <property name="userAgent" value="Mozilla/5.0 (Windows NT 6.1; WOW64) CUSTOM-CLIENT" />
    <property name="keepAliveStrategy" ref="connectionKeepAliveStrategy" />
</bean>

<bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build" destroy-method="close" />
[/code]

HttpShortKeepAliveStrategy 클래스는...
[code]
package com.http.client;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

/**
 * HttpShortKeepAliveStrategy (UTF-8) created : 2016. 5. 25
 *

 */
public class HttpShortKeepAliveStrategy implements ConnectionKeepAliveStrategy {

    /**
     *
     * @param response
     * @param context
     * @return
     */
    @Override
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        // Honor 'keep-alive' header
        HeaderElementIterator it = new BasicHeaderElementIterator(
                response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase("timeout")) {
                try {
                    return Long.parseLong(value) * 100;
                } catch (NumberFormatException ignore) {
                }
            }
        }


        HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
        if ("www.mydomain.com".equalsIgnoreCase(target.getHostName())) {
            // Keep alive for 5 seconds only
            return 5 * 1000;
        } else {
            // otherwise keep alive for 1 seconds
            return 1 * 1000;
        }
    }

}
[/code]

코드로 표현하면 대강 다음과 같음.
[code]
static final CloseableHttpClient httpClient;
static {
        PoolingHttpClientConnectionManager pooledManager = new PoolingHttpClientConnectionManager(20L,java.util.concurrent.TimeUnit.MILLISECONDS);
        pooledManager.setMaxTotal(15);
        pooledManager.setDefaultMaxPerRoute(5);
        pooledManager.closeIdleConnections(20L, TimeUnit.MILLISECONDS);
        pooledManager.setDefaultSocketConfig(SocketConfig.custom()
                // nagle 알고리즘 적용 여부
                .setTcpNoDelay(true)
                // SO_KEEPALIVE를 활성화 할 경우 소켓 내부적으로 일정시간 간격으로 heartbeat을 전송하여, 비정상적인 세션 종료에 대해 감지.
                // unix 계열 : /etc/sysctl.conf
                // windows : \HKEY_LOCAL_MACHINE\SystemCurrentControlSet\Services\TCPIP\Parameters
                .setSoKeepAlive(true)
                // socket이 close 될 때 버퍼에 남아 있는 데이터를 보내는데 기다려주는 시간(blocked)
                .setSoLinger(200)
                // 비정상종료된 상태에서 아직 커널이 소켓의 bind정보를 유지하고 있을 때 해당 소켓을 재사용 할 수 있도록
                .setSoReuseAddress(true)
                //소켓이 연결된후 InputStream에서 읽을때 timeout
                .setSoTimeout(10000)
                .build()
        );
        httpClient = HttpClients.custom()
                        .setConnectionManager(pooledManager)
                        .setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) API-CLIENT")
                        .setRedirectStrategy(new DefaultRedirectStrategy() {
                                @Override
                                public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) {
                                        boolean isRedirect = false;
                                        try {
                                                isRedirect = super.isRedirected(request, response, context);
                                        } catch (ProtocolException e) {
                                                logger.error(null, e);
                                        }
                                        if (!isRedirect) {
                                                int responseCode = response.getStatusLine().getStatusCode();
                                                if (responseCode == 301 || responseCode == 302) {
                                                        return true;
                                                }
                                        }
                                        return false;
                                }
                        })
                        .setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
                                @Override
                                public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                                        // Honor 'keep-alive' header
                                        HeaderElementIterator it = new BasicHeaderElementIterator(
                                                        response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                                        while (it.hasNext()) {
                                                HeaderElement he = it.nextElement();
                                                String param = he.getName();
                                                String value = he.getValue();
                                                if (value != null && param.equalsIgnoreCase("timeout")) {
                                                        try {
                                                                return Long.parseLong(value) * 100;
                                                        } catch (NumberFormatException ignore) {
                                                        }
                                                }
                                        }
                                        HttpHost target = (HttpHost) context.getAttribute(
                                                        HttpClientContext.HTTP_TARGET_HOST);
                                        if ("www.mydomain.com".equalsIgnoreCase(target.getHostName())) {
                                                // Keep alive for 5 seconds only
                                                return 5 * 1000;
                                        } else {
                                                // otherwise keep alive for 0.1 seconds
                                                return 1 * 100;
                                        }
                                }
                        })
                        .build();
}
[/code]

2016/05/25 15:28 2016/05/25 15:28
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. Blog Icon
    개발 김

    해당 글 너무나도 잘 봤습니다!! ㅎㅎ 해결하신 방법은 일정한 타임 아웃을 주고 해당 시간동안 아무런 반응이 없으면 해당 커넥션을 없애는 방식인데 그렇다면 사용자가 잠깐 아무 입력없이 가만히 있게 되면 커넥션이 죽게 될 텐데 이 부분에 대해서는 어떻게 생각하시는지요???

  2. Blog Icon
    서비

    개발 김님 안녕하세요.
    질문 내용은 일반적인 연결 지향인 소켓 상황과 헷갈리신것 같습니다.
    http 역시 소켓에 기반하고있습니다만 아무입력없이 대기 시 커넥션이 죽는
    현상은 일반적인 http 에서는 해당되지 않는 사항입니다.
    http 프로토롤 자체가 필요 시 연결, 데이터 송수신하고 연결을 끊는 컨셉이기 때문입니다.
    keep-alive 는 비교적 짧은 리드타임으로 송수신을 반복할 경우 접속 오버헤드를 줄이고자하는 기법의 하나입니다.

  3. Blog Icon
    개발 김

    너무 감사합니다~ 최근 이부분을 좀 관심있게 보고 있는 중이여서요!!
    HttpShortKeepAliveStrategy 클래스에서
    위에 while문이 어떤 부분을 하는 건가요??
    밑에는 해당 도메인이면 keep alive 시간을 조금 더 주는 건가요??
    직접 이 에러를 발생시켜보고 싶은데 잘 안돼네요 일부러 버전도 4.3.6 쓰는데 close_wait인 커넥션은 자기가 알아서 피해가네요;;

  4. Blog Icon
    서비

    @개발 김님
    링크의 기술문서가 도움이 되지 않을까해서 소개 드립니다.
    http://tech.kakao.com/2016/04/21/closewait-timewait/

Spring Framework 5.0

2017년 이후를 겨냥한 다음 세대 Spring 프레임워크 소식이 들립니다.
프레임워크의 전체 코드를 java 8 기반( lambda , stream api , nio2 등..)으로 작성.
JDK 9 포괄적 지원과 HTTP/2에 집중하며 Spring-style의 reactive 아키텍처를 목표로 개발 진행 중.
5.0 milestone 1 : 2016년 중순
5.0 RC1 : 2016년 12월
최소 사양
JDK 8+, Servlet 3.0+, JMS 2.0+, JPA 2.1+, JUnit 5
주요 기반
JDK 9 과 Jigsaw ( 모듈러 java를 구현하기 위한 java key feature )
Servlet 4.0 과 HTTP/2
Reactive 아키텍처

2016/04/07 14:57 2016/04/07 14:57
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Gson ExclusionStrategy 를 활용한 annotation기반 json 출력 제외

2015/12/18 10:53

서비 JAVA ,

Json Object Mapper로 널리 쓰이고 있는 Gson,
객체를 Json으로 변환하는데 클래스의 특정 필드를 출력하지 않고 싶을 때 사용 가능한 방법.
아쉽게 Gson은 필드를 json으로 출력하라는 @Expose 어노테이션만 존재할 뿐 제외하라는 @Exclude 같은 어노테이션은 아직까진 없다.

그러니까 현재까지는 어떤 필드 출력을 제외하고 싶으면 출력할 모든 필드에 @Expose 어노테이션을 적용 하고 Gson create 시에 다음처럼 @Expose 어노테이션이 없는 필드를 제외하라.. 고 설정할 필요가 있다.
[code java]
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
[/code]

그런데 대부분의 필드를 출력하고 일부만 제외하고 싶을 경우가 더 많을것 같은데.. 그 몇개 제외하자고 모든 필드에 @Expose 붙이는것도 귀찮은 일.
간단하게 @NonExpose 라든가 @Exlude 같은 어노테이션을 제공해주면 편할텐데... 우리가 생각치 못한 어떤 이유가 있겠지...

다행히 Gson 생성시 제외 조건을 설정할 수 있는 .setExclusionStrategies() 메소드를 제공한다. 그러면 다음처럼 @Exclude 어노테이션 기반 출력 제외 처리가 가능하다.

우선, 우리가 사용할 어노테이션을 정의하고...
[code java]
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}
[/code]

feildSkip 확인 코드에서 위에서 작성한 어노테이션이 있으면 제외처리되도록 Gson의 ExclusionStrategy 구현체를 작성 
[code java]
public class AnnotationBasedExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}
[/code]

마지막으로 Gson 객체 생성 시 위 AnnotationExclusionStrategy 를 적용.
[code java]
new GsonBuilder().setExclusionStrategies(new AnnotationBasedExclusionStrategy()).create();
[/code]
이제 제외하고 싶은 필드에 @Exclude 적용하면 끝~.
2015/12/18 10:53 2015/12/18 10:53
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 저는 GSon 을 사용안하고 Jackson을 주로 사용하는 편입니다.
    exclude,include 관련 옵션은 Jackson이 더 편한것 같네요.

    하지만 아무튼 가급적 exclude를 지정하기 보다는 expose(include)를 지정하는 것이 올바를 것 같습니다. 그래서 GSon도 exclude관련 기본 옵션을 두지 않은 것은 아닌가 싶습니다.

    white list 방식(expose할 필드를 명시적으로 지정하는 방식)의 처리를 하는게 맞다고 보는 것이지요.

    black list 방식(exclude를 설정하는 방식)으로 하게되면 신규 필드가 추가되었는데 그게 보안상 노출되면 안되는 필드라고 했을 때 해당 필드를 추가한 개발자가 명시적으로 exclusion을 하지 않으면 의도치 않게 그것을 외부에 노출시켜버리게 됩니다.

    더불어 다른 문제도 있는데 객체가 양방향 관계를 맺은 상태일 때입니다. 양방향 관계를 맺은 필드를 추가한 상황에서 exclusion을 잊어버리고 표시하지 않으면 실행시점이 될 때까지 오류 상황 파악을 못하고 stackoverflow를 만나게 됩니다.

    내부 API용이라면 그런대로 괜찮겠지만 특히나 아무나 데이터를 볼 수 있는 web browser와 통신하는 경우에는 white list 방식을 사용하는게 조금 귀찮더라도 보안성 더 좋을 것 같습니다.

  2. Blog Icon
    서비

    권남님 의견 감사합니다.
    말씀 주신 의도가 맞는것 같습니다.GSon 개발팀은 보안과 편의 중 보안 쪽 손을 들었을듯 합니다.... 댓글 달면서 갑자기 궁금해졌는데 @Expose 와 ExclusionStrategy를 동시에 적용하면 어느 쪽이 적용될까요? 구글 개발팀의 원래 의도대로라면 비노출 되어야 할것 같은데... 한번 확인해봐야 겠네요.

TimerTask와 ExecutorService의 차이점

  • Timer 는 시스템 시간에 의존적임. TimerTask 동작 후 시스템 시간을 변경하면 task 동작 주기도 변경된다는 말. ScheduledThreadPoolExecutor은 그렇지 않음.
  • Timer는 해당 VM안에서 하나의 실행 쓰레드만 가짐. 이런 이유로 긴 시간동안 수행되는 task는 다른 task를 지연시킬 가능성 있음. ScheduledThreadPoolExecutor은 동시 수행될 쓰레드 수를 조절가능하며, 원한다면 ThreadFactory를 통해서, 생성된 쓰레드에 대한 제어권을 획득할 수 있음.
  • TimerTask에서 Runtime exception이 발생하면 그 하나 뿐인 쓰레드가 죽어버림. 고로 모든 나머지 스케줄링된 task들은 더 이상 수행되지 않음.. ScheduledThreadPoolExecutor는 runtime exception을 catch할 뿐만 아니라 ThreadPoolExecutor의 afterExecute 오버라이딩을 통해 익셉션을 핸들링할 수도 있으며 익셉션이 발생한 task는 cancel되지만 나머지 task는 계속 수행 됨.


ScheduledThreadPoolExecutor를 사용할 수 있는 환경이라면 Timer 대신에 ScheduledThreadPoolExecutor를 써라.
[code java]
java.util.concurrent.Executors.newScheduledThreadPool(8);
[/code]

ScheduledThreadPoolExecutor는 java5에 도입되었지만 java1.4 이전 환경을 위한 backport 을 통해
http://backport-jsr166.sourceforge.net/  ScheduledThreadExecutor 클래스를 제공함.

2015/12/10 13:06 2015/12/10 13:06
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

java 8 DateTime

With Java < 8, you would need to write something like:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR, cal.get(Calendar.HOUR) + 2);

vs. with Java 8:

LocalTime now = LocalTime.now();
LocalTime later = now.plus(2, HOURS);

The improvements are essentially on

  • readability:
    • Calendar.getInstance() is not very well named: it is hard to tell which instance you are getting without reading the Javadoc. LocalTime.now() is quite self-describing: you get a time and it is now.
    • To offset a date, you call an offsetting method (plus) whereas with the Calendar API, you have to manually change the fields of the object (in this example, the hour) which is error prone.
  • ease of use (see for example the table towards the bottom of this page for a comparison):
    • the Calendar API is complicated to use because it mixes concepts, such as a simple date (June 26th 2015) and an instant in time (June 26th 2015 at 10am UTC) - there isn't a class for the former concept
    • The new Time API has a clear separation between the various date/time concepts
  • safety:
    • The Calendar API is not safe: nothing prevents you from writing cal.set(123, 2) which would throw a not-so-helpful ArrayOutOfBoundsException. The new API uses enums which solves that problem.
    • The new API uses immutable objects, which makes it thread safe.

Overall, the new API is significantly inspired from jodatime which has been the preferred Java Date API for quite some time now. You can also read this detailed comparison of Java (<1.8) date vs. JodaTime (most of it should apply to the Java 8 Date API).

참고 : http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html

2015/10/28 10:05 2015/10/28 10:05
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Migrating from Spring Security 3.x to 4.x

WebSocket과 Spring Data, Test를 지원하는 Spring Security 4.0이 출시되었는데요.
기존 3.X 버전에서 4.X 버전으로 마이그레이션에 참고할 수 있는 코드가 github에 있네요.

XML 기반 프로젝트 마이그레이션
https://github.com/spring-projects/spring-security-migrate-3-to-4/compare/xml
코드기반의 프로젝트 마이그레이션
https://github.com/spring-projects/spring-security-migrate-3-to-4/compare/jc
2015/07/21 07:35 2015/07/21 07:35
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Jetty 9.3 출시와 HTTP/2 지원

올해로 Jetty 탄생 20주년이되었습니다. 지난 7월12일 Jetty 9.3이 출시되었는데요. 가장 큰 특징은 HTTP/2 를 지원하고 최소 구동 Java 버전이 8이 되었습니다. 9.2.X 버전에 있었던 버그를 400개 이상 fix하고 SPDY를 제거했다고 하네요.
https://dev.eclipse.org/mhonarc/lists/jetty-announce/msg00080.html

Jetty의 리드개발자인 Greg Wikins는 그의 블로그를 통해 Jetty에서의 HTTP/2 에 대해 글을 올렸습니다.(https://webtide.com/introduction-to-http2-in-jetty/)

[code]$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2,http2c[/code]
와 같은 형식으로 HTTP/2 를 활성화 할 수 있으며, HTTP/2 를 지원하기 위해 새로운 커넥터를 생성하거나 포트를 할당하는 방식이 아닌 기존 커넥터의 HTTP/2 지원을 활성화 하는 방식이라고 합니다.

2015/07/21 07:23 2015/07/21 07:23
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Java8 Stream API와 Lambda

타니모토 신 (  谷本 心 , @cero_t  ) 님이 작성한 자료에서 영감을 받아 Java8 의 Stream API와 Lambda 에 대해 정리해 보았습니다.

2015/06/09 13:54 2015/06/09 13:54
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

ConcurrentHashMap , Collections.synchronizedMap(Map) 어떤걸 써야 하는거?

멀티쓰레드 환경하에서 적절히 사용할 수 있는 Map계열 자료형으로 ConcurrentHashMap , Collections.synchronizedMap(Map) 이 있는데 어떤걸 사용하는게 더 효과적인지 확인해 봤다.

https://ria101.wordpress.com/2011/12/12/concurrenthashmap-avoid-a-common-misuse/ 과 http://stackoverflow.com/questions/1291836/concurrenthashmap-vs-synchronized-hashmap 의 글을 통해 Collections.synchronizedMap(Map) 과 ConcurrentHashMap은 lock 메카니즘이 다름을 알 수 있었다.

다음은 대표적인 map 계열 클래스의 차이점을 표로 정리.
속성 HahsMap Hashtable ConcurrentHashMap
Null value/key 허용 허용 비허용
thread safe 아님 안전
lock 메카니즘 없음 전체 lock portion of map
iterator fail-fast fail-safe
2015/02/04 17:41 2015/02/04 17:41
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Java SE 8 의 새로운 기능.

2014/06/30 21:34

서비 JAVA

Java 8 의 closure, lambda, Nashorn 등 굵직한 변화에 묻혀 잘 화자되진 않지만 몇 가지 유용한 기능을 살펴보면
  1. Stamped Lock
    멀티 쓰레드 코드는 오랜 기간 (지금도 여전히) 서버 개발자의 골칫거리 중에 하나이다. 오랜 시간 Java Core에는 공유된 자원에 대해 최소한의 잠금시간을 위한 여러 라이브러리들이 추가되어 왔다. 대표적인 ReadWriteLock는 읽기와 쓰기를 위한 lock을 분리하여 잠금시간을  최소화할 수 있도록 지원( 동기화 클래스 - ReadWriteLock 인터페이스의 ReentrantReadWriteLock ) 하고 있다. 이게 이론상으로는 나이스한 아이디어 이긴한데 실제로 이 ReadWriteLock은 너무 느리다는 문제가 있다.
    이 문제를 해결하기 위해 Java 8에서는 StampedLock 이라는 새로운 ReadWrite lock을 소개하고 있다. 좋은 소식은 이 StampedLock은 정말 빠르다는거고 나쁜소식은 코드를 작성하는게 좀 더 복잡하다는거다. 이 lock은 reentrant 하지 않으며, 이는 곧 deadlocking에서 자유로움을 의미한다.
    [code java]StampedLock lock = new StampedLock();
    //빠른 non-blocking lock 획득
    long stamp = lock.tryOptimisticRead();   
    // 원하는 동작 작성.
    work();
    if (lock.validate(stamp)) {
    //work() 작업 성공. 다른 writer thread 와 경합 없었음.
    } else { // 다른 쓰레드의 write 작업으로 경합이 있다면 read lock으로 재시도.
    //스탬프를 변경하는동안 다른 스레드는 쓰기 잠금을 획득해야함.    
    //기존의 무거운 read lock으로 변경.
    //전통적인 blocking lock 획득
    stamp = lock.readLock(); 
    try {
    //다시 한번 시도
    work();
    } finally {
    //lock 해제
    lock.unlock(stamp);
    }
    }
    [/code]
    StampedLock과 기존의 ReadWriteLock , synchronized 과의 성능 비교 아티클도 있으니 참고.

  2. Concurrent Adder
    가장 기본적인 동시성 패턴은 읽기 / 쓰기에 대한 숫자 카운터이다. 이걸 처리하는데는 많은 방법이 있지만 Java 8에서 제공하는것 만큼 효율적이거나 우아하진 못했다. 지금까지는 sun.misc.Unsafe class를 활용한 CPU의 CAS( Compare and Swap ) 인스트럭션에 직접 counter를 설정하는 Atomics를 이용하는것이었다. 이 방식의 문제는 경합에의해 CAS에 실패하는 경우 AtomicInteger를 증가하면서 CAS에 성공할때까지 루프 상태가 되는데 이 부분이 성능의 발목을 잡고 있었다.
    Java8의 Concurrent Adder 계열의 클래스는 read / write에 함께 사용하는 편리한 방법을 제공한다. 엄청 간단해서 LongAdder 객체를 만들고 add() 나 intValue() 같은 메소드를 호출하기만 하면된다. 

    기존 Atomics와 Adder의 차이점은 경합에의한 CAS 실패 시 Adder는 CPU를 spin하는 대신 증분값(delta)를 해당 쓰레드를 위해 할당된 internal cell object에 기록한다. 그런 다음 intValue() 결과에 다른 보류중인 cell에 기록해둔 delta를 더한다. 이것으로 CAS를 시도하거나 다른 쓰레드를 block하는 횟수를 줄일 수 있게된다.

  3. Parallel Sorting
    conccurent Adder의 고속화에 힘입어 Java 8에서는 소팅을 고속화 할 수 있는 간결한 벙법을 제공한다. 사용방법도 상당히 단순하다.[code java]
    //기존방식
    Array.sort(arrayToSort);
    //새로운 방식
    Array.parallelSort(arrayToSort);
    [/code]

  4. new Date API
    Java 8 에서는 완전히 새로운 date-time API를 제공한다. 사용하기 편하고 정확도가 높은 joda time API가 Java core library에 포함 되었다.지금까지의 Date 관련 API는 대부분 deprecated 된다. 새로운 API는 기능적이면서도 우아하지만 아쉽게도 기존 api를 사용하는 방대한 코드는 여전히 남아있고 빠른시간에 새로운 api로 교체되지는 못할것이다.

    기존 api (java.util.Date java.sql.Date 등)와 신규 api의 갭을 메우기위해 기존 Date 객체를 Instant로 변경하기 위한 toInstant() 메소드와 ㅑInstant를 Date 로 변경하기 위한 from(Instant instant) static 메소드를 추가하였다. 이는 특히 새로운 API보다는 기존 API로 작업하는것을 선호하는 개발자에게 효과적일 수 있다.

  5. OS Process Control
    JNI를 통한 OS 프로세스 호출이 필요악일 지라도 자바 코드 내에서 OS 프로세스를 실행한 경우 실행중인 프로세스를 제어하기 어려웠다. 이를 개선하기위해 Process class에 다음 세개의 메소드를 추가하였다.
    - destroyFprcibly() : 기존 destroy() 보다 더 높은 프로세스 종료율을 제공.
    - isAlive() : java code를 통해 실행한 프로세스가 살아있는지 확인.
    - waitFor() : 새로 오버로드한 waitFor() 메소드를 통해 프로세스 종료를 얼마나  기다릴지 정할수 있음.

    프로세스가 끝나지 않은 경우 끝내고 다음 코드로 진행.
    [code java] if (process.wait(MY_TIMEOUT, TimeUnit.MILLISECONDS)){
    //success!
    } else {
    process.destroyForcibly();
    } [/code]
    코드가 완료되기 전 어떤 프로세스도 남겨두지 않으려 할 경우, 느리지만 확실하게 OS에서 사라지게하기.
    [code java] for (Process p : processes) { if (p.isAlive()) { p.destroyForcibly(); } } [/code]

  6. Overflow 대응 숫자 연산
    숫자 연산에서의 overflow는 가끔 끔찍한 버그를 만든다. 특히 int 값을 증가하는 count 값을 이용하는 시스템에서 특히 그런데, 개발계나 스테이징에서는 발견하지 못하고 오랜기간 운영하는 production 모드에서 발생하면 그야말로 재앙이다.

    이런 상황에 도움이 되고자 Java 8에는 부호에 민감한 계산에서 overflow발생 시 ArithmeticException을 던지는 새로운 exact 메소드가 추가되었다.
    [code java]
    //overflow 시 ArithmeticException 발생
    int multi = Math.multiplyExact( bigA, bigB ); [/code]
    이 새로운 메소드의 한가지 단점이라면 overflow가 발생할 코드의 위치를 찾는것은 개발자의 몫이란거다.

  7. Secure Random Generator
    Java는 근 몇년 간 보안 허점과 분투하고 있다. JVM과 Framework에 걸친 광범위한 보안조치가 취해지고 있다.  낮은 수준의 난수로 암호화 키를 생성하거나, 민감한 정보를 해시하는 경우도 해킹에 취약한 시스템을 만든다.

    오랜기간동안 Random Number Generator algorithm은 개발자 영역으로 남겨져 있었고. 구현체가 특정 하드웨어 / OS / JVM에 의존하는 경우, 원하는 알고리즘을 사용하지 못할 수 있었다. 문제는 이런 경우 응용 프로그램은 공격의 더 취약한 Generator를 기본값으로하는 경향이 있었다.

    Java 8에는 JVM 보안 공급자/알고리듬을 선택하기를 원하는 개발자를 위해 SecureRandom.getInstanceStrong() 를 새로이 추가하였다. 이 메소드는 hardware / OS / JVM에 대한 완전한 통제권이 없는 상황에서 ( cloud , PaaS 등.. ) 개발 할 때 적절한 솔루션이 될 수 있다.

  8. Optional 참조
    NullPointer는 말하자면 문지방에 찧은 발가락 같다. 실제로 저명한 언어 디자이너 / 석학 중 일부는 null reference를 고안한 건 엄청남 실수라고 말하는 사람도 있다. 나는 개인적으로 null reference 가 있는게 더 자연스럽다고 생각하는 쪽이긴 하지만... 여하튼 null pointer 관련 에러는 성가신 존재임에는 틀림없다. Java 8 에서는 이런 Null pointer 처리를 도와줄 Optional<T> 템플릿이 도입되었다.

    Scala와 Haskell에서 빌려온 이 템플릿은, 템플릿에 전달하거나 함수에의해 반환 된 참조가 null이 될 수있는 경우를 명시적으로 언급하는 것이 가능해졌다. 이렇게 함으로써 코드 작성 시 document 도움 없이도 런타임에 특정 참조가 null인 경우가 있을지 없을지 guess game 하는 정력 낭비를 줄여준다.
    [code java]Optional<User> tryFindUser(int userID) { }
    //혹은
    void processUser(User user, Optional<Cart> shoppingCart) { }
    [/code]또한, Optional 템플릿은 null 이외의 값을 사용할 수 있는지 확인하기 위해, isPresent() 람다에서는 ifPresent() 메소드를 제공한다.
2014/06/30 21:34 2014/06/30 21:34
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

lambda 와 closure 의 차이점 ?

Java SE 8에 도입되기 전부터 관심을 받던 람다식과 closure. 이 둘의 차이는 뭘까?

한마디로 lambda는 이름이 없는 익명 함수( anonymous function ) 를 가르키며,
closure는 하나 이상의 엮인(bound) 변수가 있는 환경에서 실행( or 평가)되는 함수 정도로 이해할 수 있겠다.

자유변수(메소드 파라메터도 아니고 코드 내부에도 저장되지 않는 변수)의 값을 포함하는 코드 블록의 기술용어가 클로저 closure이다.누군가 자신이 사용하는 언어에 클로저가 있다고 흐뭇해한다면, 자바에도 클로저가 있으니 안심해도 좋다. 자바에서는 람다 표현식이 클로저다. 사실 이너 클래스가 클로저로 존재해왔다. 다만, 자바8에서는 매력적인 문법으로 클로저를 제공한다.
- 가장 빨리 만나는 자바8 (길벗) 에서.


두 의미에 대한 깊은 논의는 다음 링크에서 확인 할 수 있음.
http://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda
http://www.langdev.org/posts/38

그럼, Clojure 는?
http://clojure.org/ 에서 정의하고 있는것과 같이 JVM(Java Virtual Macine) , CLR(Common Language Runtime), JavaScript 엔진에서의 동작을 목표하는 dynamic programing language. 단어를 처음 접했을 때 정의를 찾아보지 않고, Kroisse님이 알려주기전까지 두 단어가 혼용되어 쓰이고 있다고 적당해 생각해 버렸던게 부끄럽넹...

2014/06/25 15:13 2014/06/25 15:13
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. Clojure는 프로그래밍 언어 이름이고, 말씀하시는 개념은 closure입니다.

  2. Blog Icon
    서비

    Kroisse님. 고맙습니다.
    영미권 검색 시에 Clojure, closure 모두 검색되어서 혼용해서 사용하는 줄 알았습니다. 알려주신 내용으로 좀 더 명확하게 공부하도록 하겠습니다.

java se 8 에서 람다식 사용하기 - 기초

java 8 에 람다 표현식이 도입되면서 closure를 사용하지 않고도 함수형 언어 비슷한 코드를 작성하는게 가능해졌다. 람다 표현식은 이름이 없는 익명 함수를 의미한다.
람다 표현식에 관심을 둬야하는 몇가지 이유는 다음과 같다.
  • OOP 언어인 Java에서 함수형 코드를 작성할 수 있게 해준다.
  • 람다 표현식을 이용함으로써 간결하고 명확한 코드로 그 의도를 표현할 수 있다.
  • Collection filtering, Iteration , Extraction 등에서 놀라운 코드 생산성을 보인다.


아주 아주 기본적인 람다 표현식에 대해 익혀보자.
Syntax
[code javascript](argument) -> (body)[/code]
0개 이상의 인자를 실행하고자 하는 함수 body 로 전달한다는 의미이며, 이는 람다 표현식의 일반적인 구문이다.
예를 들면 다음과 같이 표현된다.
[code javascript](int a, int b) -> a*b
(Person p) -> { return p.getAge();}[/code]

다음 코드는 swing 에서 자주 보이는 이벤트 핸들러를 람다로 변경하는 코드이다.
[code java]btnOk.setOnAction( newEventHandler() {
 @Override
 public voidhandle(ActionEvent e) { //버튼 action에 처리할 코드를 여기에 작성
 }
});[/code]
위 코드를 람다 표현식으로 변경하면 다음과 같다.
[code javascript]btnOk.setOnAction( e -> { //버튼 action에 처리할 코드를 여기에 작성
});[/code]

표현식의 또 다른 예로 익명 클래스는 다음과 같이 작성할 수 있다.
[code java]new Thread(
() -> System.out.println("This is a seperate thread")
).start();[/code]

마지막으로 collection을 다루는 예제만 더 보고 끝내자.
[code java]ArrayList arrayList= new ArrayList<>();
for(int i=1; i<=100; i++)
arrayList.add(i);

//일반적인 for-loop 문
for(Integer i: arrayList)
{ System.out.println(i);
}

//lambda식으로 표현한 for-loop문
arrayList.forEach( i -> {System.out.println(i);});[/code]

이상으로 람다 표현식이 어떻게 코드 수를 줄이고 Java와 함수형 언어 사이의 갭을 매워주는지에 대해 간략히 알아보았다.

2014/06/25 14:32 2014/06/25 14:32
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다