반응형


javax.mail.SendFailedException: Invalid Addresses 에러 

 

javax.mail.SendFailedException: Invalid Addresses;
  nested exception is:
 class com.sun.mail.smtp.SMTPAddressFailedException: 550 5.7.1 <idtong@nate.com>... Relaying denied
[MSG]  
Invalid Addresses;
  nested exception is:
 class com.sun.mail.smtp.SMTPAddressFailedException: 550 5.7.1 <idtong@nate.com>... Relaying denied

 

체크리스트 1

props.put("mail.smtp.host", "netian.com");

이 정보가 정확한지 확인해보세요..

 

만약 outlook 으로 위 smtp 서버를 통하여 메일이 전송된다면 저랑은 다른 문제일 것같군요..

 

telnet smtpaddress smtp 하면 해당 smtp 서버에 접근이 가능한지 그리고 활성화 중인지 확인이 가능합니다.

 

체크리스트 2

SMTP서버에서 릴레이를 거부한겁니다.

 

SMTP서버에 릴레이 허용 IP에 글쓴분 IP가 등록이 되어있어야 됩니다.

 

IIS가 설치된 컴퓨터가 있으면 SMTP서비스를 설치해서 SMTP서비스 등록정보에

 

들어가 보시면 좀 더 이해가 쉽게 될 것 같습니다.

 

항목중에 릴레이 라는게 있는데 거기서 특정 IP에서 들어오는 요청만 처리하게 하는

 

메뉴가 있습니다.

 

SMTP서버 지정해 주는 부분을 한번 확인해 보세요.

 

체크리스트 3

 

javax.mail.SendFailedException: Invalid Addresses;
익셉션은

웹메일에서 dns 에 등록되지 않은 서버에 메일을 보낼수 없게 막아놓습니다.

그래서 익셉션이 나는 도메인은 고정되어있을겁니다.

익셉션이 나는 곳에 catch 하는 부분에서 아래의 코드를 넣어보세요..

 

catch (MessagingException me) { 
me.printStackTrace();
Exception ex = me;
do {

if (ex instanceof SendFailedException) {
SendFailedException sfex = (SendFailedException)ex;
Address[] invalid = sfex.getInvalidAddresses();
if (invalid != null) {
System.out.println(" ** Invalid Addresses");
if (invalid != null) {
for (int i = 0; i < invalid.length; i++)
System.out.println(" " + invalid[i]);
}
}

그리고 위에서 캐취된 도메인중 사용가능한 도메인으로 하기 위해서는 서버 관리자에서 도메인을 등록해돌라고 합니다..

서버 관리자가 없으면 .. 찾아서 해야할듯하고요..

수고하세요.

출처 : http://blog.naver.com/PostView.nhn?blogId=idtong&logNo=130035405925


반응형

'Error 유형/해결' 카테고리의 다른 글

not all tokens processed  (0) 2013.12.26
[Mail] Sendmail 오류 메시지  (0) 2013.12.05
반응형

[Mail] Sendmail 오류 메시지

Mail 2012/02/16 12:40


◈ 일시적인 전송 실패
- 417 Temporary delivery error
- The maximum number of delivery attempts has been reached
- End of socket stream data
- Invalid server address
- Bad server response 
◈ 받는사람이 존재하지 않는 경우
- 550 User unknown
- 550 Mailbox unavailable
- 550 5.1.1 No such user
- 554 delivery error: dd This user doesn't have a yahoo.co.kr account (userid@domain.com) [-5] - mta114.mail.krs.yahoo.com 
◈ 받는사람이 사용중지된 경우
- 550 Account disabled
- 550 5.1.1 Inactive mbox
- 550 5.1.1 Suspended user
- 554 delivery error: dd Sorry your message to userid@domain.com cannot be delivered. This account has been disabled or discontinued [#102]. - mta115.mail.krs.yahoo.com 
◈ 받는사람의 메일용량이 초과된 경우
- 552 Requested mail action aborted: exceeded storage allocation - <userid@domain.com>
- The recipient's mailbox is full 
◈ 받는사람의 스팸설정(수신거부)에 의해 차단된 경우
- 550 You are registered as spammer (by user's reject list)
- 554 5.0.0 Service unavailable (userid: mail is denied, by spam filter) 
◈ 메일발송권한이 없거나 상대방 메일서버 설정 문제
- 550 Relay denied
- 554 Relay operation rejected
- 550 5.7.1 Unable to relay for userid@domain.com 
◈ 대량 메일 발송으로 인해 대형포털로부터 IP가 차단된 경우
- 550 5.7.1 <userid@domain.com>... Error.your access was denied.? Since you sent too many e-mails,you are not allowed to send more e-mails within 24 hours.After 24 hours,you can send e-mails as usual.If you did not send any e-mails,which is considered as a spam,you'd better register
- 421 4.3.2 Your IP(111.112.113.114) is filtered and this connection will be closed. You must register your IP to http://realip.naver.com
- 421 4.3.1 You send too many messages.
- 5.1.0 - Unknown address error 550-'Transaction failed. (SPAM_SUSPECT:IP_CONTROL) (http://cleanip.paran.com)'
- 451 mta113.mail.krs.yahoo.com Resources temporarily unavailable. Please try again later [#4.16.5]. 
◈ RBL에 의해 차단된 경우
- 421 4.3.2 Your ip is filtered by RBL (http://realip.naver.com/rbl/rblinfo.html)(IP:111.112.113.114)(Caues:http://dsbl.org/listing?111.112.113.114)
- 553 5.3.0 ... Rejected - see http://www.mail-abuse.org/rbl/
- 553-mail rejected because your IP is in DUL. See http://www.mail-abuse.com/enduserinfo.html 
◈ 상대방 메일서버에서 스팸으로 인식해서 차단된 경우
- 553 Blocked for spam
- 550 5.5.0 Your message was rejected. 
◈ 상대방 메일서버에서 바이러스로 인식해서 차단된 경우
- 550 Virus in your mail!!! 
◈ 첨부파일에 실행파일을 첨부할 수 없는 경우 - 구글
- 552 5.7.0 Illegal Attachment 36si248323nzk+I9:I15 
◈ 받는사람과 보낸사람에 문제가 있어 리턴메일이 반복되는 경우
- Mail loop detected 
◈ 받는 메일서버에서 허용하는 최대메일크기를 초과한 경우
- Message size excedes the server limit 






ㆍ일시적인 전송 실패 
- 417 Temporary delivery error
- The maximum number of delivery attempts has been reached
- End of socket stream data
- Invalid server address
- Bad server response
상대방 메일서버에 연결할 수 없을때 발생하는 에러로 일시적인 현상일 수 도 있습니다.

리턴메일의 에러는 정해진 재전송횟수만큼 모두 실패했다는 의미입니다.
[<00>] XMail bounce: Rcpt=[touser@to.com];Error=[The maximum number of delivery attempts has been reached]

리턴메일의 자세한 로그중 "ErrString" 항목에 따라 몇가지 원인으로 분류됩니다.
[<04>] Here is listed the message log file:

[PeekTime] 1142791127 : Mon, 20 Mar 2006 02:58:47 +0900
<<
ErrCode = -162
ErrString = End of socket stream data
SMAIL SMTP-Send MX = "mx.to.com." SMTP = "from.com" From = "fromuser@from.com" To = "touser@to.com" Failed !
SMTP-Error = "417 Temporary delivery error"
SMTP-Server = "mx.to.com."
>>

a) End of socket stream data
DNS 질의를 통해 상대방 메일서버(MX record)를 찾았지만 메일서버에 접속할 수 없다는 의미입니다.

이 경우 상대방 메일서버가 동작중이 아니거나 과도한 부하로 응답대기시간을 초과한 상태일 가능성이 높습니다.
상대방 메일서버의 관리자가 조치를 취하지 않으면 별도의 해결방법이 없습니다.

만약 대형포털등 다른 메일서버에서 상대방 메일서버로 메일이 잘 들어간다면 다음 문제일 가능성이 있습니다.

-. 상대방 메일서버와의 네트워크 회선 문제.
: 드물지만 일부 네트워크간에 속도가 아주 느려 문제가 발생하는 경우가 있습니다. 이 때는 메일서버가 설치된 네트워크와 
다른 외부 네트워크 두 곳에서 상대방 메일서버의 25번 포트를 열어서 속도를 비교해보면 됩니다. 만약 응답속도가 
현저하게 차이가 난다면 네트워크 관리업체에 문의해서 해결하시면 됩니다.

-. 상대방 메일서버에서 별도의 조치를 취해둔 경우.
: 드물지만 이 경우 상대방 메일서버 관리자에게 문의하셔서 해결하셔야 합니다.

b) Invalid server address
받는사람의 도메인이 존재하지 않거나, 상대방 메일서버의 DNS 설정상의 문제가 있어 메일을 보낼 수 없는 상황입니다.
주로 사용자들이 받는사람의 도메인을 잘못 적어서 발생한 경우가 많습니다.

c) Bad server response
메일서버에서 인식할 수 없는 응답코드로 원인은 IP가 차단된 경우가 많습니다.
이때는 "ErrString"대신 "SMTP-Error"항목을 가지고 [대량 메일 발송으로 인해 대형포털로부터 IP가 차단된 경우]에서 원인을 찾아보시면 됩니다.







ㆍ받는사람이 존재하지 않는 경우 
- 550 User unknown
- 550 Mailbox unavailable
- 550 5.1.1 No such user
- 554 delivery error: dd This user doesn't have a yahoo.co.kr account (userid@domain.com) [-5] - mta114.mail.krs.yahoo.com
상대방 메일서버에 받는사람이 존재하지 않습니다. 메일주소를 다시 확인해보시기 바랍니다.
다른 메일서버에서 보냈을때 실제로 존재하는 유저라면 메일서버의 셋팅에 관련된 문제일 수 도 있습니다.
예를 들어 domain.com 이 실제로 A 서버에서 사용하지만, 테스트 혹은 관리자의 실수로 B 서버에도
설정되어 있는 경우가 있습니다. 이때, B 서버에서 userid@domain.com 으로 메일을 보내게되면 
A 서버가 아닌 B 서버 내부로 메일 전송을 시도하기 때문에 받는사림이 존재하지 않는다고 나올 수 있습니다.







ㆍ받는사람이 사용중지된 경우 
- 550 Account disabled
- 550 5.1.1 Inactive mbox
- 550 5.1.1 Suspended user
- 554 delivery error: dd Sorry your message to userid@domain.com cannot be delivered. This account has been disabled or discontinued [#102]. - mta115.mail.krs.yahoo.com
상대방 메일서버에서 받는사람이 사용중지/휴면계정으로 처리된 상태이므로 메일을 보낼 수 없습니다.







ㆍ받는사람의 메일용량이 초과된 경우 
- 552 Requested mail action aborted: exceeded storage allocation - <userid@domain.com>
- The recipient's mailbox is full
상대방 메일서버에서 받는사람의 메일저장용량이 초과되어 메일을 보낼 수 없습니다.
잠시후 다시 시도해보시거나 받는사람에게 다른 방법으로 연락해보시기 바랍니다.







ㆍ받는사람의 스팸설정(수신거부)에 의해 차단된 경우 
- 550 You are registered as spammer (by user's reject list)
- 554 5.0.0 Service unavailable (userid: mail is denied, by spam filter)
받는사람이 스팸설정 혹은 수신거부 목록에 보낸사람의 주소를 등록해두었을 가능성이 높습니다.
받는사람에게 다른 방법으로 연락해보시기 바랍니다.







ㆍ메일발송권한이 없거나 상대방 메일서버 설정 문제 
- 550 Relay denied
- 554 Relay operation rejected
- 550 5.7.1 Unable to relay for userid@domain.com
☞ 같은 도메인을 제외한 다른 모든 메일서버에서 에러가 발생한 경우.
사용자의 메일클라이언트(아웃룩 등)설정 문제일 가능성이 높습니다. 먼저 계정 셋팅시 [보내는 메일서버에 인증 필요]옵션을
체크했는지 확인해보시기 바랍니다. 자세한 내용은 [POP3 이용안내]에 나와있습니다.

☞ 특정 메일서버에서만 에러가 발생할 경우
상대방 메일서버에서 DNS 설정 혹은 메일서버의 설정 오류로 인해 발생한 문제입니다.
상대방 메일서버의 관리자가 조치를 취하지 않으면 별도의 해결방법이 없습니다.







ㆍ대량 메일 발송으로 인해 대형포털로부터 IP가 차단된 경우 
- 550 5.7.1 <userid@domain.com>... Error.your access was denied.? Since you sent too many e-mails,you are not allowed to send more e-mails within 24 hours.After 24 hours,you can send e-mails as usual.If you did not send any e-mails,which is considered as a spam,you'd better register
- 421 4.3.2 Your IP(111.112.113.114) is filtered and this connection will be closed. You must register your IP to http://realip.naver.com
- 421 4.3.1 You send too many messages.
- 5.1.0 - Unknown address error 550-'Transaction failed. (SPAM_SUSPECT:IP_CONTROL) (http://cleanip.paran.com)'
- 451 mta113.mail.krs.yahoo.com Resources temporarily unavailable. Please try again later [#4.16.5].
대형포털의 경우 IP를 등록해두지 않고 "1일 1,000통(시간당 100통) 이상의 대량 메일"을 보내면 IP가 차단될 가능성이 있습니다.
관리자가 대량 메일을 발송하지 않더라도 다음 경우에도 차단될 가능성이 높습니다.
1. 사용자가 대형포털로 포워딩을 설정해두면 하루에 1000통의 메일을 받을 경우 대형포털로 똑같이 1000통을 보내게 되므로 차단될 수 있습니다.
2. 사용자가 대형포털로 자동응답을 설정해두면 보낸사람주소를 대형포털로 위장한 스팸의 경우에는 대형포털로 자동응답을 계속 하게되므로 차단될 수 있습니다.

이로 인해 회원수나 사용량이 많은 곳에서는 각 대형포털사이트마다 IP등록을 해두시는것이 유리합니다.
이미 차단된 경우에는 각각 사이트에 방문하셔서 차단해지 신청을 하셔야 합니다.

daum.net/hanmail.net - http://ipregister.daum.net
error) 550 5.7.1 <userid@domain.com>... Error.your access was denied.? Since you sent too many e-mails,you are not allowed to send more e-mails within 24 hours.After 24 hours,you can send e-mails as usual.If you did not send any e-mails,which is considered as a spam,you'd better register
naver.com - http://realip.naver.com
error) 421 4.3.2 Your IP(111.112.113.114) is filtered and this connection will be closed. You must register your IP to http://realip.naver.com
nate.com - http://antispam.nate.com
error) 421 4.3.1 You send too many messages.
paran.com - http://cleanip.paran.com
error) 5.1.0 - Unknown address error 550-'Transaction failed. (SPAM_SUSPECT:IP_CONTROL) (http://cleanip.paran.com)'
yahoo.com/yahoo.co.kr - http://kr.antispam.yahoo.com/bulkmail.html
error) 451 mta113.mail.krs.yahoo.com Resources temporarily unavailable. Please try again later [#4.16.5]. 
chol.com - http://regip.chol.com
korea.com - http://mail.korea.com/default_whiteip.asp
empas.com - http://help.empas.com/guide/spam_mail02.html







ㆍRBL에 의해 차단된 경우 - 421 4.3.2 Your ip is filtered by RBL (http://realip.naver.com/rbl/rblinfo.html)(IP:111.112.113.114)(Caues:http://dsbl.org/listing?111.112.113.114)
- 553 5.3.0 ... Rejected - see http://www.mail-abuse.org/rbl/
- 553-mail rejected because your IP is in DUL. See http://www.mail-abuse.com/enduserinfo.html
RBL(Real-time Black List)은 스팸메일을 발송하는 IP를 수집해 관리하는 리스트입니다.
RBL을 활용해 일부 사이트에서 차단하는 경우가 있는데 이 때는 에러메세지에 나와있는 사이트로 방문하셔서 RBL 해지신청을 진행하셔야 합니다.

해지 신청을 하시기전에 먼저 Reverse DNS (PTR record) 를 설정해두시는게 좋습니다.
대부분의 일반적인 환경에서는 자체적으로 설정이 불가능하며 상위기관에 의뢰를 하셔야 합니다.
서버호스팅일 경우 해당 호스팅업체에 의뢰를 하시면 되고, 임대회선을 사용해서 사무실내에서 운영중이라면 해당 회선업체에 의뢰를 하시면 됩니다.
의뢰방법은 보통 "RBL 해지를 위한 Reverse DNS (PTR record) 등록 요청"정도로 제목을 적으셔서
사용하시는 도메인, IP, 회사명(고객번호 등)을 적으신후 팩스나 메일로 요청하시면 대부분 바로 처리해주고 있습니다.

단, 가정에서 사용되는 초고속인터넷 사용자는 유동IP를 할당받으므로 Reverse DNS (PTR record) 등록이 불가능합니다.
이로 인해 해지신청이 거의 어렵고, 국내 대형 포털들에서도 유동IP로 부터 발송되는 메일은 대부분 차단하고 있습니다.







ㆍ상대방 메일서버에서 스팸으로 인식해서 차단된 경우 - 553 Blocked for spam
- 550 5.5.0 Your message was rejected.
상대방 메일서버에서 스팸으로 인식하여 거부한 것으로 추측됩니다.
단 거부한 원인에 대해 언급되어 있지 않으므로 어떤 조건에 의해 거부되었는지 리턴메일만으로는 알 수 없습니다.
이 경우 제목이나 본문내에 광고,성인등 스팸으로 분류될만한 단어가 있는지 확인해보시고
해결이 안될 경우 상대방 메일서버의 관리자에게 문의해보셔야 합니다.







ㆍ상대방 메일서버에서 바이러스로 인식해서 차단된 경우 - 550 Virus in your mail!!!
상대방 메일서버가 메일내에 바이러스를 검출하여 차단한 경우입니다.
그런데 실제 바이러스 메일을 보내지 않았더라도 "보낸 사람"주소가 위조되어 잘못된 리턴메일을 받는 경우가 있는데
이는 무시하시면 됩니다.







ㆍ첨부파일에 실행파일을 첨부할 수 없는 경우 - 구글 - 552 5.7.0 Illegal Attachment 36si248323nzk+I9:I15
Gmail의 경우 바이러스 차단을 위해 첨부 파일에 실행 파일이 들어간 경우까지 모두 차단하고 있습니다.
실행파일을 압축해서 보내더라도 동일하게 차단되니 꼭 보내야 한다면 실행파일의 확장자를 다른것으로 바꾸는 방법밖에 없습니다.







ㆍ받는사람과 보낸사람에 문제가 있어 리턴메일이 반복되는 경우 - Mail loop detected
메일링, 서버내 서비스 안내메일 등 자동툴을 통해 메일을 보낼때 많이 발생하는 문제입니다.
메일을 보낸주소와 받는주소가 모두 잘못 되었을때 반복적으로 송수신이 일어나는데 
이때 서버부하를 방지하기 위해 자동 차단하는 것입니다.

예를 들어 보낸주소가 id@from.com 이고, 받는주소가 no-user@to.com 일 경우
엔메일에서 no-user@to.com 로 메일을 보냈을때 상대방 메일서버에서
존재하지 않는 사용자일땐 보낸주소로 리턴메일을 보내게 됩니다.
이때 보낸주소인 id@from.com 이 없는 주소일때는 다시 상대방 메일서버로
존재하지 않는 사용자라고 리턴메일을 보내게 됩니다.
이처럼 2개의 메일서버간에 동일한 리턴메일을 계속 주고 받는 현상입니다.

이 에러메세지가 안나오게 하려면 메일링 서비스시 다음 사항에 유의해야 합니다.
1. 보낸사람의 주소가 실제로 존재하는지 메일을 받을 수 있는 상태인지 확인합니다.
2. 보낸사람의 메일계정 용량이 리턴메일을 받을 수 있을만큼 충분한지 확인합니다.







ㆍ받는 메일서버에서 허용하는 최대메일크기를 초과한 경우 
- Message size excedes the server limit
대부분의 메일서버에서는 부하를 방지하기 위해 메일의 크기가 일정크기 이상일 경우 받지 않도록 조치하고 있습니다.
따라서 첨부파일을 삭제해서 메일의 크기를 줄인뒤 다시 보내보시고 첨부파일은 다른 방법으로 전달하셔야 합니다.
일반적으로 첨부파일은 10MB 정도까지만 허용하고 있습니다.


반응형

'Error 유형/해결' 카테고리의 다른 글

not all tokens processed  (0) 2013.12.26
javax.mail.SendFailedException: Invalid Addresses 에러  (0) 2013.12.06
반응형

@SuppressWarnings("rawtypes")


이클립스가 권해주는 어노테이션.

컴파일러가 일반적으로 경고하는 내용 중 "이건 하지마"하고 제외시킬 때 쓰는 것.
옵션을 확인해보니 다음과 같았다. (blog의 내용이다) 
  1. all : 모든 경고  
  2. cast : 캐스트 연산자 관련 경고
  3. dep-ann : 사용하지 말아야 할 주석 관련 경고
  4. deprecation : 사용하지 말아야 할 메서드 관련 경고
  5. fallthrough : switch문에서 break 누락 관련 경고
  6. finally : 반환하지 않는 finally 블럭 관련 경고
  7. null : null 분석 관련 경고
  8. rawtypes : 제너릭을 사용하는 클래스 매개 변수가 불특정일 때의 경고
  9. unchecked : 검증되지 않은 연산자 관련 경고
  10. unused : 사용하지 않는 코드 관련 경고


출처 : http://agagomty.blogspot.kr/2012/12/suppresswarningsrawtypes.html


반응형
반응형

EHCache를 이용한 기본적인 캐시 구현 방법 및 분산 캐시 구현 방법을 살펴본다.

EHCache의 주요 특징 및 기본 사용법

게시판이나 블로그 등 웹 기반의 어플리케이션은 최근에 사용된 데이터가 또 다시 사용되는 경향을 갖고 있다. 80:20 법칙에 따라 20%의 데이터가 전체 조회 건수의 80%를 차지할 경우 캐시를 사용함으로써 성능을 대폭적으로 향상시킬 수 있을 것이다.

본 글에서는 캐시 엔진 중의 하나인 EHCache의 사용방법을 살펴보고, Gaia 시스템에서 EHCache를 어떻게 사용했는 지 살펴보도록 하겠다.

EHCache의 주요 특징

EHCache의 주요 특징은 다음과 같다.

  • 경량의 빠른 캐시 엔진
  • 확장(scable) - 메모리 & 디스크 저장 지원, 멀티 CPU의 동시 접근에 튜닝
  • 분산 지원 - 동기/비동기 복사, 피어(peer) 자동 발견
  • 높은 품질 - Hibernate, Confluence, Spring 등에서 사용되고 있으며, Gaia 컴포넌트에서도 EHCache를 사용하여 캐시를 구현하였다.

기본 사용법

EHCache를 사용하기 위해서는 다음과 같은 작업이 필요하다.

  1. EHCache 설치
  2. 캐시 설정 파일 작성
  3. CacheManager 생성
  4. CacheManager로부터 구한 Cache를 이용한 CRUD 작업 수행
  5. CacheManager의 종료

EHCache 설치

EHCache 배포판은 http://ehcache.sourceforge.net/ 사이트에 다운로드 받을 수 있다. 배포판의 압축을 푼 뒤, ehcache-1.2.x.jar 파일이 생성되는 데, 이 파일을 클래스패스에 추가해준다. 또한, EHCache는 자카르타의 commons-logging API를 사용하므로, commons-logging과 관련된 jar 파일을 클래스패스에 추가해주어야 한다.

ehcache.xml 파일

EHCache는 기본적으로 클래스패스에 존재하는 ehcache.xml 파일로부터 설정 파일을 로딩한다. 가장 간단한 ehcache.xml 파일은 다음과 같이 작성할 수 있다.

<ehcache>
    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
    
    <cache name="simpleBeanCache"
            maxElementsInMemory="10"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />

</ehcache>


위 코드에서 <defaultCache> 태그는 반드시 존재해야 하는 태그로서, 코드에서 캐시를 직접 생성할 때 사용되는 캐시의 기본 설정값을 저장한다. <cache> 태그는 하나의 캐시를 지정할 때 사용된다. name 속성은 캐시의 이름을 지정하며, 코드에서는 이 캐시의 이름을 사용하여 사용할 Cache 인스턴스를 구한다.

설정 파일에 대한 자세한 내용은 뒤에서 살펴보기로 하자.

CacheManager 생성

ehcache.xml 파일을 작성했다면 그 다음으로 할 작업은 net.sf.ehcache.CacheManager 객체를 생성하는 것이다. CacheManager 객체는 다음의 두 가지 방법 중 한가지 방식을 사용하여 생성할 수 있다.

  • CacheManager.create() : 싱글톤 인스턴스 사용
  • new CacheManager() : 새로운 CacheManager 인스턴스 생성

CacheManager.create() 메소드는 싱글톤 인스턴스를 생성하기 때문에 최초에 한번 호출될 때에만 CacheManager의 초기화 작업이 수행되며, 이후에는 동일한 CacheManager 인스턴스를 리턴하게 된다. 아래는 CacheManager.create() 메소드의 사용 예이다.

CacheManager cacheManager = CacheManager.create();


싱글톤 인스턴스가 아닌 직접 CacheManager 객체를 조작하려면 다음과 같이 new를 사용하여 CacheManager 인스턴스를 생성해주면 된다.

CacheManager cacheManager = new CacheManager();


두 방식 모두 클래스패스에 위치한 ehcache.xml 파일로부터 캐시 설정 정보를 로딩한다.

만약 클래스패스에 위치한 ehcache.xml 파일이 아닌 다른 설정 파일을 사용하고 싶다면 다음과 같이 URL, InputStream, 또는 String(경로) 객체를 사용하여 설정 파일의 위치를 지정할 수 있다.

URL configFile = this.getClass().getResource("/ehcache_config_replicate.xml")
CacheManager cacheManager = new CacheManager(configFile);


Cache에 CRUD 수행

CacheManager 인스턴스를 생성한 다음에는 CacheManager 인스턴스로부터 Cache 인스턴스를 구하고, Cache 인스턴스를 사용하여 객체에 대한 캐시 작업을 수행할 수 있게 된다. 

Cache 구하기
net.sf.ehcache.Cache 인스턴스는 CacheManager.getCache() 메소드를 사용하여 구할 수 있다.

CacheManager cacheManager = new CacheManager(configFileURL);
Cache cache = cacheManager.getCache("simpleBeanCache");


CacheManager.getCache() 메소드에 전달되는 파라미터는 ehcache.xml 설정 파일에서 <cache> 태그의 name 속성에 명시한 캐시의 이름을 의미한다. 지정한 이름의 Cache 인스턴스가 존재하지 않을 경우 CacheManager.getCache() 메소드는 null을 리턴한다.

Create/Update 작업 수행
Cache 인스턴스를 구한 다음에는 Cache.put() 메소드를 사용하여 캐시에 객체를 저장할 수 있다. 아래 코드는 Cache.put() 메소드의 사용예이다.

Cache cache = cacheManager.getCache("simpleBeanCache");

SimpleBean newBean = new SimpleBean(id, name);
Element newElement = new Element(newBean.getId(), newBean);
cache.put(newElement);


Cache.put() 메소드는 net.sf.ehcache.Element 객체를 전달받는다. Element 클래스는 캐시에 저장될 원소를 나타내며, 키와 값을 사용하여 원소를 표현한다. Element 객체를 생성할 때 첫번째 파라미터는 원소의 키를 의미하며, 두번째 파라미터는 원소의 값을 의미한다.

EHCache는 캐시에 저장될 각각의 객체들을 키를 사용하여 구분하기 때문에, Element 객체를 생성할 때 (의미상) 서로 다른 객체는 서로 다른 키를 사용해야 한다.

Map과 마찬가지로 EHCache가 제공하는 Cache는 삽입을 하거나 기존의 값을 수정할 때 모두 Cache.put() 메소드를 사용한다. 기존에 캐시에 저장된 객체를 수정하길 원한다면 다음과 같이 동일한 키를 사용하는 Element 객체를 Cache.put() 메소드에 전달해주면 된다.

Element newElement = new Element(id, someBean);
cache.put(newElement);
...
Element updatedElement = new Element(id, updatedBean);
cache.put(updatedElement);


Read 작업 수행
Cache에 보관된 객체를 사용하려면 Cache.get() 메소드를 사용하면 된다. Cache.get() 메소드는 키를 파라미터로 전달받으며, 키에 해당하는 Element 객체를 리턴하며 관련 Element과 존재하지 않을 경우 null을 리턴한다. 아래 코드는 Cache.get() 메소드의 사용예이다.

Element element = cache.get(key);
SimpleBean bean = (SimpleBean) element.getValue();


Element.getValue() 메소드는 캐시에 저장된 객체를 리턴한다. 만약 Serializable 하지 않은 객체를 값으로 저장했다면 다음과 같이 Element.getObejectValue() 메소드를 사용하여 값을 구해야 한다.

Element element = cache.get(key);
NonSerializableBean bean = (NonSerializableBean) element.getObjectValue();


Delete 작업 수행
Cache에 보관된 객체를 삭제하려면 Cache.remove() 메소드를 사용하면 된다. 아래 코드는 Cache.remove() 메소드의 사용예이다.

boolean deleted = cache.remove(key);


Cache.remove() 메소드는 키에 해당하는 객체가 존재하여 삭제한 경우 true를 리턴하고, 존재하지 않은 경우 false를 리턴한다.

CacheManager의 종료

사용이 종료된 CacheManager는 다음과 같이 shutdown() 메소드를 호출하여 CacheManager를 종료해야 한다.

cacheManager.shutdown();


Cache 값 객체 사용시 주의사항

캐시에 저장되는 객체는 레퍼런스가 저장된다. 따라서, 동일한 키에 대해 Cache.put()에 전달한 Element의 값과Cache.get()으로 구한 Element의 값은 동일한 객체를 참조하게 된다.

SimpleBean bean = ...;
Element element = new Element(key, bean);
cache.put(element);

Element elementFromCache = cache.get(key);
SimpleBean beanFromCache = (SimpleBean)elementFromCache.getValue();

(bean == beanFromCache); // true
(element == elementFromCache); // false


위 코드에서 Cache.put()에 전달된 element 객체와 Cache.get()으로 구한 elementFromCache 객체는 서로 다른 객체이다. 하지만, 두 Element 객체가 갖고 있는 값은 동일한 객체를 참조하고 있다. 따라서, 캐시에 값으로 저장된 객체를 변경하게 되면 캐시에 저장된 내용도 변경되므로, 캐시 사용시 이 점에 유의해야 한다.

캐시 설정

캐시 설정 파일에 <cache> 태그를 이용하여 캐시를 설정했었다. 캐시 설정과 관련하여 <cache> 태그는 다양한 속성을 제공하고 있는데, 이들 속성에는 다음과 같은 것들이 존재한다.

name캐시의 이름필수
maxElementsInMemory메모리에 저장될 수 있는 객체의 최대 개수필수
eternal이 값이 true이면 timeout 관련 설정은 무시되고, Element가 캐시에서 삭제되지 않는다.필수
overflowToDisk메모리에 저장된 객체 개수가 maxElementsInMemory에서 지정한 값에 다다를 경우 디스크에 오버플로우 되는 객체는 저장할 지의 여부를 지정한다.필수
timeToIdleSecondsElement가 지정한 시간 동안 사용(조회)되지 않으면 캐시에서 제거된다. 이 값이 0인 경우 조회 관련 만료 시간을 지정하지 않는다. 기본값은 0이다.선택
timeToLiveSecondsElement가 존재하는 시간. 이 시간이 지나면 캐시에서 제거된다. 이 시간이 0이면 만료 시간을 지정하지 않는다. 기본값은 0이다.선택
diskPersistentVM이 재 가동할 때 디스크 저장소에 캐싱된 객체를 저장할지의 여부를 지정한다. 기본값은 false이다.선택
diskExpiryThreadIntervalSecondsDisk Expiry 쓰레드의 수행 시간 간격을 초 단위로 지정한다. 기본값은 120 이다.선택
memoryStoreEvictionPolicy객체의 개수가 maxElementsInMemory에 도달했을 때,모메리에서 객체를 어떻게 제거할 지에 대한 정책을 지정한다. 기본값은 LRU이다. FIFO와 LFU도 지정할 수 있다.선택


아래 코드는 몇 가지 설정 예이다.

<!--
sampleCache1 캐시. 최대 10000개의 객체를 저장할 수 있으며, 
5분 이상 사용되지 않거나 또는 10분 이상 캐시에 저장되어 있을 경우 
캐시에서 제거된다. 저장되는 객체가 10000개를 넘길 경우, 
디스크 캐시에 저장한다.
-->
<cache name="sampleCache1"
       maxElementsInMemory="10000"
       maxElementsOnDisk="1000"
       eternal="false"
       overflowToDisk="true"
       timeToIdleSeconds="300"
       timeToLiveSeconds="600"
       memoryStoreEvictionPolicy="LFU"
       />

<!--
sampleCache2 캐시. 최대 1000개의 객체를 저장한다. 
오버플로우 된 객체를 디스크에 저장하지 않기 때문에 
캐시에 최대 개수는 1000개이다. eternal이 true 이므로, 
timeToLiveSeconds와 timeToIdleSeconds 값은 무시된다.
-->
<cache name="sampleCache2"
       maxElementsInMemory="1000"
       eternal="true"
       overflowToDisk="false"
       memoryStoreEvictionPolicy="FIFO"
       />

<!--
sampleCache3 캐시. 오버플로우 되는 객체를 디스크에 저장한다.
디스크에 저장된 객체는 VM이 재가동할 때 다시 캐시로 로딩된다.
디스크 유효성 검사 쓰레드는 10분 간격으로 수행된다.
-->
<cache name="sampleCache3"
       maxElementsInMemory="500"
       eternal="false"
       overflowToDisk="true"
       timeToIdleSeconds="300"
       timeToLiveSeconds="600"
       diskPersistent="true"
       diskExpiryThreadIntervalSeconds="600"
       memoryStoreEvictionPolicy="LFU"
       />


분산 캐시

EHCache는 분산 캐시를 지원한다. EHCache는 피어(peer) 자동 발견 및 RMI를 이용한 클러스터간 데이터 전송의 신뢰성 등 분산 캐시를 위한 완전한 기능을 제공하고 있다. 또한, 다양한 옵션을 통해 분산 상황에 맞게 설정할 수 있도록 하고 있다.

참고로, EHCache는 RMI를 이용하여 분산 캐시를 구현하고 있기 때문에, Serializable 한 객체만 분산 캐시에서 사용 가능하다. 키 역시 Serializable 해야 한다.

분산 캐시 구현 방식

EHCache는 한 노드의 캐시에 변화가 생기면 나머지 노드에 그 변경 내용을 전달하는 방식을 사용한다. 즉, 클러스터에 있는 캐시 인스턴스가 n개인 경우, 한번의 변경에 대해 n-1개의 변경 통지가 발생한다.

각 노드의 캐시간 데이터 전송은 RMI를 통해서 이루어진다. EHCache가 데이터 전송 기술로서 RMI를 사용하는 이유는 다음과 같다.

  • 자바에서 기본적으로 제공하는 원격 메커니즘
  • 안정화된 기술
  • TCP 소켓 옵션을 튜닝할 수 있음
  • Serializable 한 객체를 지원하기 때문에, 데이터 전송을 위해 XML과 같은 별도의 포맷으로 변경할 필요가 없음

노드 발견

EHCache는 클러스터에 새로운 노드가 추가돌 경우 해당 노드를 자동적으로 발견하는 방식과, 지정된 노드 목록에 대해서만 클러스터의 노드로 사용하는 방식을 지원하고 있다.

멀티캐스트 방식

멀티캐스트 모드를 사용한 경우, 지정한 멀티캐스트 IP(224.0.0.1~239.255.255.255)와 포트에 참여하는 노드를 자동으로 발견하게 된다. 지정한 IP와 포트에 참여한 노드는 자기 자신을 다른 노드에 통지한다. 이 방식을 사용하면 클러스터에 동적으로 노드를 추가하거나 제거할 수 있다.

노드 목록 지정 방식

클러스터에 포함되는 노드 목록을 지정한다. 동적으로 새로운 노드를 추가하거나 기존 노드를 제거할 수 없다.

분산 캐시 설정

분산 캐시를 사용하기 위해서는 다음과 같은 세 개의 정보를 지정해주어야 한다.

  • CacheManagerPeerProvider - 피어 발견 관련 설정
  • CacheManagerPeerListener - 메시지 수신 관련 설정
  • 캐시별 CacheReplicator - 메시지 생성 규칙 설정

CacheManagerPeerProvider 설정

CacheManagerPeerProvider는 새롭게 추가된 노드를 발견하는 방식을 지정한다.

노드를 자동으로 발견하는 멀티캐스트 방식을 사용하려면 다음과 같이 설정한다.

<cacheManagerPeerProviderFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
    properties="peerDiscovery=automatic, 
                    multicastGroupAddress=230.0.0.100, multicastGroupPort=1234" />


위 코드에서 properties 속성의 값에 사용된 프로퍼티는 다음과 같다.

peerDiscoveryautomatic으로 지정하면 멀티캐스트 방식을 사용한다.
multicaseGroupAddress멀티캐스트 IP
multicaseGroupPort포트 번호


하나의 클러스터에 포함될 노드들은 동일한 멀티캐스트 IP와 포트 번호를 사용해야 한다.

클러스터에 참여할 노드 목록을 지정하는 IP 방식을 사용하려면 다음과 같이 설정한다.

<cacheManagerPeerProviderFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
    properties="peerDiscovery=manual, 
                    rmiUrls=//server2:12345/cache1|//server2:12345/cache2" />


위 코드에서 properties 속성의 값에 사용된 프로퍼티는 다음과 같다.

peerDiscoverymanual로 지정한 IP 지정 방식이다.
rmiUrls분산 노드에 참여할 서버 및 캐시 목록을 지정한다. 현재 노드의 정보는 포함시켜서는 안 된다.


이 경우, rmiUrls에 명시된 포트 번호는 뒤에 살펴볼 CacheManagerPeerListener가 사용할 포트 번호를 지정해주어야 한다.

CacheManagerPeerListener 설정

노드를 발견하는 방식을 지정했다면, 다음으로 할 작업은 클러스터에 있는 다른 노드에서 발생한 변경 정보를 수신할 때 사용할 포트 번호를 지정하는 것이다. 다음과 같은 코드를 이용하여 수신과 관련된 포트 번호를 설정할 수 있다.

<cacheManagerPeerListenerFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
    properties="port=12345, socketTimeoutMillis=120000" />


위 코드에서 properties 속성의 값에 사용된 프로퍼티는 다음과 같다.

port메시지를 수신할 때 사용되는 포트
socketTimeoutMillis이 노드에 메시지를 보냈을 때 메시지 전송을 기다리는 시간. 기본값은 2000ms.


캐시별 CacheReplicator 설정

분산 환경에 적용되어야 하는 캐시는 캐시의 내용이 변경되었을 때 다른 노드에 있는 캐시에 변경 내역을 알려주어야 한다. <cacheEventListenerFactory> 태그를 사용하면, 언제 어떻게 캐시의 변경 내역을 통지할지의 여부를 지정할 수 있다. 아래 코드는 설정의 예이다.

<cache name="simpleBean"
      maxElementsInMemory="100"
      eternal="false"
      overflowToDisk="false"
      timeToIdleSeconds="300"
      timeToLiveSeconds="600"
      memoryStoreEvictionPolicy="LRU">
       <cacheEventListenerFactory 
           class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" 
           properties="replicateUpdatesViaCopy=true,replicateUpdates=true" />
</cache>


위 코드와 같이 <cacheEventListenerFactory>의 구현 클래스로 RMICacheReplicatorFactory를 지정하면 캐시에 변경이 생길 때 마다 해당 변경 내역을 클러스터에 참여하고 있는 노드의 캐시에 통지하게 된다. properties 속성에 프로퍼티를 지정하면, 캐시 요소의 추가, 변경, 삭제 등에 대해 통지 방식을 적용할 수 있다. 설정할 수 있는 프로퍼티는 다음과 같다.

replicatePuts캐시에 새로운 요소가 추가됐을 때 다른 노드에 복사할지의 여부
replicateUpdates캐시 요소의 값이 변경되었을 때 다른 노드에 값을 복사할지의 여부
replicateRemovals캐시 요소가 삭제되었을 때 다른 노드에 반영할지의 여부
replicateAsynchronously비동기로 값을 복사할지의 여부
replicateUpdatesViaCopy새로운 요소를 다른 노드에 복사할 지 아니면 삭제 메시지를 보낼지의 여부
asynchronousReplicationIntervalMillis비동기 방식을 사용할 때 변경 내역을 다른 노드에 통지하는 주기. 기본값은 1000.


위 속성의 기본값은 모두 true이다. 따라서, 기본 설정값을 사용하려면 다음과 같이 properties 속성을 사용하지 않아도 된다.

<cache name="simpleBean" ...
      memoryStoreEvictionPolicy="LRU">
       <cacheEventListenerFactory 
           class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
</cache>


어플리케이션 구동시 캐시 데이터 로딩하기

CacheManager가 초기화 될 때, 클러스터에 있는 다른 캐시로부터 데이터를 로딩할 수 있다. 이는 초기 구동이 완료된 후 곧 바로 서비스를 제공할 수 있음을 의미한다. 초기 구동시 다른 노드로부터 캐시 데이터를 로딩하려면 다음과 같이 <bootstrapCacheLoaderFactory> 태그의 구현 클래스를 RMIBootstrapCacheLoaderFactory로 지정해주면 된다.

<cache name="simpleBean" ...
      memoryStoreEvictionPolicy="LRU">
       <bootstrapCacheLoaderFactory
           class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
           properties="bootstrapAsynchronously=true,
                       maximumChunkSizeBytes=5000000" />

       <cacheEventListenerFactory 
           class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
</cache>


RMIBootstrapCacheLoaderFactory에 전달 가능한 프로퍼티 목록은 다음과 같다.

bootstrapAsynchronously비동기적으로 수행할지의 여부를 지정
maximumChunkSizeBytes클러스터의 다른 노드로부터 로딩 가능한 데이터의 최대 크기


RMIBoostrapCacheLoaderFactory를 설정하면 캐시를 초기화 할 때, 원격지 노드의 캐시에 저장된 데이터를 로딩하여 로컬 캐시에 저장한다.

분산 캐시 고려사항

분산 캐시를 사용할 때에는 다음과 같은 내용을 고려해야 한다.

  • 노드 증가에 따라 네트워크 트래픽 증가:
    많은 양의 네트워크 트래픽이 발생할 수 있다. 특히 동기 모드인 경우 성능에 영향을 받을 수 있다. 비동기 모드인 경우 버퍼에 변경 내역을 저장하였다가 일정한 주기로 버퍼에 쌓인 내역을 다른 노드에 통지하기 때문에 이 문제를 다소 완하시킬 수 있다.
  • 데이터 불일치 발생 가능성:
    두 노드에서 동시에 동일한 캐시의 동일한 데이터에 대한 변경을 수행할 경우, 두 노드 사이에 데이터 불일치가 발생할 수 있다. 캐시 데이터의 불일치가 매우 심각한 문제가 될 경우, 동기 모드(replicateAsynchronously=false)와 복사 메시지 대신 삭제 메시지를 전송(replicateUpdatesViaCopy=false)함으로써 이 문제를 해결할 수 있다.

관련링크:



반응형
반응형

자바 디자인 패턴 6 - Strategy

1. Strategy 패턴은..

Template Method 패턴이 상속을 이용해서 어떤 구현을 했다면, Strategy 패턴은 구성을 이용합니다. Template Method와 마찬가지로 바뀌는 부분과 바뀌지 않는 부분을 나눠서 생각할 수 있습니다. Template Method가 하위 클래스에서 바뀌는 부분을 처리한다면 Starategy는 바뀌는 부분을 인터페이스로 분리하여 처리합니다. 그 인터페이스의 구현체를 바꿈으로서 로직을 변경하는 것입니다. 또 Template Method와 크게 다른 점은 Template Method에서는 외부로 공개되는 것이 Template Method를 가지고 있는 상위 클래스였지만, Strategy에서는 인터페이스를 사용하는 클래스(그 클래스를 Context라고 합니다.)입니다.

2. 예제

------------------------ 상위 인터페이스 --------------------
package ch06_Strategy;

public interface Seller {
    public void sell();
}

------------------------- 인터페이스 구현체1 -----------------
package ch06_Strategy;

public class CupSeller implements Seller {
    public void sell() {
        System.out.println("컵을 팔아요.");
    }
}
------------------------- 인터페이스 구현체2 -----------------
package ch06_Strategy;

public class PhoneSeller implements Seller {
    public void sell() {
        System.out.println("전화기를 팔아요.");
    }
}
------------------------- 인터페이스 사용하는 클래스 -----------------
package ch06_Strategy;

public class Mart {
    private Seller seller;
    public Mart(Seller seller) {
        this.seller = seller;
    }
    public void order(){
        seller.sell();
    }
}
------------------------- 테스트 클래스 -----------------
package ch06_Strategy;

public class Test {
    public static void main(String[] args) {
        Seller cupSeller = new CupSeller();
        Seller phoneSeller = new PhoneSeller();
        Mart mart1 = new Mart(cupSeller);
        mart1.order();
        Mart mart2 = new Mart(phoneSeller);
        mart2.order();
    }
}

위에서 보시다 시피 테스트 클래스에서는 Seller의 sell()을 호출하지 않습니다. Mart의 order()를 호출합니다. Seller의 메쏘드는 외부로 공개되지 않습니다. 
Mart 클래스가 여기서는 외부로 공개되는 Context가 됩니다. Mart는 멤버 변수로 Seller를 가집니다. Mart에서 가지는 Seller를 바꿔치기함으로써 Mart의 order()에서 실제 실행되는 로직이 달라질 수 있습니다.

3. Strategy의 유용성

예제에서는 Context 클래스가 한 개의 Strategy 인터페이스만을 가집니다. Seller 외에 여러가지 인터페이스를 가질 수도 있습니다. 예를 들어 만드는 사람, 운반하는 사람, 파는 사람은 각각 다를 수 있습니다. 예제에서는 코드를 줄이기 위해 파는 사람만 2가지 종류의 클래스를 만들었습니다. 그러나, 만드는 사람 인터페이스와 운반하는 사람 인터페이스 등을 만들고 그 구현체 들을 만들면, 상당히 다양한 로직이 나올 수 있습니다. 만드는 사람의 구현체가 3종류, 운반하는 사람의 구현체가 3종류, 파는 사람의 구현체가 3종류라하면, 만들어서 운반해서 파는 로직은 총 3*3*3= 27가지가 나옵니다. 이를 상속을 이용한 클래스를 제작하면, 27가지의 구현체가 필요합니다. Strategy를 쓰면, 9개의 구현체만 필요하며, 또 인터페이스를 이용한 프로그램이 가능합니다.


4. JAVA API에 있는 Strategy

java.util.Collections 에 sort(List<T> list, Comparator<? super T> c) 라는 메쏘드가 있습니다. List를 Comparator에서 지정한 방법으로 정렬하는 메쏘드입니다. Comparator는 compare(T o1, T o2) 메쏘드 하나만 있는 인터페이스 입니다. 이 인터페이스를 구현하는 방법에 따라서 정렬된 결과가 달라집니다. "101"이 먼저일까요, "11"이 먼저일까요? 일반적인 순서에서는 "101"이 먼저입니다. 그러나 이게 숫자라면, 정렬 방법이 달라져야 합니다. Comparator를 구현함으로써 해결할 수 있습니다.

출처 : http://iilii.egloos.com/3826810


반응형

'Design Pattern' 카테고리의 다른 글

abstract factory pattern  (0) 2013.07.09
facade패턴  (0) 2013.07.05
반응형

참고 및 출처 ::

 - http://www.buggymind.com/36

 - http://pupustory.tistory.com/161



* Facade Pattern (퍼사드 패턴)

 - 어떤 서브 시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.

 - 퍼사드에서 고수준의 인터페이스를 정의하기 때문에 서브 시스템을 더 쉽게 사용할 수 있다.

퍼사드 패턴은 디자인 원칙의 최소 지식 원칙을 준수하는 패턴으로써 클라이언트를 복잡한 서브 시스템과 분리시켜주는 역할을 한다. 무슨 말인고 하니, 서브 시스템을 가지고 퍼사드를 만들고 실제 작업은 서브 클래스에 맡긴다는 뜻이다.



* 예제

어항을 청소한다고 생각해보자. 먼저 물고기를 옮기고, 어항 안에 악세사리를 꺼내고, 물을 제거한 후 어항을 청소하고, 다시 악세사리를 넣고 물을 채우고 물고기를 넣는다. 이를 메소드 호출로 생각한다면 아래와 같을 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 물고기를 꺼내고
getFish();
 
// 악세사리를 꺼내고
getDecorator();
 
// 물을 비우고
getWater();
 
// 어항을 청소하고
wash();
 
// 악세사리를 설치하고
setDecorator();
 
// 물을 넣고
setWater();
 
// 물고기를 넣는다.
setFish();

이러한 어항 청소 프로세스를 하나의 메소드 또는 인터페이스로 정의한 다음 서브 시스템에서 최소한의 지식만 가지고 사용할 수 있도록 하는 것이 퍼사드 패턴이다.




* 퍼사드 패턴 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
doWash() {
    // 물고기를 꺼내고
    getFish();
 
    // 악세사리를 꺼내고
    getDecorator();
 
    // 물을 비우고
    getWater();
 
    // 어항을 청소하고
    wash();
 
    // 악세사리를 설치하고
    setDecorator();
 
    // 물을 넣고
    setWater();
 
    // 물고기를 넣는다.
    setFish();
}
 

물론 많은 부분이 생략되었지만 아래 다이어로그램을 참고한다면 어떤식으로 적용해야 할지 알 것이다.


실제로 퍼사드 패턴은 우리가 이미 자주 사용하고 있는 패턴 중 하나이다. 클라이언트는 캡슐화된 프로세스 내의 동작을 알 필요 없이 해당 인터페이스가 요구하는 정보만 넣어서 호출하면 원하는 결과를 얻을 수 있도록 한다. 한 마디로 복잡한 클래스들과 패키지들을 어떤 단순한 인터페이스 객체 뒤에 감추는 것이다. 


예를 들어 Java의 Date 클래스는 사실 사용하기가 좀 까다롭다. 그런 복잡한 사용 방법은 전부 다 감추고, 사용자로 하여금 단순히 '현재 날짜에 며칠을 더하면 언제가 되는지' 알려주는 인터페이스만 주고 싶다고 하자.

그러면 퍼사드 클래스를 설계해서, 그 클래스가 내부적으로 Date 클래스나 기타 관련 클래스들을 조작하게 한 다음, 퍼사드 클래스가 제공하는 인터페이스만 사용하도록 하면 된다. 결국 이렇게 해서 만들어진 퍼사드 클래스는 Date 클래스에 대한 '대안적'인 인터페이스가 되는 것이다.


따라서 퍼사드 패턴은 어떻게 보면 어댑터 패턴하고도 비슷하나 Adaptation할 기능의 규모가 좀 더 큰 경우에 보다 적합하다고도 할 수 있다.


출처 : 

http://warmz.tistory.com/764

반응형
반응형

이클립스 UML 그리기 미션!!

인디고 버젼 설명~~~~

Install New Software...  -> Indigo - http://download.eclipse.org/releases/indigo 선택!!


Modeling -> EMF, GMF SDK 설치!!
 




http://www.soyatec.com/euml2/installation/offline.php#offline
 
접속 후 버젼에 맞는(indigo) 파일 다운로드!!(eUML2 Free Edition 3.7.1.20110624 + required plugins for Indigo/Eclipse 3.7.x)

다시 install.......... -> Add -> Name 쓰시고 Archive 클릭!! 후 다운 받은 압축파일 경로로 지정



eUML2 Free Edition 설치!!!  Modeler Documentation 설치 시 오류 발생 경험............



완료~ 패키지 선택 시 아래 버튼이 활성화 되어 UML 그릴 수 있음..




출처 : http://lovelyhun.tistory.com/38

반응형
반응형
문서를 정리하다가 예전에 헤드퍼스트 디자인 패턴 책을 읽고 정리해 놓은 파일이 있어 옮겨봅니다.

==============================================================

디자인 원칙
    - 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리한다.
    - 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
    - 상속보다는 구성을 활용한다.
    - 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결한하는 디자인을 사용해야 한다.
    - 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다.
       (OCP : Open-Closed Principle)
    - 추상화된 것에 의존하도록 만들어라. 구상클래스에 의존하도록 만들지 않도록 한다.
    - 최소 지식 원칙 - 정말 친한 친구하고만 얘기하라.
       (다음 네 종류의 객체의 메서드만 호출한다.
         1. 객체 자체 / 2. 메서드에 매개변수로 전달된 객체 / 3. 그 메서드에서 생성하거나 인스턴스를 만든 객체 / 4. 그 객체에 속하는 구성 요소)
    - 헐리우드 원칙 - 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
    - 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.
    + ... 나중에 어떻게 바뀔 것인가?


스트래티지 패턴(Strategy Pattern)
    - 알고리즘군을 정의하고 각각을 캐슐화하여 교환해서 사용할 수 있도록 만든다.
    - 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.
    - 구성을 사용한다.
    - 일반적으로 서브클래스를 만드는 방법을 대신하여 유연성을 극대화하기 위한 용도로 쓰인다.
    - 예: QuarkBehavior & FlyBehavior
    

옵저버 패턴(Observer Pattern)
    - 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고
       자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.
    - 주제(Subject) & 옵저버(Observer)
    - Observable & Observer:
         Observable 에 register, remove, notify 가 있고, 
         Observer 에 update 가 있다. (notify 에서 update 를 호출)
    - 예: 신문 구독 서비스, 기상관측 시스템
    

데코레이터 패턴(Decorator Pattern)
    - 객체에 추가적인 요건을 동적으로 첨가한다.
    - 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.    
    - 예: 스타버즈 커피
    
    
팩토리 패턴(Factory Pattern)    
    - 팩토리 메서드 패턴 :
       객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다.
       클래스의 인스턴스를 만드는 일을 서브클래스에 맡긴다.
    - 제품을 생산하는 부분과 사용하는 부분을 분리시킬 수 있다.
    - 추상 팩토리 패턴 :
       인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고 생성한다.
       구상 클래스는 서브 클래스에 의해 만들어진다.


싱글턴 패턴(Singleton Pattern)
    - 해당 클래스의 인스턴스가 하나만 만들어지고,
       어디서든지 그 인스턴스에 접근할 수 있도록(전역 접근) 하기 위한 패턴
       
       
커맨드 패턴(Command Pattern)
    - 요구 사항을 객체로 캡슐화 할 수 잇으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수 있다.
       또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 잇으며, 작업취소 기능도 지원 가능하다.
    - 예: 리모콘
    - 서블릿의 doGet(), doPost() 또는 스트럿츠의 Action() 메서드도 커맨드 패턴이지 않을까?
    

어댑터 패턴(Adapter Pattern)
    - 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.
       어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.  
       

퍼사드 패턴(Facade Pattern)
    - 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.
       퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있다.
    - 서브시스템의 호출을 퍼사드에서 처리해준다. (기본 명령 호출 정도랄까...)
    - 일련의 클래스들에 대한 인터페이스를 단순화 시킨다.
    
    - 각 패턴별 차이점:
       데코레이터 패턴 : 인터페이스는 바꾸지 않고 책임(기능)만 추가
       어댑터 패턴 : 한 인터페이스를 다른 인터페이스로 변환
       퍼사드 패턴 : 인터페이스를 간단하게 바꿈


템플릿 메서드 패턴(Template Method Pattern)
    - 메서드에서 알고리즘의 골격을 정의한다.
       알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있다.
       템플릿 메서드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있다.
    - 스트래티지 패턴과 다른 점:
       템플릿 메서드 패턴은 알고리즘의 개요를 정의한다. 실제 작업 중 일부는 서브클래스에서 처리.
       스트래티지 패턴은 객체 구성을 통해서 알고리즘을 캡슐화 및 구현
    - 예) Arrays.sort(배열); --- compareTo() 를 구현하도록 되어 있다.
            Applet , init(), start(), stop(), destory()
            그렇다면 서블릿에도 템플릿 메서드가 쓰이는 거구나. init() - service() - destory()

            
이터레이터 패턴(Iterator Pattern)
    - 컬렉션 구현 방법을 노출시키지 않으면서도
       그 잡합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공한다.
    - 컬렉션의 구현을 드러내지 않으면서 컬렉셔네 있는 모든 객체들에 대해 반복작업할 수 있다.
    

컴포지트 패턴(Composite Pattern)
    - 객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있다.
       이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된
       복합 객체(composite)를 똑같은 방법으로 다룰 수 있다.
    - 클라이언트에서 객체 컬렉션과 개별 객체를 똑같은 식으로 처리할 수 있다.
    - 예) 트리 구조의 패턴, 디렉토리 구조
    - 예) XMLObject 객체가 컴포지트 패턴을 구현한 게 아닐까
    

스테이트 패턴(State Pattern)
    - 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있다.
       마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.
    - 상태 전환의 흐름을 결정하는 코드를 어느 쪽에 집어넣는지 잘 고려해야 한다.
       (상태 객체인지, Context 객체인지)
    - 각 상태를 클래스로 캡슐화함으로써 나중에 변경시켜야 하는 내용을 국지화시킬 수 있다.
    - 스트래티지 패턴:
          어떤 클래스의 인스턴스를 만들고 그 인스턴스에게 어떤 행동을 구현하는 전략 객체를 건내준다.
       스테이트 패턴:
          컨텍스트 객체를 생성할 때 초기 상태를 지정해주는 경우 이후로는 컨텍스트 객체가 알아서 상태를 변경.
          
          
프록시 패턴(Proxy Pattern)
    - 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴
    - 다른 객체를 대변한느 객체를 만들어서 주 객체에 대한 접근을 제어할 수 있다.
    - 원격프록시(remote proxy): 원격 객체에 대한 접근 제어
                                                클라이언트와 원격 객체 사이에서 데이터 전달을 관리
       가상프록시(virtual proxy): 생성하기 힘든(인스턴스를 만드는 데 많은 비용이 드는) 자원에 대한 접근 제어
       보호프록시(protection proxy): 접근 권한이 필요한 자원에 대한 접근 제어
                                                     호출하는 쪽의 권한에 따라서 객체에 있는 메소드에 대한 접근 제어
       방화벽 프록시: 일련의 네트워크 자원에 대한 접근 제어
       스마트 레퍼런스 프록시: 주 객체가 참조될 때마나 추가 행동을 제공. 객체에 대한 레퍼런스 개수를 세는 등
       캐싱 프록시: 비용이 많이 드는 작업의 결과를 임시로 저장
                          웹 서버 프록시 또는 컨텐츠 관리 및 퍼블리싱 시스템 등에서 사용
       동기화 프록시: 여러 스레드에서 주 객체에 접근하는 경우 안전하게 작업을 처리할 목적(분산 환경 등에서 사용)
       복잡도 숨김 프록시: 복잡한 클래스들의 집합에 대한 접근을 제어하고 복잡도를 숨겨줌
                                     퍼사드 프록시라고도 함.
                                     프록시에서는 접근을 제어하지만 퍼사드 패턴에서는 대체 인터페이스만 제공
       지연 복사 프록시: 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사 제어
       
    - 아래 객체들은 모두 클라이언트와 객체 사이에 끼여들어서 요청을 전달한다.
         데코레이터 패턴: 클래스에 새로운 행동을 추가하기 위한 용도
         어댑터 패턴: 다른 객체의 인터페이스를 바꿔주기 위한 용도
         프록시 패턴: 어떤 클래스에 대한 접근을 제어하기 위한 용도
    - java.reflect.Proxy 에 기능이 내장되어 있다.
    
    
디자인 패턴 정의
    - 패턴이란 특정 컨텍스트 내에서 주어진 문제에 대한 해결책이다.
    - 어떤 컨텍스트 내에서 일련의 제약조건에 의해 영향을 받을 수 있는 문제에 봉착했다면,
       그 제약조건 내에서 목적을 달성하기 위한 해결책을 찾아낼 수 있는 디자인을 적용하면 된다.    
      

주의점 및 추가 사항      
    - 디자인 패턴의 과다한 사용은 불필요하게 복잡한 코드를 초래할 수 있다.
       항상 가장 간단한 해결책으로 목적을 달성할 수 있도록 하고, 반드시 필요할 때만 디자인 패턴을 적용하자.      
    - 코딩할 때 어떤 패턴을 사용하고 있는지 주석으로 적어주자.
       클래스와 메서드 이름을 만들 때도 사용 중인 패턴이 분명하게 드러날 수 있도록 해보자.
       다른 개발자들이 그 코드를 볼 때 무엇을 어떻게 구현했는지 훨씬 빠르게 이해할 수 있다.

출처 : http://ohgyun.com/279


반응형
반응형



출처 : http://javacan.tistory.com/entry/

반응형
반응형

인터셉터는 필터와 유사한 동작을 한다. 요청이 controller 에 전달되고 또는 나가는 시점에 요청, 또는 응답을 가로채어 원하는 작업을 수행해줄 수 있다.

 인터셉터에서 사용할 수 있는 세 가지 메소드는 preHandler,  postHandle,  afterCompletion 이다.

preHandle() 메소드는 요청이 controller에서 들어가기 전, poastHandle()은 요청이 controller에 의해 처리되고, 뷰로 보내지기 전,

afterCompletion() 메소드는 뷰까지 보여지고 난 후, 필요한 작업을 수행할 수 있다. 마치 AOP와 조금 유사한 모습을 보이고 있다.


- preHandle : 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출됨. false를 리턴하면 다음 내용은 실행하지 않는다.
- postHandle : 클라이언트의 요청을 처리한 뒤에 호출됨. 컨트롤러에서 예외가 발생되면 실행하지 않는다.
- afterCompletion : 클라이언트 요청 처리뒤 클리이언트에 뷰를 통해 응답을 전송한뒤 실행 됨. 뷰를 생설항때 예외가 발생해도 실행된다
 

아래는 urlHandlerMapping에 인터셉터를 인젝션해주고 있다. 이 맵핑이 정의하는 url로 들어오는 요청은 인터셉터에 의해 가로채진다.

  1. <!-- Member : 로그인한 사람 허용 -->
     <bean id="memberSecureUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
         <property name="interceptors">
             <list>
              <ref bean="loginCheckInterceptor"/>
             </list>
         </property>
         <property name="urlMap">
             <map>
              <entry key="/member/edit.do" value-ref="memberSimpleFormController"/>
              <entry key="/member/logout.do" value-ref="memberMultiActionController"/>
          </map>
         </property>
     </bean>

 

 아래는 preHandler()  메소드를 사용하여, session을 검사하여 로그인 체크를 하고 있다. 로그인이 되지 않은 요청이면, 요청을 끝내버리고,

로그인 된 요청이면 컨트롤러에게 요청이 전달될 것이다. 인터셉터에서 반환된 요청에 의해 웹브라우저에서는 하얀 화면이 보여질 것이다.

  1. public class LoginCheckInterceptor extends HandlerInterceptorAdapter {
  2.  private String sessionUserIdKey;
       ....

  3.  // 로그인 했는지 체크

     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         HttpSession session = request.getSession(false);
         if (session == null) {
          // 처리를 끝냄 - 컨트롤로 요청이 가지 않음
          return false;
      }

  4.   String userId = (String)session.getAttribute(this.sessionUserIdKey);
         if (userId == null) { 
          return false;
         }
      return true;
     }
     
     
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
     
         super.postHandle(request, response, handler, modelAndView);
     }
     
     
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  5. super.afterCompletion(request, response, handler, ex);

  6.   }
  7. }



