레이블이 자바인 게시물을 표시합니다. 모든 게시물 표시
레이블이 자바인 게시물을 표시합니다. 모든 게시물 표시

2017-02-24

enum 네 이놈! - 제네릭과 람다와 enum과 헬게이트

조금 전에 enum들에 널려있는 중복 코드들을 제거하는 와중에 일어난 이야기.

원래대로라면 enum에는 코드값만 넣고 텍스트는 따로 properties에 빼놓는데, 이번 고객은 아아주 특이하게도 그렇게 하면 유지보수하기 귀찮다고 소스에 다 때려박아달랜다.
그래서 다 때려박으면서 예제삼아 코드값 <-> 텍스트 상호전환 코드까지 같이 만들었는데…

누가 (울 회사 직원이라고 말 안할거임) 그걸 그대로 복붙해서 enum 수십 개를 만들었고, CPD는 복붙 고마해라 라고 지랄대고…
해서 코드값 <-> 텍스트 상호전환 하는 부분을 별도의 유틸리티 클래스로 분리했다.

문제는 여기서 발생하는데…

먼저 WithCodeLabel이라는 인터페이스를 만들고 메소드를 정의한 다음, 해당 enum들에 implements를 걸었다.
그리고 맨 처음 유틸리티 클래스를 람다식을 활용하여 만들었다.

public class EnumCodeLabelCache<E extends Enum<E> & WithCodeLabel> {

    private final Map<String, E> codeToLabelMap;
    private final Map<String, E> labelToCodeMap;

    private EnumCodeLabelCache(Class<E> cls) {
        final EnumSet<E> set = EnumSet.allOf(cls);
        codeToLabelMap = set.stream().collect(Collectors.toMap(WithCodeLabel::getCode, Function.identity());
        labelToCodeMap = set.stream().collect(Collectors.toMap(WithCodeLabel::getLabel, Function.identity());
    }
    ...
}

그리고 테스트를 돌렸을 때, 처음 보는 당황스러운 에러가…
java.lang.BootstrapMethodError: call site initialization exception
 at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
 at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
 at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
 at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
 at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)

…모라고요?

곰곰히 머리를 싸매고 생각하다가 분석을 해보니
enum의 각 값의 RTTI에 WithCodeLabel 인터페이스에 대한 정보가 누락되어 있다는 결과밖에 나오지를 않는다.

그래서 람다 말고 메소드 직접 호출로 변경.

public class EnumCodeLabelCache<E extends Enum<E> & WithCodeLabel> {

    private final Map<String, E> codeToLabelMap = new HashMap<>();
    private final Map<String, E> labelToCodeMap = new HashMap<>();

