'분류 전체보기'에 해당되는 글 418건
- 2009.01.02 [안영회의 UML 강좌3] - Use Case Diagram
- 2008.12.30 Java FX for Eclipse
- 2008.12.30 토드(Toad)에서 실행중인 sql 중지하는 법
- 2008.12.29 log4j.properties 사용 방법 (ConversionPattern 사용 방법)
- 2008.12.17 carttest
- 2008.12.16 CLIENTQBOARDFLEXDIV
- 2008.12.12 질문 sun.io.MalformedInputException에 대해서
- 2008.12.11 [JAVA]16진수 ascii문자를 특정 문자로 치환하기
- 2008.12.10 jNetServer Socket Framework
- 2008.12.09 eclipse TCP/IP 모니터 사용하기
RUP (Rational Unified Process)
지난 시간에는 UML의 필요성과 간단한 개념을 살펴보았습니다. 이번 시간부터는 UML을 구성하는 각종 다이어그램들을 하나씩 살펴봅시다. 또한 깊은 이해와 함께 몸으로 익힐 수 있도록 UML 모델링 도구로 가장 유명한 Rational Rose를 이용하여 간단한 실습을 해보도록 하죠. 실습을 위한 소프트웨어는 UML 관련 책자의 부록이나 Rational 사의 웹사이트를 통해 평가판을 구하실 수 있습니다. 평가판을 다운로드 할 수 있는 웹 페이지의 URL은 다음과 같습니다.
본 내용을 진행하기에 앞서 다음의 서적들을 참고로 이 글을 작성했음을 밝힙니다. 더 깊은 이해를 위해서는 이들 서적을 참고하세요.
� Visual Modeling with Rational Rose and UML, Terry Quatrani, Addison Wesley
� UML Distilled Second Edition, Martin Fowler with Kendall Scott, Addison Wesley
� 초보자를 위한 UML, Joseph Schmuller, 곽용재 역, 인포북
� The Rational Unified Process An Introduction Second Edition, Philippe Kruchten
� The Unified Modeling Language Reference Manual, James Rumbaugh, Ivar Jacobson, Grady Booch, Addison Wesley
예제 화면은 rose2001A.04.00 버전을 기준으로 사용했습니다. 다른 버전을 사용하시더라도 UML을 배우는 입장에서 큰 차이는 없을 것입니다. 예제 내용은 일반적이고 검증된 것을 사용하고자 Visual Modeling with Rational Rose and UML의 예제를 인용했습니다. UML 구성 요소들의 이름을 한글로 사용할 수 있지만, 코드와의 일관성 유지 등의 목적으로 영어를 사용했습니다. 다만, 한글로 충분히 설명을 기술하도록 하겠습니다.
RUP (Rational Unified Process)
유즈케이스(Use Case)에 대해 본격적으로 설명하기에 앞서 RUP에 대한 언급을 하지 않을 수가 없습니다. UML은 모델링을 위한 표기법입니다. UML이 시스템 개발에 매우 중요하다 하겠지만, UML만으로는 아무 것도 되지 않습니다. 객체 지향으로 시스템 개발을 하겠다고 UML을 사용하면서 개발은 기존의 전통적인 방식을 따른다면 효과가 높지 않을 것입니다.
객체 지향의 시스템 개발을 하려고 한다면 개발의 방법론 역시 객체 지향을 따라야 할 것입니다. 수많은 객체 지향 방법론이 존재한다고 합니다. 이 중에 가장 부각되고 있는 것이 RUP입니다. 무엇보다 RUP는 Rational의 소프트웨어 군을 이용한 개발 방법론으로서 이론뿐만 아니라 구체적인 솔루션이 동반된다는 강점을 지니고 있습니다.
쉽게 얘기하면Rational의 도구들과 RUP에 맞춰서 UML을 사용하여 개발 한다면 삼박자를 갖추게 된다는 매력적인 제안이죠. 우리는 RUP를 배우는 게 아니고 UML을 배우는 것이지만, 시스템을 개발하는 과정을 염두에 두지 않고 UML을 논하는 것은 공허할 수 있습니다. UML이 어떻게 쓰이는지를 생각하지 않겠다는 것이 될 수 있으니까요.
RUP가 최상의 개발 방법론은 아니지만, 개발 공정에 대한 한 예로 RUP를 간단하게 엿보도록 하죠.
다음은 RUP의 개발 공정에 대한 개괄적 그림입니다.

[RUP 개발 공정]
RUP의 개발 공정은 크게 두 축으로 나눠 볼 수 있습니다. 우선 그림의 가로축으로 시간의 흐름에 따른 네 가지 단계(Phases)로 구분할 수 있고, 세로축의 9가지 웍플로우(Workflow)로 나눌 수 있습니다. 웍플로우는 컴포넌트처럼 작업의 성격에 따라 일을 분리한 것입니다.
기존의 방법론이 도입기에는 주로 타당성 검증 등을 하고, 분석 및 설계, 구현, 검증 및 배포와 같은 식으로 일원적인 관점에서 개발을 했다면 RUP는 이차원적인 관점을 갖는다고 하겠습니다. 도입기라고 할 수 있는 도입(Inception) 단계에서는 주로 비즈니스 모델링(Business Modeling)을 수행하지만 이를 위해 상당량의 요구사항 분석을 수행해야 하고, 개발 프로젝트의 타당성이나 위험도 등의 검증을 위해 프로토타입을 만들어 본다든가 하는 구현도 일부분 수행하게 됩니다. 마찬가지로 향후 프로젝트를 정교하게 발전시켜가는 정련(Elaboration) 단계에서도 요구사항 수집과 분석 설계는 물론 도입 단계에서 만들어진 비즈니스 모델링(Business Modeling)을 검증하고 더욱 정교하게 수정하는 일도 계속하게 됩니다. RUP는 이와 같은 식으로 점진적인 개발 방법을 채택하고 있습니다. 이러한 단계들과 웍플로우의 적절한 조합은 두말할 필요 없이 매우 중요하다고 하겠죠. 프로젝트 관리자에 의해서 이러한 적절한 조합이 계획되는데 이를 이터레이션(Iteration)이라고 합니다. 결국 RUP는 이터레이션의 연속으로 개발을 수행하게 되는 것이죠.
유즈케이스 이해하기
이제야 본격적으로 유즈케이스를 얘기할 차례군요. 유즈케이스는 우리말로는 쓰임새라고도 합니다. 두 가지를 동시에 사용하는 혼돈을 막기 위해서 여기서는 원어로 유즈케이스라고 표기하는 것을 원칙으로 하겠습니다.
유즈케이스라 함은 말 그대로 ‘쓰이는 경우’라던가 ‘용도’ 같은 의미로 받아들여도 큰 무리가 없다고 보여집니다. 어떤 일에 쓰느냐 하는 것이죠. 시스템이 쓰여지는 용도를 모아서 시스템을 만들어낸다면 다용도 시스템이 만들어지겠죠. 유즈케이스들을 모아서 시스템으로 매핑 시키는 것을 개발 과정의 간단한 정의로 보아도 무리가 없을 만큼 유즈케이스는 가치 있는 것입니다.
제가 한국 Rational 이사님의 세미나를 들은 일이 있습니다. 그때 UML에 관한 부분에서는 유즈케이스를 유난히 강조하시더군요. 유즈케이스는 사용자 시각에 맞춘 분석입니다. 어떤 시스템을 만드느냐를 사용자 입장에서 조명하는 것이죠. 최근 비즈니스가 발전함에 따라 고객 지향 마인드가 널리 퍼져 있습니다. 당연한 결과라 하겠죠. 마찬가지로 시스템 개발에 있어서도 고객 관점에서 바라보는 시각이 부각되는 것은 당연한 일이라 하겠습니다.
객체 지향 개발 자체가 기존 개발 방법들에 비해 상당히 인간위주의 개발 방법론이라는 느낌이 드는데 유즈케이스는 이러한 휴머니즘의 선봉에 서있다고 해도 큰 비약은 아니라는 생각이 듭니다. 아무튼 유즈케이스는 시스템 보다는 그것을 사용하는 인간, 즉 사용자의 입장을 우선해서 시스템이 어때야 하는가를 알아보는 것입니다. 아무리 잘 만든 시스템도 인간에게 가치를 주지 못하면 무의미한 것이죠.
이러한 휴머니즘을 잊지 마시고, 유즈케이스를 배워 봅시다. 유즈케이스는 시스템의 행위를 결정하는 것입니다. 구체적으로는 시스템의 기능을 정의하고, 범위를 결정함으로써 시스템과 외부 환경 변수를 구분하고, 상호 관계를 정립하는 것이라고 볼 수 있습니다.
개발 공정과 연관해서 보면 도입 단계에서 주요 유즈케이스를 뽑아내고, 차츰 이를 정련하게 됩니다.
유즈케이스를 나타내는 유즈케이스 모델(Use case Model)은 유즈케이스 다이어그램으로 표현됩니다. 유즈케이스 다이어그램은 액터(Actor, 행위자)와 유즈케이스, 그리고 관계(Relationship)로 나타냅니다.
유즈케이스, 액터, 관계
액터(Actors)
액터는 시스템의 일부가 아닙니다. 액터는 시스템과 상호작용을 하는 모든 것들을 나타냅니다. 시스템을 사용하게 될 사람은 물론이고, 연관된 다른 시스템도 액터입니다. 대체로 액터의 행위는 정보의 입력과 출력으로 살펴 볼 수 있습니다. 정보를 입력하거나 출력하는 액터가 있고, 입출력을 모두 행하는 액터가 있을 것입니다.
액터를 뽑아내는 일은 매우 중요한 일입니다. 모든 주요 액터를 고려해야만 모두에게 가치 있는 시스템이 될 수 있을 테니까요. Visual Modeling with Rational Rose and UML에 따르면 다음과 같은 질문들이 액터를 뽑아내는데 도움을 준다고 합니다.
� 특정 요구사항에 이해관계자는 누구인가?
� 어떠한 부서나 집단에서 시스템을 사용하는가?
� 시스템을 사용함으로써 이익을 얻는 이는 누구인가?
� 누가 시스템에 정보를 입력하고 사용하며 삭제하는가?
� 누가 시스템의 유지보수를 수행하는가?
� 시스템이 외부 자원을 사용하는가?
� 한 사람이 복수의 역할을 수행하는가?
� 여러 사람이 한 가지 역할을 수행하는가?
� 시스템이 레거시 시스템(Legacy System)과 상호 작용 하는가?
액터는 다이어그램 상에서 막대인간(stickman)으로 표현됩니다.

[액터의 UML 표기법]
유즈케이스 (Use Cases)
유즈케이스 모델은 시스템과 액터와의 의사소통을 표현합니다. 각각의 유즈케이스는 시스템이 제공해야 하는 기능을 묘사하고, 이러한 유즈케이스들이 시스템 전체의 기능을 나타냅니다. 하나의 유즈케이스는 액터가 원하는 기능을 수행하기 위해 시스템이 수행하는 일련의 처리들의 연속입니다. Visual Modeling with Rational Rose and UML에 따르면 다음과 같은 질문들이 유즈케이스를 뽑아내는데 도움을 준다고 합니다.
� 각각의 액터의 업무는 무엇인가?
� 액터가 시스템의 정보를 생성, 저장, 수정, 삭제하고 읽는가?
� 어떠한 유즈케이스가 시스템의 정보를 생성, 저장, 수정, 삭제하고 읽는가?
� 액터가 돌연한 외부 변화에 대한 정보를 시스템에게 알릴 필요가 있는가?
� 시스템에 갑자기 발생한 일들을 액터가 알아야 하는가?
� 어떠한 유즈케이스들이 시스템을 지원하고 유지하는가?
� 유즈케이스들이 모든 요구되는 기능을 포괄하여 수행하는가?
유즈케이스의 UML 표기법은 타원(Oval)입니다.