Spring MVC - Annotation Base HandlerInterceptor

Spring core에서는 AOP를 이용하여 처리내용의 전, 후에 일괄적으로 적용하고 싶은 처리를 추가할수가 있다. 하지만 AOP는 interface를 구현한 곳에만 사용할 수가 있습니다. 그래서 Spring MVC의 Controller에는 상용할수 없죠 자 그럼 만약 로그인 체크를 해야한다면 모든 Controller에 로그인 체크 로직을 넣어줘야할까?. 아니다! 그럼 상위 Controller를 만들고 여기에 로그인 체크로직을 넣고 모든 Controller가 이를 상속 받아 쓰면 어떨까. 그남아 났군.. 이런 것을 위해 Spring MVC 에서는 HandlerMapping이 HandlerInterceptor를 이용하해서 Controller가 요청하는 처리의 전과, 후후에 원하는 기능을 수행할수 있도록 해준다고한다.  ( 뭐 깊숙한 내용까지 알려면 더 공부를하고, 아니면 그냥 Controller에서도 AOP와 같이 쓸수 있도록 제공해준다고 알자 ) HandlerInterceptor는 세가지 메소드를 정의한다

--*   * This implementation always returns <code>true</code>.   --  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)      throws Exception {   return true;  }

 --*   * This implementation is empty.   --  public void postHandle(    HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)    throws Exception {  }

 --*   * This implementation is empty.   --  public void afterCompletion(    HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)    throws Exception {  }

