참고 : http://www.infoworld.com/article/3138018/security/developers-dont-ddos-your-own-apps.html ,
https://cloudplatform.googleblog.com/2016/11/how-to-avoid-a-self-inflicted-DDoS-Attack-CRE-life-lessons.html
앱의 기본 데이터 리프레시 주기와 서버의 장애시 재시도 주기가 고정된 값을 갖는 경우 둘이 겹치는 상황에서 트래픽이 배가 되어 앱 스스로 자신의 서비스에 DDoS 공격과 유사한 상황을 야기하고 이러한 좋지않은 주기가 반복되는 문제에 대한 이야기인데..
구글 엔지니어가 이야기하는 3가지 해법은 다음과 같다.
지수의 형태로 증가하는 지연시간(exponential backoff)
고정된 짧은 재시도 주기를 사용할 경우 로드벨런서에 요청이 쌓이고 서버가 정상으로 돌아왔을 때 과부하 상황을 야기하기 좋기 때문에 고정된 재시도 주기가 아닌 1, 2, 4, 8, 16과 같은 형태로 재시도 주기를 증가 시키고 정상적인 상황의 동기화 주기가 15분이라면 재시도 주기는 최대 16 정도로 잡을 수 있다. 하지만 이역시 다수의 장비들의 재시도 주기가 동기화 되는 현상을 해결하기엔 부족하다..
무작위 주기를 더하거나 빼기(a little jitter)
장시간 서버의 다운타임을 겪게 되면 지수의 형태로 증가하는 지연시간을 이용하더라도 재시도 주기가 맞물리는 현상은 발생하며 이를 피하기 위해 지연시간에 +/- 고정 비율의 주기에서 랜덤하게 시간을 선택할 수 있다. 예를 들면 다음 재시도 주기가 4분에 30% 비율을 이용하기로 했다면 최소 2.8분 최대 5.2분 사이의 값을 랜덤하게 적용하도록 하는 것이다.
이 방법은 실생활에서 폰과 컴퓨터 사용자들의 수면과 기상 패턴과 같은 점을 고려 했을 때 일반적인 동기화 주기에 적용해도 효과가 있다고 한다.
재시도 횟수 기록하기(retry marking)
대규모 분산 시스템의 경우 장애에서 전체 처리량이 완전히 복구 되기 까지 어느정도 시간이 걸리기 때문에 앞의 두가지 방법 만으로는 충분하지 않다.
이를 해소하기 위한 손쉽고 효과적인 방법으로 클라이언트에서 정상적인 연결과 재시도 횟수의 비율을 기록해 백엔드에서 접속의 우선순위를 두는 것이며 이에대한 판단은 사업의 성격에 따라 다를 수 있다.
2016년 11월 21일 월요일
2016년 10월 28일 금요일
Cloud9에 Oracle JDK8 설치하기..
참고 : http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html
여러 장비에서 해커랭크의 문제를 심심풀이로 풀다보니 소스코드 관리가 필요하지는 않지만 코드를 저장할 수 있는 언제 어디서나 접근 가능한 작업 공간이 필요해졌다. 방법을 생각하다보니 클라우드 기반 IDE가 적당한 솔루션인 것 같아 보였고 최근에 많이 쓰이는 서비스로 Cloud9, Codenvy, Codeanywhere가 있었다.
비교 자료를 보면 가장 강력한 툴로는 Codenvy가 언급되고 있기는 했지만 가볍게 알고리즘 문제 정도만 작성하고 실행해 볼 수있으면 되는 무료 툴을 원하다 보니 얼마전 아마존에서 인수한 Cloud9을 사용해 보기로 하였다.
기본적으로 우분투 리눅스 컨테이너 이미지를 기반으로 동작하며 Bash 콘솔을 제공하기 때문에 무료 사용자에게 허용된 시스템 리소스 한도 내에서는 원하는대로 세팅이 가능하며 그래서 인지 기본 세팅에서는 Java 어플리케이션 개발 환경이 정상적으로 세팅이 되어 있지 않다...
위 코드는 기존에 설치된 jre를 제거하고 Oracle JDK8을 설치하는 방법을 기술하고 있으며 여러개의 JDK가 설치되어 있는경우 Java 8을 기본 사용으로 설정하는 방법 역시 포함하고 있다.
여러 장비에서 해커랭크의 문제를 심심풀이로 풀다보니 소스코드 관리가 필요하지는 않지만 코드를 저장할 수 있는 언제 어디서나 접근 가능한 작업 공간이 필요해졌다. 방법을 생각하다보니 클라우드 기반 IDE가 적당한 솔루션인 것 같아 보였고 최근에 많이 쓰이는 서비스로 Cloud9, Codenvy, Codeanywhere가 있었다.
비교 자료를 보면 가장 강력한 툴로는 Codenvy가 언급되고 있기는 했지만 가볍게 알고리즘 문제 정도만 작성하고 실행해 볼 수있으면 되는 무료 툴을 원하다 보니 얼마전 아마존에서 인수한 Cloud9을 사용해 보기로 하였다.
기본적으로 우분투 리눅스 컨테이너 이미지를 기반으로 동작하며 Bash 콘솔을 제공하기 때문에 무료 사용자에게 허용된 시스템 리소스 한도 내에서는 원하는대로 세팅이 가능하며 그래서 인지 기본 세팅에서는 Java 어플리케이션 개발 환경이 정상적으로 세팅이 되어 있지 않다...
위 코드는 기존에 설치된 jre를 제거하고 Oracle JDK8을 설치하는 방법을 기술하고 있으며 여러개의 JDK가 설치되어 있는경우 Java 8을 기본 사용으로 설정하는 방법 역시 포함하고 있다.
라벨:
Developement,
Java,
Linux,
Tip,
Ubuntu
2016년 5월 3일 화요일
Spring Transaction의 Propagation과 Isolation에 대해..
참고 : http://stackoverflow.com/questions/8490852/spring-transactional-isolation-propagation , http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial , http://www.byteslounge.com/tutorials/spring-transaction-isolation-tutorial
헷갈릴 땐 라이브러리의 소스코드의 각 타입별 주석을 참고해도 되지만 이참에 정리하자 싶어서 작성합니다. Transactional 어노테이션의 propagation과 isolation 항목에 대한 설정 정보에 대해 알아보도록 하겠습니다.
PROPAGATION
비지니스 메소드가 호출될 때 자기 자신과 내부에서 호출 된 Transaction(이하 tx)간에 어떻게 상호 작용할 것인지에 대한 상태를 정의 하는 항목으로 다음과 같은 설정들이 존재 합니다.
ACID tx 요소 중 isolation 레벨에 대한 설정으로 특정 위치의 데이터에 대해 동시 다수의 tx가 존재 할 때 해당 데이터에 어떻게 접근할 것인지에 대해 정의할 수 있습니다.
헷갈릴 땐 라이브러리의 소스코드의 각 타입별 주석을 참고해도 되지만 이참에 정리하자 싶어서 작성합니다. Transactional 어노테이션의 propagation과 isolation 항목에 대한 설정 정보에 대해 알아보도록 하겠습니다.
PROPAGATION
비지니스 메소드가 호출될 때 자기 자신과 내부에서 호출 된 Transaction(이하 tx)간에 어떻게 상호 작용할 것인지에 대한 상태를 정의 하는 항목으로 다음과 같은 설정들이 존재 합니다.
- REQUIRED : 해당 상태가 선언된 메소드가 실행 될 때 tx가 존재 한다면 공유해서 사용하고 그렇지 않다면 새 tx를 생성해서 사용합니다. 만약 내부에서 호출 된 메소드가 자신과 마찬가지로 REQUIRED가 선언된 상태라면 자신과 내부 tx각각은 논리적으로는 구분 되지만 물리적으로는 하나의 tx가 됩니다. 내부에서 호출 된 메소드의 tx가 롤백 된다면 자신을 포함한 물리적인 하나의 tx가 롤백 되는 구조를 만들 수 있습니다.
- REQUIRES_NEW : 해당 상태가 선언된 메소드가 실행될 때 항상 새로운 물리적인 tx를 생성하고 해당 tx가 롤백 된다고해서 물리적으로 분리된 다른 tx가 롤백 되지는 않습니다. 만약 내부적으로 새로운 tx가 시작 된다면 자신의 tx는 잠시 정지 되었다가 내부 tx가 종료된 시점에 이어서 수행되게 됩니다.
- NESTED : 자신과 내부 tx들이 하나의 물리적인 tx를 사용하지만 내부에서 호출된 NESTED tx들에 savepoint를 세팅하는 형태 입니다. 그렇기 때문에 내부의 NESTED tx중 하나가 롤백이 된다고 하더라도 자신이 롤백 되지는 않습니다. 이 기능은 JDBC savepoint를 통해 구현되기 때문에 Spring JDBC로 관리되는 tx를 사용할때만 써야 합니다.
- MANDATORY : 해당 상태가 호출될 때 열려있는 tx가 반드시 존재해야하며 그렇지 않으면 컨테이너에서 예외를 던집니다.
- NEVER : 해당 상태가 호출될 때 열려있는 tx가 반드시 존재하지 않아야 하며 그렇지 않으면 컨테이너에서 예외를 던집니다.
- NOT_SUPPORTED : 해당 상태는 tx의 범위에 포함되지 않는 것을 수행합니다. 만약 열려있는 tx가 존재 한다면 해당 tx를 잠시 멈추고 작업을 수행합니다.
- SUPPORTS : 열려있는 tx가 이미 존재한다면 해당 tx에 포함되어 수행되며 만약 열려있는 tx가 존재하지 않더라도 수행은 되지만 tx와 관계없이 처리 됩니다.
- 기본적으로 tx을 롤백 상태로 만들기 위해서는 unchecked exceptions를 필요로 하며 checked exceptions를 이용하기 위해서는 별도의 설정이 필요합니다.
- Transactional 어노테이션만 선언된 메소드(Declarative transactions)에서 동일한 Bean 내부의 다른 메소드를 직접 호출할 경우(Self-invocation) 컨테이너에 의해 Transactional 선언이 무시되기 때문에 이경우는 aspectj를 사용해서 구성해야 합니다.
ACID tx 요소 중 isolation 레벨에 대한 설정으로 특정 위치의 데이터에 대해 동시 다수의 tx가 존재 할 때 해당 데이터에 어떻게 접근할 것인지에 대해 정의할 수 있습니다.
- READ_UNCOMMITTED : 여러 tx가 동시에 x라는 데이터에 접근 할 때 x에 변경이 일어난 시점과 읽는 시점에 따라 롤백 여부와 상관없이 변경된 값을 읽는 더티 리드(dirty reads)를 허용하는 상태입니다.
- READ_COMMITTED : 더티 리드의 문제는 없지만 동시에 x라는 데이터에 접근할 때 타임라인 상 A라는 tx에서 x를 읽은 후 tx가 종료 되기 전에 B라는 tx에서 x를 변경후 커밋을 하게 될 경우 A에서 x를 다시 읽는 케이스가 존재 한다면 처음 읽은 x의 값과 나중에 읽은 x의 값이 다른 문제(non-repeatable reads)가 발생하게 됩니다.
- REPEATABLE_READ : 하나의 tx에서 단일 레코드를 여러번 읽는 경우 결과가 항상 같아야 하는 상태이며 dirty reads와 non-repeatable reads 문제를 제거할 수 있습니다. 하지만 범위 스캔을 하는 경우 tx A에서 범위의 조건이 0-20 사이의 값을 읽고 tx A가 종료되기 전에 tx B에서 0-20사이에 레코드를 추가하게 된다면 tx A에서 0-20 사이의 값을 다시 읽었을 때 tx B에서 추가한 레코드(phantom records)가 포함되는 팬텀 리드(phantom reads)의 문제가 있습니다.
- SERIALIZABLE : 가장 제한적인 상태이며 읽기, 범위 스캔, 쓰기에 tx락을 거는 형태입니다. 앞서 언급한 모든 문제를 해결할 수 있지만 tx의 동시성을 허용하지 않아 퍼포먼스 패널티를 지속적으로 증가시키는 문제가 있습니다.
- DEFAULT : DBMS에서 정의된 Default Isolation 레벨에 대한 상태를 적용합니다.
- READ_UNCOMMITTED : dirty reads, non-repeatable reads, phantom reads
- READ_COMMITTED : non-repeatable reads, phantom reads
- REPEATABLE_READ : phantom reads
2016년 4월 18일 월요일
Reactive Java 참고 자료들..
6월에 Spring Framework 5가 나올 예정인데 Reactor 프로젝트의 스프링 적용 버전인 Spring Reactive가 포함되어 나올 예정임. Spring Framework 4.3 부터 선행 적용하기로 되서 미리 사용이 가능한데 간략히 설명하면 기존의 멀티쓰레드 방식 코드에 이벤트 루프 방식이 추가되는 형태라고 볼 수 있는.. 단적으로 Node.js의 경우는 싱글쓰레드 이벤트 루프 방식임.
Spring MVC에서 서비스 클래스 영역까지는 비동기 코드 작성에 대한 패턴들이 나와 있었는데 이제 컨트롤러까지 확장되는 추세이고 데이터소스 영역도 이미 NoSQL 데이터베이스 같은 경우 Non-block I/O를 지원하는 JDBC 드라이버들이 제작되고 있는 상황.
기존에 Java의 비동기 코드가 .NET에 비해 깔끔하지 못하다는 해외 커뮤니티의 평가가 많았는데 여기에서 모티브를 얻은 RxJava가 인기를 끌면서 RxJava의 Spring 버전인 Spring Reactive가 메인스트림에 합류하는 분위기.
선행 학습을 위해 참고한 자료들의 링크를 남깁니다.
메인 동영상은 기존의 레거시 코드를 Java 8으로 마이그레이션 하는 방법에 대한 내용을 다루는데 주로 Stream을 이용한 람다코드와 Consumer를 이용한 리소스 관리에 대한 이야기를 하고 있다. 강연 내용중에 기억에 남는 글귀가 있는데 "A good code is like a story not like a puzzle." 이라는 말이.. Spring Reactive에 대한 자료는 이곳 을 참고할 수 있고 관련자료의 슬라이드 링크를 포함하고 있다. 슬라이드 중 Reactive Web Applications에 대한 강연 영상은 이곳 에서 확인할 수 있고 서블릿에서 비동기 서블릿의 진화 과정에 대한 간략한 내용과 Reactive Stream에 대한 이야기를 담고 있다. Spring Boot와 RxJava를 이용해 RESTful API를 만드는 방법에 대한 내용은 이곳 에서 확인할 수 있고 Observable을 이용한 데이터 핸들링과 컨트롤러의 비동기 응답과 테스팅에 대한 힌트를 주고 있다. Spring Reactive의 REST 컨트롤러 구성에 대한 코드 예제는 이곳 에서 확인 가능하다.
2016년 3월 24일 목요일
경험 많은 자바 개발자와 아키텍트가 조심해야할 10가지 함정
원문 : http://zeroturnaround.com/rebellabs/watch-out-for-these-10-common-pitfalls-of-experienced-java-developers-architects/
좀 오래된 글이긴 하지만 아직도 유효한 글인듯 싶어서 정리해 봅니다. 코드 예제는 원문의 링크에..
#10 Dependency Injection(이하 DI)의 잘못된 사용 혹은 잘못된 이해
DI를 이용하여 미리 정의된 객체를 삽입 하는 것 까진 좋지만 다른 객체의 초기화를 위해 의존성이 삽입 된 객체에 트레인 코드(. 연산자를 이용한 기차 처럼 길게 연결된 코드)를 사용하는 것과 같은 형태는 좋지 않다. 그냥 DI만 쓰시오..
#9 Java를 Perl을 사용하듯 쓰는경우
시스템이 커질수록 런타임 보다 컴파일 타임에 문제를 발견할 수 있는 것이 중요하다. 팩토리 형태의 코드의 매게변수로 스트링을 사용하게 되면 잘못된 스펠링의 변수가 전달되면 런타임에 문제가 발생하는 경우가 있다. 자바는 타입 언어이기 때문에 enum과 같은 타입을 이용하여 문제를 방지할 수 있다.
#8 OOP를 이해하지 못해 C를 사용하듯 자바를 쓰는경우
if-else와 instanceof를 통해 객체를 구분하여 캐스팅을 통해 이용하기 보다는 인터페이스나 추상 클래스의 상속과 메소드 오버라이딩을 이용하는 편이 코드가 간결하고 유지보수가 쉬워진다.
#7 객체의 라이프사이클을 이해하지 못해 과도하게 Lazy loading을 사용하는 경우
Lazy loading을 사용하지 전에 체크할 요소.
1. 정말 고비용의 객체인건가?(그것에 대해 어떻게 정의 할 것인가?)
2. 객체가 사용되지 않는 경우가 있어 생성되어 있을 필요가 없는가?
Lazy loading을 사용할 경우 해당 객체의 정확한 로딩 시점을 알아야 하기 때문에 디버깅을 힘들게 할 수 있어 디플로이 시점에 모두 로딩되게 하면 관련 이슈를 사전에 발견할 수 있다. 개인적으론 개발과 배포 시점의 구성을 다르게 가져가는 방법도 고민해 볼만하지 싶은..
#6 'Gang Of Four'(GOF) 책을 종교와 같은 수준으로 의존 하는 경우
책의 적절한 사용법은 서문에 나와있으니 참고하시오... 이미 안티패턴이 된 #5의 싱글턴과 같은 패턴도 있음..
#5 안티패턴이 된 싱글턴을 사용하는 경우
최근의 DI 프레임웍에선 더이상 사용할 필요가 없어짐.. (사실 Spring 프레임웍의 경우 빈 생성시 기본 조건이 싱글턴인..)
#4 메소드의 가시성을 무시하는 경우
public 메소드의 경우 가능한 작고 간결해야 하며 재사용 가능한 라이브러리를 작성하는 경우 이러한 점이 더 중요해진다. private 메소드로 지정 되어야 할 메소드가 단위 테스트를 위해 public으로 지정되는 케이스가 종종 있는데 이런 경우는 지양되어야 한다.
#3 NIH(Not Invented Here) 증후군이나 프로젝트에 특화된 StringUtils와 같은 것으로 고통받는 경우
충분히 테스트 된 현존 솔루션들을 활용하라. 예를 들면 Apache Commons, Guava, joda Date와 같은 것들이 있다. 특히나 익숙하지 않은 영역의 작업을 하게 될 경우 이런 점에 대한 사전 조사를 충분히 하자.
#2 환경에 의존적인 빌드
환경에 의존적인지 체크할 수 있는 시나리오..
1. 어느날 회사에 새 개발자인 톰이 왔다.
2. 톰에게 기본적인 설명을 하였고 설명을 들은 그는 자신이 좋아하는 개발 환경으로 세팅 했다
3. 톰은 소스 저장소로부터 소스를 체크아웃 한다.
4. 톰은 어떤 빌드 시스템을 이용하는지 파악 하는데 5분을 소모했다.
5. 톰은 단하나의 커맨드로 어플리케이션 빌드를 실행하고 그것은 성공했다.
조건을 만족하지 못한다면 문서화와 빌드시스템에 대해 다시 생각해볼 필요가 있다.
#1 리플렉션/인트로스펙션을 사용하는 경우
리플렉션은 이슈가 발생할 경우 이해하기 어렵게 하고 디버그가 힘들어지며 고치기 어렵게 만든다. 리플렉션을 사용하는 것은 시한 폭탄을 심는 것과 같다. 리플렉션을 사용하지 않더라도 잘 설계된 객체의 계층적 구조를 이용해 좀더 깔끔하게 해결할 수도 있다.
보너스 : 정말로 병목을 유발하는 요소가 어디인지 아는 경우에만 최적화를 시도하라
추측하지 말고 측정하라!
1. 현재 시스템을 유효한 측정 방법을 이용해서 벤치마크 하라
2. 변경을 가한다.
3. 다시 벤치마크 한다.
4. 가해진 노력과 퍼포먼스 향상 비율에 대해 평가한다
좀 오래된 글이긴 하지만 아직도 유효한 글인듯 싶어서 정리해 봅니다. 코드 예제는 원문의 링크에..
#10 Dependency Injection(이하 DI)의 잘못된 사용 혹은 잘못된 이해
DI를 이용하여 미리 정의된 객체를 삽입 하는 것 까진 좋지만 다른 객체의 초기화를 위해 의존성이 삽입 된 객체에 트레인 코드(. 연산자를 이용한 기차 처럼 길게 연결된 코드)를 사용하는 것과 같은 형태는 좋지 않다. 그냥 DI만 쓰시오..
#9 Java를 Perl을 사용하듯 쓰는경우
시스템이 커질수록 런타임 보다 컴파일 타임에 문제를 발견할 수 있는 것이 중요하다. 팩토리 형태의 코드의 매게변수로 스트링을 사용하게 되면 잘못된 스펠링의 변수가 전달되면 런타임에 문제가 발생하는 경우가 있다. 자바는 타입 언어이기 때문에 enum과 같은 타입을 이용하여 문제를 방지할 수 있다.
#8 OOP를 이해하지 못해 C를 사용하듯 자바를 쓰는경우
if-else와 instanceof를 통해 객체를 구분하여 캐스팅을 통해 이용하기 보다는 인터페이스나 추상 클래스의 상속과 메소드 오버라이딩을 이용하는 편이 코드가 간결하고 유지보수가 쉬워진다.
#7 객체의 라이프사이클을 이해하지 못해 과도하게 Lazy loading을 사용하는 경우
Lazy loading을 사용하지 전에 체크할 요소.
1. 정말 고비용의 객체인건가?(그것에 대해 어떻게 정의 할 것인가?)
2. 객체가 사용되지 않는 경우가 있어 생성되어 있을 필요가 없는가?
Lazy loading을 사용할 경우 해당 객체의 정확한 로딩 시점을 알아야 하기 때문에 디버깅을 힘들게 할 수 있어 디플로이 시점에 모두 로딩되게 하면 관련 이슈를 사전에 발견할 수 있다. 개인적으론 개발과 배포 시점의 구성을 다르게 가져가는 방법도 고민해 볼만하지 싶은..
#6 'Gang Of Four'(GOF) 책을 종교와 같은 수준으로 의존 하는 경우
책의 적절한 사용법은 서문에 나와있으니 참고하시오... 이미 안티패턴이 된 #5의 싱글턴과 같은 패턴도 있음..
#5 안티패턴이 된 싱글턴을 사용하는 경우
최근의 DI 프레임웍에선 더이상 사용할 필요가 없어짐.. (사실 Spring 프레임웍의 경우 빈 생성시 기본 조건이 싱글턴인..)
#4 메소드의 가시성을 무시하는 경우
public 메소드의 경우 가능한 작고 간결해야 하며 재사용 가능한 라이브러리를 작성하는 경우 이러한 점이 더 중요해진다. private 메소드로 지정 되어야 할 메소드가 단위 테스트를 위해 public으로 지정되는 케이스가 종종 있는데 이런 경우는 지양되어야 한다.
#3 NIH(Not Invented Here) 증후군이나 프로젝트에 특화된 StringUtils와 같은 것으로 고통받는 경우
충분히 테스트 된 현존 솔루션들을 활용하라. 예를 들면 Apache Commons, Guava, joda Date와 같은 것들이 있다. 특히나 익숙하지 않은 영역의 작업을 하게 될 경우 이런 점에 대한 사전 조사를 충분히 하자.
#2 환경에 의존적인 빌드
환경에 의존적인지 체크할 수 있는 시나리오..
1. 어느날 회사에 새 개발자인 톰이 왔다.
2. 톰에게 기본적인 설명을 하였고 설명을 들은 그는 자신이 좋아하는 개발 환경으로 세팅 했다
3. 톰은 소스 저장소로부터 소스를 체크아웃 한다.
4. 톰은 어떤 빌드 시스템을 이용하는지 파악 하는데 5분을 소모했다.
5. 톰은 단하나의 커맨드로 어플리케이션 빌드를 실행하고 그것은 성공했다.
조건을 만족하지 못한다면 문서화와 빌드시스템에 대해 다시 생각해볼 필요가 있다.
#1 리플렉션/인트로스펙션을 사용하는 경우
리플렉션은 이슈가 발생할 경우 이해하기 어렵게 하고 디버그가 힘들어지며 고치기 어렵게 만든다. 리플렉션을 사용하는 것은 시한 폭탄을 심는 것과 같다. 리플렉션을 사용하지 않더라도 잘 설계된 객체의 계층적 구조를 이용해 좀더 깔끔하게 해결할 수도 있다.
보너스 : 정말로 병목을 유발하는 요소가 어디인지 아는 경우에만 최적화를 시도하라
추측하지 말고 측정하라!
1. 현재 시스템을 유효한 측정 방법을 이용해서 벤치마크 하라
2. 변경을 가한다.
3. 다시 벤치마크 한다.
4. 가해진 노력과 퍼포먼스 향상 비율에 대해 평가한다
2016년 3월 17일 목요일
Spring 4 WebSocket
스프링 프레임워크 4.2 에서 웹소켓을 이용한 통신에 대한 소개 영상인데 개인적으로 꽤 재미있게 봐서 기억용으로 글을 남긴다. WebSocket은 Node.js 프로젝트 때 게임 세션 서버 만드느라 Redis를 브로커로 해서 이용해보고 Java로는 구현 해본적이 없는데 한번 만들어 봐야 겠다는 생각이 드는..
동영상의 내용은 웹에서 클라이언트와 서버의 통신 방식이 단방향에서 양방향으로 변해온 역사에 대한 간략한 소개를 포함하고 있고 HTML5의 EventSource를 이용하여 HTTP 프로토콜 위에서 동작하는 Server Sent Event에 대한 예제 그리고 양방향 통신과 관련하여 TCP/IP 레벨에서 동작하는 WebSocket, SockJS, Stomp 프로토콜에 대한 예제들을 다루고 있다.
메시지 브로커와 관련한 내용에는 어플리케이션의 메모리 공간을 이용하는 심플 브로커의 구현 코드와 시스템의 스케일 아웃을 위해 RabbitMQ와 같은 전담 브로커를 이용하는 코드 그리고 StompClient와 User Principal을 이용한 클라이언트 간의 메시지 전달에 대한 예제를 소개하고 있으며 메시지 보안과 관련한 간략한 코드 소개로 마무리 되는 영상이다.
2015년 8월 10일 월요일
Java의 stack size에 대해..
참고 : http://stackoverflow.com/questions/20030120/java-default-stack-size , http://stackoverflow.com/questions/10481528/in-java-which-objects-are-put-on-the-stack-and-which-on-the-heap , http://stackoverflow.com/questions/6366211/what-are-the-roots
JVM의 VM 옵션의 -XX옵션 중에 ThreadStackSize라는 스택의 크기를 지정할 수 있는 옵션이 있다(JRockit의 경우 -Xss). JVM은 Java 메소드 호출 정보와 Native 메소드 호출 정보를 저장할 장소가 필요한데 JVM에 따라 둘을 한 스택 프레임에서 관리하는 경우도 있고 분리해서 관리하는 경우도 있다. 따라서 여기서 지칭하는 스택은 자바에서 각각의 쓰레드(thread)별로 최소 1개 이상 할당되는 저장소를 가리킨다.
스택의 기본 크기는 플렛폼에 따라 다르며 신규 쓰레드가 생성되면 정해진 스택사이즈의 메모리 공간이 힙(heap)과는 별도의 공간을 이용하여 각각의 쓰레드에 할당되게 되는 것이다.
쓰레드에 할당된 스택에는 지역변수, 파라메터, 반환주소 등을 기억하며 일반적으로 객체는 저장되지 않으며 참조 주소만 기억하게 된다. 이와 같은 특성을 가지고 있기 때문에 스택에 저장되는 원시타입(primitive type) 데이터가 많다면 array와 같은 컬렉션 객체로 관리하게 되면 스택에 저장되는 데이터의 사이즈를 줄일 수 있을 것이다.
이와 관련하여 예외가 존재하긴 하는데 그럴 경우 장점은 스택은 캐쉬될 가능성이 높기 때문에 지역변수에 대해 캐시의 지역성을 높여 접근시간을 줄이고 힙에서 관리할 데이터를 줄여 가비지 컬렉터(gargabe collector)를 덜 바쁘게 하는 효과와 싱글 쓰레드 액세스가 보장되기 때문에 쓰레드 잠김(locking)과 관련한 체크가 필요없어지는 장점을 얻을 수 있다고 한다. 그밖에 참고할 만한 자료로 상단 링크의 Escape Analysis 항목이 도움이 될 것이다.(일단 글의 주제는 스택 사이즈이다보니..-_-;;)
스택의 크기 설정과 관련해서 발생할 수 있는 문제가 몇가지 있는데 만약 스택의 크기를 크게 잡았을 때 쓰레드가 기하급수적으로 늘어난다면 OutOfMemory 오류를 접할 수 있을 것이고 반대로 너무 작게 잡은 상태라면 쓰레드에서 기억해야 하는 데이터의 크기보다 스택의 크기가 작아 StackOverFlow 오류를 보게될 수 있다.
마지막으로 사용된 스택 프레임은 해당 쓰레드가 종료되면 자동으로 제거 되게 될 것이다..
JVM의 VM 옵션의 -XX옵션 중에 ThreadStackSize라는 스택의 크기를 지정할 수 있는 옵션이 있다(JRockit의 경우 -Xss). JVM은 Java 메소드 호출 정보와 Native 메소드 호출 정보를 저장할 장소가 필요한데 JVM에 따라 둘을 한 스택 프레임에서 관리하는 경우도 있고 분리해서 관리하는 경우도 있다. 따라서 여기서 지칭하는 스택은 자바에서 각각의 쓰레드(thread)별로 최소 1개 이상 할당되는 저장소를 가리킨다.
스택의 기본 크기는 플렛폼에 따라 다르며 신규 쓰레드가 생성되면 정해진 스택사이즈의 메모리 공간이 힙(heap)과는 별도의 공간을 이용하여 각각의 쓰레드에 할당되게 되는 것이다.
| -XX:ThreadStackSize=512 | Thread Stack Size (in Kbytes). (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.] |
쓰레드에 할당된 스택에는 지역변수, 파라메터, 반환주소 등을 기억하며 일반적으로 객체는 저장되지 않으며 참조 주소만 기억하게 된다. 이와 같은 특성을 가지고 있기 때문에 스택에 저장되는 원시타입(primitive type) 데이터가 많다면 array와 같은 컬렉션 객체로 관리하게 되면 스택에 저장되는 데이터의 사이즈를 줄일 수 있을 것이다.
이와 관련하여 예외가 존재하긴 하는데 그럴 경우 장점은 스택은 캐쉬될 가능성이 높기 때문에 지역변수에 대해 캐시의 지역성을 높여 접근시간을 줄이고 힙에서 관리할 데이터를 줄여 가비지 컬렉터(gargabe collector)를 덜 바쁘게 하는 효과와 싱글 쓰레드 액세스가 보장되기 때문에 쓰레드 잠김(locking)과 관련한 체크가 필요없어지는 장점을 얻을 수 있다고 한다. 그밖에 참고할 만한 자료로 상단 링크의 Escape Analysis 항목이 도움이 될 것이다.(일단 글의 주제는 스택 사이즈이다보니..-_-;;)
스택의 크기 설정과 관련해서 발생할 수 있는 문제가 몇가지 있는데 만약 스택의 크기를 크게 잡았을 때 쓰레드가 기하급수적으로 늘어난다면 OutOfMemory 오류를 접할 수 있을 것이고 반대로 너무 작게 잡은 상태라면 쓰레드에서 기억해야 하는 데이터의 크기보다 스택의 크기가 작아 StackOverFlow 오류를 보게될 수 있다.
마지막으로 사용된 스택 프레임은 해당 쓰레드가 종료되면 자동으로 제거 되게 될 것이다..
라벨:
Developement,
Java,
JVM
2014년 7월 6일 일요일
Java Object의 hashCode()와 equals() 메소드
참고 : http://www.javaworld.com/article/2074996/hashcode-and-equals-method-in-java-object---a-pragmatic-concept.html
java.lang.Object의 메소드인 hashCode()와 equals()는 보편적으로 사용되는 메소드는 아니지만 상황에 따라 오버라이드 되어 사용되기도 합니다. 오버라이드시 어떤 기능을 할 수 있는지 한번 알아보도록 하겠습니다.
hashCode()
Object에 포함된 기본 상태의 hashCode() 메소드는 객체에 대한 메모리 주소 매핑 정보를 기반으로 정수 값을 반환 합니다. Object 클래스의 소스를 보면 알 수 있겠지만 hashCode() 메소드는 다음과 같이 작성 되어 있습니다.
hashCode() 메소드가 JVM상에 네이티브 코드로 직접 구현되어 있다는 것을 의미하며 메모리 주소를 특정한 지역 정보 형태로 제공한다는 것을 알 수 있습니다. 이런 특징이 있음에도 불구하고 이 메소드는 구현체에서 오버라이드 될 수 있습니다.
equals()
equals() 메소드는 두개의 객체가 동일한지 비교할 수 있는 특별한 메소드 입니다. 자바에는 동일함을 비교하는 방식이 두 종류가 있는데 하나는 "==" 연산자를 사용하는 것이며 다른 하나는 "equals()" 메소드 입니다. equals() 메소드의 경우 다음과 같이 작성 되어 있습니다.
오버라이드가 가능하다는 측면에서 둘의 차이를 좀더 생각해 보자면 객체의 비교에 있어 ".equals()"는 서로 같은 값을 갖는 관계를 이야기 하며 "==" 연산자의 경우 물리적으로 완전히 동일함을 이야기 한다고 볼 수 있을 것 입니다.
규약
hashCode()와 equals()는 결국 상호 연관관계에 있는 메소드로 둘은 동시에 오버라이드 되어야 하며 그렇지 않으면 API문서에 나와있는 메소드 규약을 위반하게 됩니다.
테스트
테스트를 위해 hashCode()와 equals()모두를 오버라이드한 클래스와 둘다 변경하지 않은 클래스를 작성 하였습니다.
이 둘의 차이를 알아보기 위해 다음과 같은 테스트 클래스를 작성하였습니다.
실행한 결과는 다음과 같습니다.
HashSet의 경우 중복을 배제하는 컬렉션 클래스이기 때문에 사이즈 또한 이에 기반하여 증가하는 것을 확인 할 수 있으며 HashMap의 경우 이를 이용하여 Key에 일반적으로 사용되는 String이 아닌 별도의 객체를 이용하여 키를 구성하는 것 또한 가능하게 됩니다.
java.lang.Object의 메소드인 hashCode()와 equals()는 보편적으로 사용되는 메소드는 아니지만 상황에 따라 오버라이드 되어 사용되기도 합니다. 오버라이드시 어떤 기능을 할 수 있는지 한번 알아보도록 하겠습니다.
hashCode()
Object에 포함된 기본 상태의 hashCode() 메소드는 객체에 대한 메모리 주소 매핑 정보를 기반으로 정수 값을 반환 합니다. Object 클래스의 소스를 보면 알 수 있겠지만 hashCode() 메소드는 다음과 같이 작성 되어 있습니다.
public native int hashCode();
hashCode() 메소드가 JVM상에 네이티브 코드로 직접 구현되어 있다는 것을 의미하며 메모리 주소를 특정한 지역 정보 형태로 제공한다는 것을 알 수 있습니다. 이런 특징이 있음에도 불구하고 이 메소드는 구현체에서 오버라이드 될 수 있습니다.
equals()
equals() 메소드는 두개의 객체가 동일한지 비교할 수 있는 특별한 메소드 입니다. 자바에는 동일함을 비교하는 방식이 두 종류가 있는데 하나는 "==" 연산자를 사용하는 것이며 다른 하나는 "equals()" 메소드 입니다. equals() 메소드의 경우 다음과 같이 작성 되어 있습니다.
public boolean equals(Object obj) {
return (this == obj);
}
오버라이드가 가능하다는 측면에서 둘의 차이를 좀더 생각해 보자면 객체의 비교에 있어 ".equals()"는 서로 같은 값을 갖는 관계를 이야기 하며 "==" 연산자의 경우 물리적으로 완전히 동일함을 이야기 한다고 볼 수 있을 것 입니다.
규약
hashCode()와 equals()는 결국 상호 연관관계에 있는 메소드로 둘은 동시에 오버라이드 되어야 하며 그렇지 않으면 API문서에 나와있는 메소드 규약을 위반하게 됩니다.
테스트
테스트를 위해 hashCode()와 equals()모두를 오버라이드한 클래스와 둘다 변경하지 않은 클래스를 작성 하였습니다.
package reference.value;
public class Robot {
private int productNumber;
private String modelName;
public Robot(int productNumber, String modelName) {
super();
this.productNumber = productNumber;
this.modelName = modelName;
}
public int getProductNumber() {
return productNumber;
}
public void setProductNumber(int productNumber) {
this.productNumber = productNumber;
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
}
package reference.value;
public class CustomRobot {
private int productNumber;
private String modelName;
public CustomRobot(int productNumber, String modelName) {
super();
this.productNumber = productNumber;
this.modelName = modelName;
}
public int getProductNumber() {
return productNumber;
}
public void setProductNumber(int productNumber) {
this.productNumber = productNumber;
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((modelName == null) ? 0 : modelName.hashCode());
result = prime * result + productNumber;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomRobot other = (CustomRobot) obj;
if (modelName == null) {
if (other.modelName != null)
return false;
} else if (!modelName.equals(other.modelName))
return false;
if (productNumber != other.productNumber)
return false;
return true;
}
}
이 둘의 차이를 알아보기 위해 다음과 같은 테스트 클래스를 작성하였습니다.
package reference.value;
import java.util.HashSet;
public class TestHashAndEqual {
public static void main(String[] args) throws Exception {
Robot r1 = new Robot(1999, "R2D2");
Robot r2 = new Robot(1999, "R2D2");
Robot r3 = r1;
// toString in Object
System.out.println("r1 is " + r1 + " , r2 is " + r2 );
System.out.println("r1 == r2 result : " + (r1 == r2 ? true : false));
System.out.println("r1 == r3 result : " + (r1 == r3 ? true : false));
System.out.println("Equals between r1 and r2 : " + r1.equals(r2));
System.out.println("HashCode r1 : " + r1.hashCode() + " , r2 : " + r2.hashCode());
System.out.println("Identity HashCode r1 : " + System.identityHashCode(r1) + " , r2 : " + System.identityHashCode(r2));
HashSet<Robot> rHS = new HashSet<Robot>();
rHS.add(r1);
rHS.add(r2);
rHS.add(r3);
System.out.println("HashSet Size : " + rHS.size());
System.out.println("\n==============================\n");
HashSet<CustomRobot> cRHS = new HashSet<CustomRobot>();
CustomRobot cR1 = new CustomRobot(1999, "R2D2");
CustomRobot cR2 = new CustomRobot(1999, "R2D2");
CustomRobot cR3 = cR1;
System.out.println("cR1 is " + cR1 + " , cR2 is " + cR2 );
System.out.println("cR1 == cR2 result : " + (cR1 == cR2 ? true : false));
System.out.println("cR1 == cR3 result : " + (cR1 == cR3 ? true : false));
System.out.println("Equals between cR1 and cR2 : " + cR1.equals(cR2));
System.out.println("HashCode cR1 : " + cR1.hashCode() + " , cR2 : " + cR2.hashCode());
System.out.println("Identity HashCode cR1 : " + System.identityHashCode(cR1) + " cR2 : " + System.identityHashCode(cR2));
cRHS.add(cR1);
cRHS.add(cR2);
cRHS.add(cR3);
System.out.println("HashSet Size : " + cRHS.size());
}
}
실행한 결과는 다음과 같습니다.
r1 is reference.value.Robot@15db9742 , r2 is reference.value.Robot@6d06d69c r1 == r2 result : false r1 == r3 result : true Equals between r1 and r2 : false HashCode r1 : 366712642 , r2 : 1829164700 Identity HashCode r1 : 366712642 , r2 : 1829164700 HashSet Size : 2 ============================== cR1 is reference.value.CustomRobot@49b52c2 , cR2 is reference.value.CustomRobot@49b52c2 cR1 == cR2 result : false cR1 == cR3 result : true Equals between cR1 and cR2 : true HashCode cR1 : 77288130 , cR2 : 77288130 Identity HashCode cR1 : 2018699554 cR2 : 1311053135 HashSet Size : 1두 메소드의 오버라이드를 통해 내용면에서 같은 멤버변수의 값을 갖는 객체라면 물리적으로 동일하지 않더라도 논리적으로는 서로 같은 객체로 인식하는 것을 알 수 있습니다.
HashSet의 경우 중복을 배제하는 컬렉션 클래스이기 때문에 사이즈 또한 이에 기반하여 증가하는 것을 확인 할 수 있으며 HashMap의 경우 이를 이용하여 Key에 일반적으로 사용되는 String이 아닌 별도의 객체를 이용하여 키를 구성하는 것 또한 가능하게 됩니다.
2014년 6월 17일 화요일
Java 8의 새로운 기능 Lambda
참고 : http://programming.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html
아직 익숙한 개념은 아니지만 Java 8에서 새롭게 추가된 기능 중 큰 비중을 차지하는 람다 표현식에 대해 이야기 해보려고 합니다.
람다란 무엇인가?
람다는 어떠한 행위를 나타내는 축약된 단일 메소드 클래스이며 변수 처럼 어딘가에 할당시키거나 다른 메소드들에 변수를 이용해 값을 전달 하는 것과 동일한 형태로 전달 될 수 있습니다. 솔식히.. 말로는 잘 와닿지 않습니다.. 그렇기 때문에 진행하면서 코드로 이야기를 하도록 하죠..
람다 구문
람다의 타입 : Functional Interface
자바는 형식화된 언어(Typed Language)로 필수적으로 타입을 선언하는 것이 일반적입니다. 그럼 람다의 타입은 대체 무엇인가? 라는 질문을 하지 않을 수 없는데 람다는 다음과 같이 설계 되었다고 합니다.
람다는 기존의 익명 클레스를 통해서 익숙한 익명 메소드 전략을 재활용한 것으로 새롭게 타입이 추가된 것은 아니라고 합니다. 대신 특별한 인터페이스인 Functional Interface를 갖습니다. 이는 일반적인 인터페이스와 동일하지만 다음의 추가적인 2개의 특징을 갖는다고 합니다.
조건에 맞춰 다음과 같은 인터페이스를 정의해 보았습니다.
TestFunctionalInterface가 generic type 인터페이스이기 때문에 서로 다른 타입으로 구현이 가능한 것을 알 수 있습니다. 결국 람다 표현식의 타입은 Functional Interface라고 볼 수 있습니다.
이렇게 정의된 것은 다음과 같이 사용 될 수 있습니다.
Java 8 이전과의 차이
Functional Interface인 Runnable
단일 메소드를 갖는 인터페이스 중 가장 있기 있는 걸 꼽으라면 아마도 Runnable일 것 입니다. 아무것도 반환하지 않는 run 메소드를 갖고 있는데 보통 쓰레드를 이용해서 프로그램의 성능을 향상시키기 위해 많이 사용되는 인터페이스 입니다.
기존에 익명클래스 방식을 사용한 코드는 다음과 같습니다.
이것을 람다 표현식을 사용하면 다음과 같이 사용할 수 있습니다.
이것을 조금 더 응용해본다면 다음과 같은 코드도 작성할 수 있습니다.
마치며..
개인적으로 람다 표현식은 기존의 Java 프로그래밍에 꽤 많은 변화를 가져오지 않을까 생각됩니다. 아마도 병렬 프로그래밍과 비동기 프로그래밍이 보편화된 시대의 도래를 촉진하겠죠.. 아직 람다에 대해 많은 것을 알지는 못하지만 Java 8에서 가장 핫한 녀석인것 같다는 생각입니다.
아직 익숙한 개념은 아니지만 Java 8에서 새롭게 추가된 기능 중 큰 비중을 차지하는 람다 표현식에 대해 이야기 해보려고 합니다.
람다란 무엇인가?
람다는 어떠한 행위를 나타내는 축약된 단일 메소드 클래스이며 변수 처럼 어딘가에 할당시키거나 다른 메소드들에 변수를 이용해 값을 전달 하는 것과 동일한 형태로 전달 될 수 있습니다. 솔식히.. 말로는 잘 와닿지 않습니다.. 그렇기 때문에 진행하면서 코드로 이야기를 하도록 하죠..
람다 구문
input arguments -> body람다 표현식은 일반 메소드와 같이 입력 변수부분과 동작을 기술하는 부분 그리고 선택적인 반환값으로 이루어져 있습니다. 위와 같이 화살표(->)를 기준으로 좌측은 메소드 변수를 우측은 이 변수들로 무엇을 할 것인가 하는 행위를 나타냅니다.
람다의 타입 : Functional Interface
자바는 형식화된 언어(Typed Language)로 필수적으로 타입을 선언하는 것이 일반적입니다. 그럼 람다의 타입은 대체 무엇인가? 라는 질문을 하지 않을 수 없는데 람다는 다음과 같이 설계 되었다고 합니다.
람다는 기존의 익명 클레스를 통해서 익숙한 익명 메소드 전략을 재활용한 것으로 새롭게 타입이 추가된 것은 아니라고 합니다. 대신 특별한 인터페이스인 Functional Interface를 갖습니다. 이는 일반적인 인터페이스와 동일하지만 다음의 추가적인 2개의 특징을 갖는다고 합니다.
- 단 하나의 추상 메소드를 갖음.
- 선택적 이지만 @FunctionalInterface 주석을 추가하여 람다 표현식으로 사용 될 수 있음(이 방식이 강력 추천 됨).
조건에 맞춰 다음과 같은 인터페이스를 정의해 보았습니다.
package lambda.works;
@FunctionalInterface
public interface TestFunctionalInterface<T> {
public T doSomething(T t1, T t2);
}
그리고 다음과 같은 모델 클레스를 하나 작성 하였습니다.package lambda.works;
public class CargoWorks {
private int boxQty;
public CargoWorks(int boxQty) {
this.boxQty = boxQty;
}
public int getBoxQty() {
return boxQty;
}
public void setBoxQty(int boxQty) {
this.boxQty = boxQty;
}
}
람다 표현식을 이용하여 Functional Interface를 다음과 같이 구현해 보았습니다.//두 스트링의 연결
TestFunctionalInterface<String> stringAdder = (String s1, String s2) -> s1 + s2;
//두 수의 곱
TestFunctionalInterface<Integer> multipleNumbers = (Integer i1, Integer i2) -> i1 * i2;
//두 박스의 합
TestFunctionalInterface<CargoWorks> quantityAdder = (CargoWorks c1, CargoWorks c2) -> {
c1.setBoxQty(c1.getBoxQty() + c2.getBoxQty());
return c1;
};
TestFunctionalInterface가 generic type 인터페이스이기 때문에 서로 다른 타입으로 구현이 가능한 것을 알 수 있습니다. 결국 람다 표현식의 타입은 Functional Interface라고 볼 수 있습니다.
이렇게 정의된 것은 다음과 같이 사용 될 수 있습니다.
package lambda.works;
public class ImplFunctions {
//두 스트링의 연결
TestFunctionalInterface<String> stringAdder = (String s1, String s2) -> s1 + s2;
private void concatnateStrings(String s1, String s2) {
System.out.println("Concatenated result : " + stringAdder.doSomething(s1, s2));
}
}
매우 다양하게 정의 된 비지니스 로직이 위와 같이 단순한 절차에 의해 사용될 수 있습니다. Java 8 이전과의 차이
package lambda.works;
public class TestPreJava8 {
TestFunctionalInterface<CargoWorks> quantityMerger = new TestFunctionalInterface<CargoWorks>() {
@Override
public CargoWorks doSomething(CargoWorks c1, CargoWorks c2) {
c1.setBoxQty(c1.getBoxQty() + c2.getBoxQty());
return c1;
}
};
}
Java 8 이전이라면 앞서 소개한 람다 표현식은 위의 익명 클레스와 같이 표현 될 것입니다. 이걸 사용하려면 다음과 같겠죠..public void preJava8Method() {
TestFunctionalInterface<CargoWorks> quantityMerger = ...;
CargoWorks c1 = new CargoWorks(1000);
CargoWorks c2 = new CargoWorks(2000);
CargoWorks mergedQunatiy = quantityMerger.doSomething(c1, c2);
}
아마도 기존의 방식대로면 람다 표현식과 같은 기능을 구현할 수는 있겠지만 불필요한 코드들이 많이 늘어나게 될 것 입니다. 람다의 간결함을 예로 들기 위해 다음은 다양한 행위가 있을 경우에 대한 예 입니다.//두 박스의 합
TestFunctionalInterface<CargoWorks> quantityAdder = (CargoWorks c1, CargoWorks c2) -> {
c1.setBoxQty(c1.getBoxQty() + c2.getBoxQty());
return c1;
};
//수가 많은 박스
TestFunctionalInterface<CargoWorks> compareBoxes = (CargoWorks c1, CargoWorks c2) -> {
if(c1.getBoxQty() > c2.getBoxQty()) {
return c1;
} else {
return c2;
}
};
//또 다른 박스관련 작업(람다에서는 기존에 존재하는 다른 메소드(여기에선 thatThingYouDo)도 사용이 가능합니다)
TestFunctionalInterface<CargoWorks> otherJobs = (CargoWorks c1, CargoWorks c2) -> thatThingYouDo(c1, c2);
위와 같은 여러 행위가 있다고 가정하면 다음의 메소드는 어떻게 처리할 것인지를 매개변수로 함께 받아서 처리할 수 있습니다.private void applyBehavior(TestFunctionalInterface<CargoWorks> applySomething, CargoWorks c1, CargoWorks c2) {
applySomething.doSomething(c1, c2);
}
Functional Interface인 Runnable
단일 메소드를 갖는 인터페이스 중 가장 있기 있는 걸 꼽으라면 아마도 Runnable일 것 입니다. 아무것도 반환하지 않는 run 메소드를 갖고 있는데 보통 쓰레드를 이용해서 프로그램의 성능을 향상시키기 위해 많이 사용되는 인터페이스 입니다.
기존에 익명클래스 방식을 사용한 코드는 다음과 같습니다.
new Thread(new Runnable() {
@Override
public void run() {
doSomething();
}
}).start();
이것을 람다 표현식을 사용하면 다음과 같이 사용할 수 있습니다.
new Thread(() -> doSomething()).start();바로 Thread의 생성자에 람다 표현식을 이용해서 행위를 전달할 수 있게 되는 거죠. 바로 Thread 클래스가 받아들이는 Runnable이 Functional Interface이기 때문에 가능해 지는 코드 입니다.
이것을 조금 더 응용해본다면 다음과 같은 코드도 작성할 수 있습니다.
package lambda.works;
public class AsyncManager {
public void runAsync(Runnable r) {
new Thread(r).start();
}
}
그리고 이것을 이용하여..package lambda.works;
public class TestFunctions {
private AsyncManager manager = new AsyncManager();
//두 스트링의 연결
TestFunctionalInterface<String> stringAdder = (String s1, String s2) -> s1 + s2;
//두 수의 곱
TestFunctionalInterface<Integer> multipleNumbers = (Integer i1, Integer i2) -> i1 * i2;
//두 박스의 합
TestFunctionalInterface<CargoWorks> quantityAdder = (CargoWorks c1, CargoWorks c2) -> {
c1.setBoxQty(c1.getBoxQty() + c2.getBoxQty());
return c1;
};
private void takeResults() {
CargoWorks cargoNoOne = new CargoWorks(1000);
CargoWorks cargoNoTwo = new CargoWorks(2000);
manager.runAsync(() -> System.out.println("Running in Async mode"));
manager.runAsync(() -> {
for(int i = 0; i < 100; i++) {
System.out.println("just counting : " + i);
}
});
manager.runAsync(() -> System.out.println("String : " + stringAdder.doSomething("A", "b")));
manager.runAsync(() -> System.out.println("Number : " + multipleNumbers.doSomething(1, 2)));
manager.runAsync(() -> System.out.println("Qty : " + quantityAdder.doSomething(cargoNoOne, cargoNoTwo).getBoxQty()));
}
public static void main(String[] args) throws Exception {
new TestFunctions().takeResults();
}
}
이렇게 테스트를 해보면 각각은 쓰레드로 동작하기 때문에 결과가 뒤섞여서 나옴을 알 수 있습니다. 참 쉽죠?마치며..
개인적으로 람다 표현식은 기존의 Java 프로그래밍에 꽤 많은 변화를 가져오지 않을까 생각됩니다. 아마도 병렬 프로그래밍과 비동기 프로그래밍이 보편화된 시대의 도래를 촉진하겠죠.. 아직 람다에 대해 많은 것을 알지는 못하지만 Java 8에서 가장 핫한 녀석인것 같다는 생각입니다.
2014년 2월 16일 일요일
Java에서 예외가 발생할 경우 프로그램의 흐름에 대해..
Java 프로그램을 작성하는데 있어 예외를 처리하는 것은 프로그램의 신뢰성을 보장하기 위해 매우 중요합니다. 실제 프로젝트를 진행하게 되면 예외처리와 관련한 코드의 비중이 매우크기도 하죠.
만약 하나의 커다란 테스크가 여러개의 작업으로 나뉘어진 경우 그중 하나의 작업에서 예외가 발생할 경우 테스크 전체를 어떻게 처리할 것인가 하는 문제를 생각해보면 예외처리를 통해 상황에 맞는 전략을 세우게 될 것 입니다.
다른 측면에서 보면 이는 로깅 전략과도 연관이 있는데 이 글에서는 Java에서 예외가 발생하는 경우 기본적인 코드의 흐름이 어떻게 되는지에 대해 이야기 하고자 합니다.
Java의 코드 수행은 좌에서 우로 위에서 아래로 흘러간다는 것을 염두하고 다음의 몇가지 상황을 상정해 보았습니다.
1. 예측하지 못한 상황에서 예외가 발생할 경우.
서브루틴이라면 예외를 상위로 전가시킬 수 있겠지만 메인 프로그램이라면 예외가 발생한 시점에 프로그램의 동작이 중단됩니다. 현실에 빗대어 생각해 보자면 프로그램 상의 일련의 작업 파이프 라인을 공장에서의 하나의 생산 라인이라고 하면 예외가 발생한다는 것은 생산 라인의 어딘가에서 문제가 발생해서 라인 전체의 작업이 중단되는 것과 같다고 볼 수 있겠습니다.
1의 상황이 발생한다면 프로그램의 치명적인 결함이 되기 때문에 이를 해소하기 위해 try-catch 구문을 이용한 예외처리를 Java에서는 제공하고 있습니다.
2. 예외가 발생하는 시점의 코드를 try-catch 구문으로 처리한 경우.
예외는 역시 발생하지만 예외처리를 하지 않은 곳에서 문제가 발생하지 않는 한 코드는 끝까지 수행되게 됩니다. 하지만 이경우에도 고려해야할 문제가 있는데 다음의 코드를 보면..
이 코드에서 MakeError 클래스는 단순히 강제로 예외를 발생시켜 호출한 곳으로 전가(throws)시키는 메소드인 makeSomeErrors를 가지고 있습니다.
error.makeSomeErrors(); 호출 시점에 예외가 발생하게 되는데 해당 라인 이후의 try 영역의 코드는 수행되지 않고 catch 영역으로 넘어가는 것을 알 수 있습니다. 그렇기 때문에 try영역으로 감싸게 될 코드는 이러한 흐름을 고려하여 포함시켜야 한다고 볼 수 있습니다. finally 영역의 경우는 예외 발생 여부와 관계없이 반드시 수행을 진행하기 때문에 이러한 문제를 해소하기 위해 사용될 수 있습니다.
실행 결과는 다음과 같습니다.
"After Error" 메시지는 건너 뛴것을 확인할 수 있습니다.
2-1. catch 영역에서 예외가 발생할 경우.
샘플 코드의 주석을 해제하고 테스트해보면 알 수 있겠지만 catch 영역의 예외가 발생한 시점 이후의 코드는 수행되지 않습니다 "Message in catch area" 메시지는 건너 뛰게 되며 finally 영역의 코드가 수행된 후 1번과 같은 결과에 도달함을 알 수 있습니다. 만약 하위 Exception 클래스로 catch 영역을 세분화 하는 경우 정의되지 않은 예외가 발생하는 문제에 주의 해야함을 알 수 있습니다.
2-2. finally 영역에서 예외가 발생할 경우.
역시 샘플 코드의 주석을 해제하고 테스트해보면 finally 영역의 예외 발생 이후의 코드는 수행되지 않고 "Message in finally area" 메시지는 건너 뛰는 것을 확인할 수 있습니다. 역시 1과 같은 결과에 도달함을 알 수 있습니다.
마치며..
일반적으로 J2EE 어플리케이션을 작성하는 경우 서블릿 컨테이너 위에서 프로그램이 동작하기 때문에 main 메소드를 작성할일이 없어 어지간한 상황이 아니라면 컨테이너가 죽는 경우는 없겠지만 독립 어플리케이션을 작성하는 경우라면 이런 흐름을 충분히 고려해서 프로그램을 작성해야 할 것 입니다.
만약 하나의 커다란 테스크가 여러개의 작업으로 나뉘어진 경우 그중 하나의 작업에서 예외가 발생할 경우 테스크 전체를 어떻게 처리할 것인가 하는 문제를 생각해보면 예외처리를 통해 상황에 맞는 전략을 세우게 될 것 입니다.
다른 측면에서 보면 이는 로깅 전략과도 연관이 있는데 이 글에서는 Java에서 예외가 발생하는 경우 기본적인 코드의 흐름이 어떻게 되는지에 대해 이야기 하고자 합니다.
Java의 코드 수행은 좌에서 우로 위에서 아래로 흘러간다는 것을 염두하고 다음의 몇가지 상황을 상정해 보았습니다.
1. 예측하지 못한 상황에서 예외가 발생할 경우.
서브루틴이라면 예외를 상위로 전가시킬 수 있겠지만 메인 프로그램이라면 예외가 발생한 시점에 프로그램의 동작이 중단됩니다. 현실에 빗대어 생각해 보자면 프로그램 상의 일련의 작업 파이프 라인을 공장에서의 하나의 생산 라인이라고 하면 예외가 발생한다는 것은 생산 라인의 어딘가에서 문제가 발생해서 라인 전체의 작업이 중단되는 것과 같다고 볼 수 있겠습니다.
1의 상황이 발생한다면 프로그램의 치명적인 결함이 되기 때문에 이를 해소하기 위해 try-catch 구문을 이용한 예외처리를 Java에서는 제공하고 있습니다.
2. 예외가 발생하는 시점의 코드를 try-catch 구문으로 처리한 경우.
예외는 역시 발생하지만 예외처리를 하지 않은 곳에서 문제가 발생하지 않는 한 코드는 끝까지 수행되게 됩니다. 하지만 이경우에도 고려해야할 문제가 있는데 다음의 코드를 보면..
package when.error.occurs;
public class SumOfErrors {
public static void main(String[] args) throws Exception{
MakeError error = new MakeError();
int loopCount = 1;
System.out.println("Start Message !");
while (true) {
System.out.println("Number of loop count : " + loopCount);
try {
System.out.println("Before error");
error.makeSomeErrors();
System.out.println("After error");
} catch (Exception e) {
e.printStackTrace();
//error occurs in catch area..
//error.makeSomeErrors();
System.out.println("Message in catch area");
} finally {
//error occurs in finally area..
//error.makeSomeErrors();
System.out.println("Message in finally area");
}
loopCount++;
System.out.println("Wait for 3000ms...");
Thread.sleep(3000);
}
}
}
이 코드에서 MakeError 클래스는 단순히 강제로 예외를 발생시켜 호출한 곳으로 전가(throws)시키는 메소드인 makeSomeErrors를 가지고 있습니다.
error.makeSomeErrors(); 호출 시점에 예외가 발생하게 되는데 해당 라인 이후의 try 영역의 코드는 수행되지 않고 catch 영역으로 넘어가는 것을 알 수 있습니다. 그렇기 때문에 try영역으로 감싸게 될 코드는 이러한 흐름을 고려하여 포함시켜야 한다고 볼 수 있습니다. finally 영역의 경우는 예외 발생 여부와 관계없이 반드시 수행을 진행하기 때문에 이러한 문제를 해소하기 위해 사용될 수 있습니다.
실행 결과는 다음과 같습니다.
Start Message ! Number of loop count : 1 Before error java.lang.Exception: Exception in subroutine at when.error.occurs.MakeError.makeSomeErrors(MakeError.java:5) at when.error.occurs.SumOfErrors.main(SumOfErrors.java:14) Message in catch area Message in finally area Wait for 3000ms... ...
"After Error" 메시지는 건너 뛴것을 확인할 수 있습니다.
2-1. catch 영역에서 예외가 발생할 경우.
샘플 코드의 주석을 해제하고 테스트해보면 알 수 있겠지만 catch 영역의 예외가 발생한 시점 이후의 코드는 수행되지 않습니다 "Message in catch area" 메시지는 건너 뛰게 되며 finally 영역의 코드가 수행된 후 1번과 같은 결과에 도달함을 알 수 있습니다. 만약 하위 Exception 클래스로 catch 영역을 세분화 하는 경우 정의되지 않은 예외가 발생하는 문제에 주의 해야함을 알 수 있습니다.
2-2. finally 영역에서 예외가 발생할 경우.
역시 샘플 코드의 주석을 해제하고 테스트해보면 finally 영역의 예외 발생 이후의 코드는 수행되지 않고 "Message in finally area" 메시지는 건너 뛰는 것을 확인할 수 있습니다. 역시 1과 같은 결과에 도달함을 알 수 있습니다.
마치며..
일반적으로 J2EE 어플리케이션을 작성하는 경우 서블릿 컨테이너 위에서 프로그램이 동작하기 때문에 main 메소드를 작성할일이 없어 어지간한 상황이 아니라면 컨테이너가 죽는 경우는 없겠지만 독립 어플리케이션을 작성하는 경우라면 이런 흐름을 충분히 고려해서 프로그램을 작성해야 할 것 입니다.
2014년 2월 15일 토요일
Springframework release 저장소 링크
Springframework의 라이브러리가 Maven이나 Gradle을 이용하여 프로젝트에 임포트 시키는 것을 권장하게 되면서 기존에 zip파일 형태로 다운 받을 수 있었던 링크 찾기에 애로사항이 꽃피는 관계로 해당 링크를 포스팅 합니다.
http://repo.spring.io/release/org/springframework/spring/
최상위 저장소 위치는 http://repo.spring.io/release/ 입니다.
http://repo.spring.io/release/org/springframework/spring/
최상위 저장소 위치는 http://repo.spring.io/release/ 입니다.
라벨:
Developement,
Java,
Tip
2014년 2월 1일 토요일
java.util.logging.Logger의 콘솔 출력 레벨 변경
java.util.logging.Logger를 사용하는 경우 로깅 레벨을 코드상에서 변경 하더라도 콘솔에 출력되는 항목은 INFO 레벨 이상만 표시되게 된다. 이유는 기본적으로 참조하는 JAVA_HOME\jre\lib\logging.properties 파일에 콘솔항목은 INFO레벨 이상일 경우만 출력 하도록 되어 있으며 코드상에서 레벨을 변경 하더라도 둘의 교집합 영역만 표시되기 때문이다.
콘솔 출력과 관련한 기본 설정 값은 다음과 같다.
로깅 레벨은 "java.util.logging.ConsoleHandler.level = INFO" 항목을 수정하여 변경할 수 있다. 참고로 java.util.logging.Logger에서 제공하는 로깅 레벨은 다음과 같다.
콘솔 출력과 관련한 기본 설정 값은 다음과 같다.
# Limit the message that are printed on the console to INFO and above. java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
로깅 레벨은 "java.util.logging.ConsoleHandler.level = INFO" 항목을 수정하여 변경할 수 있다. 참고로 java.util.logging.Logger에서 제공하는 로깅 레벨은 다음과 같다.
- SEVERE (highest value)
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST (lowest value)
라벨:
Developement,
Java,
Tip
2014년 1월 17일 금요일
오라클, Java 9에서 Java 8으로의 하위 호환성에 제약을 걸다
원문 : http://www.javaworld.com/article/2078927/java-se/oracle-to-limit-backward-compatibility-from-java-9-to-java-8.html
개발자들과 함께 JDK 8에서 JDK 9으로 JDK 구축의 이행을 준비중인 오라클의 자바 최고 책임자는 두 JDK간의 코드라인의 병합을 제한 할 것을 제안하였다.
오라클 자바 플랫폼 그룹의 치프 아키텍트인 Mark Reinhold는 OpenJDK 메일 링 리스트에서 JDK 8으로의 변경 내역은 줄어드는 반면 JDK 9의 포레스트(참고: OpenJDK Mercurial Forest)는 조만간 오픈 될 것 이며 개발자들은 이제 두 개의 개별 릴리즈에 대한 변경 내역들을 각각 관리 해야 한다는 것 을 언급 하였다.
현재의 일반적인 규칙은 변경 내역 들이 개발 중인 릴리즈에 먼저 이행 되고 앞선 릴리즈에 백 포트 되는 것 이다. 하지만 새롭게 시작하는 후속 릴리즈(지금의 경우 JDK 9)에서 보다 기능 릴리즈의 최종단계인 릴리즈 준비단계 이후(지금의 경우 JDK 8)의 주기에서 변경사항 들이 좀더 충분히 테스트 될 수 있을 것이기 때문에 이러한 규칙은 크게 의미가 없게 된다. 이는 후속 릴리즈에 변경 내역들이 먼저 전달 됨으로 인해 최종 릴리즈 단계의 작업이 지연되는 것을 의미한다.
JDK 7까지는 이러한 두 버전간의 평행선상의 변경 내역을 처리하기 위한 정책이 없었다. 개발자는 일반적으로 먼저 요청 된 릴리즈를 대상으로 변경 내역을 적용하는 반면 썬/오라클의 릴리즈 엔지니어링 팀의 누군가는 최종 버전과 후속 릴리즈 사이의 반자동 병합을 수행 하며 이러한 병합 작업은 더 이상 병합이 불가능 하게 되는 시점까지 수행되었다. 그렇게 되면 개발자는 이러한 변경 내역들이 정확하게 해당하는 릴리즈에 반영되는 것을 보장하는데 도움을 주는 버그 데이터베이스 쿼리를 이용하여 두 릴리즈 모두에 변경 내역을 적용하는 것을 요청하게 되어 있었다.
Reinhold는 이러한 접근 방식은 최종 릴리즈에 기여하는 수백명의 개발자들 모두가 반자동 병합 작업 들이 아직 수행 중에 있는지 모니터링 해야만 하고 병합 작업이 중단 된 즉시 자신들의 코드 통합과 관련한 작업 절차들을 변경해야 하기 때문에 작업들을 효율적으로 조율하기가 어렵다고 이야기 하고 있다.
이러한 최총 릴리즈 절차를 단순화 하기 위해 Reinhold는 JDK 9의 개발 포레스트는 JDK 8의 특정 빌드로부터 초기화 할 것을 제안 하였다. 이 특정 빌드의 JDK 8에서 JDK 9이 분기 된 이후에는 두 릴리즈 간의 코드 라인 병합은 허용되지 않을 것 이며 JDK 8에 변경 내역을 반영한 개발자는 해당 변경 내역이 JDK 9에도 적용 가능한 경우 JDK 9에도 개별적으로 적용 시켜야 한다.
Reinhold는 이러한 변화로 인해 기존 프로세스의 문제가 해결되길 원하고 있다. 유일한 단점으로 보이는 것은 GA(General Availability)이전의 JDK 8으로부터 분기된 JDK 9의 포레스트를 이용해서는 JDK 8 GA의 빌드가 불가능해 진다는 것이다. 기술적인 관점에서 이를 가능하게 하는 것은 다소 편리하면서도 멋진 일이겠지만 이러한 변화는 미학적인 면을 좀더 고려한 것 임을 밝혔다. 한편 JDK 8 포레스트를 통해 JDK 7 업데이트 릴리즈를 빌드 할 수 없다는 점에서 본다면 이 문제와 관련하여 상황이 달라지는 것은 아니다.
Java SE 8 기반의 환경에서 멀티코어 프로세서에서 동작하는 코드의 작성을 손쉽게 하기 위한 람다 프로젝트를 지원할 JDK 8은 해당 체험판 빌드가 이미 존재하는 상태이다. 한편 2016년 초 등장 예정이며 Java의 모듈화 기능인 Jigsaw 프로젝트를 제공할 Java SE 9의 릴리즈에 착수한 상태이다.
JDK 9이 JDK 8에서 초기화 되면 릴리즈 간의 코드라인 병합은 중단 될 예정.
개발자들과 함께 JDK 8에서 JDK 9으로 JDK 구축의 이행을 준비중인 오라클의 자바 최고 책임자는 두 JDK간의 코드라인의 병합을 제한 할 것을 제안하였다.
오라클 자바 플랫폼 그룹의 치프 아키텍트인 Mark Reinhold는 OpenJDK 메일 링 리스트에서 JDK 8으로의 변경 내역은 줄어드는 반면 JDK 9의 포레스트(참고: OpenJDK Mercurial Forest)는 조만간 오픈 될 것 이며 개발자들은 이제 두 개의 개별 릴리즈에 대한 변경 내역들을 각각 관리 해야 한다는 것 을 언급 하였다.
현재의 일반적인 규칙은 변경 내역 들이 개발 중인 릴리즈에 먼저 이행 되고 앞선 릴리즈에 백 포트 되는 것 이다. 하지만 새롭게 시작하는 후속 릴리즈(지금의 경우 JDK 9)에서 보다 기능 릴리즈의 최종단계인 릴리즈 준비단계 이후(지금의 경우 JDK 8)의 주기에서 변경사항 들이 좀더 충분히 테스트 될 수 있을 것이기 때문에 이러한 규칙은 크게 의미가 없게 된다. 이는 후속 릴리즈에 변경 내역들이 먼저 전달 됨으로 인해 최종 릴리즈 단계의 작업이 지연되는 것을 의미한다.
JDK 7까지는 이러한 두 버전간의 평행선상의 변경 내역을 처리하기 위한 정책이 없었다. 개발자는 일반적으로 먼저 요청 된 릴리즈를 대상으로 변경 내역을 적용하는 반면 썬/오라클의 릴리즈 엔지니어링 팀의 누군가는 최종 버전과 후속 릴리즈 사이의 반자동 병합을 수행 하며 이러한 병합 작업은 더 이상 병합이 불가능 하게 되는 시점까지 수행되었다. 그렇게 되면 개발자는 이러한 변경 내역들이 정확하게 해당하는 릴리즈에 반영되는 것을 보장하는데 도움을 주는 버그 데이터베이스 쿼리를 이용하여 두 릴리즈 모두에 변경 내역을 적용하는 것을 요청하게 되어 있었다.
Reinhold는 이러한 접근 방식은 최종 릴리즈에 기여하는 수백명의 개발자들 모두가 반자동 병합 작업 들이 아직 수행 중에 있는지 모니터링 해야만 하고 병합 작업이 중단 된 즉시 자신들의 코드 통합과 관련한 작업 절차들을 변경해야 하기 때문에 작업들을 효율적으로 조율하기가 어렵다고 이야기 하고 있다.
이러한 최총 릴리즈 절차를 단순화 하기 위해 Reinhold는 JDK 9의 개발 포레스트는 JDK 8의 특정 빌드로부터 초기화 할 것을 제안 하였다. 이 특정 빌드의 JDK 8에서 JDK 9이 분기 된 이후에는 두 릴리즈 간의 코드 라인 병합은 허용되지 않을 것 이며 JDK 8에 변경 내역을 반영한 개발자는 해당 변경 내역이 JDK 9에도 적용 가능한 경우 JDK 9에도 개별적으로 적용 시켜야 한다.
Reinhold는 이러한 변화로 인해 기존 프로세스의 문제가 해결되길 원하고 있다. 유일한 단점으로 보이는 것은 GA(General Availability)이전의 JDK 8으로부터 분기된 JDK 9의 포레스트를 이용해서는 JDK 8 GA의 빌드가 불가능해 진다는 것이다. 기술적인 관점에서 이를 가능하게 하는 것은 다소 편리하면서도 멋진 일이겠지만 이러한 변화는 미학적인 면을 좀더 고려한 것 임을 밝혔다. 한편 JDK 8 포레스트를 통해 JDK 7 업데이트 릴리즈를 빌드 할 수 없다는 점에서 본다면 이 문제와 관련하여 상황이 달라지는 것은 아니다.
Java SE 8 기반의 환경에서 멀티코어 프로세서에서 동작하는 코드의 작성을 손쉽게 하기 위한 람다 프로젝트를 지원할 JDK 8은 해당 체험판 빌드가 이미 존재하는 상태이다. 한편 2016년 초 등장 예정이며 Java의 모듈화 기능인 Jigsaw 프로젝트를 제공할 Java SE 9의 릴리즈에 착수한 상태이다.
2013년 11월 30일 토요일
웹페이지에서 .js파일 비동기 방식으로 로드하기
참고 : http://www.phpied.com/async-javascript-callbacks/ , http://stackoverflow.com/questions/1834077/which-browsers-support-script-async-async , https://developers.google.com/analytics/devguides/collection/gajs/asyncTracking
본 문서는 Google Analytics Tracking Code 삽입과 관련한 예제를 바탕으로 작성 되었습니다.
웹페이지 호출 시 일반적으로 .js 파일은 다운로드 되고 파싱되는 절차를 거치게 되며 이 작업이 완료되기까지 대기 상태를 거쳐 페이지 로드가 완료되게 됩니다. 이 경우 .js 파일의 용량이 클 경우 페이지 로딩 시간이 길어지는 문제가 발생하게 되고 HTML5에서는 이를 보완하여 non-blocking한 동작을 지원하기 위해 Script 태그에 Async 속성이 추가 되었습니다.
HTML5의 경우 아직 Draft단계이기 때문에 추후 변동사항이 발생할 가능성이 있습니다. 의존 관계를 갖지 않는 단일 파일의 비동기 방식 로딩에 대한 예제는 다음과 같습니다.
위 코드를 이용해서 의존 관계를 갖는 .js 파일의 로딩이나 코드의 수행을 위해 다음과 같이 수정해 보았습니다.
만약 jQuery를 사용할 수 있는 환경이라면 $.getScript(url, successCallback)을 사용 할 수 있습니다.
위 코드는 레거시 코드를 혼용해서 작성한 코드이기 때문에 IE7 모드 부터 동작하는 것을 확인 하였지만(본 블로그의 SyntaxHighlighter에 적용되어 있습니다 xD) 제가 알지 못하는 최신 브라우저에서만 지원하는 튜닝요소가 존재할 수 있습니다. 해당 기능을 정식으로 지원하는 브라우저는 다음과 같다고 합니다.
본 문서는 Google Analytics Tracking Code 삽입과 관련한 예제를 바탕으로 작성 되었습니다.
웹페이지 호출 시 일반적으로 .js 파일은 다운로드 되고 파싱되는 절차를 거치게 되며 이 작업이 완료되기까지 대기 상태를 거쳐 페이지 로드가 완료되게 됩니다. 이 경우 .js 파일의 용량이 클 경우 페이지 로딩 시간이 길어지는 문제가 발생하게 되고 HTML5에서는 이를 보완하여 non-blocking한 동작을 지원하기 위해 Script 태그에 Async 속성이 추가 되었습니다.
HTML5의 경우 아직 Draft단계이기 때문에 추후 변동사항이 발생할 가능성이 있습니다. 의존 관계를 갖지 않는 단일 파일의 비동기 방식 로딩에 대한 예제는 다음과 같습니다.
(function() {
var foo = document.createElement('script'); foo.type = 'text/javascript'; foo.async = true;
foo.src = 'foo.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(foo, s);
})();
위 코드를 이용해서 의존 관계를 갖는 .js 파일의 로딩이나 코드의 수행을 위해 다음과 같이 수정해 보았습니다.
(function() {
var foo = document.createElement('script'); foo.type = 'text/javascript'; foo.async = true;
foo.src = 'foo.js';
foo.onload = foo.onreadystatechange = function() {
//subsequent executions..
foo.onload = foo.onreadystatechange = null;
}
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore foo, s);
})();
만약 jQuery를 사용할 수 있는 환경이라면 $.getScript(url, successCallback)을 사용 할 수 있습니다.
위 코드는 레거시 코드를 혼용해서 작성한 코드이기 때문에 IE7 모드 부터 동작하는 것을 확인 하였지만(본 블로그의 SyntaxHighlighter에 적용되어 있습니다 xD) 제가 알지 못하는 최신 브라우저에서만 지원하는 튜닝요소가 존재할 수 있습니다. 해당 기능을 정식으로 지원하는 브라우저는 다음과 같다고 합니다.
- IE10+ (Preview2 이상)
- Chrome 12+
- Safari 5.1+
- Firefox 4+
2012년 5월 23일 수요일
Spring framework의 싱글턴 인스턴스에 대한 동시다중액세스 상황과 관련하여 생각해 봄직한 의문..
참고 : http://stackoverflow.com/questions/4617437/when-multiple-access-spring-singleton-instance-at-same-time
Springframework의 bean 객체는 기본적으로 싱글턴 인스턴스를 갖습니다(물론 변경도 가능합니다!). 이 싱글턴 객체에 대해 액세스가 동시에 무한히 증가하는 상황이라면 어떻게 될까? 라는 의문을 갖게 된적이 있었는데 이런 상황에 대해 정리를 한번 해보고자 합니다.
생각해 보고자 한 상황을 정리해 보면 다음과 같습니다.
Springframework의 bean 객체는 기본적으로 싱글턴 인스턴스를 갖습니다(물론 변경도 가능합니다!). 이 싱글턴 객체에 대해 액세스가 동시에 무한히 증가하는 상황이라면 어떻게 될까? 라는 의문을 갖게 된적이 있었는데 이런 상황에 대해 정리를 한번 해보고자 합니다.
생각해 보고자 한 상황을 정리해 보면 다음과 같습니다.
- Spring framework에서 서비스 객체를 싱글턴 스콥으로 설정하고 컨트롤러에서 DI(Dependency Injection)를 통해 객체를 생성하였을 때 해당 싱글턴 객체에 동시에 다수의 사용자가 접근을 시도하면 충톨이 발생하거나 하는 염려는 없나? 그게 아니라면 IoC(Inversion of Control) 컨테이너가 좀더 나중에 호출 된 작업을 앞서 호출된 작업이 완료될 때 까지 기다렸다가 호출하는 것 인가? 만약 이게 맞다면 퍼포먼스에 심각한 영향을 주는것은 아닌가?
- 싱글턴 객체는 동시에 수차례 액세스 될 수 있습니다. 그리고 이러한 상황에서 충돌을 피하기 위해 thread-safe 하게 만들어져야 합니다. 만약 해당 빈이 thread-safe 하지 않다고 판단되면 싱글턴 스콥이 아닌 다른 영역을 사용하는 것을 고려해야 할 것 입니다.
2012년 5월 17일 목요일
Java 7이 개발자를 위해 준비한것은?
참고 : http://www.infoworld.com/d/application-development/java-7-whats-in-it-developers-170636?page=0,0 , http://openjdk.java.net/projects/coin/
자바 커뮤니티 내부의 정치적 문제와 연기된 다수의 신기능들
자바의 아버지 제임스 고슬링의 2010년 오라클 퇴사 그리고 아파치 재단과의 불화 및 각종 커뮤니티 내부의 갈등 등으로 별다른 진전없는 5년을 보낸후 마침내 Java SE 7이 발표 되었습니다. 어찌 되었건 오라클은 이런 문제들을 이제는 어느정도 정리하고 극복해 낸 것으로 보입니다.
5년의 노력에도 불구하고 Java SE 7은 처음 계획되었던 것과는 많이 다른 모습으로 발표되었습니다. 최초 JDK 7에서 계획 되었던 수많은 계획들은 2012년 발표 예정(?)인 JDK 8으로 연기되었으며 Java SE 7은 어쨌든 2파트로 나눈 릴리즈의 첫번째 제품이 되었습니다. 상황이 이렇다 보니 Java SE 7은 잠시 거쳐가는 버전으로 보이는건 어쩔 수 없는듯 합니다.
연기된 기능들 중에는 멀티코어 프로그래밍을 위한 자바의 람다표현식이나 클로저기능, 모듈러 프로그래밍을 위한 언어와 VM 지원 그리고 JDK의 모듈 시스템들이 있습니다. 많은 기능들이 빠진것에도 불구하고 Java SE 7은 유용한 새 기능들을 제공하고 있습니다.
다음에 언급할 내용은 Java SE 7에서 제공하는 새로운 주요 기능들 입니다.
동적 언어들의 지원
Java SE 7의 주요 기능으로는 JRuby와 Groovy 같은 언어의 출현으로 인해 최근 JVM에서 눈에 띄게 된 동적 언어에 대한 편의성 제공을 들 수 있습니다. 예를 들어 새로운 InvokeDynamic 기능은 동적인 형태로 정의된 객체 지향 언어들의 구현을 지원합니다. JSR-292에 따르면 InvokeDynamic 바이트코드는 정적인 형태의 정보 없이도 효율적이고 유연한 메소드 호출을 수행할 수 있다고합니다. 이러한 동적 언어의 지원은 Java 생태계를 확장하는데 있어 Java SE 7의 매우 중요한 기능으로 볼 수 있습니다.
향상된 멀티코어 그리고 병렬처리 지원
Fork/Join 프레임웍으로부터 시작된 멀티코어 대응의 API는 개발자들이 작업을 다중 프로세서 코어를 이용하여 병렬 수행하는데 있어 좀더 손쉽게 작업을 분배할 수 있도록 도와줄 것입니다. 이는 개발자들이 다중 코어 프로세서를 좀더 잘 활용할 수 있도록 도와줄 것입니다.
개발자 생산성을 위한 컴파일러 최적화
개발자의 생산성 향상에 대한 지원으로 Project Coin을 통한 일반적인 프로그래밍 작업의 단순화와 줄어든 코드와 같은 언어상의 변화가 제공됩니다. 이는 구문을 명확히 하고 코드의 가독성을 높여줄 것이라고 합니다.
Project Coin의 생성자 호출을 위한 다이아몬드 구문은 컴파일러가 타입변수를 명시적으로 기술하지 않더라도 추론을 통해 찾을 수 있도록 하며 try-with-resources 문(하나 이상의 자원을 선언하는 try 구문, 여기서 자원은 프로그램이 끝날때 반드시 닫혀야 하는 객체임)은 파일, 소켓 그리고 DB커넥션과 같은것을 닫는걸 잊더라도 컴파일러에서 자동으로 닫힌 안정적인 코드를 생성합니다.
* Project Coin과 관련하여 별도의 게시물을 작성할 생각입니다.
File I/O, 그래픽 그리고 사운드 향상
Java의 아버지 제임스 고슬링은 파일 시스템 기능중 하나인 NIO2를 특별히 좋아한다고 밝힌적이 있었습니다. 새로운 NIO2의 기능으로 파일 시스템상 좀더 다양한 파일 속성의 작업에 대한 인터페이스를 제공하고 에러와 관련해서도 좀더 많은 정보를 제공한다고 합니다.
네트워크 파일 I/O에 있어 Socket Direct Protocol(SDP) 기능을 중요하게 꼽고 있는데 iSCSI의 경쟁자인 Infiniband에 있어 가상화 환경에서의 진전을 위한 향상된 지원을 할 것이라고 합니다.
Java SE 7은 2D 그래픽 렌더링을 위한 XRender 파이프라인 기능을 제공하며 이는 XWindow 시스템 최상위에서 동작하며 최신 그래픽 프로세서에 접근할 수 있습니다.
Gervill이라 불리는 새로운 사운드 엔진은 리눅스 상에서 다중 어플리케이션이 Audio Synthesis Engine Project MIDI synthesizer를 이용하여 소리를 낼수 있도록 합니다.
마치며..
복잡한 환경속에서 새로운 기능들이 추가된 Java 7이 발표 되었지만 여전히 주변에는 많은 문제가 산재해 있는것 같습니다. 오라클과 구글의 소송에 대한 뭔가 명확하지 않은 상황, 아파치 재단과의 불화 그리고 래리 아저씨의 탐욕은 승리할 수 있을것인가? 이런 사실들은 분명 많은 사람들이 Java에 대해 긍정적일수 만은 없는 상황을 만들고 있는 것 같습니다.
2012년 5월 3일 목요일
Eclipse & JVM 튜닝을 위한 기초지식
참고 :
http://wiki.eclipse.org/Eclipse.ini ,
http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fruntime-options.html ,
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
개요
Eclipse의 시작은 $ECLIPSE_HOME/eclipse.ini파일에 정의된 옵션으로 제어된다. 만약 $ECLIPSE_HOME이 정의되지 않았다면 Eclipse가 설치된 디렉토리(맥을 사용하고 있다면 Eclipse.app/Contents/MacOS 디렉토리를 참조하게 된다)의 eclipse.ini파일을 참조하게 된다.
eclipse.ini는 커맨드라인 옵션을 포함하는 텍스트 파일로 커맨드라인에 추가된 명령어들은 Eclipse가 시작되었을 때 사용된다. 여기에는 많은 옵션들이 존재하며 다음의 좌표를 참고할 수 있다.
*중요
- 각옵션과 옵션에 해당하는 각각의 변수들은 반드시 해당 라인에 위치해야 한다.
- -vmargs다음의 라인들은 JVM의 변수로 통과되어 지기 때문에 eclipse를 위한 변수나 옵션들은 반드시 -vmargs 앞에 위치해야한다(1번의 규칙과 비슷하다고 볼 수 있음)
JVM 지정하기
Eclipse 튜닝을 위해 가장 추천되는 방법으로는 Eclipse를 실행하기 위한 특정 JVM을 지정하는 것 이다. 이렇게 하는 것은 정확히 어떤 JVM에서 Eclipse가 동작하고 있는것인지를 보장해주며 시스템에대한 표준 JVM이 변경되는 것과 같은 시스템 변화에 대한 영향을 받지 않도록 하여준다(JVM은 여러개가 설치될 수 있고 어떤것을 시스템 표준으로 사용할지는 설정하기 나름임). 많은 사용자들이 기본값으로 어떤 JVM이 사용될거라는 것을 알고 있다고 생각하기 때문에 당황스러운 실수를 하기도 한다. eclipse.ini는 그런 문제에 대해 확신을 갖을 수 있도록 해줄 것 이다.
다음의 eclipse.ini의 예시는 -vm 옵션의 정확한 용법의 예를 보여주고 있다.
*주의 -vm 옵션의 형식과 관련해서 정확하게 기술하는 것이 무엇보다 중요하다.
- -vm 옵션과 해당하는 값(경로)는 반드시 다른 라인에 위치해야 한다.
- 값은 반드시 Java 홈 디렉토리만이 아닌 Java 실행을 위한 절대경로 혹은 상대경로이어야 한다.
- -vm 옵션은 반드시 -vmargs 옵션의 앞에서 선언 되어야 하며 -vmargs 다음에 오는 모든 값은 JVM으로 곧바로 전달된다.
다음은 eclipse.ini에 -vm 변수를 추가하고 최대 힙 공간을 증가시킨 예제이다.
-startup
plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502
-product
org.eclipse.epp.package.java.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
-vm
C:\Java\JDK\1.6\bin\javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx1024m
한가지 기억해야 할점은 이러한 옵션들에 대한 적절한 값이 운영체제나 Eclipse 패키지 버전에 따라 다를 수 있다는거다.
Java HotSpot VM 옵션
Java HotSpot VM 옵션들의 범주
- -X로 시작되는 옵션들은 비표준(모든 VM에 대해 지원되는 것을 보장하지 않음)이며 추후 JDK 릴리즈에서 별다른 공지 없이 변경될 수 있다.
- -XX로 정의되는 옵션들은 불안정한 옵션이며 마찬가지로 별다른 공지 없이 변경 될 수 있다.
1.3.0 이전의 JDK 사용자의 경우 Exact VM 플래그를 참고하여야 한다(Java HotSpot VM은 1.3.0이후에 도입되었음).
일부 유용한 -XX 옵션들
기본 값들은 Java SE6의 Solaris Sparc 버전에서 -server옵션 목록에 포함되며 일부 옵션들은 아키텍쳐/OS/JVM 버전에 따라 다양할 수 있다. 플렛폼 별로 갖는 다른 기본 값은 설명을 참고하면 된다.
- Boolean 옵션들은 -XX:+<option>을 이용해서 켜거나 -XX:-<option>을 이용해서 끌 수 있다.
- Numeric 옵션들은 -XX:<option>=<number>를 이용해서 설정할 수 있다. 이렇게 표기된 숫자들은 메가바이트에 대해 'm' 또는 'M'을 붙여 표현할 수 있고 킬로바이트는 'k' 또는 'K' 그리고 기가바이트에 대해 'g' 또는 'G'를 붙일 수 있다.(예를 들자면 32k는 32768과 같다)
- String 옵션들은 -XX:<option>=<string>을 이용해서 설정할 수 있다. 일반적으로 파일 또는 경로를 지정하거나 옵션에 해당되는 명령어들을 사용할 수 있다.
설명에 manageable이라는 플래그가 표시된 것은 JDK 관리 인터페이스(com.sun.management.HotSpotDiagnosticMXBean API)와 JConsole을 통해 동적으로 사용될 수 있다. Java SE 6 플렛폼 어플리케이션의 모니터링과 관리의 Figure 3에서 예를 보이고 있다. manageable 플래그는 jinfo-flag를 통해서 설정할 수도 있다.
다음의 옵션들은 다소 느슨한 기준으로 그룹지어져 각 카테고리에 포함되어 있다.(클릭하면 해당 리스트로 이동합니다)
- VM의 기본 동작을 변경하는 Behavioral 옵션들
- Garbage First(G1) 가비지 컬렉션 옵션들
- VM 퍼포먼스 튜닝에 사용 될 수 있는 다이얼스위치(knob) 역할을 하는 퍼포먼스 튜닝 옵션들
- 일반적인 추적을 가능하게 하거나 프린팅 또는 VM 정보를 내보낼 수 있는 디버깅 옵션들
2012년 4월 12일 목요일
XML 1.0을 써야하는 이유
참고 : http://www.cafeconleche.org/books/effectivexml/chapters/03.html , http://en.wikipedia.org/wiki/C0_and_C1_control_codes
XML을 쓰다보면 1.1버전이 존재하는데 왜 1.0만을 쓰는걸까 라는 의문이 들었던게 생각나서 정리해 봅니다.
XML 1.1에 대해 알아야 할 모든것은 다음 두가지로 요약될 수 있다.
</ምዕራፋ>
</መጽሐፋ>
XML을 쓰다보면 1.1버전이 존재하는데 왜 1.0만을 쓰는걸까 라는 의문이 들었던게 생각나서 정리해 봅니다.
XML 1.1에 대해 알아야 할 모든것은 다음 두가지로 요약될 수 있다.
- 사용하지 마시오.
- (전문가 한정)혹시 몽고어, 일본 메이지 시대에 사용되다 없어진 Yi음절, 캄보디아어, 암하라어, 몰디브어, 버마어 또는 일부 다른 소수언어로 이야기 하고 있거나 이런 언어를 이용해서 마크업(일반 텍스트가 아닌 마크업임)을 표기하길 원한다면 XML 버전 선언부 속성을 1.1로 설정할 수 있다. 만약 그게 아니라면 첫번째 규칙으로 돌아가시오.
- 네임 문자열(예를 들면 엘리먼트명, 어트리뷰트명, 엔티티명 등등)에 대해 문자열 셋을 확장할 수 있게 한다. 이는 잠재적으로 문서를 이해하기 힘들게 한다.
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title>የማቴዎሰ ወንጌል</title>
<chapter number="፩">
<title>የኢየሱሰ የትው ልድ ሐረግ</title>
<verse number="፩">
የዳዊት ልጅ፣ የአከርሃም ልጅ የሁ ነው የኢየሱሰ ከርሰቶሰ የትው ልድ ሐረግ የሚከተለው ነው፤
</verse>
<verse number="፪">
አከርሃም ይሰሐቅነ ወለደ፤
ይሰሐቅ ያዕቆብነ ወለደ፤
ያዕቆብ ይሁዳነና ወነድሞቹነ ወለደ፤
</verse>
</chapter>
</book>
이랬던 것이
<?xml version="1.1" encoding="UTF-8"?>
<መጽሐፋ>
<አርእስት>የማቴዎሰ ወንጌል</አርእስት>
<ምዕራፋ ዌጥር="፩">
<አርእስት>የኢየሱሰ የትው ልድ ሐረግ</አርእስት>
<ቤት ዌጥር="፩">
የዳዊት ልጅ፣ የአከርሃም ልጅ የሁ ነው የኢየሱሰ ከርሰቶሰ የትው ልድ ሐረግ የሚከተለው ነው፤
</ቤት>
<ቤት ዌጥር="፪">
አከርሃም ይሰሐቅነ ወለደ፤
ይሰሐቅ ያዕቆብነ ወለደ፤
ያዕቆብ ይሁዳነና ወነድሞቹነ ወለደ፤
</ቤት></ምዕራፋ>
</መጽሐፋ>
이렇게도 쓸 수 있게됨 -_-;
- 폼피드, 수직탭, BEL 그리고 DC1에서 DC4의 C0 제어 문자들(NUL 제외)들이 XML 문서 상에서 일반 문자 처럼 이스케이프 되어 인식된다. 하지만 정말 의미 없는 기능중 하나다.
- C1 제어 문자들(NEL 제외)은 반드시 이스케이프 된 문자이어야 한다. XML 1.1이 상위셋이 아닌데다가 XML 1.0과 상호 호환성 또한 유지되지 않는 문제가 있음 (원문의 예시 참고)
- NEL(2바이트의 CR LF를 단일바이트로 대체하는 문자)을 XML 문서에서 사용할 수 있지만 LF로 파싱됨. IBM의 메인프레임에서나 사용하던 문자로 일반적인 다른 시스템의 에디터에서 열 경우 엉망진창인 문서를 볼 수 있게된다. (원문의 예시 참고)
- 파서는 아마도(꼭 그럴 필요가 있는건 아니지만) 유니코드 데이터가 정규화 되지 않았다고 클라이언트 어플리케이션에 알릴것이다. 유니코드에서는 동일한 문자가 다른코드 에서도 제공되는 케이스가 있는데 이럴 경우 XML1.1에선 더블케릭터가 아닌 싱글케릭터를 사용해야만 한다.
- 네임스페이스 접두어가 불명확 할 수 있다. 실제로 사용할 일이 거의 없을 뿐더러 XML1.1과의 호환성을 유지하기 위한 부대비용이 많이 들게된다. (원문의 예시 참고)
2011년 10월 11일 화요일
TOMCAT Multi-Instance & Service Regist (Windows)
* 이 글은 윈도우즈 시스템 및 JDK1.6, TOMCAT6를 기반으로 작성 되었습니다.
startup.bat (인스턴스 시작 스크립트)
set JAVA_HOME=D:\Java\JDK1.6 (JAVA_HOME을 설정한다.)
set CATALINA_HOME=D:\Tomcat 6 (TOMCAT이 설치된 경로를 설정한다.)
set CATALINA_BASE=C:\TestServer (신규 생성할 인스턴스 경로를 설정한다.)
%CATALINA_HOME%/bin/startup.bat (기존 TOMCAT 인스턴스의 startup.bat 파일을 호출한다.)
shutdown.bat (인스턴스 종료 스크립트)
set JAVA_HOME=D:\Java\JDK1.6 (JAVA_HOME을 설정한다.)
set CATALINA_HOME=D:\Tomcat 6 (TOMCAT이 설치된 경로를 설정한다.)
set CATALINA_BASE=C:\TestServer (신규 생성할 인스턴스 경로를 설정한다.)
%CATALINA_HOME%/bin/shutdown.bat (기존의 TOMCAT 인스턴스의 shutdown.bat 파일을 호출한다.)
* 웹어플리케이션의 디플로이 경로는 C:\TestServer\webapps\SampleWebApp\에 하는 것으로 가정한다.
TomcatServiceRegist.bat (서비스 등록 스크립트, 환경에 따라 수정 필요)
D:\Tomcat 6\bin\tomcat.exe -install TomcatService D:\Java\JDK1.6\jre\bin\server\jvm.dll -Djava.library.path=C:\TestServer\webapps\SampleWebApp\WEB-INF\lib -Djava.class.path=C:\TestServer\webapps\SampleWebApp\WEB-INF\lib\sample-lib.jar;C:\TestServer\webapps\SampleWebApp\WEB-INF\classes;C:\TestServer\webapps\SampleWebApp\WEB-INF\classes\sample;D:\Tomcat 6\bin\bootstrap.jar; -Dcatalina.base=C:\TestServer -Dcatalina.home=D:\Tomcat 6 -server -Xmx256M -Xms256M -Xrs -Xnoclassgc -start org.apache.catalina.startup.Bootstrap -params start
@echo Please start the TomcatService now.
@pause
TOMCAT의 인스턴스가 이미 디폴트 상태로 존재하고 있음을 가정으로 진행한다. 신규 인스턴스는 C:\TestServer 폴더에 생성하는 것으로 가정한다. 배치 스크립트명은 다음과 같이 정한다. 인스턴스 시작 : startup.bat, 인스턴스 종료 : shutdown.bat, 서비스 등록 : TomcatServiceRegist.bat 신규 인스턴스의 TOMCAT 서비스명은 TomcatService로 등록한다.
- C:\TestServer 폴더를 생성한다.
- 생성한 C:\TestServer 폴더 내부에 최초 설치된 톰켓과 동일하게 비어있는 폴더(bin, conf, logs, temp, webapps)를 생성한다.
- 기존에 등록된 인스턴스의 conf 폴더에서 server.xml, web.xml 파일을 신규 생성할 인스턴스의 C:\TestServer 내부의 conf 폴더로 복사한다.
- server.xml 파일의 port 정보를 기존에 등록 된 인스턴스의 포트와 중복되지 않게 설정한다.
- C:\TestServer 폴더 내부의 bin 폴더에 다음의 스크립트를 생성한다.
startup.bat (인스턴스 시작 스크립트)
set JAVA_HOME=D:\Java\JDK1.6 (JAVA_HOME을 설정한다.)
set CATALINA_HOME=D:\Tomcat 6 (TOMCAT이 설치된 경로를 설정한다.)
set CATALINA_BASE=C:\TestServer (신규 생성할 인스턴스 경로를 설정한다.)
%CATALINA_HOME%/bin/startup.bat (기존 TOMCAT 인스턴스의 startup.bat 파일을 호출한다.)
shutdown.bat (인스턴스 종료 스크립트)
set JAVA_HOME=D:\Java\JDK1.6 (JAVA_HOME을 설정한다.)
set CATALINA_HOME=D:\Tomcat 6 (TOMCAT이 설치된 경로를 설정한다.)
set CATALINA_BASE=C:\TestServer (신규 생성할 인스턴스 경로를 설정한다.)
%CATALINA_HOME%/bin/shutdown.bat (기존의 TOMCAT 인스턴스의 shutdown.bat 파일을 호출한다.)
* 웹어플리케이션의 디플로이 경로는 C:\TestServer\webapps\SampleWebApp\에 하는 것으로 가정한다.
TomcatServiceRegist.bat (서비스 등록 스크립트, 환경에 따라 수정 필요)
D:\Tomcat 6\bin\tomcat.exe -install TomcatService D:\Java\JDK1.6\jre\bin\server\jvm.dll -Djava.library.path=C:\TestServer\webapps\SampleWebApp\WEB-INF\lib -Djava.class.path=C:\TestServer\webapps\SampleWebApp\WEB-INF\lib\sample-lib.jar;C:\TestServer\webapps\SampleWebApp\WEB-INF\classes;C:\TestServer\webapps\SampleWebApp\WEB-INF\classes\sample;D:\Tomcat 6\bin\bootstrap.jar; -Dcatalina.base=C:\TestServer -Dcatalina.home=D:\Tomcat 6 -server -Xmx256M -Xms256M -Xrs -Xnoclassgc -start org.apache.catalina.startup.Bootstrap -params start
@echo Please start the TomcatService now.
@pause
스크립트 작성 완료 후 웹어플리케이션을 디플로이 한다. 신규 인스턴스를 시작하여 웹어플리케이션이 정상적으로 동작하는지 확인한다. 신규 인스턴스를 종료하고 서비스등록 스크립트를 실행하여 Windows Service로 등록하고 정상적으로 등록이 되었는지 확인한다.
2011년 9월 5일 월요일
Oracle Weblogic 11g HTTP Basic Authentication 문제
흔히 웹로직 엔지니어들이 이야기하는 고객들이 '톰캣에서는 되는데 왜 웹로직에서는 안되요?'라고 묻는 문제중 하나일듯 싶은 케이스 -ㄴ-; 웹로직 엔지니어는 아니지만 워낙 잡다하게 많이 하다보니..
웹로직 보안 정책상 디폴트 상태는 Authorization 헤더 데이터를 어플리케이션으로 직접 전달 할 수 없도록 막아둔 상태인데 config.xml 파일을 열어 <security-configuration>항목에 다음을 추가하면 정상적으로 데이터가 전달 됨.. 좀더 베스트 케이스가 있는지는 아직 모르겠음;
웹로직 보안 정책상 디폴트 상태는 Authorization 헤더 데이터를 어플리케이션으로 직접 전달 할 수 없도록 막아둔 상태인데 config.xml 파일을 열어 <security-configuration>항목에 다음을 추가하면 정상적으로 데이터가 전달 됨.. 좀더 베스트 케이스가 있는지는 아직 모르겠음;
<security-configuration> ... <enforce-valid-basic-auth-credentials>false</enforce-valid-basic-auth-credentials> </security-configuration>ps. 그러고보니.. 이거 9.2부터 해당되는 문제라는걸 빼먹은..;
피드 구독하기:
글 (Atom)