[Use Case의 UML 표기법]
관계(Relationship)
관계는 크게 두 가지로 볼 수 있습니다. 하나는 액터와 유즈케이스의 관계이고, 다른 하나는 유즈케이스간의 관계입니다. 액터와 유즈케이스와의 관계는 연관(Association) 혹은 커뮤니케이션 연관(Communicates Association)이라고 합니다. 액터와 유즈케이스간의 의사소통을 나타내기 때문이겠죠.
연관은 양방향으로 진행될 수 있습니다. 연관의 방향성은 어느 쪽이 연관을 유발하느냐에 따라 달라집니다. 오직 액터 혹은 유즈케이스만이 연관을 유발하는 단방향 연관이 있고, 양쪽 모두에서 연관을 일으키는 양방향 연관이 있습니다.
유즈케이스간의 관계는 두 가지 형태가 있습니다. 포함(Inclusion 혹은 사용 Use)과 확장(Extension)입니다. 여러 유즈케이스들이 하나의 기능 조각을 공유할 때 이를 모든 유즈케이스에 각각 집어넣는 것 보다는 이를 분리해두고 필요한 유즈케이스들이 이를 포함해서 사용하게 됩니다. 예를 들어 회원제를 기반으로 한 인터넷 사이트에 접속하셔서 각종 서비스를 제공받기에 앞서 늘 수행하는 회원 인증과 같은 유즈케이스가 포함 관계입니다.
확장 관계는 기본 유즈케이스에서 특정 조건이나 액터의 선택에 따라 발생하는 유즈케이스입니다. 가령, ATM에서 사용자의 메뉴 선택에 따라 달라지는 유즈케이스의 경우나 긴급 상황 시에 발생할 수 있는 유즈케이스가 확장의 예로 생각할 수 있습니다.
관계는 선으로 표기하며 관계의 방향성은 화살표로 나타냅니다. UML에는 스테레오타입(Stereotype)이라는 개념이 있습니다. 이는 기본적인 모델링 요소 이외의 새로운 타입을 나타내는 것입니다. 따라서 확장을 가능하게 해줍니다. 스테레오타입이라는 것이 인쇄소의 연판을 나타내는 것입니다. 어느 정도 변화를 줄 수 있는 유연한 판형이라는 것이죠. 이처럼 스테레오타입은 기본 모델링 요소에 확장성을 부여할 수 있는 개념입니다.
JavaFX가 넷빈즈에서만 돌아가는 줄 알았는데 역시나 이클립스 용 플러그인이 나와 있습니다.
출처 : http://javaora.tistory.com/entry/Java-FX-for-Eclipse
위 링크를 통해 다운로드 받은 파일을 eclipse 설치 폴더에 압축해제하면 설치는 완료 됩니다.
사용방법은 다음과 같습니다.
아래 이미지와 내용은 해당 사이트의 Getting Start 문서를 옮겨왔습니다.
JavaFX SDK 설치
JavaFX SDK for Windows or Mac Os X 다운로드 받아 설치합니다.
Eclipse 설치
Eclipse 3.4 이상이 필요하며. Eclipse IDE for Java EE Developers for Windows or Mac OS X 설치가 필요합니다.
Plugin 설치
- plugin을 다운로드 받습니다.:
- com.sun.javafx.eclipse.feature-win32-win32-x86-1.0.zip for Windows
- com.sun.javafx.eclipse.feature-macosx-carbon-x86-1.0.zip for Mac OS X
- eclipse 설치 디렉토리에 압축을 해제합니다.
- Eclipse 을 실행합니다.
JavaFX 프로젝트 만들기 와 JavaFX SDK 설정하기
- 이클립스를 시작하면 이클립스 workbench의 기본 레이 아웃이 보입니다.
- Package Explorer에서 오른쪽 클릭하여
- Create a new JavaTM project 로 자바 프로젝트를 생성합니다.
- MyJavaFXProject 라는 프로젝트 명과 버튼을 누릅니다. 새로운 프로젝트가 Package Explorer에 나타납니다.
- 프로젝트 이름을 선택하고 오른쪽 클릭하여
. - 그럼 JavaFX perspective가 활성화 됩니다. JavaFX nature 를 Java Project 에 처음 추가 하는것이라면
JAVAFX_HOME
속성값을 물어볼수도 있습니다.
JAVAFX_HOME
속성 설정은 아래 단계를 따릅니다.:- 다이얼로그 메시지의 No thanks, I prefer 버튼을 누릅니다.
- 아래와 같은 다이얼로그 창이 그면
JAVAFX_HOME 값을 (empty)로 추가시킵니다.
- 그리고
버튼을 누르면 아래와 같은 창이 뜹니다.
버튼을 눌러 the JavaFX SDK 설치 폴더를 지정해줍니다. - Edit 창의 OK 버튼을 누릅니다.
- Preferences 다이얼로그의 OK버튼을 누릅니다. 그럼
JAVAFX_HOME
변수의 세팅이 끝납니다. 만약 버튼을 누르면 에러 다이얼로그 창이 뜨며 다시 반복하여 묻게 됩니다.
- 이로서 첫번째 JavaFX Project 생성이 완료되었습니다.
Adding Files
- MyJavaFXProject 를 확장해보자. src 폴더를 선택하고. 오른쪽 클릭을 하여 package 이름에 com.sun.eclipse.example를 입력해보자.
- src/com.sun.eclipse.example 폴더를 오른쪽 클릭후. .
- New File 마법사가 보일것이다. Name 필드에 Main 이라고 입력하자.
버튼을 누르면 새로운 JavaFX 스크립트 창이 열린다. - Snippets view를 보자. 여기엔 애플리케이션 개발을 도와줄 여러 가지 code snippets 이 있다. A Stage를 선택해보자.
- Insert Template: Stage 다이얼로그가 보인다. title 변수값을 Hello World 로 바꾸자.
노드를 editor window로 Drag and drop 하면.
버튼을 누르면 template 의 결과가 소스 코드로 변환되어 insert 된다. - Mission accomplished, Sir!
Running
Eclipse 로 실행하는 것은 매우 쉽다.
- 툴바의
버튼을 누른다. - 첫번째 실행이라면 Edit Configuration 다이얼로그가 보인다.
모든 옵션은 default 값이다. 가장 흥미로운 부분은 Profil 옵션이다. 여기서 실행환경을 조정할 수 있다. 가능한 옵션은 아래와 같다.:- Desktop profile - Run as Application
- Desktop profile - Run as Applet
- Desktop profile - Run with WebStart
- Mobile Profile[?] - Run in Emulator
- Run 버튼을 눌러 애플리케이션을 작동시킨다. console 창에 실행 프로그레스가 뜰것이다.
Building
Eclipse 에서의 Build 작업은 default로 자동으로 제공된다.
메뉴에서 Build와 관련된 좀 더 많은 옵션을 볼 수 있다.Configuration Options
- Package Explorer 에서 프로젝트 선택 후 오른쪽 클릭하여
- Properties 창이 보이면 .
여기서 JavaFX Project의 실행과 디플로이 프로세스 동안 사용되는 몇가지 기본 파라미터 값을 수정할 수 있다. 좀 더 많은 정보는 JavaFX Packager Documentation 을 통해 볼 수 있다.
JavaFX Help
이 플러그인은 JavaFX SDK documentation로의 접근을 매우 쉽게 해준다. Ctrl + F1 를 누르므로서 Document에 접근 할 수 있다.
출처 : http://javaora.tistory.com/entry/Java-FX-for-Eclipse
저도 배운 겁니다.
^^;
default 옵션은 꺼져 있는데, 이것만 활성화시키면 토드에서 sql 결과가 나오지 않아서 프로세스를 죽이지 않아도 됩니다.


Options 창에서 우측에 SQL Editor를 선택하고 우측 목록에서 Process statesments in threads / allow queries to be cancelled 항목을 체크합니다. 영어로 되어있어서 눈에 안 들어왔던 거죠... 그리고 우측 하단의 OK 를 클릭합니다. cancel활성화 시켰다고 지금 Cancel 버튼 클릭하면 안됩니다.


log Level 정의
FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.
ERROR : 일반 에러가 일어 났을 때 사용합니다.
WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.
INFO : 일반 정보를 나타낼 때 사용합니다.
DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.
ERROR : 일반 에러가 일어 났을 때 사용합니다.
WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.
INFO : 일반 정보를 나타낼 때 사용합니다.
DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.
ConversionPattern 사용 방법
%p debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. rn 또는 n 일것이다.
%c 카테고리를 표시합니다
예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다.
예)클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. rn 또는 n 일것이다.
%c 카테고리를 표시합니다
예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다.
예)클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.
log4j.properties 파일 사용 예
log4j.rootLogger=INFO, stdout, rolling
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
log4j.appender.stdout.target=System.err
#rolling 어펜더는 파일로 처리한다라고 정의
log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
#로그 파일 이름은 output.log
log4j.appender.rolling.File=output.log
#true면 WAS를 내렸다 올려도 파일이 리셋되지 않습니다.
log4j.appender.rolling.Append=true
#파일 최대 사이즈는 500KB로 설정
log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#rolling 어펜더는 패턴 레이아웃 포맷
log4j.appender.rolling.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
log4j.appender.stdout.target=System.err
#rolling 어펜더는 파일로 처리한다라고 정의
log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
#로그 파일 이름은 output.log
log4j.appender.rolling.File=output.log
#true면 WAS를 내렸다 올려도 파일이 리셋되지 않습니다.
log4j.appender.rolling.Append=true
#파일 최대 사이즈는 500KB로 설정
log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#rolling 어펜더는 패턴 레이아웃 포맷
log4j.appender.rolling.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
출처 : placebo4me.egloos.com
네트웍 통신시 아래와 같은 에러가 발생합니다. 혹 이런 에러에 대해서 아시면 한수 가르침좀 부탁드립니다. 소스는 아래쪽에 써놓았습니다. READ ERROR : null : sun.io.MalformedInputException at sun.io.ByteToCharDoubleByte.flush(ByteToCharDoubleByte.java:51) at java.io.InputStreamReader.flushInto(InputStreamReader.java:154) at java.io.InputStreamReader.fill(InputStreamReader.java:178) at java.io.InputStreamReader.read(InputStreamReader.java:249) at java.io.BufferedReader.fill(BufferedReader.java:139) at java.io.BufferedReader.readLine(BufferedReader.java:299) at java.io.BufferedReader.readLine(BufferedReader.java:363) at it21.acom_tcplib.RecvMsg_FromTcp(acom_tcplib.java:97) at incheon_segu_daily.Send_Message(incheon_segu_daily.java:273) at incheon_segu_daily.Get_Result(incheon_segu_daily.java:166) at incheon_segu_daily.service(incheon_segu_daily.java:142) at xecure.servlet.XecureHttpServlet.service(XecureHttpServlet.java:261) at javax.servlet.http.HttpServlet.service(HttpServlet.java:274) at jeus.servlet.engine.ServletWrapper.execSingleThreadServlet(ServletWrapper.java:222) at jeus.servlet.engine.ServletWrapper.execServlet(ServletWrapper.java:236) at jeus.servlet.engine.ApacheRequestProcessor.run(ApacheRequestProcessor.java:85) 아래 소스 main에서 보면 args[0]는 1인 경우에는 위와 같은 에러가 발생하고 1이 아닌 경우에는 제대로 데이터를 받습니다. 물론 위의 에러를 일으킨 소스는 웹쪽에서 서비스 되는 건데 아래의 소스와 같은 라이브러리를 사용합니다. 왜인지 아무리 생각해도 모르겠네요.. 두 소스의 차이점은 args[0]는 1인 경우는 BufferedReader를 사용했고 아닌경우는 DataInputStream을 사용했다는 차임점인데 잘 모르겠습니다. 참고로 통신서버는 SUN 솔라리스이고 서블릿 엔진은 JEUS를 사용했습니다. import java.io.*; import java.net.*; import java.util.*; import java.text.*; import java.util.Date; public class TcpNet { public static void main(String args[]){ Socket sock = null; String url = "210.112.177.2"; String port="8300"; String sendMsg = send(); String recvMsg = null; int len = sendMsg.length(); long timelimit=10000 ; System.out.println("SEND=["+sendMsg+"]"); if( args[0].equals("1") ) { try{ sock = connect( url, port ); send1( sock, sendMsg, len, timelimit ); recvMsg = read1( sock, timelimit ); }catch(Exception e){ e.printStackTrace(); }finally{ close(sock); } } else { try{ sock = connect( url, port ); send2( sock, sendMsg, len, timelimit ); recvMsg = read2( sock, timelimit ); }catch(Exception e){ e.printStackTrace(); }finally{ close(sock); } } System.out.println("RECV=["+recvMsg+"]"); System.exit(0); } public static String send() { //지방세 조회 EFFV String sendMsg = "TEMP20020209Y 000000000000000000000000 0000000000000 000000000000000000 0000000000000 000000000000000000 0000000000000 000000000000000000 0000000000000 000000000000000000 0000000000000 000000000000000000 0000000000000 000000000000000000 "; return sendMsg; } public static Socket connect(String url, String port) throws Exception { Socket client; //첫글자가 숫자면 ip 로처리 if(Character.isDigit(url.charAt(0))) { client = new Socket(url, Integer.parseInt(port)); }else{ InetAddress url_ = InetAddress.getByName(url); client = new Socket(url_, Integer.parseInt(port)); } return client; } public static void send1(Socket sock, String sendMsg, int len, long timelimit) { try { sock.setTcpNoDelay(true); PrintWriter pw = new PrintWriter( new OutputStreamWriter( sock.getOutputStream() ), true ); pw.print( sendMsg ); pw.flush(); } catch(Exception e) { close(sock); e.printStackTrace(); } } public static void send2(Socket sock, String sendMsg, int len, long timelimit) { OutputStream pw = null; try { sock.setSoTimeout( (int)timelimit); pw = sock.getOutputStream(); byte[] b=sendMsg.getBytes(); pw.write(b); pw.flush(); }catch(Exception e){ if(pw!=null){ try{ pw.close(); }catch(IOException ioe){} } close(sock); e.printStackTrace(); } } public static String read1(Socket sock, long timelimit){ String recvMsg = ""; BufferedReader br=null; try { sock.setSoTimeout( (int)timelimit ); // 수신제한시간 설정 milli sec ( 1분 ) br = new BufferedReader( new InputStreamReader( sock.getInputStream() ) ); String line = null; int i = 0; while( ( line = br.readLine() ) != null ) { recvMsg += line; } br.close(); } catch(Exception e) { close(sock); e.printStackTrace(); } return recvMsg; } public static String read2(Socket sock, long timelimit){ String recvMsg = ""; DataInputStream in = null; byte[] recvData = new byte[1024]; try { sock.setSoTimeout( (int)timelimit); in = new DataInputStream(sock.getInputStream() ); int i = 0, start_pos = 0; i = in.read( recvData ); recvMsg = new String(recvData); }catch(Exception e){ e.printStackTrace(); return null; }finally{ if(in!=null){ try{ in.close(); }catch(IOException ioe){} } if(sock!=null){ try{ sock.close(); }catch(IOException ioe){} } } return recvMsg; } public static void close(Socket client) { try { client.close(); } catch (Exception e) { e.printStackTrace(); } } } |
제목 : Re: 질문 sun.io.MalformedInputException에 대해서 글쓴이: 김완석(kinyang) 2002/02/19 10:15:11 조회수:1761 줄수:66 |
> > {질문 sun.io.MalformedInputException에 대해서} > {손님(guest), lsk7up@hanmail.net} > > 네트웍 통신시 아래와 같은 에러가 발생합니다. > 혹 이런 에러에 대해서 아시면 한수 가르침좀 부탁드립니다. > 소스는 아래쪽에 써놓았습니다. > > READ ERROR : null : sun.io.MalformedInputException > at sun.io.ByteToCharDoubleByte.flush(ByteToCharDoubleByte.java:51) > at java.io.InputStreamReader.flushInto(InputStreamReader.java:154) > at java.io.InputStreamReader.fill(InputStreamReader.java:178) > at java.io.InputStreamReader.read(InputStreamReader.java:249) > at java.io.BufferedReader.fill(BufferedReader.java:139) > at java.io.BufferedReader.readLine(BufferedReader.java:299) > at java.io.BufferedReader.readLine(BufferedReader.java:363) > at it21.acom_tcplib.RecvMsg_FromTcp(acom_tcplib.java:97) > at incheon_segu_daily.Send_Message(incheon_segu_daily.java:273) > at incheon_segu_daily.Get_Result(incheon_segu_daily.java:166) > at incheon_segu_daily.service(incheon_segu_daily.java:142) > at xecure.servlet.XecureHttpServlet.service(XecureHttpServlet.java:261) > at javax.servlet.http.HttpServlet.service(HttpServlet.java:274) > at jeus.servlet.engine.ServletWrapper.execSingleThreadServlet(ServletWrapper.java:222) > at jeus.servlet.engine.ServletWrapper.execServlet(ServletWrapper.java:236) > at jeus.servlet.engine.ApacheRequestProcessor.run(ApacheRequestProcessor.java:85) > > 아래 소스 main에서 보면 args[0]는 1인 경우에는 위와 같은 에러가 발생하고 > 1이 아닌 경우에는 제대로 데이터를 받습니다. > 물론 위의 에러를 일으킨 소스는 웹쪽에서 서비스 되는 건데 > 아래의 소스와 같은 라이브러리를 사용합니다. > > 왜인지 아무리 생각해도 모르겠네요.. > 두 소스의 차이점은 args[0]는 1인 경우는 BufferedReader를 사용했고 > 아닌경우는 DataInputStream을 사용했다는 차임점인데 잘 모르겠습니다. > MalformedInputException 을 구지 해석하자면 ... 잘못된 형식의 입력으로 error 발생한 것입니다. 문제의 원인은 Stream의 끝에서 에러가 발생한 듯 싶습니다. DataInputStream 을 사용했을 때에는 문제가 없고 BufferedReader 를 사용했을때 문제가 발생하는것은.. DataInputStream 는 한바이트씩 읽어서 처리하지만, BufferedReader 는 2바이트를 읽어 하나의 char로 변환합니다. 위의 MalformedInputException 은 이 과정에서 발생한 듯 싶습니다. 한 바이트를 읽고 난 다음에 다음 바이르틀 읽어 하나의 Char를 만들려다가 실패한 것이지요 ... 제가 소스를 전체적으로 살펴본것은 아니지만, 네트웍 상에서는 데이터의 전송을 \r\n으로 구분지어서 전송합니다 .... 전송되는 데이터의 끝에 \r\n을 추가하여 전송해 보세요 ... 음냥... 그럼 수거하세요 ... |
내용 : byte array로 저장된 data에 특수문자값을 찾아서 정의된 문자로 치환하기
전제 : 10진수 0부터 32까지 문자만 그 대상으로 한다
process :
I. HashMap에 (1)16진수값 = 이름 (2) 이름 = 16진수값 이 매핑되도록 설정
II. byte array를 parameter로 받아 loop로 array 탐색
III. byte array를 16진수로 변환하여 HashMap에 그 내용이 있는지 확인
IV. 관련된 내용이 발견되면 치환
번외: 위 작업한 내용을 다시 정상 data로 복원 시키는 모듈
(CODE)
1. Integer.toHexString(int i)
설명 : 10진수 int형 i 값을 16진수로 변환한다.
(추가) 한글로 값이 넘어오는 경우 처리
2. (char)Integer.parseInt(String.valueOf(Long.decode("0x"+hexCode)))
설명 : 16진수 문자값을 그에 해당하는 문자로 변환시킴
Long.decode("0x"+hexCode) : 16진수 문자값(: hexCode)을 16진수 숫자값으로 변환
전제 : 10진수 0부터 32까지 문자만 그 대상으로 한다
process :
I. HashMap에 (1)16진수값 = 이름 (2) 이름 = 16진수값 이 매핑되도록 설정
II. byte array를 parameter로 받아 loop로 array 탐색
III. byte array를 16진수로 변환하여 HashMap에 그 내용이 있는지 확인
IV. 관련된 내용이 발견되면 치환
번외: 위 작업한 내용을 다시 정상 data로 복원 시키는 모듈
(CODE)
1. Integer.toHexString(int i)
설명 : 10진수 int형 i 값을 16진수로 변환한다.
public String hex2string(byte[] b, String prefix){
if(b == null || b.length <= 0) return null;
StringBuffer sb = new StringBuffer();
for(int i=0; i<b.length; i++){
int j = b[i];
if(j < 33){
if(ASCII_HEX_TABLE.get(Integer.toHexString(j)) != null){
sb.append(prefix+ASCII_HEX_TABLE.get(Integer.toHexString(j))+prefix);
}
}else{
sb.append((char)b[i]);
}
}
return sb.toString();
}
if(b == null || b.length <= 0) return null;
StringBuffer sb = new StringBuffer();
for(int i=0; i<b.length; i++){
int j = b[i];
if(j < 33){
if(ASCII_HEX_TABLE.get(Integer.toHexString(j)) != null){
sb.append(prefix+ASCII_HEX_TABLE.get(Integer.toHexString(j))+prefix);
}
}else{
sb.append((char)b[i]);
}
}
return sb.toString();
}
(추가) 한글로 값이 넘어오는 경우 처리
public String hex2string(byte[] b, String prefix){
if(b == null || b.length <= 0) return null;
try{
String s = new String(b, "MS949");
StringBuffer sb = new StringBuffer();
for(int i=0; i<s.length(); i++){
int j = s.charAt(i);
if(j < 33){
if(ASCII_HEX_TABLE.get(Integer.toHexString(j)) != null){
sb.append(prefix+ASCII_HEX_TABLE.get(Integer.toHexString(j))+prefix);
}
}else{
sb.append((char)s.charAt(i));
}
}
return sb.toString();
}catch(Exception e){return null;}
}
if(b == null || b.length <= 0) return null;
try{
String s = new String(b, "MS949");
StringBuffer sb = new StringBuffer();
for(int i=0; i<s.length(); i++){
int j = s.charAt(i);
if(j < 33){
if(ASCII_HEX_TABLE.get(Integer.toHexString(j)) != null){
sb.append(prefix+ASCII_HEX_TABLE.get(Integer.toHexString(j))+prefix);
}
}else{
sb.append((char)s.charAt(i));
}
}
return sb.toString();
}catch(Exception e){return null;}
}
2. (char)Integer.parseInt(String.valueOf(Long.decode("0x"+hexCode)))
설명 : 16진수 문자값을 그에 해당하는 문자로 변환시킴
Long.decode("0x"+hexCode) : 16진수 문자값(: hexCode)을 16진수 숫자값으로 변환
public byte[] string2hex(String s, String prefix){
if(s == null || s.equals("")) return null;
StringBuffer sb = new StringBuffer();
StringTokenizer st = new StringTokenizer(s,prefix);
String partString = "";
String hexCode = "";
while (st.hasMoreTokens()) {
partString = st.nextToken();
if(partString != null && !partString.equals("")){
if(ASCII_NAME_TABLE.get(partString) != null){
hexCode = (String)ASCII_NAME_TABLE.get(partString);
sb.append((char)Integer.parseInt(String.valueOf(Long.decode("0x"+hexCode))));
}else{
sb.append(partString);
}
}
}
return sb.toString().getBytes();
}
if(s == null || s.equals("")) return null;
StringBuffer sb = new StringBuffer();
StringTokenizer st = new StringTokenizer(s,prefix);
String partString = "";
String hexCode = "";
while (st.hasMoreTokens()) {
partString = st.nextToken();
if(partString != null && !partString.equals("")){
if(ASCII_NAME_TABLE.get(partString) != null){
hexCode = (String)ASCII_NAME_TABLE.get(partString);
sb.append((char)Integer.parseInt(String.valueOf(Long.decode("0x"+hexCode))));
}else{
sb.append(partString);
}
}
}
return sb.toString().getBytes();
}
jNetServer1.0 Readme
Network Server Application을 구현할 때 마다 느끼는 것이지만 맨날 반복되는 코드에 지겹다는 생각을 많이 했다. 물론 초보자에게는 Thread, Socket, IO 관련 프로그램 이 어렵게 느껴지겠고, 그리고, Thread Pool, Concurrency Issue등의 문제도 골치가 아플것이다. 아마도 이런 이유 때문에 application server를 쓰는지도 모르겠다. 하 지만 application server에서는 Socket Connector는 제공하지 않는다. 어째든 server application을 재활용하기 위한 차원에서 Socket Server Framework 을 만들어 보았다. 요즘 프로젝트가 다 웹이라 이런 Socket server를 얼마나 많이 개발 할지는 모르겠지만, 지금 참여하고 있는 프로젝트(EAI)에서는 중요한 부분으로 사용 되고 있다. (2003.11.23 일요일) |
|||||
written by Jeon HongSeong [hsjeon70@dreamwiz.com] | |||||
jNetServer1.0 Java Documentation API | |||||
1. 개 요 | |||||
◆ jNetServer1.0 개발 시 이용한 open source List | |||||
![]() | |||||
◆ jNetServer Framework은 Java Network Server Programming에 대한 Basic Infrastructure를 제공한다. ◆ Network Server Application을 구현할 때 필요한 Thread Pooling이나, Object Pooling, Logger 등의 기능을 제공한다. ◆ API에 정의된 NetTask 내 클라이언트로부터 데이터를 read하고, 결과를 write하는 로직만 간단히 구현하면 된다. ◆ Configuration 설정만으로 Network Server Connector, InputAdapter, Object Pool이 생성 초기화 된다. ◆ 클라이언트 시스템(IP) 별로 접근 권한 및 통신 프로토콜을 다르게 정의할 수 있다. ◆ 모든 Java Object들이 JMX MBean object로 관리되고, MX4J에서 제공하는 JMX Http Admin 관리 콘솔을 제공한다. ◆ 관리 콘솔 상에서 서버 및 모든 MBean object를 제어 할 수 있다. ◆ JMX Monitor Bean을 이용해 Connector의 상태를 Counter, Gauge 방식으로 모니터 링 할 수 있다. ◆ jNetServer Framework 내에서 configuration 설정 만으로 SSL(Secure Socket Layer) 를 지원한다. SSL용 NetTask의 구현은 일반 Socket 일때와 동일하다. ◆ NetTask의 확장으로 프락시 서버, 로드 발런스 등의 서버를 쉽게 구현할 수 있다. | |||||
2. 설치 및 실행 | |||||
◆ jNetServer1.0-app.jar 파일을 c:\jNetServer1.0 디렉토리 밑에 압축을 해제한다. C:\jNetServer1.0>jar xvf jNetServer1.0-app.jar C:\jNetServer1.0>dir C 드라이브의 볼륨에는 이름이 없습니다. 볼륨 일련 번호: 3D26-12D4 C:\jNetServer1.0 디렉터리 2003-11-29 12:48a <DIR> . 2003-11-29 12:48a <DIR> .. 2003-11-29 12:48a 637 runSsl.bat 2003-11-29 12:48a 789 startup.bat 2003-11-29 12:48a 18 lcp.bat 2003-11-29 12:48a 705 runMulti.bat 2003-11-29 12:48a 720 stop.bat 2003-11-29 12:48a 645 runEcho.bat 2003-11-29 12:48a <DIR> logs 2003-11-29 12:48a <DIR> docs 2003-11-29 12:48a <DIR> server 2003-11-29 12:48a <DIR> common 2003-11-29 12:48a <DIR> config 6개 파일 3,514 바이트 7 디렉터리 2,378,612,736 바이트 남음 ◆ jNetServer1.0/config/server.xml에 Server 태그의 address 속성을 설치 시스템의 IP 주소로 변경한다. <ServerGroup> <Server info="jNetServer1.0" name="svr1" address="192.168.0.13" port="8110" mode="standalone" shutdown="SHUTDOWN"> ◆ startup.bat 파일을 실행하여 jNetServer를 start 시킨다. 이때 keystore file의 비밀번호를 입력해야 하는데 "java11"을 입력한다. C:\jNetServer1.0>startup CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;server\lib\jNetServer1.0.j ar;server\lib\jakarta\commons-configuration-0.8.1.jar;server\lib\jakarta\commons -digester.jar;server\lib\jakarta\commons-net-1.0.1-dev.jar;server\lib\jakarta\co mmons-daemon.jar;server\lib\jakarta\commons-collections.jar;server\lib\jakarta\c ommons-logging-api.jar;server\lib\jakarta\commons-beanutils.jar;server\lib\jakar ta\commons-logging.jar;server\lib\jakarta\commons-pool-1.1.jar;server\lib\jakart a\commons-dbcp.jar;server\lib\jakarta\commons-lang.jar;server\lib\log4j\log4j-1. 2.8.jar;server\lib\mx4j\mx4j-jmx.jar;server\lib\mx4j\mx4j-tools.jar;common\class es;config [00:51:29] INFO - @ jNetServer1.0 @@@@@@@@@@@@@@@@@@@@@@@@@@ [00:51:29] INFO - >> jnet.home=. [00:51:29] INFO - >> jnet.server=svr1 [00:51:29] INFO - >> Logger=jnet.logger Enter the password of the keystore file : java11 [00:54:12] INFO - >> Including directory C:\jNetServer1.0\.\common\classes [00:54:12] INFO - StandardConnector{7130} listen 192.168.0.13 [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[0] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[0] [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[1] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[1] [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[2] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[2] [00:54:12] INFO - StandardTask{echo1} started [00:54:12] INFO - StandardInputAdapter{everyone} started [00:54:12] INFO - StandardMonitor[idleHandlers] started [00:54:12] INFO - StandardMonitor[curHandlers] started [00:54:12] INFO - Handler{7130}[0] has been started [00:54:12] INFO - Handler{7130}[1] has been started [00:54:12] INFO - Handler{7130}[2] has been started [00:54:12] INFO - Handler{7130}[3] has been started [00:54:12] INFO - Handler{7130}[4] has been started [00:54:12] INFO - StandardConnector{7130} started [00:54:12] INFO - StandardConnector{7131} listen 192.168.0.13 [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[0] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[0] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[1] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[1] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[2] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[2] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[3] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[3] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[4] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[4] [00:54:13] INFO - StandardTask{proxy} started [00:54:13] INFO - StandardInputAdapter{192.168.0.13} started [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[0] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[0] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[1] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[1] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[2] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[2] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[3] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[3] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[4] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[4] [00:54:13] INFO - StandardTask{echo2} started [00:54:13] INFO - StandardInputAdapter{everyone} started [00:54:13] INFO - Handler{7131}[0] has been started [00:54:13] INFO - Handler{7131}[1] has been started [00:54:13] INFO - Handler{7131}[2] has been started [00:54:13] INFO - Handler{7131}[3] has been started [00:54:13] INFO - Handler{7131}[4] has been started [00:54:13] INFO - StandardConnector{7131} started [00:54:13] INFO - StandardServer{svr1} started [00:54:13] INFO - StandardServerGroup started [00:54:13] INFO - ConsoleGaugeListener>> MonitorNotification [ sequence=1, time Stamp=Sat Nov 29 00:54:13 KST 2003, type=jmx.monitor.gauge.low, userData=null, m essage=, derivedGauge=5, observedObject=jnet.server:name=svr1/con7130, observedA ttribute=IdleHandlers, trigger=5, source=javax.management.monitor.GaugeMonitor@c 623af ] [00:54:13] INFO - ConsoleCounterListener>> MonitorNotification [ sequence=1, ti meStamp=Sat Nov 29 00:54:13 KST 2003, type=jmx.monitor.counter.threshold, userDa ta=null, message=, derivedGauge=5, observedObject=jnet.server:name=svr1/con7130, observedAttribute=CurHandlers, trigger=3, source=javax.management.monitor.Count erMonitor@50ca0c ] | |||||
3. Admin Console | |||||
◆ jNetServer가 실행되면, Http Admin Console에 접근해 관리할수 있다. http://192.168.0.13:8080 브라우저로 8080 포트로 접근해 보면 아래와 같이 로그온 화면이 나타나는데, jlook/jlook으로 오그온을 한다. admin 계정과 비밀번호는 은 config/server.xml 에 설정되어 있다. | |||||
4. EchoTask Bean 및 테스트 | |||||
◆ 설치된 jNetServer에는 예제로 EchoTask Bean이 제공된다. Task Bean의 개발은 jlook.jnet.task.NetTask 인터페이스를 구현하면 된다. 다음은 NetTask의 소스이다. | |||||
5. SSL 설정 및 테스트 | |||||
◆ config/server.xml의 내용을 보면 다음과 같이 7131 포트가 SSL 로 설정되어 있다. | |||||
6. SSLProxyTask Beans | |||||
◆ NetTask 응용으로 Proxy Server Beans를 소개한다. 서버를 7130, SSL 7131로 서비스 할때 7131로 들어온 요청을 7130쪽으로 forwarding하는 SSLProxyTask의 소스와 설정을 살펴보자. 다음은 server.xml의 7131쪽 Connector 설정 부분인데, ssl="true"로 설정된 것을 확인할 수 있다. 그리고, 앞 예제와는 다르게 InputAdapter의 source가 현 시스템의 ip{192.168.0.13}로 변경된것을 확인할 수 있다. 즉, 192.168.0.13 시스템의 클라이 언트의 요청을 SSLProxyTask 가 처리한다는 것을 설정한 것이다. | |||||
| |||||
written by Jeon HongSeong |
파이어폭스의 파이어버그나 ie의 피들러 또는 상용인 httpwatch 등을 통해서 웹브라우저에서 일어나는 통신의 안 보이는 부분을 볼 수 있습니다. 헤더 영역의 정보 같은 것이죠.
이클립스 WTP에서도 같은 기능을 지원합니다. TCP/IP 모니터뷰를 이용하면 지정된 포트에서 발생하는 교신 정보를 눈으로 확인할 수 있습니다.
설정은 다음과 같이 Preferences 에서 tcp 필터 단어를 입력하면 메뉴가 보입니다.
Add 버튼을 클릭하고 상단의 Local Monitoring port 를 8090 등 사용하지 않는 포트를 입력합니다. 모니터링할 서비스 정보를 입력합니다. 로컬의 톰캣을 모니터링하기 위해서 localhost 포트는 8080 이라고 입력했습니다.
등록을 마치면 해당 모니터링 항목을 선택하고 우측의 Start 버튼을 클릭해서 모니터링을 시작합니다.
모니터링하는 포트로 호출을 합니다. 8090이라고 정했기 때문에 http://localhost:8090/index.jsp 주소로 접근하면 TCP/IP Monitor 뷰가 자동으로 뜨게 됩니다.
TCP/IP Monitor 탭을 더블클릭해서 크게 확대해서 보면 서버로 요청한 주소 목록과 선택한 주소의 헤더 정보와 주고받은 내용들이 하단에 나옵니다.
이미지를 선택하면 해당 이미지도 볼 수 있습니다.
유용하게 쓰시기 바랍니다.