    private EnumCodeLabelCache(Class<E> cls) {
        final EnumSet<E> set = EnumSet.allOf(cls);
        set.forEach(e -> {
            codeToLabelMap.putIfAbsent(e.getCode(), e);
            labelToCodeMap.putIfAbsent(e.getLabel(), e);
        });
    }
    ...
}

이러니까 잘 된다.

이게 enum만 특히 이런건지, 제네릭 특징인지는 더 살펴봐야 알겠지만, 내가 알 게 뭐야.
​
Share:

2017-02-09

2015-04-15

이클립스 properties 에디터 플러그인

보통 구글신께 신탁을 구하면 제일 처음 나오는 플러그인이 아마도..
http://propedit.sourceforge.jp/index_en.html
이거일거다.

쓰지 마라. 특히 OS가 Windows라면...
사유는 http://kwon37xi.egloos.com/4664893 를 참고하시라.


그리고 그 대안으로
http://sourceforge.net/projects/eclipse-rbe/
이런 애가 등장했는데...

불편하다.
궁금하면 한번 써봐.


그래서 구글신의 신탁의 바다를 떠돌다가 새로운 플러그인을 발견했다.
https://github.com/gildur/SimplePropertiesEditor

소스를 대충 훓어보니 제일 위의 플러그인 같은 문제는 없을 것으로 보인다. 난 리눅스니 윈도우 쓰는 동무들이 테스트좀 해보라우!
그리고 두번째보다는 심플하니까 불편하지도 않고...


2016-09-30 추가:
이 플러그인으로 properties 파일을 열었을 경우 STS의 application.properties 자동완성이 적용이 되지 않는다. 그래서 난 application.yml을 쓰지...

Share:

2014-12-16

CentOS에 JDK Alternative 멕이기 [삐-]나게 힘들구먼

RHEL이나 CentOS에서 Oracle JDK 설치 관련 구글신께 신탁을 청하면
그냥 "rpm 올리고 alternative 멕이세요." 라고만 되어있다. 진짜...

그래서 만들었습니다.
Oracle JDK Alternative 자동 등록 스크립트!
기존 구글신 신탁처럼 일부분만 등록해버리는게 아니라 전체를 빠르고 편하게 등록해드립니다.

이거 눌러서 받으시고...

0. 먼저 등록할 JDK부터 설치하고 봅니다. (대략 yum localinstall이 좋소)
1. 일단 적절한 곳에 던저놓으신 다음
2. chmod a+x centos-jdk-alternative-update.sh
3. ./centos-jdk-alternative-update.sh <JDK-DIR>

끗!
참 쉽죠?

물론 약간의 오류가 있을 수도 있지만, 그런거 내가 알 게 뭐야

일단 CentOS 7에 Oracle JDK 8 버전으로 테스트해봤습니다.
아마 이전 버전들 (JDK 6, 7)에서도 큰 문제없이 돌아가지 시포요.

p.s.
alternative 업데이트 후에도 버전이 바뀌지 않으면 다음 명령을 입력해줍니다.

최신버전으로 올릴 때:
update-alternatives --auto java
update-alternatives --auto javac

특정버전으로 설정할 때:
update-alternatives --set java <path>
update-alternatives --set javac <path>

각 버전별 경로는 다음 명령으로 알아낼 수 있습니다.
update-alternatives --display java
update-alternatives --display javac
Share:

2014-10-06

JPA Entity와 Java 8

아직 하이버네이트에 대해서는 테스트해보지 않았지만, 이클립스링크에서 문제가 발생하므로,  비슷한 처리를 하는 하이버네이트에서도 똑같은 문제가 있을 것이다.

엔티티에 @OneToMany나 @ManyToMany로 컬렉션(List, Set, Map) 필드가 있을 때, 이 필드가 wrapper로 세팅되어있을 경우 (이클립스링크의 경우 IndirectList, IndirectSet, IndirectMap) 일부 Java 8 기능이 먹지를 않는다.

특히 가장 중요한 stream이 동작하지 않는다.
Aㅏ... 망했어요.
이보시오, 이보시오! JPA양반! 그게 무슨 소리요! Stream이 고자라니!

뭐 별 거 있나, wrap 된거를 꺼내오거나 그냥 새로운 컬렉션을 만들어서 값을 복사해서 쓰면 된다.
참 쉽죠? 너무 쉬워서 예제 따위는 없어요.



뱀발:
엔티티 내부에 Java 8 기능을 우겨넣으면 씐나게 로딩하다가 에러가 날 것이다.
아쉽게도 JPA는 Java 8 기능이 들어간 엔티티는 엔티티로 취급하지 않는다 (...)

물론 그런건 별도의 클래스로 분리해 놓고 써먹으면 그만이다.
Share:

2014-03-10

JPA - Hibernate Specific WTF Rules

JPA 구현 중 가장 널리 쓰이는 것들이 EclipseLink와 Hibernate이다.
JPA 2.0 이후로는 EclipseLink가 레퍼런스 구현임에도 일단 한국에서는 EclipseLink보다는 Hibernate를 더 많이 쓰는데, Hibernate가 아무래도 역사도 오래됐고 속도는 느리지만 그만큼 편의기능이 많아서 그렇지 않나 싶다.
한국에서 EclipseLink 미는 사람은 저밖에 없는듯?

여기서는 Hibernate 특유의 정신나간 특징들을 내 맘대로 (...) 찝어보겠다.
  1. SEQUENCE를 지원하지 않는 DBMS(예를 들면 MSSQL이나 MySQL)를 타겟으로 @GeneratedValue(strategy = GenerationType.SEQUENCE)를 사용하면 좆된다.
  2. Spring에서 엔티티를 @SessionAttribute로 물릴 경우 @ElementCollection의 fetch 속성을 EAGER로 설정해야 한다.
    안그러면 나중에 엔티티 값을 수정할 때 초기화 되지 않았다고 뭐라 그런다.
  3. @ElementCollection의 fetch 속성을 EAGER로 설정했으면 Hibernate에서 제공하는 @Fetch로 긁어올 방식을 설정해 주어야 한다.
    안붙여주면 처음 실행할 때부터 테이블 Alias가 없네 하면서 정신줄 놓는다.
일단은 여기까지.
더 밝혀낸 것이 있으면 계속 업데이트될 지도 모른다.
Share:

2013-11-04

java.io.IOException: error=12, Cannot allocate memory? WTF?!

자바의 유서깊은(?) 골칫거리로, 자바에서 fork() + exec() 하는 방식에 문제가 있기 때문이다.
(자세한 내용은 여기를 참고하면 좋다.)

위 링크에서 4가지 해결책이 나온다.
  1. 닥치고 메모리 증설
  2. fork() 호출시 swapfile을 추가하도록 꼼수 사용
  3. 리눅스: sysctl의 vm.overcommit_memory 속성을 1로 설정
  4. POSIX 호환 시스템: fork() + exec() 대신 posix_spawn() 사용하기

리눅스라면 vm.overcommit_memory=1 이 아무래도 빠르고 편하겠지만, 동작방식이 후덜덜하므로 overcommit이 일어날 때 커널에서 노는 메모리를 수거하는데, 이 때 어떠한 확인도 없이 막무가내로 수거하므로 잠자고 있는 멀쩡한 다른 프로세스의 메모리를 수거할 수 있다! 미션 크리티컬한 시스템에는 그냥 하지마라. 그런 시스템이라면 2웨이 이상일테니 그냥 닥치고 1번이 더 정신건강에 좋을 것이다. 돈은 좀 많이 깨지겠지만 내가 알 게 뭐야...

또한 자바의 fork() + exec()를 posix_spawn()으로 교체해주는 java_posix_spawn이 개발되어 있으므로, 이를 고려해보는 것도 좋을 것이다. 하지만 실제로는 posix_spawn()을 사용하지 않는다는게 함정. vfork() 쓴댄다.

덤

vm.overcommit_memory 관련 문서는 레드햇 문서와 노벨 문서를 보면 대략 좋다.


2015-02-23:
리눅스에 zram이라는게 있는데, 써본 결과 매우 좋소!
zram 쓰기 전에는 메모리 10그램으로도 헥헥대던게, zram 쓰고 난 다음부터는 메모리에 여유가 생겼다.
물논 CPU가 그만큼 일을 더 하게 되지만 내가 알 게 뭐야...
Share: