반응형

자바 Stream에서 Map, Object내부에 또 다른 Map, Set, List와 같은 Collection이 있을 때 각 value를 개별된 값으로 처리하고 싶은 경우



Student POJO

public class Student {

private String name;
private Set<String> book;

public void addBook(String book) {
if (this.book == null) {
this.book = new HashSet<>();
}
this.book.add(book);
}
//getters and setters

}



flatMap() and Set example

1차적으로 map으로 POJO에 Set에 담긴 Book데이터를 가져와 flatmap으로 펼친 후 list로 collect

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class TestExample2 {

public static void main(String[] args) {

LexiconMain.Student obj1 = new LexiconMain.Student();
obj1.setName("mkyong");
obj1.addBook("Java 8 in Action");
obj1.addBook("Spring Boot in Action");
obj1.addBook("Effective Java (2nd Edition)");

LexiconMain.Student obj2 = new LexiconMain.Student();
obj2.setName("zilap");
obj2.addBook("Learning Python, 5th Edition");
obj2.addBook("Effective Java (2nd Edition)");

List<LexiconMain.Student> list = new ArrayList<>();
list.add(obj1);
list.add(obj2);

List<String> collect =
list.stream()
.map(x -> x.getBook()) //Stream<Set<String>>
.flatMap(x -> x.stream()) //Stream<String>
.distinct()
.collect(Collectors.toList());

collect.forEach(x -> System.out.println(x));
}

}


Output

Spring Boot in Action
Effective Java (2nd Edition)
Java 8 in Action
Learning Python, 5th Edition


Stream관련 자세한 예제가 더 궁금하다면 아래 링크로


참고 : https://www.mkyong.com/java8/java-8-flatmap-example/

반응형
반응형


Java8 Stream을 사용하다 보면 자주 발생할 수 있는 미묘한 실수 중에 하나는


이미 사용했던 Stream을 다시 사용하려고 하는 것이다. Stream은 오직 한 번만 사용할 수 있다.


Stream should be operated on (invoking an intermediate or terminal stream operation) only once. 


A Stream implementation may throw IllegalStateException if it detects that the Stream is being reused.


이미 사용했던 Stream을 또 사용하게 될 경우 다음과 같은 에러 로그를 발견할 수 있을 것이다.


java.lang.IllegalStateException: stream has already been operated upon or closed

at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)

at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)

at com.payco.cm.service.CacheService.cachingPartnerUrlMap(CacheService.java:31)

at com.payco.cm.service.CacheService$$FastClassBySpringCGLIB$$d06ada09.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)

at org.springframework.cache.interceptor.CacheInterceptor$1.invoke(CacheInterceptor.java:52)

at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:344)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:407)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:326)

at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)



[ 문제가 발생했던 코드 ]



코드를 보면 Stream을 두 번 사용하고 있는 것을 볼 수있다.


로깅기능으로 잠깐 보려고 했던 코드가 문제가 되었다.


정리하자면

Stream shoud be used only Once!!!


반응형
반응형

[ Spring ] 문제해결 no suitable constructor found, can not deserialize from Object value 


API로 JSON 데이터 받아와 모델에 매핑시키는 부분에서 다음과 같은 에러가 발생하였다.


org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not construct instance of beomcess.coin.contractor.entity.Bittrex$BittrexCoin: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

 at [Source: java.io.PushbackInputStream@423b2b62; line: 1, column: 41] (through reference chain: beomcess.coin.contractor.entity.Bittrex["result"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of beomcess.coin.contractor.entity.Bittrex$BittrexCoin: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

 at [Source: java.io.PushbackInputStream@423b2b62; line: 1, column: 41] (through reference chain: beomcess.coin.contractor.entity.Bittrex["result"]->java.util.ArrayList[0])

at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:240)

at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:225)

at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)

at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)


쉽게 말해 API 응답으로 받아와 JSON 데이터가 내가 매핑하고자 하는 Model에 알맞지 않다는 것이다...


API호출 결과 날아오는 JSON의 형태는 다음과 같았다.



내가 받고자 했던 JAVA MODEL의 형태는 다음과 같았다.


문제는 Result Array 내부에 있는 데이터들을 매핑해 오지 못해생겼던 것 같다.