접기

- preHandle : 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출됨. false를 리턴하면 다음 내용은 실행하지 않는다. - postHandle : 클라이언트의 요청을 처리한 뒤에 호출됨. 컨트롤러에서 예외가 발생되면 실행하지 않는다. - afterCompletion : 클라이언트 요청 처리뒤 클리이언트에 뷰를 통해 응답을 전송한뒤 실행 됨. 뷰를 생설항때 예외가 발생해도 실행된다. Interceptor의 구현 org.springframework.web.servlet.HandlerInterceptor 를 직접 구현해서 클래스를 작성해도 되지만 이러면 세개의 메소드를 모두 구현해야한다. 즉 preHandle 만 필요해도 postHandle과 afterCompletion 두 메소드를 모두 구현해야한다. 이를위해 Spring에는 HandlerInterceptorAdaptor 클래스를 제공한다. 이는 이미 HandlerInterceptor interface를 구현한 클래스로 위의 세개의 메소드를 이미 구현하고 있어 필요한것만 구현해서 사용하면 된다. - Interceptor 구현

public class LoginCheckInterceptor extends HandlerInterceptorAdaptor {     @Override     public boolean preHandle(HttpServletRequest request,                                           HttpServletResponse response,                                           Object handler) throws Exception{          if ( !isLogin() ) {              return false;         }         return true;     } }

- dispatcher-servlet.xml의Interceptor 설정

<bean id="annotationMapper"              class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">     <property name="interceptors">         <list>             <ref bean="userLoginInterceptor"/>         </list>     </property>     <property name="alwaysUseFullPath" value="true"/> </bean> <bean id="userLoginInterceptor" class="com.questionbox.interceptor.UserLoginInterceptor" />

위와 같이 할때는 모든 페이지 URL에서 userLoginInterceptor를 실행하게 된다. 그리고  <list>    <ref bean="userLoginInterceptor"/>     <ref bean="userLoginInterceptor2"/>     <ref bean="userLoginInterceptor3"/> </list> 과 같이 여러 인터페이스를 줄수도 있다.


반응형

+ Recent posts