일단 해결은 내부의 BittrexCoin 클래스에 static을 선언해 해결하였다.



이렇게 inner class를 static으로 변경하고 나니 정상적으로 데이터를 받아 올 수 있었다.


static 지시사의 역할은 메소드나 변수를 메모리에 로딩해서 다른 클래스가 이 클래스의 인스턴스를


생성하지 않고서도 사용할 수 있게 해주는 목적이다. 


RestTemplate으로 요청을 날리면서 동시에 내가 원하는 모델에 매핑해서 데이터를 가져오는데 static을 선언해주지 않았을 경우에


inner class인스턴스를 못만들어내서 매핑이 되지 않는 것 같다. 더 정확한 원인까지는 잘 모르겠다...


나중에 시간되면 더 파고들어볼만한 이슈인 것 같다.


혹시 원인에 대해 아시는분이 계시다면 댓글남겨주시면 감사하겠습니다. 







반응형
반응형

Java NanoTime To Seconds


보통 특정 메서드를 실행하거나 로직을 수행할 때 시간이 얼마나 걸리는지 측정(성능측정)하기 위해서 nanotime을 자주사용한다.

long start = System.nanoTime();


하지만 나노타임(nanotime)은 우리가 보고 판단하기에는 너무 어렵다...초로 바꿔보자...


자바에서 nanotime을 second(초)로 변환하는 방법은 두 가지이다.


1. TimeUnit.SECONDS.convert(nanotime, TimeUnit.NANOSECONDS을 이용하는 방법


이 방법은 진짜 딱 초단위 까지만 나오게 된다. 11.035초의 시간이 걸렸더라도 딱 11초만 표기된다.


2.((double) nanotime) / 1000000000; 을 사용하는 방법


이 방법은 11.33599 형태로 double형 단위에 맞는 초단위로 표기된다.



반응형
반응형


[ 자바성능튜닝교육 회고 ]


월요일(20171030), 화요일(20171031) 이틀간 회사내에서 '자바성능튜닝 대한 교육을 6시간에 걸쳐 수강하였다.

교육도 들었겠다 관련해서 간단히 들었던 내용과 생각을 정리해보려한다.

먼저 성능을 튜닝하기 위해서는 무작정 '튜닝할거야' 라고 달려드는게 아니다.



번째로 튜닝을 하고자 하는 어플리케이션의 어떤 부분에서 병목이 발생하고 있는지 살펴볼 필요가 있다.

어떤 부분에서 병목이 발생하는지 발견했다면 튜닝을 하기전 요청에 대한 response time 철저하게 체크하고 확인해보자.

추후 효율적으로 튜닝이 되었는지 확인을 위한 필수적인 부분이다.

일반적인 어플리케이션에서 병목이 발생하는 부분은 대부분 DB관련된 부분일 확률이 높다.


성능 튜닝에 있어서 기본은  throughput(처리량), response time(응답시간) 척도가 있다.

먼저 throughput(처리량) 경우 특정한 시간내에 얼마나 많은 transaction 처리할 있느냐가 중요하다.

처리량은 보통 TPS, TPM, TPH 표시한다.

TPS = Transaction Per Seconds

TPM = Transaction Per Minutes

TPH = Transaction Per Hours

3,600 TPH = 60 TPM = 1 TPS

일반적으로는 TPS 가장 많이 사용하고 TPS 높으면 높을 수록 좋다.

반면에 요청에 대한 response time(응답시간) 당연히 짧으면 짧을 수록 좋다고 있다.



만약 성능측정툴(scouter, jmap, jvh)등을 통해서 어플리케이션 내부를 튜닝할 경우에는 실제로 

cpu 가장 많이 사용하는 모듈(메소드, 라이브러리) 확인하고 코드 개선이 필요하다.

응답시간을 가장 많이 잡아먹는 메소드를 먼저 선행 튜닝하도록 하자.


다음과 같은 경우는 당연히 a 메서드에 대한 튜닝을 진행하는게 어플리케이션 전체 효율을 높이는데 효과적일 것이다.



또한 어플리케이션 개발 초기에 특정 프레임워크를 사용할 경우 먼저 선행 성능 측정이 필요하다.

만약 어플리케이션의 특정 프레임워크의 버전을 올려 배포해야 하는 상황이라면 기능 개선 작업과 함께 

배포하여 테스트하는 것은 지양하는 것이 좋다. 실제로 문제가 발생했을 프레임워크 버전업으로 인한 문제인지

추가된 로직들에 의한 문제인지 확인이 힘들기 때문이다. 



만약 간단하게 서버에 떠있는 자바 인스턴스에 대한 모니터링이 필요하다면 jvmtop 사용하는 것을 추천한다.

생각보다 손쉽게 해당 인스턴스의 CPU점유 쓰레드, 내부에서 동작하고 있는 메서드들에 대한 정보를 확인할 있다.

https://github.com/patric-r/jvmtop

 JvmTop 0.8.0 alpha   amd64  8 cpus, Linux 2.6.32-27, load avg 0.12

 https://github.com/patric-r/jvmtop


  PID MAIN-CLASS      HPCUR HPMAX NHCUR NHMAX    CPU     GC    VM USERNAME   #T DL

 3370 rapperSimpleApp  165m  455m  109m  176m  0.12%  0.00% S6U37 web        21

11272 ver.resin.Resin [ERROR: Could not attach to VM]

27338 WatchdogManager   11m   28m   23m  130m  0.00%  0.00% S6U37 web        31

19187 m.jvmtop.JvmTop   20m 3544m   13m  130m  0.93%  0.47% S6U37 web        20

16733 artup.Bootstrap  159m  455m  166m  304m  0.12%  0.00% S6U37 web        46



위의 내용 모두 중요하지만 가장 기본은 역시 코딩을 해당 코드가 어떻게 동작할지 어떻게 하면 메모리, cpu 적게 사용할 있을지


고민하면서 효율적인 코드를 작성하는 것이다.


'좋은 코드가 좋은 시스템을 만든다


반응형
반응형


작업 중 java.sql.SQLException: Before start of result set 오류가 발생하였다.



뭔가 하고 보니 소스코드에서 mysql에 질의를 던져 데이터를 가져와서 ResultSet에 담는데 ResultSet에서 데이터를 읽을 경우 cursor의 points를 첫 번째 로우에 맞추어주어야 한다.


즉, 결과값을 읽기전에 next() 메서드를 호출 후 사용 하여야 한다. 


[ 오류가 발생했던 소스코드 ] 




[ 문제 해결 소스코드 ] 



보다시피 result.next() 전에 result.getString()으로 값을 불러오려고 했을 때 문제가 발생했다.

result.next() while문 안으로 넣어서 해결~!

반응형
반응형

 

 안녕하세요. 오늘은 간단하게 자바 EXCEPTION처리시  EXCEPTION이 처리되는 우선순위에 대한 궁금증이 생겨 확인한 내용 간단히 공유해봅니다. 포스팅을 너무 장황하게 하려하니 글쓰는 수도 줄어들고 해서 앞으로는 간단히라도 자주 기록을 남기는 습관을 들이려합니다:)


상 황 ]

 2개의 exceptionHandler 클래스가 있고 범위는 다음과 같다.  

 

1번 - @ControllerAdvice("com.nhnent.webcc")       (프로젝트 전체 패키지)          

  -   (2번이 1번에 완전 귀속)   - 

2번 - @ControllerAdvice("com.nhnent.webcc.controller.api")  (API 관련 패키지) 


 

[ 궁금증 ]

2번 패키지 범위의 클래스에서 1번 범위의 메서드 실행하다가 에러가 발생할 경우 1번 handler에서 처리가 될지 2번 handler에서 처리가 될지?


예측이 되시나요? ㅎㅎ

 

동작확인  ]

실제로 1번 범위의 클래스에서 exception이 발생했을지라도 ExceptionHandlerExceptionResolver에 의해 메서드를 호출한 상위

클래스를 찾아가게 되고 그 클래스내에서 exception이 발생하게 된다. 따라서 2번 handler에 의해서 exception이 처리되게 된다.

 

궁금하시거나 잘못된 부분이 있다면 코멘트 부탁드립니다.




오늘도 좋은하루보내세요:)

 



반응형
반응형


 안녕하세요. 오늘은 ZIP파일 안의 파일 중 한글명으로 된 파일이 있을 경우 해당 파일에 접근하지 못하는 문제를 COMMONS COMPRESS라이브러리를 통해 해결한 방법을 공유하도록 하겠습니다. 최근 작업 중에 고객들이 넣는 문의들 중 파일 첨부된 ZIP 파일의 경우 해당 ZIP파일 내에 .exe파일이 있는지 확인해 인입이 안되도록 처리해야 하는 작업이 있었습니다. 처음엔 COMMONS COMPRESS를 사용하지 않고 ZIP파일내의 파일들의 확장자를 확인하는 방법으로 작업하였습니다. 작업 후 테스트 결과 ZIP 파일 내에 한글명으로 된 파일이 있을 경우 제대로 파일내의 ZIPENTRY에 접근을 하지 못하는 문제점을 발견할 수 있었습니다. COMMONS COMPRESS 라이브러리를 통해 해결하였고 그 방법에 대해 포스팅하도록 하겠습니다.


[ 코 드 ]  

 


코드에서 보면 알겠지만 ZIP파일을 굳이 따로 디렉토리를 만들어 압축을 푼 후 체크하지 않고 ZipEntry를 통해 ZIP파일의 파일들에 접근하는 것을 볼 수 있다. 이렇게 JDK에서 지원하는 java.util.zip을 사용했을 경우 위에서 언급했던 대로 ZIP파일 내에 한글 파일 처리를 할 수 없는 문제가 발생합니다. 이를 해결하기 위한 방법으로는 jazzlib 및 commons-compress를 사용하는 방법이 있는데 jazzlib 같은 경우 라이브러리 자체도 상당히 오래 되었고 라이센스 문제가 발생할 수 있어 commons-compress를 사용하게 되었다. 



[ Commons Compress 적용 ]

먼저 메이븐 프로젝트이기 때문에 dependency를 추가해주었다. 

 


라이브러리를 추가 하고 다음과 같이 변경하였다.

 

다른 점은 ZipArchiveInputStream을 사용하여 인코딩 방식을 EUC-KR로 지정해 주었다. 수정 후 테스트를 해본 결과 ZIP파일 내에 한글명의 파일이 있을 경우에도 정확히 처리하는 것을 확인 할 수 있었다.



반응형
반응형

최근 자바 어플리케이션에서 hdfs파일을 local파일로 쓰는 외부 명령을 실행시켜야 하는 작업이 있었다.


Runtime 객체를 생성하고 exec 메소드를 이용하여 외부 명령을 실행시키는 프로세스를 생성하였다.


코드를 보면 알겠지만 command 명령에 파이프라인(|)이 사용되었다.


Runtime runtime = Runtime.getRuntime();

Process process = null;


String command = "hadoop fs -text /log/dmp_log/2017/09/19/dmp_log.*.1505786400000.gz | head -100"


process = runtime.exec(command);


이렇게 해서 동작을 시키니 100라인만 읽어와야하는데 해당 hdfs파일의 모든 로그를 읽어왔었다...


한참을 헤매다가 파이프라인(|)이 안먹는다는 생각이 들었고 찾아보니 문제가 있었다.


파이프라인(|)을 사용하는 것은 shell 실행 후 또 다른 프로세스로 실행하기 때문에 정상적으로 작동을 안한 것이다.


다음과 같이 해결을 하면 된다.


String commandHdfs = "hadoop fs -text /log/dmp_log/" + date + "/dmp_log.*." + unixTime + "000.gz | head -150";


String[] command = {

      "/bin/sh",

      "-c",

     commandHdfs

};


[ stack overflow 참고 ]

https://stackoverflow.com/questions/5928225/how-to-make-pipes-work-with-runtime-exec


간단히 요약하자면, 자바 어플레케이션에서 exec로 실행하는 system command는 실제로 unix, linux의 shell을 불러와 명령을 실행시키는게 아니라고하네요.


그렇기 때문에 쉘의 기능(shell feature)인 pipeline (파이프라인)을 사용하기 위해서는 명시적으로 shell(/bin/sh)을 불러주고 


해당 쉘 안에서 command를 실행시켜야 한다고 합니다.


더 정확한 원인을 알고 싶다면 참고부탁드려요

https://alvinalexander.com/java/java-exec-system-command-pipeline-pipe

반응형

+ Recent posts