'분류 전체보기'에 해당되는 글 2491건

  1. 2010.02.04 ie8 확인 스크립트
  2. 2010.01.25 모든 브라우저 버전별 확인 스크립트 2
  3. 2010.01.25 IE8 확인하는 스크립트 : 익스플러 버전체크
  4. 2010.01.22 alert 대신 사용하면 좋은 라이브러리
  5. 2010.01.07 script 문제 1
  6. 2010.01.05 XMLHttpRequest 객체
  7. 2010.01.05 Array.prototype 에 max 값 정의 예제
  8. 2010.01.05 한글도 바이트단위로 처리하기
  9. 2010.01.04 javascript OS 버전 체크
  10. 2009.12.28 Spring Security
  11. 2009.12.28 N사 신입 사원 교육을 위한 Spring 프레임워크 강의
  12. 2009.12.22 [Java] JAVA - 개발자가 놓치기 쉬운 자바의 개념, 기본원리
  13. 2009.12.21 classpath내 properties파일로 Properties객체 생성 1
  14. 2009.12.21 배열을 for 사용하지 않고 출력하기...
  15. 2009.12.21 Spring - Java/J2EE Application Framework 1
  16. 2009.12.21 간단한 JavaScriptMethodOverloading
  17. 2009.12.21 중급 자바 개발자의 난관, 클래스 로딩 문제 분석하기
  18. 2009.12.21 Spring MVC 초간단 예제 1
  19. 2009.12.21 framework jpetstore 샘플 분석기
  20. 2009.12.21 Simplest Spring 3 Application, 초간단 Spring 3 어플
  21. 2009.12.21 내가 Prototype에서 jQuery로 옮긴 이유
  22. 2009.12.18 Paging.java 페이지 계산 및 링크 출력 클래스
  23. 2009.12.09 [Apache - Tomcat] 로드 밸런싱, 세션 클러스터링
  24. 2009.12.09 apache 에서 제공하는 Common Validator 사용하기
  25. 2009.12.09 자바 String Util 들
  26. 2009.12.09 오라클 데이타딕셔너리
  27. 2009.12.08 Insert BLOG(Picture or Photo) Data Type Into Oracle Database
  28. 2009.12.08 Create a table in database
  29. 2009.12.08 Demo ResultSet Oracle
  30. 2009.12.03 20091203 ajax 모듈화
반응형
//ie8 
var isOldIE = false;
if(Prototype.Browser.IE) {
  if(parseFloat(navigator.appVersion.split("MSIE")[1].split(";")[0].split(" ").join("")) <= 7) {
    isOldIE = true;
  }
}
Posted by 1010
반응형

<%@ page language="java" contentType="text/html; charset=EUC-KR"
 pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
<script type="text/javascript">

 // InternetVersion
 function getInternetVersion(ver) {
  var rv = -1; // Return value assumes failure.     
  var ua = navigator.userAgent; 
  var re = null;
  if(ver == "MSIE"){
   re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
  }else{
   re = new RegExp(ver+"/([0-9]{1,}[\.0-9]{0,})");
  }
  if (re.exec(ua) != null){
   rv = parseFloat(RegExp.$1);
  }
  return rv; 
 }
 
 //브라우저 종류 및 버전확인 
 function browserCheck(){
  var ver = 0; // 브라우저  버전정보
  if(navigator.appName.charAt(0) == "N"){
   if(navigator.userAgent.indexOf("Chrome") != -1){
    ver = getInternetVersion("Chrome");
    alert("Chrome"+ver+"입니다.");
   }else if(navigator.userAgent.indexOf("Firefox") != -1){
    ver = getInternetVersion("Firefox");
    alert("Firefox"+ver+"입니다.");
   }else if(navigator.userAgent.indexOf("Safari") != -1){
    ver = getInternetVersion("Safari");
    alert("Safari"+ver+"입니다.");
   }
  }else if(navigator.appName.charAt(0) == "M"){
   ver = getInternetVersion("MSIE");
   alert("MSIE"+ver+"입니다.");
  }
  }
</script>  
</head>
<body>  
 <input type="button" value="브라우저판별" onclick="browserCheck();">  
</body>
</html>

Posted by 1010
반응형
<%@ page language="java" contentType="text/html; charset=EUC-KR"
 pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
<script type="text/javascript">
 function getInternetExplorerVersion() {
  var rv = -1; // Return value assumes failure.  
 
  
  if (navigator.appName == 'Microsoft Internet Explorer') {
   var ua = navigator.userAgent;
   alert(ua); 
   var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
   if (re.exec(ua) != null)
    rv = parseFloat(RegExp.$1);
  }
  return rv;
 }
 function checkVersion() {
  var msg = "You're not using Windows Internet Explorer.";
  var ver = getInternetExplorerVersion();
  if (ver > -1) {
   if (ver >= 8.0)
    msg = "You're using a recent copy of Windows Internet Explorer.";
   else
    msg = "You should upgrade your copy of Windows Internet Explorer.";
  }
  alert(msg);
 }
 function IsIE8Browser() {
  var rv = -1;
  var ua = navigator.userAgent;
  var re = new RegExp("Trident\/([0-9]{1,}[\.0-9]{0,})");
  if (re.exec(ua) != null) {
   rv = parseFloat(RegExp.$1);
  }
  alert(ua);
  alert(re);
  alert("re.exec(ua) : "+re.exec(ua));
  alert(rv);
  return (rv == 4);
 }
</script>
</head>
<body>
<input type="button" value="브라우저테스트1" onclick="checkVersion();">
<input type="button" value="브라우저테스트2" onclick="IsIE8Browser();">
</body>
</html>
Posted by 1010
반응형
http://www.gscottolson.com/blackbirdjs/



Demos

Output all message types

log.debug( 'this is a debug message' );
log.info( 'this is an info message' );
log.warn( 'this is a warning message' );
log.error( 'this is an error message' );

Click the block to execute the code

Generate test string

log.profile( 'generate test string' );

var testContent = '';
for ( var i = 0; i < 3000; i++ ) {
  testContent += '-';
}

log.profile( 'generate test string' );

Click the block to execute the code

Setup

Adding Blackbird to your page

  1. Download the files and place them on your server or in a local directory on your computer.
  2. Include blackbird.js in your page.
  3. Inlcude blackbird.css in your page.

Your HTML source should look similar to the following code:

<html>
  <head>
    <script type="text/javascript" src="/PATH/TO/blackbird.js"></script>
    <link type="text/css" rel="Stylesheet" href="/PATH/TO/blackbird.css" />
    ...
  </head>
  ...

Browser compatibility

Blackbird is has been smoke-tested on the following browsers:

  • Internet Explorer 6+
  • Firefox 2+
  • Safari 2+
  • Opera 9.5
Usage

Public API

log.toggle()
Hide/show Blackbird
log.move()
Move Blackbird to next fixed positions: top-left, top-right, bottom-left, bottom-right
log.resize()
Expand/contract Blackbird
log.clear()
Clear all contents of Blackbird
log.debug( message )
Add a debug message to Blackbird
message: the string content of the debug message
log.info( message )
Add an info message to Blackbird
message: the string content of the info message
log.warn( message )
Add a warning message to Blackbird
message: the string content of the warn message
log.error( message )
Add an error message to Blackbird
message: the string content of the warn message
log.profile( label )
Start/end a time profiler for Blackbird. If a profiler named string does not exist, create a new profiler. Otherwise, stop the profiler string and display the time elapsed (in ms).
label: the string identifying a specific profile timer

Keyboard controls

Hide/show
F2
Move
Shift + F2
Clear
Alt + Shift + F2

Interface controls

Pressing the Alt key while clicking any message filter control will only show messages of that type. For example, Alt + click debug control to show only debug messages.

Bookmarklets

Drag the following links to your bookmarks toolbar for quick access to Blackbird commands:

Customization

Changing the namespace

The default configuration attaches the public API for Blackbird to the global variable log. It is possible to have conflicts with existing variable declarations, and it is possible to redefine the global variable log in other blocks of JavaScript. If you want to define a different variable for Blackbird, such as blackbird, replace the assignment for NAMESPACE with any other string.

var NAMESPACE = 'log';

can be replaced with:

var NAMESPACE = 'blackbird';

or:

var NAMESPACE = 'myCustomLog';

The global API will be affected by this change. If you choose to replace log with myCustomLog, then log.debug will become myCustomLog.debug.

Capture & cancel logging statements

If you're shipping code with logging statements, you might want to capture and cancel commands intended for Blackbird. The following code will do this:

var log = {
  toggle: function() {},
  move: function() {},
  resize: function() {},
  clear: function() {},
  debug: function() {},
  info: function() {},
  warn: function() {},
  error: function() {},
  profile: function() {}
};
About

Credits

The code (blackbird.js, blackbird.css) and image assets (blackbird_panel.png, blackbird_icons.png) were created by G. Scott Olson.

The concept for Blackbird was sparked by JavaScript Logging, an article by David F. Miller. The first iteration of this project, named jsLogger, was developed for internal use at Orbitz. In the fall of 2007, jsLogger was rewritten from the ground up, given a facelift, and named project Blackbird.

Contributing to the Blackbird Project

Blackbird is an open source project. If you're intereted in contributing, you can start by checking out the project page on Google Code.

Posted by 1010
반응형

-- 문제


<script type="text/javascript">
 function tt(){
  var external_links = document.getElementById('external_links');
  var links = external_links.getElementsByTagName('a');
  for (var i=0;i < links.length;i++) {
      var link = links.item(i);
      link.onclick = function() {
          return confirm('You are going to visit: ' + this.href);
      };
  }
 } 
</script>

<div id="external_links">
 <a href="http://www.naver.com">link</a>
</div>  
<input type="button" name="dddd" onclick="tt();"/>

---
실행조건(1) : 링크를 클릭한다.
실행조건(2) : 버튼을 클릭한 후, 링크를 클릭한다.

결과가 다른 이유는...?

-----------------------------
ex)
window.onload = function() {
  tt();
 };

Posted by 1010
90.개발관련문서2010. 1. 5. 17:49
반응형

요약

The XMLHttpRequest Object 규격은 서버와 클라이언트간 데이터를 전달하기 위한 스크립트화된 클라이언트 기능을 제공하는 API를 정의한다.

이 문서의 상태

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This is the 26 October 2007 Working Draft of The XMLHttpRequest Object specification. Please send comments to public-webapi@w3.org (archived) with either [XHR] or [XMLHttpRequest] at the start of the subject line.

This document is produced by the Web API Working Group, part of the Rich Web Clients Activity in the W3C Interaction Domain. Changes made to this document can be found in the W3C public CVS server.

Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

목차

1. 소개

이 절은 비규범적이다.

XMLHttpRequest object는 HTTP 클라이언트 인터페이스를 구현한다. 이 인터페이스는 스크립트 엔진을 통해 노출되며 폼 데이터를 보내거나 서버로부터 데이터를 받아오는 기능을 수행한다.

XMLHttpRequest라는 이름은 웹의 호환성을 위해 지어졌지만 이름의 각 부분은 잠재적으로 오해를 일으킬 수 있다. 첫째, XMLHttpRequest는 XML을 포함한 텍스트 기반의 어떠한 포맷도 지원한다. 둘째, XMLHttpRequest는 HTTP와 HTTPS 프로토콜 모두 사용할 수 있다. (몇몇 구현은 HTTP와 HTTPS 이외의 프로토콜을 지원하기도 하지만 그러한 기능은 본 규격에서는 다루지 않는다.). 마지막으로 XMLHttpRequest의 "request"는 HTTP에 정의된 용어에 비해 광의적으로 사용된다. 즉, HTTP method들을 위한 HTTP request와 response를 수행하기 위한 행위들을 포함한다.

1.1. 사용예

이 절은 비규범적이다.

몇몇 [ECMAScript] 예제들은 본 규격에 열거되었다. 추가로 아래 코드를 참고할 수 있다.

네트워크를 통해 전달된 XML 문서를 가지고 무엇인가를 하는 간단한 코드:

function test(data) {
 // taking care of data
}

function handler() {
 if(this.readyState == 4 && this.status == 200) {
  // so far so good
  if(this.responseXML != null && this.responseXML.getElementById('test').firstChild.data)
     // success!
   test(this.responseXML.getElementById('test').firstChild.data);
  else
   test(null);
 } else if (this.readyState == 4 && this.status != 200) {
  // fetched the wrong page or network error...
  test(null);
 }
}

var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "test.xml");
client.send();

만약 서버에게 로그 메시지를 전달하고 싶다면:

function log(message) {
 var client = new XMLHttpRequest();
 client.open("POST", "/log");
 client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
 client.send(message);
}

만약 서버에 있는 문서의 상태를 확인하고 싶다면:

function fetchStatus(address) {
 var client = new XMLHttpRequest();
 client.onreadystatechange = function() {
  // in case of network errors this might not give reliable results
  if(this.readyState == 4)
   returnStatus(this.status);
 }
 client.open("HEAD", address);
 client.send();
}

1.2. 준수(Conformance)

다이어그램, 예제, 노트 그리고 비규범적이라고 명시된 절을 제외한 본 문서의 모든 부분은 규범적이다.

이 문서에서 사용된 must, must not, should 와 may 이라는 용어는 RFC 2119 [RFC2119]에 따라 해석된다.

본 규격은 다음과 같은 종류의 산출물을 정의한다.:

준수하는 유저 에이전트

유저 에이전트는 본 규격에 준수하기 위해 반드시(must) 이 규격에 정의된 대로 동작해야 한다.

만약 유저에이전트가 XML 유저에이전트에 준수하지 않는다면 XML response entity body는 반드시(must) 항상 null이어야 한다.

유저 에이전트는 본 규격에 정의된 알고리즘으로 얻을 수 있는 결과물과 구별 불가능한 결과물을 얻는 알고리즘을 구현할 수 있다. (may)

본 규격에서 사용하는 "준수하는 유저 에이전트(들)"와 "유저 에이전트(들)"는 이 종류의 산출물은 가르킨다.

준수하는 XML 유저 에이전트

준수하는 유저 에이전트이면서 동시에 namespace well-formed를 보고하는 준수하는 XML처리기인 유저 에이전트를 말한다.[XML] [XMLNS]

1.2.1. 의존성

본 규격은 몇몇 기초 규격에 기반을 두고 있다.

DOM

준수하는 유저에이전트는 반드시(must) DOM Core와 DOM Events에 정의된 기능의 부분 집합을 지원해야한다. [DOM3Events] [DOM3Core]

이 규격이 의존하고 있는 반드시(must) Window Objects 1.0 규격에 정의된 기능의 부분 집합을 지원해야 한다. [Window ]

HTTP

준수하는 유저에이전트는 반드시(must) 특정버전의 HTTP를 지원해야 한다. 꼭(should) Method 형식에 맞는 HTTP method를 제공해야 하며, 반드시(must) 최소한 아래의 method들을 지원해야 한다.:

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE
  • OPTIONS

HTTP에 관한 다른 요구사항은 다음 문서에 정의되어 있다. [RFC2616]

1.2.2. 용어

s1s2 문자열의 case-insensitive match는 두 문자열을 모두 대문자로 만들고 나서 (a-z를 A-Z로 매핑함으로써) 두 문자열이 동일함을 의미한다.

두 개의 URI는 RFC 3987에 정의된 sceheme-based normalization을 수행한 후에 scheme, ihost, port가 모두 동일 할 경우 same-origin 이라고 정의된다. 만약 URI가 두 URI 모두 ihost를 가지고 있지 않을 경우 반드시(must not) same-origin으로 간주되지 말아야 한다. [RFC3987]

1.2.3. 확장

본 규격에 정의된 API의 확장은 강력히 추천되지 않는다. 유저 에이전트들, 워킹 그룹, 그 밖에 관심있는 단체들은 반드시 이러한 확장을 public-webapi@w3.org 와 같은 공개적인 방법으로 논의 해야한다.

2. XMLHttpRequest 객체

XMLHttpRequest 객체는 스크립트에 의해 프로그램적으로 originating server에 접속할 수 있다.

XMLHttpRequest 인터페이스를 구현하는 객체는 반드시(must) EventTarget 인터페이스를 구현해야 한다. [DOM3Events]

Window 인터페이스를 구현하는 객체는 반드시(must) XMLHttpRequest() 생성자를 제공해야한다. [Window]

ECMAScript에서는 다음과 같이 쓰일 수 있다:

var client = new XMLHttpRequest();

XMLHttpRequest() 생성자가 호출될 경우 연관된 Window 객체에 대한 영속적인 포인터가 반드시(must) 새로 생성된 객체에 저장되어야 한다. 이것은 Window 포인터이다. 연관된 Window 객체는 XMLHttpRequest() 생성자가 호출된 것 중 하나이다. 이 포인터는 반드시(must) 해당 윈도우가 속해있는 브라우징 콘텍스트가 파괴된 경우에도 유지되어야 한다. (예를들면 부모 콘텍스트로 부터 제거하는 것과 같이).

브라우징 콘텍스트라는 용어는 Window Object 1.0 규격에 의해 정의된다. [Window]

만약 winWindow 객체라면, client 는 아래 예제에서 win 에 대한 포인터를 가질 것이다:

var client = new win.XMLHttpRequest()
interface XMLHttpRequest {
  // event handler
           attribute EventListener onreadystatechange;

  // state
  const unsigned short UNSENT = 0;
  const unsigned short OPENED = 1;
  const unsigned short HEADERS_RECEIVED = 2;
  const unsigned short LOADING = 3;
  const unsigned short DONE = 4;
  readonly attribute unsigned short readyState;

  // request
  void open(in DOMString method, in DOMString url);
  void open(in DOMString method, in DOMString url, in boolean async);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password);
  void setRequestHeader(in DOMString header, in DOMString value);
  void send();
  void send(in DOMString data);
  void send(in Document data);
  void abort();

  // response
  DOMString getAllResponseHeaders();
  DOMString getResponseHeader(in DOMString header);
  readonly attribute DOMString responseText;
  readonly attribute Document responseXML;
  readonly attribute unsigned short status;
  readonly attribute DOMString statusText;
};

XMLHttpRequest 객체는 다섯 가지 상태를 가질 수 있다: UNSENT, OPENED, HEADERS_RECEIVED, LOADING 그리고 DONE. 현재 상태는 readyState 속성에 의해 노출된다. 아래 정의된 방법이 언제 상태 전이가 일어나는지를 정의한다.

생성되었을 때, XMLHttpRequest 객체는 반드시(must) UNSENT 상태여야 한다. 이 상태는 값이 0UNSENT 상수에 의해 표현된다.

OPENED 상태는 open() 메서드가 성공적으로 불렸을 때의 객체의 상태이다. 이 상태에서는 setRequestHeader()를 이용해 request 헤더를 설정할 수 있으며, send()를 통해 request를 할 수 있다. 이 상태는 값이 1OPENED 상수에 의해 표현된다.

OPENED 상태는 연관된 send() 플래그를 가지는데, "true" 또는 "false"의 값을 가진다. send() 플래그의 초기값은 "false"이다.

HEADERS_RECEIVED 상태는 모든 response 헤더가 받아진 객체의 상태이다. 이 상태는 값이 2HEADERS_RECEIVED 상수에 의해 표현된다.

LOADING 상태는 response entity body가 받아지고 있는 중의 객체의 상태이다. 이 상태는 값이 3LOADING 상수에 의해 표현된다.

DONE 상태는 데이터의 전송이 끝나거나 전송 중 무한 리다이렉션 같은 오류가 발생한 객체의 상태이다. 이 상태는 값이 4DONE 상수에 의해 표현된다.

DONE는 연관된 error 플래그를 가지며 그 값은 "true" 또는 "false"이다. error 플래그의 초기값은 "false"이다.

response entity body는 현재까지 수신된 entity body의 일부이거나 (LOADING 상태), 완전한 entity body 이다. (DONE 상태). 만약 entity body가 없다면 response entity body 는 "null"이다.

text response entity bodyresponse entity body를 나타내는 DOMString이거나 null이다. Text response entity body는 다음과 같은 알고리즘의 리턴값이다:

  1. 만약 response entity body가 "null"이면, null을 리턴하고 종료한다.

  2. charset을 "null"로 하자.

  3. 만약 Content-Type 헤더가 없거나 MIME type이 text/xml , application/xml이거나 +xml로 끝나는 Content-Type 헤더를 가졌다면 (파라메터는 무시한다), character encoding을 판별하기 위해 XML 규격에 설명되어 있는 방법을 사용한다. charset을 판별된 character encoding 값으로 한다.

  4. 만약 MIME type이 text/htmlContent-Type 헤더를 가졌다면, character encoding을 판별하기 위해 HTML 5 규격에 설명되어 있는 방법을 사용한다. charset을 판별된 character encoding 값으로 한다. [HTML5]

  5. 만약 Content-Type헤더에 지정된 MIME type이 파라메터를 가지고 있고, charset이 "null" 이라면 charset이 그 파라메터의 값으로 한다.

    XML과 HTML 규격에 정의된 알고리즘은 이미 Content-Type 값을 참고한다.

  6. 만약 charset이 "null"이면, 아래 테이블의 각 행을 첫 줄부터 시작해서 대조하여 처음으로 첫번째 열과 일치하는 행의 두번째 컬럼 값을 charset의 값으로 한다. 만약 일치 하는 행이 없다면 charset 는 "null"로 남는다.

    16진수 바이트열 설명
    00 00 FE FF UTF-32BE BOM
    FF FE 00 00 UTF-32LE BOM
    FE FF UTF-16BE BOM
    FF FE UTF-16LE BOM
    EF BB BF UTF-8 BOM
  7. 만약 charset 이 "null"이면 charset를 UTF-8로 한다.

  8. Response entity body를 charset를 이용해 디코딩 한 값을 리턴한다. 만약 실패한다면 null을 리턴한다.

XML response entity bodyresponse entity body를 나타내는 Document이거나 null이다. XML response entity body 는 다음과 같은 알고리즘의 리턴값이다:

  1. 만약 response entity body가 "null"이면, null을 리턴하고 종료한다.

  2. 만약 Content-Type 헤더가 존재하고 MIME type이 text/xml , application/xml이거나 +xml로 끝나는 Content-Type 헤더를 가지지 않았다면 (파라메터는 무시한다), null을 리턴하고 종료한다. (만약 Content-Type 가 전혀 없다면 종료하지 않는다.)

  3. Response entity body를 XML 규격에 정의된 방법을 따라서 document tree로 파싱한다. parsed document의 값을 그 결과값으로 한다. 만약 지원되지 않는 character encoding이거나 namespace well-formedness 에러 등에 의해 실패한다면 null을 리턴하고 종료한다. [XML] [XMLNS ]

    Document tree에 있는 스크립트는 실행되지 않으며, 여기서 참조된 리소스들도 로딩되지 않는다. 또한 연관된 XSLT도 적용되지 않는다.

  4. parsed document값을 가지는 Document 인터페이스를 구현한 객체를 리턴한다.

onreadystatechange of type EventListener

bubbling phase 동안readystatechange 이벤트가 전달될 경우 이 객체에 등록된 다른 적절한 이벤트 핸들러와 함께 반드시(must) 호출되어야 하는 EventListener를 값을 취하는 속성이다. 초기값은 반드시 (must) null이다.

readyState of type unsigned short, readonly

이 속성을 읽을 경우 반드시(must) 해당 객체의 현재 상태 값을 리턴해야 한다.

open(method, url, async, user, password), method

호출될 경우 유저 에이전트는 반드시(must) 다음 단계를 따라야 한다. (다른 방식으로 지시되지 않는 한):

  1. 만약 method인자가 RFC 2616의 5.1.1절에 정의된 Method 형식에 맞지 않은 경우 SYNTAX_ERR 예외를 발생하고 종료한다. [RFC2616]

  2. 만약 주어진 method 인자가 보안상의 이유로 지원되지 않을경우 유저 에이전트는 꼭(should) SECURITY_ERR 예외를 발생하고 종료해야 한다.

  3. 저장된 method값을 method로 한다.

  4. 만약 methodGET, POST, HEAD, PUT, DELETE 또는 OPTIONS case-insensitively match하다면 stored method에 이를 a-z를 A-Z로 매핑함으로써 대문자로 변환한 값을 저장한다.

  5. url에 Fragment identifier가 있다면 이를 제외한 부분을 저장된 url값에 저장한다.

  6. 만약 저장된 url이 상대적 경로라면, Window 포인터에 연관된 Document 객체의 현재 baseURI 속성을 이용해 해결한다. 만약 이것이 실패한다면 SYNTAX_ERR 예외를 발생시키고 종료한다.

  7. 만약 저장된 url이 지원되지 않는 scheme을 가졌을 경우, NOT_SUPPORTED_ERR 예외를 발생시키고 종료한다.

  8. 만약 "user:password" 형식의 userinfo 값이 해당 scheme에서 지원되지 않고, 저장된 url값에 이 형식이 존재한다면, SYNTAX_ERR 예외를 발생시키고 종료한다. [RFC3986]

  9. 만약 저장된 url 값이 "user:password" 형식을 가진다면 저장된 user 가 user 부분, 저장된 password 가 password 부분이 되도록한다.

  10. 만약 저장된 url"user" 형식만 가진다면, 저장된 user값이 user 부분이 되도록 한다.

  11. 만약 저장된 urlWindow 포인터에 연관된 Document 객체와 same-origin이 아니라면, 유저 에이전트는 꼭(should) SECURITY_ERR 예외를 발생시키고 종료해야 한다. 용어 origin은 HTML 5 규격에 의해 정의된다. [HTML5]

  12. async 값을 async 인자의 값이 주어졌을 경우에는 그 값으로, 생략되었을 경우에는 true로 한다.

  13. 만약 user 인자가 주어졌고, 그 형식이 해당 인증 scheme에 적합하지 않은 경우, SYNTAX_ERR 예외를 발생시키고 종료한다.

  14. 만약 user 인자가 주어졌고 null이 아니라면 저장된 user 값을 해당 인증 scheme에 지정된 방법으로 인코딩한 값으로 한다. 만약 scheme에서 encoding이 지정되지 않은 경우에는 UTF-8로 인코딩 한 값으로 한다.

    이 단계는 url 인자에 의해 지정된 user 값보다 우선 순위를 가진다.

  15. 만약 user 인자가 생략되지 않았고 null이라면 저장된 user 값을 지운다.

  16. 만약 password 인자가 주어졌고, 그 형식이 해당 인증 scheme에 적합하지 않은 경우, SYNTAX_ERR 예외를 발생시키고 종료한다.

  17. 만약 password 인자가 주어졌고 null이 아니라면 저장된 user 값을 해당 인증 scheme에 지정된 방법으로 인코딩한 값으로 한다. 만약 scheme에서 encoding이 지정되지 않은 경우에는 UTF-8로 인코딩 한 값으로 한다.

  18. 만약 password 인자가 생략되지 않았고 null이라면 저장된 password 값을 지운다.

  19. send() 알고리즘을 취소하고, response entity body를 "null"로 하고, request header의 목록들을 리셋한다.

  20. 유저 에이전트는 꼭(should) 해당 객체가 수행하는 모든 네트워크 활동을 취소해야 한다.

  21. 객체의 상태를 OPENED 상태로 세팅하고, send() 플래그 값을 "false"로 새팅한다. 그리고 동기적으로 readystatechange 이벤트를 해당 객체에 보내고, 메서드 호출을 리턴한다.

본 규격의 향후 버전 또는 확장은 cross-site request에 대한 방법을 정의할 가능성이 높다.

setRequestHeader(header, value), method

각각의 request는 연관된 값을 지닌 request header 목록을 가진다. 이 메서드는 그 값을 조작하거나 새로운 request header를 지정하는데 사용할 수 있다.

호출 될 경우, 유저 에이전트는 반드시(must) 다음 단계를 따라야한다. (다른 방법으로 지시되지 않았을 경우):

  1. 만약 객체의 상태가 OPENED 가 아닐 경우 INVALID_STATE_ERR 예외를 발생하고 종료한다.

  2. 만약 send() 플래그가 "true"라면, INVALID_STATE_ERR 예외를 발생하고 종료한다.

  3. 만약 header 인자가 RFC 2616의 4.2절에 정의된 field-name 형식과 맞지 않거나 null 일 경우 SYNTAX_ERR 예외를 발생하고 종료한다. [RFC2616]

  4. 만약 value 인자가 null 이라면 종료한다. (예외를 발생시키지 않는다.)

  5. 만약 value 인자가 RFC 2616의 4.2절에 정의된 field-value 형식에 맞지 않는다면 SYNTAX_ERR 예외를 발생시키고 종료한다. [RFC2616]

  6. 만약 header 인자가 아래의 값 중 하나와 case-insensitively match한다면 꼭(should) 보안상의 이유로 종료되어야 한다:

    • Accept-Charset
    • Accept-Encoding
    • Connection
    • Content-Length
    • Content-Transfer-Encoding
    • Date
    • Expect
    • Host
    • Keep-Alive
    • Referer
    • TE
    • Trailer
    • Transfer-Encoding
    • Upgrade
    • Via
  7. 역시 보안상의 이유로 만약 header 인자의 첫 여섯글자가 Proxy-case-insensitively match 한다면 종료해야 한다.

  8. 만약 header 인자가 request header 목록에 없다면 연관된 value를 목록에 추가하고 종료한다.

  9. 만약 header 인자가 request header 목록에 있다면, 복수의 header를 사용하거나 값을 합치거나 그 두 개의 조합을 사용한다. (RFC 2616 4.2절) [RFC2616]

유저 에이전트가 캐싱, 인증, 프록시, 쿠키와 관련해서 header를 어떻게 다루는지 보려면 send() 메서드를 참조하다.

// 다음의 스크립트는:
var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
client.send();

// ...아래와 같은 request header를 보내게 된다:
...
X-Test: one, two
...
send(data), method

send() 메서드는 요청을 시작하며, 이의 선택적 인자는 entity body를 제공한다. 저작자들은 send()null이 아닌 data 인자와 함께 호출하기 전에 setRequestHeader()Content-Type header를 지정 했는지 확인 하도록 강력히 권장된다.

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 따라야 한다. (다른 방식으로 지정되지 않았을 경우). 이 알고라즘은 open() 또는 abort() 메서드가 호출됨으로써 취소될 수 있다. send() 알고리즘이 중단될 경우에 유저 에이전트는 반드시(must) 현재 진행 중인 단계를 끝내고 종료해야 한다.

아래 알고리즘은 asyncfalse일 경우, 스크립트를 통해 중단될 수 없다. 오직 asynctrue 이고 해당 메서드가 리턴한 이후에만 중단될 수 있다.

  1. 만약 객체의 상태가 OPENED 가 아니라면 INVALID_STATE_ERR 예외를 발생 시키고 종료한다.

  2. 만약 send() 플래그가 "true"라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  3. 만약 asynctrue라면 send() 플래그를 "true"로 한다.

  4. 만약 data 인자가 생략되지 않았고, null이 아니라면 이 값을 RFC 2616의 7.2절에 정의된 대로 entity body 값으로 사용하되 다음의 규칙을 따른다. [RFC2616]

    dataDOMString이다.

    전송을 위해 data를 UTF-8로 인코딩한다.

    dataDocument이다.

    data를 namespace well-formed한 XML document로 직렬화(serialize)하고 data.xmlEncoding에서 주어진 값으로 인코딩한다. 만약 주어지지 않았을 경우 UTF-8로 인코딩한다. 만약 Document가 직렬화(serialize) 되지 못해서 실패한다면, datanull인 것 처럼 동작한다.

    만약 Content-Type 헤더가 request header 목록에 존재하지 않는다면, request header 목록에 application/xml를 값으로 Content-Type 헤더를 추가한다.

    Document에 대한 이후의 변경은 전송되는 값에 영향을 미치지 않는다.

    dataDOMString도 아니고, Document도 아니다.

    data의 호스트 언어에서 지정된 문자열화(stringification) 방법을 사용하고, dataDOMString인 것처럼 동작한다. 만약 실패할 경우 datanull인 것처럼 동작한다.

    만약 data 인자가 생략되었거나, null이라면 이 요청에서 entity body가 사용되지 않는다.

  5. 이 단계 직후에 stored urlstored method의 HTTP method, stored user의 user (만약 있다면), stored password의 password (만약 있다면), entity body와 request header 목록을 이용해 요청을 한다.

  6. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    객체의 상태는 변경되지 않는다. 이 이벤트는 역사적인 이유로 보내지는 것이다.

  7. 만약 asynctrue 라면 send() 메서드 호출을 리턴한다. (하지만 이 알고리즘을 종료하지는 않는다.)

  8. 리소스가 다운로드 되는 동안 아래의 규칙이 적용된다.

    만약 response가 HTTP redirect라면

    만약 redirect가 보안상의 문제 점이 없고, (예를 들면 same-origin이라면) 무한 루프에 빠질 위험이 없다면 그 scheme은 투명하게(transparently) 지원되며, redirect를 따라간 후 이 단계 맨 처음으로 돌아한다.

    HTTP는 유저에이턴트가 redirect 하는 동안 request method와 entity body를 유지하도록 요구한다. 또한 이러한 자동 redirection이 사용자에게 알려지도록 요구한다.

    그렇지 않다면 다음의 단계를 따른다:

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 NETWORK_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 error 이벤트를 보내게 될 가능성이 높다.

    만약 사용자가 다운로드를 취소한다면

    다음 단계를 수행한다:

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 ABORT_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 abort 이벤트를 보내게 될 가능성이 높다.

    네트워크 에러가 발생한 경우라면

    DNS 에러 또는 다른 종류의 네트워크 에러의 경우 다음의 단계를 수행한다. 여기에는 HTTP status code 410과 같은 어떤 종류의 에러를 나타내는 HTTP response를 포함하지 않는다.

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 NETWORK_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 error 이벤트를 보내게 될 가능성이 높다.

    만약 모든 HTTP 헤더들을 수신했다면

    message body(있다면)를 수신하기 전에 만약 모든 HTTP header들을 수신했다면 다음 단계를 따른다:

    1. 동기적으로 객체의 상태를 HEADERS_RECEIVED으로 전환한다.

    2. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    만약 response entity body의 첫 바이트 (또는 더 많은 바이트)를 수신했다면
    만약 response entity body가 없다면

    만약 response entity body의 첫 바이트 (또는 더 많은 바이트)를 수신했거나 response entity body가 없다면 다음의 단계를 따른다:

    1. 동기적으로 객체의 상태를 LOADING으로 전환한다.

    2. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    마지막으로, 리소스의 다운로드가 완료되면 다음 단계로 이동한다.

  9. 해당 요청이 로딩을 성공적으로 끝내면, 동기적으로 상태를 DONE로 전환하고, 동기적으로 readystatechange 이벤트를 객체에 보낸다. 만약 asyncfalse라면 이 메서드를 리턴한다.

만약 유저 에이전트가 사용자가 프록시를 설정할 수 있도록 허용한다면 꼭(should) 적절히 요청을 수정해야 한다; , origin server 대신 proxy host에 접속하고, Request-Line을 수정하며 Proxy-Authorization 헤더를 지정된 대로 보내야 한다.

만약 유저 에이전트가 HTTP 인증을 지원한다면 꼭(should) 이 객체로부터 발생한 request에 대해 보호된 영역으로 간주해야 한다. 즉, accessed URI를 포함하고 Authorization 헤더를 보내며 401 Unauthorized 요청을 적절히 처리해야한다. 만약 인증이 실패할 경우, 유저 에이전트는 꼭(should) 사용자에게 자격(credentials)에 대해 물어야 한다. [RFC2617]

만약 유저 에이전트가 HTTP State Management를 지원한다면 꼭(should) 적절히 쿠키를 저장하고, 제거하고, 보내야 한다. (Set-CookieSet-Cookie2 response 헤더에 의해 받아지고, Cookie헤더에 의해 보내진다.) [RFC2965]

만약 유저 에이전트가 HTTP cache를 구현한다면 꼭(should) Cache-Control request 헤러들 준수해야 한다. (, Cache-Control: no-cache는 cache를 사용하지 않는다.) 유저 에이전트는 사용자에 의해 명시적으로 요청된 경우가 아니라면 Cache-Control이나 Pragma헤더를 자동으로 보내서는 안된다(must not). (, (강제로-)페이지를 Reloading할 경우). 유저 에이전트의 조건 요청에 따른 304 Not Modified response는 반드시(must) 적절한 콘텐트로 200 OK response처럼 보여져야 한다. 유저 에이전트는 304 Not Modified가 반드시(must) 전달되어야 할때 반드시(must) If-None-Match, If-Modified-Since와 같은 request header를 설정함으로써 자동 캐시 검증 방법보다 높은 우선 순위의 검증 방법을 제공할 수 있어야한다. [RFC2616]

만약 유저 에이전트가 서버 기반 content-nogotiation을 구현한다면 꼭(should) Accept-Language, Accept-EncodingAccept-Charset 헤더를 적절히 세팅해야한다; 자동으로 Accept 헤더를 세팅해서는 안된다(must not). 이러한 request에 대한 response에는 자동으로 디코딩된 content-encoding 들이 반드시must있어야 한다. [RFC2616]

abort(), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다. (다른 방법으로 지시되지 않을 경우):

  1. send() 알고리즘을 취소하고, response entity body를 "null"로 세팅하고, error flag를 "true"로 한 뒤 등록된 모든 request header들을 제거한다.

  2. 유저 에이전트는 꼭(should) 해당 객체가 수행하는 모든 네트워크 활동을 취소해야 한다.

  3. 만약 상태가 UNSENT 또는 OPENED 이고 send() 플래그 가 "false"이거나, 상태가 DONE 히면 다음 단계로 간다.

    그렇지 않을 경우 상태를 DONE으로 바꾸고, send() 플래그를 "false"로 한 뒤, 동기적으로 객체에게 readystatechange 이벤트를 보낸다.

  4. 객체 상태를 UNSENT로 바꾼다. (readystatechange 이벤트를 발생시키지는 않는다.)

    향후 버전의 XMLHttpRequest 객체는 여기에서도 abort 이벤트를 발생시킬 가능성이 높다.

getAllResponseHeaders(), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다:

  1. 만약 상태가 UNSENT 또는OPENED라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. 만약 error flag 가 "true" 라면, null을 리턴하고 종료한다.

  3. 모든 HTTP 헤더를 리턴한다. 이는 단일 문자열로 이루어지며 각각의 헤더 라인은 상태 라인을 제외하고는 U+000D CR U+000A LF 짝으로 분리된다.

// 아래의 스크립트는:
var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
 if(this.readyState == 3) {
  print(this.getAllResponseHeaders());
 }
}

// ...다음과 유사한 텍스트를 출력해야 한다:
Date: Sun, 24 Oct 2004 04:58:38 GMT
Server: Apache/1.3.31 (Unix)
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/plain; charset=utf-8
getResponseHeader(header), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다:

  1. 만약 상태가 UNSENT 또는 OPENED라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. 만약 header 인자가 field-name 형식에 맞지 않는 다면 빈 문자열을 리턴하고 종료한다.

  3. 만약 error 플래그 가 "true"라면 null을 리턴하고 종료한다.

  4. 만약 header 인자가 마지막으로 보낸 request의 헤더들 중 여러 개와 case-insensitively match 한다면, 이 헤더들의 값을 U+002C COMMA와 그 뒤에 U+0020 SPACE로 구별되는 한 개의 문자열로 합쳐서 리턴하고 종료한다.

  5. 만약 header 인자가 마지막으로 보낸 request의 헤더들 중 한 개와 case-insensitively match 한다면, 이 헤더의 값 리턴하고 종료한다.

  6. null을 리턴한다.

// 아래의 스크립트는:
var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
 if(this.readyState == 3) {
  print(client.getResponseHeader("Content-Type"));
 }
}

// ...다음과 같은 문자열을 출력해야 한다:
Content-Type: text/plain; charset=utf-8
responseText of type DOMString, readonly

얻을때, 유저 에이전트는 반드시(must) 다음과 같은 단계를 수행해야 한다:

  1. 만약 상태가 LOADING 또는 DONE이 아니라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. text response entity body를 리턴한다.

responseXML of type Document, readonly

얻을때, 유저 에이전트는 반드시(must) 다음과 같은 단계를 수행해야 한다:

  1. 만약 상태가 DONE이 아니라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. XML response entity body를 리턴한다.

status of type unsigned short, readonly

얻을때, 존재한다면 이는 반드시(must) 서버에서 보내진 HTTP status code를 리턴해야 한다. (보통 성공적인 요청에 대해서는 200 이다). 존재하지 않는 다면 유저 에이전트는 반드시(must) INVALID_STATE_ERR 예외를 발생시켜야 한다.

statusText of type DOMString, readonly

얻을때, 존재한다면 이는 반드시(must) 서버에서 보내진 HTTP status text를 리턴해야 한다. (status code 바로 뒤에 있다). 존재하지 않는다면 유저 에이전트는 반드시(must) INVALID_STATE_ERR 예외를 발생시켜야 한다.

2.1. XMLHttpRequest 객체의 이벤트

이 절은 XMLHttpRequest 인터페이스를 구현한 객체가 보내는 다양한 이벤트에 대해 설명한다. 이 버전의 규격에서는 한 개의 이벤트만 정의되어 있다.

readystatechange
유저 에이전트가 readystatechange 이벤트를 발생시킬 경우에는 (위에서 지시된바와 같이) bubble 하지 말아야 하며(must not), cancelable 하지 않아야 하고(must not), 반드시(must) Event 인터페이스를 구현해야한다. 이의 namespaceURI 속성은 반드시(must) null이어야 한다. [DOM3Events]

2.2. XMLHttpRequest 객체의 예외

이 규격에서 정의된 몇몇 알고리즘은 예외를 발생시킬 수 있다. 이 예외들은 모두 DOM Level 3 Core에서 정의된 ExceptionCode의 일부이며 DOMException 객체를 사용한다. 여기에 더해 본 규격에서는 ExceptionCode 에 아래와 같은 새로운 상수들을 정의한다. [DOM3Core]

const unsigned short SECURITY_ERR = 18;
const unsigned short NETWORK_ERR = 101;
const unsigned short ABORT_ERR = 102;

SECURITY_ERR 예외는 유저 에이전트의 보안 정책에 위배되거나 보안상의 위험이 있는 방식으로 동작을 수행하거나 데이터에 접근할 경우 발생한다.

SECURITY_ERR 예외는 궁극적으로 DOM Level 3 Core 규격의 향후 버전에 동일한 정의와 상수값으로 포함되어야 한다. 그 때까지 구현자에 대한 가이드로서 여기에 정의된다. (이것은 다른 예외들과 다른 상수값을 가지는 이유이기도 하다.)

NETWORK_ERR 예외는 동기적인 요청에서 네트워크 에러가 생겼을때 발생한다.

ABORT_ERR 예외는 동기적인 요청에서 사용자가 요청을 취소했을 때 발생한다.

본 규격에 정의되지 않은 것들

이 절은 비규범적이다.

본 규격은 향후 버전의 규격에서 고려되고 있는 다음과 같은 기능들을 포함하지 않는다:

  • load 이벤트와 onload 속성;
  • error 이벤트와 onerror 속성;
  • progress 이벤트와 onprogress 속성;
  • abort 이벤트와 onabort 속성;
  • 타이머가 제안되었으며, ontimeout 속성이 고려 중;
  • redirect를 막는 속성;
  • text/html 문서에 대한 responseXML;
  • Cross-site XMLHttpRequest;
  • 바이트 스트립을 다루기위한 responseBody;
  • MIME 타입을 수정하기 위한 overrideMimeType;
  • getRequestHeader()removeRequestHeader().

참조

[DOM3Core]
Document Object Model (DOM) Level 3 Core Specification, A. Le Hors, P. Le Hégaret, L. Wood, G. Nicol, J. Robie, M. Champion, S. Byrne, editors. W3C, April 2004.
[DOM3Events]
Document Object Model (DOM) Level 3 Events Specification (work in progress), Björn Höhrmann, editor. W3C, April 2006.
[ECMAScript]
ECMAScript Language Specification, Third Edition. ECMA, December 1999.
[HTML5]
HTML 5 (work in progress), Ian Hickson, editor. WHATWG, 2007.
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels, S. Bradner. IETF, March 1997.
[RFC2616]
Hypertext Transfer Protocol -- HTTP/1.1, R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P. Leach, T. Berners-Lee, editors. IETF, June 1999.
[RFC2617]
HTTP Authentication: Basic and Digest Access Authentication, P. Hallam-Baker, J. Hostetler, S. Lawrence, P. Leach, A. Luotonen, L. Stewart, editors. IETF, June 1999.
[RFC2965]
HTTP State Management Mechanism, D. Kristol, L. Montulli, editors. IETF, October 2000.
[RFC3986]
Uniform Resource Identifier (URI): Generic Syntax, T. Berners-Lee, R. Fielding, L. Masinter, editors. IETF, January 2005.
[RFC3987]
Internationalized Resource Identifiers (IRIs), M. Duerst, M. Suignard, editors. IETF, January 2005.
[Window]
Window Object 1.0 (work in progress), I. Davis, M. Stachowiak, editors. W3C, April 2006.
[XML]
Extensible Markup Language (XML) 1.0 (Fourth Edition), T. Bray, J. Paoli, C. Sperberg-McQueen, E. Maler, F. Yergeau, editors. W3C, September 2006.
[XMLNS]
Namespaces in XML (Second Edition), T. Bray, D. Hollander, A. Layman, R. Tobin, editors. W3C, August 2006.

감사의 말

이 절은 비규범적이다.

The editor would like to thank to the following people who have contributed to this specification (ordered by first name):

  • Ahmed Kamel
  • Alex Hopmann
  • Alex Vincent
  • Alexey Proskuryakov
  • Asbjørn Ulsberg
  • Boris Zbarsky
  • Björn Höhrmann
  • Cameron McCormack
  • Christophe Jolif
  • Charles McCathieNevile
  • Dan Winship
  • David Håsäther
  • Dean Jackson
  • Denis Sureau
  • Doug Schepers
  • Douglas Livingstone
  • Elliotte Harold
  • Eric Lawrence
  • Gideon Cohn
  • Gorm Haug Eriksen
  • Hallvord R. M. Steen
  • Håkon Wium Lie
  • Ian Davis
  • Ian Hickson
  • Ivan Herman
  • Jeff Walden
  • Jens Lindström
  • Jim Deegan
  • Jim Ley
  • Joe Farro
  • Jonas Sicking
  • Julian Reschke
  • Karl Dubost
  • Maciej Stachowiak
  • Magnus Kristiansen
  • Marc Hadley
  • Marcos Caceres
  • Mark Baker
  • Mark Nottingham
  • Mohamed Zergaoui
  • Pawel Glowacki
  • Robin Berjon
  • Ruud Steltenpool
  • Simon Pieters
  • Stewart Brodie
  • Sunava Dutta
  • Zhenbin Xu

Special thanks to the Microsoft employees who first implemented the XMLHttpRequest interface, which was first widely deployed by the Windows Internet Explorer browser.

Special thanks also to the WHATWG for drafting an initial version of this specification in their Web Applications 1.0 document (now renamed to HTML 5). [HTML5]

Thanks also to all those who have helped to improve this specification by sending suggestions and corrections. (Please, keep bugging us with your issues!)

Posted by 1010
반응형
<script type="text/javascript">
 function array_max( )  
 {    
  var i, max = this[0];    
  for (i = 1; i < this.length; i++)    
  {     
   if (max < this[i])      
   max = this[i];    
  }        
  return max;   
 }   
 Array.prototype.max = array_max;   
 var x = new Array(1, 2, 3, 4, 5, 6);   
 var y = x.max( );
 function tt(){
  alert("y : "+y);
 } 
</script>
<input type="button" onclick="tt();">
Posted by 1010
반응형


출처 : http://jsguide.net/ver2/examples/index.php?mode=view&category=5&qstr=&uid=197&page=1


자바스크립트에서는 2바이트 문자를 한글자로 처리하기 때문에 바이트 단위로 잘라야 하거나 검사할때 문제가 많습니다.
그래서 바이트 단위로 길이를 검사하고 자르는 메소드를 추가해봤습니다.

서핑중에 sms 보내는 폼이 나오면... 혹시나하고 소스를 열어보곤 하는데.. ㅇ_ㅇ(힉..)
80바이트가 넘는지 검사하는걸 상당히 복잡하게 코딩해논게 보이더군요..
코드 나갑니다


<script>
 /**
 * string String::cut(int len)
 * 글자를 앞에서부터 원하는 바이트만큼 잘라 리턴합니다.
 * 한글의 경우 2바이트로 계산하며, 글자 중간에서 잘리지 않습니다.
 */
 String.prototype.cut = function(len) {
  var str = this;
  var l = 0;
  for (var i=0; i<str.length; i++) {
   l += (str.charCodeAt(i) > 128) ? 2 : 1;
   if (l > len) return str.substring(0,i) + "...";
  }
  return str;
 }

 /**
 * bool String::bytes(void)
 * 해당스트링의 바이트단위 길이를 리턴합니다. (기존의 length 속성은 2바이트 문자를 한글자로 간주합니다)
 */
 String.prototype.bytes = function() {
  var str = this;
  var l = 0;
  for (var i=0; i<str.length; i++) l += (str.charCodeAt(i) > 128) ? 2 : 1;
  return l;
 }

 aaa = "동a해b물c과 백두산이";
 alert(aaa.cut(15));

 // 또는

 bbb = "너무긴내용너무긴내용너무긴내용";

 alert("length: " + bbb.length + "\nbytes(): " + bbb.bytes());

 //응용

 if (bbb.bytes() > 20) {
  alert("내용이 너무 깁니다");
 } else {
  // 처리
 }
/* 여기서부터는 실행되지는 않습니다 ---

 // 다시응용 (sms폼에서 80바이트 넘는지 처리할경우)

 var sms = document.forms['폼이름'].elements['입력창'];
 if (sms.value.bytes() > 80) {
  alert('80바이트까지만 전송할 수 있습니다');
  sms.value = sms.value.cut(80);
 }
여기까지 실행되지 않습니다 */
</script>

Posted by 1010
반응형

<script type="text/javascript">
<!--
 function ttt(){
  var request_os = window.navigator.appVersion;
   var os_array = request_os.split(";");
  //alert("os_array : "+os_array[2]);
  //alert("request_os : "+request_os);
  //alert(document.cookie);
  switch(os_array[2])
 {
    case " Windows NT 6.1" :
        os_version = "Windows7";
        break;
    case " Windows NT 6.0" :
        os_version = "WindowsVista";
        break;
    case " Windows NT 5.1" :
        os_version = "WindowsXP";
        break;
    case " Windows NT 5.0" :
        os_version = "Windows2000";
        break;
    case " Windows NT 4.0" :
        os_version = "WindowsNT";
        break;
    case " Windows 98" :
        os_version = "Windows98";
        break;
    case " Windows 95" :
        os_version = "Windows95";
        break;
 }
 alert("os_version : "+os_version);
 
 }
-->
</script>

<input type="button" onclick="ttt();">

Posted by 1010
반응형
  • Acegi Security 분석 - Authentication과 Authorization에 대한 기본 개념 : Authentication(인증)과 Authorization(인가)에 대한 기본적인 개념에 대하여 다루며, 엔터프라이즈 애플리케이션 개발에 있어서의 Security Concerns을 4가지로 나뉘어 살펴본다.
  • Acegi Security 분석 - Servlet Filter 설정 및 기본 메커니즘 : 웹 애플리케이션의 경우 Acegi Security를 구현하기 위하여 Servlet Filter를 기반으로 구현하고 있다. 따라서 Servlet Filter에 대한 기본적인 설정 및 기본 메커니즘에 대하여 살펴본다.
  • Acegi Security 분석 - Authentication 기본 : Acegi Security가 지원하고 있는 Authentication의 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Authorization 기본 : Acegi Security가 지원하고 있는 Authorization의 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Acl : Acl 정보를 관리하기 위하여 Acegi Security가 지원하고 있는 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Web URL Authorization : 웹 애플리케이션에서 Web URL에 대한 Authorization을 Acegi Security가 어떻게 지원하고 있는지에 대하여 살펴본다.
  • [Acegi Security 분석 - Service Layer Authorization] : 애플리케이션의 서비스 계층에 대한 Authorization을 지원하는 방법에 대하여 살펴본다.
  • Acegi Security 분석 - Domain object instance Authorization : 애플리케이션의 도메인 모델 클래스에 대한 Authorization을 지원하는 방법에 대하여 살펴본다.

  • 출처 : http://www.javajigi.net/display/SFL/Spring+Security

    Posted by 1010
    반응형

    개발 환경 세팅

    사전 준비물 및 버전

    • JDK 5.0
    • Eclipse 3.2.1
    • MySQL 5.0.x
    • Tomcat 5.5.20

    개발 환경 세팅을 위해 필요한 파일들

    예제 소스

    강의 문서


    •Architecture
    •Spring Introduction
    •Spring IoC 이해 및 활용
    •Spring AOP 이해 및 활용
    •Spring Abstract API
    •Spring Transaction
    •Spring Test


    Spring 2.0

    • Aspect Weaver 사용 : Run As.. >> Arguments >> VM Arguments에 -javaagent:lib/aspectjweaver.jar 를 추가해주어야 한다.
    • 모든 spring 프로젝트에 적용 : Window || Preferences || Java || Installed JREs || 사용하는 JDK의 VM Aguments에 -javaagent:lib/aspectjweaver.jar을 추가한다.
      • 이 경우 모든 프로젝트의 lib 디렉토리 하위에 aspectjweaver.jar가 존재해야한다.

    출처 : http://www.javajigi.net/pages/viewpage.action?pageId=7777

    Posted by 1010
    01.JAVA/Java2009. 12. 22. 17:58
    반응형
    다 알고 있는 내용이지만 복습 차원에서 다시 한번 읽어 보시길... 년차가 오래될수록 기본에 충실해야 할 거 같다.

    출처 :
    http://www.aboutjsp.com/spring/board/viewArticle.do?cpage=1&pgroup=1&boardId=JSPTip&keyfield=contentTitle&keyword=&boardSeq=62

    JAVA를 배우는 사람, 또는 프로그래밍에 종사하시는 개발자 분들이 다시한번 봐도 괜찮을거 같은
    마치 이론 시험 직전 보는 컨닝페이퍼 같이 정리 되어있는 자바의 기본원리 에 관한 자료입니다.
    특히 각 주제에 맞는 목차에는 예제가 있어 JAVA를 입문하시는 초급프로그래머 들에게는 좋은 자료 같습니다.
    그럼 길고긴 스크롤 내려서 꼭 보시길..

    목차

    1 객체지향의 구멍 static
    1.1 Java는 객체지향 언어이다?
    1.2 전역변수
    2 Java는 Pointer언어이다? (Java에는 Pointer밖에 없다?)
    2.1 Java는 primitive형을 제외하곤 모두 Pointer이다
    2.2 null은 객체인가?
    2.3 String에 대하여
    2.4 객체지향의 캡슐화 파괴 주의
    2.5 배열에 대하여
    2.5.1 배열은 object 인가?
    2.5.2 배열의 length는 왜 field(member variable)인가?
    2.5.3 final과 배열에 대하여...
    2.5.4 "Java에서의 다차원 배열은 존재하지 않는다."
    2.6 인수(parameter/argument)전달의 개념
    2.6.1 "Java에서 parameter(argument) 전달은 무조건 'call by value' 이다"
    2.6.2 "C와 같은 언어는 static linking이지만, Java는 dynamic linking이다."
    2.7 GC 에 대하여 잠깐!
    2.7.1 "Garbage Collection은 만능이 아니다."
    2.8 Java Pointer 결론
    2.8.1 "결국 Java에는 pointer가 있는 것인가, 없는 것인가?"
    3 상속과 interface의 문제점
    3.1 상속
    3.1.1 상속에 있어서의 생성자(constructor)
    3.1.2 "down cast는 본질적으로 매우 위험하다"
    3.1.3 "추상클래스에 final이 있으면 compile error이다"
    3.2 interface
    3.2.1 "interface는 interface일뿐 다중 상속의 대용품이 아니다."
    3.3 상속 제대로 사용하기
    4 package와 access 제어에 관한 이해
    4.1 package
    4.1.1 "package는 '계층구조' 인가?"
    4.1.2 "compiler 가 인식하는 class검색 순서(소스코드내 클래스가 발견될 경우 그 클래스의 위치를 찾는 순서)"
    4.2 access 제어
    4.2.1 "interfacde member의 access 제어"
    4.2.2 그렇다면 interface를 다른 package에 대하여 숨기고 싶으면 어떻게 하는가?
    5 기타 Java 기능
    5.1 Thread
    5.1.1 "Multi Thread에서는 흐름은 복수이지만 data는 공유될 수 있다."
    5.1.2 "Thread는 객체와 직교하는 개념이다."
    5.1.3 "Synchronized 의 이해"
    5.1.4 "Thread 사용법의 정석은?"
    5.2 Exception
    5.2.1 "finally 절은 반드시 어떠한 경우에도 실행되는가?"
    5.2.2 "예외의 종류 3가지 (Error, RuntimeException, 그밖의 Exception)"
    5.2.2.1 Error
    5.2.2.2 RuntimeException
    5.2.2.3 그밖의 Exception
    5.2.3 "OutOfMemoryError는 어떻게 처리해야 하는가?"
    5.3 Object Serialize
    5.3.1 "Serialize를 위해서는 marker interface인 java.io.Serializable interface를 implements해야한다."
    5.3.2 "super class는 Serializable이 아닌데 sub class만 Serializable인 경우의 문제점"
    5.3.3 "transient field의 복원(?)관련"
    5.3.4 "Stack Overflow에 주의하라!"
    5.4 "nested class / inner class / 중첩클래스"
    5.4.1 "중첩클래스의 개념"
    5.4.2 "내부클래스는 부모의 참조를 몰래 보유하고 있다."
    5.4.3 "local inner class에 대하여"
    5.4.4 "anonymous class(무명클래스)에 대하여"
    6 이래도 Java가 간단한가?
    6.1 method overload 에서의 혼란?
    6.1.1 "overload란 이름이 가고 인수가 다른 method에 compiler가 다른 이름을 붙이는 기능"
    6.1.2 "그렇다면 overload에서 실제로 혼동되는 부분은 무엇인가?"
    6.1.3 (참고) 또다른 혼동, overload한 method를 override 하면?
    6.2 상속/override/은폐 에서의 복잡함
    6.2.1 "Java class의 member 4 종류"
    6.2.2 "override시 method 이름에 대한 함정"
    6.2.3 "또다른 나의(?) 실수 - 말도 안되는 오타"
    6.2.4 "static member를 instance를 경유하여 참조해서는 안 된다."
    6.2.5 "super keyword는 부모의 this"
    6.3 상속에 관한 또 다른 문제
    6.4 그밖의 함정
    6.4.1 "생성자에 void 를 붙인다면?"
    6.4.2 "if / switch 의 함정"
    7 Java 기능 적용 몇가지
    7.1 대규모 개발에서 interface 분리하기
    7.1.1 "interface 분리의 필요성"
    7.2 Java에서의 열거형
    7.3 Debug write
    8 Java 5.0 Tiger 에 대하여
    8.1 Working with java.util.Arrays
    8.2 Using java.util.Queue interface
    8.3 java.lang.StringBuilder 사용하기
    8.4 Using Type-Safe Lists
    8.5 Writing Generic Types
    8.6 새로운 static final enum
    8.7 Using java.util.EnumMap
    8.8 Using java.util.EnumSet
    8.9 Convert Primitives to Wrapper Types
    8.10 Method Overload resolution in AutoBoxing
    8.11 가변적인 argument 개수 ...
    8.12 The Three Standard Annotation
    8.13 Creating Custom Annotation Types
    9 The for/in Statement
    9.1 for/in 의 자주 사용되는 형태
    10 Static Import
    10.1 static member/method import
    11 References

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

    1 객체지향의 구멍 static #

    1.1 Java는 객체지향 언어이다? #

    "Java는 완전한 객체지향 언어이다" 라는 주장을 자주 접하게 된다. 만일 이것이 사실이라면 Java를 사용하는 한 "기존의 절차지향 프로그래밍을 전혀 할수 없을것 같지만 그렇지 않다. 빠져나갈 구멍이 있는 것이다. static을 이용하면 비 객체지향 언어처럼 코딩할 수 있다.

    static method는 instance가 아닌 클래스에 속하는 method로, class method라고 부른다. 반대로 static이 아닌 method는 instance method라고 부른다.

    static method는 this가 없다. instance method에는 숨겨진 파라미터로 this가 건네진다. (아래 "객체지향에 흔희 있는 오해" 참고) 하지만 static method는 절차지향의 함수와 동일하므로 숨겨진 파라미터 this는 없다. 그래서 static method에서는 전달한 this가 없으므로 instance method를 호출하거나 instance field를 참조할 수 없는 것이다.

    (참고) 객체지향에 흔히 있는 오해

    • 오해1. "객체지향에서는 객체끼리 서로 메세지를 주고 받으며 동작한다." 라는 말을 듣고 다음과 같이 생각할 수 있다. "객체지향에서는 객체가 각각 독립하여 움직인다는 것인가, 그러면 각 객체에 독립된 thread가 할당되어 있단 말인가?" 그렇지 않다. "메세지를 보낸다"라는 것은 단순히 각 객체의 함수 호출에 불과하다.
    • 오해2. "객체지향에서는 method가 class에 부속되어 있다"는 말을 듣고 다음과 같이 생각할 수 있다. "그러면 instance별로 method의 실행코드가 복제되고 있는 것이 아닌가?" 물론 이것도 오해다. method의 실행코드는 종래의 함수와 동일한 어딘가 다른곳(JVM의 class area)에 존재하며 그 첫번째 파라미터로 객체의 포인터 this가 건네질 뿐이다.
    • 오해3. "그렇다면 각 instance가 method의 실행코드를 통째로 갖고 있지 않는 것은 확실하지만, method의 실행 코드의 포인터는 각 instance별로 보관하고 있는것이 아닌가?" 이것은 약가 애매한 오해이긴 하다. JVM 스펙에서는 class영역에 실행코드를 갖고 있으며, method 호출시 별도의 stack frame이 생성되어 실행되고 실행 완료시 복귀 주소를 전달한다.

    1.2 전역변수 #

    static에서 public field는 전역변수(global variable, 글로벌 변수)이다. 여기서 "글로벌 변수는 왜 안 되는가"에 대해 잠깐 생각해 본다. 우리는 흔히 "글로벌 변수는 될수있는한 사용하지 않는 것이 좋다"라고 한다. 그 이유는 글로벌 변수는 어디서든 참조할 수 있고 값을 변경할 수 있기 때문이다.

    또한 파라미터나 리턴값으로 교환해야 할 정보를 글로별 변수를 경유(사용)하여 건네주면 함수의 역할이 불분명 해지고 흐름도 애매해 진다. 마지막 이유로는 "글로벌 변수는 하나밖에 없다"는 것이다. 이는 어디서 이값을 변경했는지 알 수 없게 하는 지름길이고 실무에서도 간혹 발생하긴 하지만, 이 하나밖에 없는 변수가 버전업으로 두개가 필요하게 되었을때 확장도 대형 프로젝트에서는 힘들어진다.

    따라서 static에서 public은 final을 붙여 상수로 사용해야지 그 외의 용도는 자제하는 것이 좋을 것이다.

    • (참고) final 초기화에서의 주의점. 예를 들어 다음과 같은 코드를 보았을때 우려되는 점은 무엇인가?
    public final static Color WHITE = new Color(255, 255, 255);

    위의 코드는 java.awt.Color에서 발췌한 것인데, final 변수는 한번 초기화 되면 변경이 불가능한데 object로 초기화 할 경우 WHITE라는 필드가 변경될 수 없는 것이지 그것이 가리키는 객체는 아니라는 점이다.

    과거 신규 서비스 개발시 final 변수 필드에 설정파일을 읽어 cache하는 singleton class의 특정 member 를 이용하여 초기화 할 경우 이 멤버값이 변경되면 final 변수의 값이 변경되었는데 프로그램에서는 이상한 짓을 하는 원인을 찾기가 상당히 어려웠던 경험을 하고 난 후 부터 이런 코드는 냄새나는 코드로 여겨지게 되었다.

    static은 글로벌변수와 동일하므로 남발해서는 안된다. static을 사용할 경우 다음 두가지는 최소한 기억한다.

    1. static field는 final의 경우와 달리 정말 "하나여도 되는지" 여부를 잘 생각해야 한다.
    2. static method는 주저하지 말고 쓰되 다음 두가지의 경우 매우 활용적이다.
      1. 다른 많은 클래스에서 사용하는 Utility Method 군을 만드는 경우. (주로 Utility Class의 method)
      2. 클래스 안에서만 사용하는 "하청 메소드(private method)". 이유를 예를 들어 설명하면, 아래와 같은 조금은 과장된 클래스가 있다고 하자.
    public class T ..
                        private int a;
                        private int b;
                        private int c;
                        
                        private int calc(){
                            c = a + b;
                            return c * c;
                        }
       
                        ....other method or getter/setter...

    위의 클래스 T의 경우 내부에서 calc라는 instance 함수를 사용하게 되면 c 의 값이 매번 변하게 된다. 이는 무심코 하는 실수로 클래스내에서 private method는 모든 멤버 instance 변수에 접근 가능하게 되면서 발생한다. c의 값이 변하지 않기를 바랄수 있다. 이때 안전한 방법은 다음과 같이 calc 하청 메소드를 static method로 수정하면 안전하다.

    private static int calc(int a, int b){
                   int c = a + b;
                   return c * c;
                }

    여기서 a와 b는 멤버 변수를 접근할수 없어 전달해야한다.(static에는 this가 없어 instance field를 참조할 수 없다는 것은 이미 위에서 설명했다.) 또한 c도 같은 이유로 사용할 수 없어 로컬 변수로 선언하고 사용하고 있다. 이럴 경우 메소드가 약간 커질수 있지만 instance member 변수를 안전하게 사용할 수 있다는 장점이 있다. 이것은 static을 다시한번 생각하게 하는 좋은 예가 되었을 것이다.
     

    2 Java는 Pointer언어이다? (Java에는 Pointer밖에 없다?) #

    2.1 Java는 primitive형을 제외하곤 모두 Pointer이다 #

    "Java에는 포인터가 없다" 라고 Java의 장점 이라고 생각하는 것은 입문자도 외우고 있다. 하지만 이 부분은 의외로 Java를 혼란스럽게 하는 주범이라고 생각한다. Java에 포인터가 없기는 커녕 primitive(int, short, char, long...등 JVM의 Heap에 object로 생성되지 않는것들)를 제외하면 "포인터 밖에 없는 언어이다"라는 명제가 성립되게 된다. 사실 여기서 포인터라고 함은 C 의 그것과는 조금 다른 reference(참조)이긴 하지만...

    "즉, 자바의 클래스형의 변수는 모두 포인터이다."
     

    2.2 null은 객체인가? #

    Java에서 공참조(힙에 실제로 참조되는 object가 없는 참조)의 경우는 당연히 객체가 붙어 있지 않다. 그러나, Java API 레퍼런스의 NullPointerException 항에는 다음과 같이 기술되어 있다.

    "object가 필요한 경우 application이 null을 사용하려고 하면 throw된다. 가령 다음과 같은 경우이다."
    • null object의 instance method 호출
    • null object의 field(member variables)에 대한 액세스 또는 그 값의 변경
    • null의 길이를 배열처럼 취득할 경우
    • null의 slot을 배열처럼 액세스 또는 수정
    • null을 Throwable처럼 throw 할 경우
    위에서 null object라는 말이 등장하는데 이는 공참조에 객체가 붙어 있지 않은 것이 아니라 null을 가리키는 객체라고 볼수 있다. 즉, 공참조라는 것은 JVM에서 봤을때 아무것도 참조하지 않는것이 아니라 null이라고 하는 object를 참조하고 있는것이다. 그러나 JSL 4.3.1에서는 다음과 같이 나와있다.

    "참조값(reference)은 이러한 객체의 포인터나 어떤 객체도 참조하지 않는 특수한 null참조가 된다"

    즉, 공참조는 어떤 객체도 참조하지 않는다고 단정하고 있다. 하지만 '==' 연산에 있어 두개의 객체가 모두 null이거나 동일한 객체 또는 배열 참조의 경우 true라고 되어있는것으로 봐서 서로 다른 두 객체가 동일한 null을 참조하고 있으므로 true가 된것이 아닌가 하는 생각을 할 수 있다.

    즉, null이 Object의 instance 형태는 아니지만 개념적으로 봤을때 null도 object라고 봐야 하지 않을까?
     

    2.3 String에 대하여 #

    String Object에 대한 생각.

    String str = "111222";
                String a = "111";
                String b = "222";
                String c = "111";
                String d = b;
                String t = str.substring(0,3);  //111

    위의 소스를 보고 다음이 참인지 생각해 보자. (==연산자는 포인터의 비교이지 값의 비교가 아님)

    1. str == (a + b) ==> 이것은 두개의 참조와 하나의 참조를 비교했으므로 당연히 false이다.
    2. a == b ==> 이것은 당연히 false
    3. d == b ==> 이것은 동일한 reference이므로 true
    4. a == t ==> a 와 t 는 둘다 값이 "111"이다. 하지만 이것은 서로 다른 참조를 가져 false이다. 그렇다면 다음 5번도 false일까?
    5. a == c ==> 이것은 true이다. 아.. 4번과 혼란스럽다. 이것이 참인 이유는? ==> 이것의 해답은 JSR 3.10.5에 다음과 같이 나와 있기 때문이다.
    "동일한 내용의 문자열 리터럴에 관해서는 인스턴스를 하나밖에 생성하지 않는다."

    즉, 위의 a와 c 는 '=' 를 이용하여 문자열 리터럴을 생성하게 되는데 a 에서 이미 만들어 놓았으므로 c에서는 그것을 참조한다.
     

    2.4 객체지향의 캡슐화 파괴 주의 #

    "object pointer를 반환하는 getter method는 객체지향의 캡슐화가 파괴될 가능성이 있다." 이는 object형의 field(member variable)의 getter에서 이 object를 그냥 반환하면 이 object를 받은쪽이나 참조하고 있는 다른쪽에서 이 object의 내용을 변경하게 되므로 사실 캡슐화(은닉)는 이루어 지지 않았다고 봐야한다.

    "이럴 경우 object를 clone(복제) 하여 반환하지 않아도 되는지를 반드시 생각해 본다."

    object의 복사에는 shallow copy와 deep copy가 있다.

    //(참고)Member에는 두개의 field(Identity Class 형의 ID와 Family Class 형의 family)가 있다. 
            
            /** shallow copy */
            Member shallowCopy(){
                Member newer = new Member();
                newer.id = this.id;
                newer.family = this.family;
                
                return newer;
            }     
            
            /** deep copy */
            Member deepCopy(){
                Member newer = new Member();
                newer.id = new Idetity(this.id.getId(), this.id.getName());
                newer.family = new Family(this.family.getFamilyName(), this.family.getFamilyInfo());
                
                return newer;
            }

    위 소스에서 보듯이 shallowCopy 는 object를 복사하여 반환한것 처럼 보이지만, 사실은 Member object만 새로 생성되었을뿐 Member의 field는 newer와 this 둘다 서로같은 힙의 id와 family를 참조한다. 하지만 두번째 method인 deepCopy의 경우 Member의 field를 새로 생성하여 복사하므로 서로 다른 id와 family이다.

    "Java에서는 clone이라는 method가 준비되어 사용되는데 이는 기본이 shallow copy임을 명심해야 한다. deep copy를 사용하기 위해서는 clone method를 overload하거나 따로 만들어 직접 기술해야 한다."
     
    (참고) object를 immutable(변하지 않는, 불변의 객체)로 만드는 요령
    1. 모든 field(member variable)를 생성자(constructor)를 이용하여 초기화 한다.
    2. 모든 field는 private으로 선언하고, getter method는 만들되 setter는 기술하지 않는다.
    즉, 값을 변경하기 위해서는 object를 다시 만들어야만 하는 불편은 있지만 안전하게 사용하려 할때 유용하다.
     

    2.5 배열에 대하여 #

    2.5.1 배열은 object 인가? #

    JVM에서 배열은 object로 취급되어 object와 같이 aload, astore와 같이 bytecode로 기술되어 진다. int[] iarr = new int10; 에서 보는것과 같이 new로 Heap 영역에 object를 생성하므로 object임을 알 수 있다.
     

    2.5.2 배열의 length는 왜 field(member variable)인가? #

    String의 길이를 구할때는 length()와 같이 method를 이용하는데 배열은 object임에도 불구하고 legth와 같이 필드로 되어있다. '이래도 Java가 완전한 객체지향 언어인가' 라는 의심이 들게 한다. 그렇다면 length가 public이므로 array.length = 100; 과 같이 하면 배열 크기가 변경되나?

    이것은 컴파일 오류가 난다. length는 final이라 값을 변경 할 수 없다는 것이다. 그렇다면 final field로 한 이유는 무엇이냐는 Java News Group에 찾아보면 대부분이 "효율을 위해서"라고 되어 있다. JIT compiler를 사용하지 않는한은 method보다는 field가 빠른건 당연한 것이다.

    그런데 정말 알수 없는것은 byte code에서는 arraylength라는 전용명령으로 컴파일 된다. 즉, length는 Java의 문법이 어찌되었든 JVM레벨에서는 field가 아닌것이 분명하다. 그렇다면 효율을 위해서 field로 했다는 것은 도데체 무슨 소리인가?

    전문가들의 대답에는 이것은 Java의 수수께끼 중 하나라고 대답하는 사람이 많다고 한다.^^;

    2.5.3 final과 배열에 대하여... #

    우리가 흔희 앞에서도 나온바 있지만 final은 값을 변경할 수 없는 것이라고만 생각하지 object로 되어 있을 경우 그 object는 변경 가능하다는 것을 잊곤한다. 배열도 object이므로 마찬가지다.

    final int[] iarr = new int[5]; 일경우 iarr = null; 은 에러가 나지만 iarr[3] = 5; 는 에러가 나지 않는다. 즉, final이 지정되어 있는것은 iarr이지 iarr이 가리키는 곳 배열의 요소가 아닌 것이다.
     

    2.5.4 "Java에서의 다차원 배열은 존재하지 않는다." #

    가령 2차원 배열 처럼 보이는 int[][] iarr 또는 int[] iarr[] 은 일차원 배열 두개이지 2차원 행열 구조가 아닌것이다. 즉, 두개의 배열은 각각이 배열로 되어 있는 것이지 테이블(행열)형태가 아니다.

    2.6 인수(parameter/argument)전달의 개념 #

    2.6.1 "Java에서 parameter(argument) 전달은 무조건 'call by value' 이다" #

    primitive type의 경우 호출한 쪽의 변수값은 호출 받은 method내에서 값이 변경되어도 변경되지 않는다. reference type의 경우도 reference되는 object에 대해서는 함께 변경되지만 reference pointer는 call by value이다. object를 가리키는 pointer는 call by value로 변경되지만 Heap의 실제 object내용은 변경되지 않는다.
     

    2.6.2 "C와 같은 언어는 static linking이지만, Java는 dynamic linking이다." #

    따라서 Java는 Class 파일이 처음에 한꺼번에 memory에 읽혀지는 것이 아니라 런타임시에 그것이 필요해 졌을때 읽혀지고 링킹된다. static field의 영역도 Class가 읽혀지는 시점에 비로서 확보된다. 이렇게 되면 최초 가동시간이 단축되고 끝까지 사용하지 않는 Class의 경우 신경쓸 필요가 없어지게 된다.

    따라서 static field는 프로그램이 시작되어 해당 Class가 필요해 졌을때 JVM이 알아서 load/link 해 준다. 즉, static field는 프로그램이 실행되기 시작할 때부터 끝날때까지 계속해서 존재하는 것이라고 보면 된다.
     
    (참고) 링킹(linking)의 의미

    link된다는 것은 Class가 memory에 loading될 때 특정 메모리 번지에 loading되는데 이 메모리 번지는 loading될때 마다 다른 번지수에 loading된다. 이때의 메모리 주소값(Java에서는 실제 메모리 값이 아닐 수 있다)을 현재 실행중인 프로그램에서 알 수 있도록 하여 해당 Class에 대한 참조가 가능하도록 연결하는 과정이다.

    정적(static) link라는 것은 이러한 메모리에 대한 주소 정보를 컴파일 시에 compiler가 미리 결정하는 것이고, 동적(dynamic) link라는 것은 프로그램 수행 중 결정되는 것을 의미한다. 정적인 link의 경우 직접적으로 메모리의 번지값이 할당 되는 것이 아니라 offset값(기준위치로 부터의 index값)으로 연결시킨다.

    2.7 GC 에 대하여 잠깐! #

    2.7.1 "Garbage Collection은 만능이 아니다." #

    Java에는 free가 없다. GC가 알아서 해준다. 하지만 GC 수행중에는 프로그램이 멈추는 것과 동일한 증상이 나타나기 때문에 GC가 자주 발생하지 않도록 프로그램 해야 한다. 서비스 되고 있는 시스템에서도 가끔 시스템이 응답이 늦어지는 시점이 있는데, 이는 GC가 수행되고 있는 중이 대부분이다.

    그렇다면 GC가 자주 발생하지 않도록 해야 하는데 가장좋은 방법은 무엇일까? 그것은 바로 불필요한 객체를 생성하지 않는 것이 아닐까?

    개인적으로 Java에 free가 없는것이 너무나 든든하게 느껴진다. 이유는 두개의 변수가 Heap내의 하나의 object를 reference하고 있을 경우 실수로 하나의 변수만 free해 버리면 나머지 하나는 dangling pointer라하여 reference pointer가 모르는 사이데 사라져 버려 곤경에 처하는 것을 예방해 주기 때문이다.

    참고로 Object class에는 finalizer라는 method가 있어 GC 수행시점에 호출되는 method가 있지만 이것은 GC가 언제 수행될지 알 수 없으므로 과신하지 말아야 할 것이다.

    2.8 Java Pointer 결론 #

    2.8.1 "결국 Java에는 pointer가 있는 것인가, 없는 것인가?" #

    Java는 Heap내의 Object를 참조(reference)하고 있고, 참조는 결국 개념이 포인터와 같은 것이므로, "Java에는 pointer가 없다"는 것은 어불성설이다.
    // 이부분에 대해 Object를 이해하시면 족히 이런 문제는 사라질것으로 봅니다.
    // 클래스에 대한 인스턴스(object)들은 reference로 밖에 가질(참조될)수 없기 때문입니다.
    // 컴파일러 입장이 아닌 언어 자체의 사상을 가지고 쉽게 이해시키는 것이 좋을것 같습니다.

    JSR 4.3.1을 보면 다음과 같은 말이 나온다.

    "참조값(reference)은 객체의 pointer이거나, 또는 어떠한 객체도 참조하지 않는 특수한 null 참조가 된다"

    또한 java.sun.com의 Java programmer's FAQ에 "Java는 pointer가 없다고 하는데, linked list는 어떻게 만들어야 하나?"라는 질문에 다음과 같이 답변이 나와있다.

    (답변) Java에 관한 많은 오해중에서 이것이 가장 심각한 것이다. 포인터가 없기는 커녕 Java에 있어 객체지향 프로그래밍은 오로지 pointer에 의해 행해진다. 다시 말새 객체는 항상 포인터를 경유해서만 access되며 결코 직접적으로 access되지 않는다. pointer는 reference(참조)라고 불리며 당신을 위해 자동으로 참조된다.

    "Java에는 pointer가 없고 주장하는 모든 서적과 글들은 Java의 reference사양에 모순된다고 봐야 할 것이다."

    3 상속과 interface의 문제점 #

    3.1 상속 #

    3.1.1 상속에 있어서의 생성자(constructor) #

    "child의 default 생성자가 있으면 그 생성자에는 parent의 생성자(super()) 호출이 compile시 자동 삽입된다." 따라서 super() 이전에 다른 코드가 있으면 object가 생성되기 이전이므로 오류를 발생하게 된다.

    3.1.2 "down cast는 본질적으로 매우 위험하다" #

    down cast - child의 type으로 parent를 casting - 는 parent 형태의 type이 정말 child type인지 compile시에는 알 수 없다. 실행시에 type check가 이루어 지므로 runtime시에 ClassCastException이 발생할 가능성이 커진다.

    "프로그래밍시 오류는 가능한한 compile시에 처리하는것이 좋다."

    3.1.3 "추상클래스에 final이 있으면 compile error이다" #

    abstract method가 있는 클래스는 추상 클래스이고 추상클래스는 상속되지 않으면 아무런 의미가 없기 때문이다.

    3.2 interface #

    3.2.1 "interface는 interface일뿐 다중 상속의 대용품이 아니다." #

    interface를 method signature - 추상클래스와 같이 구현부는 없이 선언부만 있는 method - 의 용도로 생각하는것이 Java에서는 옳다. 즉, interface는 final field와 abstract method가 있는 클래스와 동일하긴 하지만 상속의 의미와는 그 용도가 다르다. 공통된 type을 정의하는것으로 보는것이 맞는 의미일 것이다.

    또한 interface는 클래스를 재이용하기 위해 상속을 사용하여 캡슐화의 파괴를 수반하는 것을 방지하는 기능이있다. 상속을 사용하면 모두 구현후 마치 소스 코드가 여기저기 천 조각을 주워 모아 만든 '누더기'같이 보이는 것에 한숨을 쉰 경험이 있을 것이다. 이 부분을 interface로 구현하면 보다 깔끔한 코드가 나오게 된다. 물론 public과 protected를 적절히 잘 사용해도 되긴 하지만 말이다.

    하지만 상속은 메소드 오버라이드한 경우 클래스를 마음대로 개조해 버린 셈이 되므로 어디선가 묘한 모순이 발생하게 될 가능성도 높아질뿐 아니라 추상클래스의 경우 실제 구현부가 어디에 위치하는지도 에매하게 느껴질 수 있어 불안한 코드가 되고 만다.
     

    3.3 상속 제대로 사용하기 #

    "그렇다면 제대로 된 상속은 어떻게 판단할 수 있을까?"

    상속은 'is a'관계가 성립해야 올바르다. 즉 '서브클래스(자식) is a 슈퍼클래스(부모)'가 성립해야 한다. 예를 들면 Red is a Color는 올바른 명제이지만 Engine is a Car는 'has a'관계이므로 상속이라고 볼 수 없다.
     
    "따라서 'has a'관계는 상속이 아니므로 composition과 delegation을 이용하면 된다."

    composition은 '객체를 field가 갖게 하는 방법'을 의하므로 'has a'관계가 정확히 성립한다.
     
    "상속 대신 composition과 delegation(조작이나 처리를 다른 객체에 위임)을 사용하면 다음과 같은 이점이 있다."

    1. 상속에서는 슈퍼클래스가 허용하고 있는 조작을 서브클래스에서 모두 허용해야 하지만, composition과 delegation에서는 조작을 제한할 수 있다.
    2. 클래스는 결코 변경할 수 없지만, composition하고 있는 객체는 자유롭게 변경할 수 있다. 예를 들면 학생 클래스가 영원이 학생이 아니라 나중에 취직을 하여 직장인 클래스가 될수 있다.
       
    상속을 composition과 delegation으로 변경하는 요령은? 여기서 Shape를 상속한 Polyline과 Circle을 변경한다면 다음과 같다.
    1. Shape(부모)의 공통된 내용을 구현한 구현 클래스(ShapeImpl)를 만든다.
    2. Polyline과 Circle 클래스에서 ShapeImpl을 composition하고 부모와 공통되지 않는 method를 각각 위임 받는다.
    3. ShapeImpl 클래스의 method를 추출한 ShapeIF interface를 작성하고 Polyline과 Circle에서는 implements 한다.

    4 package와 access 제어에 관한 이해 #

    4.1 package #

    4.1.1 "package는 '계층구조' 인가?" #

    처음 Java를 접하면서 package에 대해 이해할때 마치 파일시스템과 같은 계층구조라고 이해하게 되어 ' import /test/*.class '는 왜 안되는지 의아해 했던 기억이 있다. 그리고 부모 directory에 있는 클래스에서 왜 자식 directory에 있는 Class를 import없이 사용할 수 없는지도 이상했다.

    즉, package에서 동일 부모라도 서로 다른 package는 완전히 별개의 package였던 것이다. 이 부분에 관해서는 JLS 7.1 에서 다음과 같이 기술되어 있다고 한다.

    "package가 계층적인 이름 구조로 되어 있는 것은 관련된 package를 일정 규약에 따라 체계화하기 위해서이고, package 안에서 선언되어 있는 top-level형과 동일한 이름을 가진 서브 package를 갖는 것이 금지되어 있는 점을 제외하면 특별한 의미는 없다."

    즉, Java에서는 package이름을 계층적으로 명명할 수 있을뿐 package구조 자체에는 어떤 계층적인 의미 부여도 할 수 없는 것이다. 다시 말해서 Java에서는 package이릉을 계층적으로 명명할 수 있을 뿐 구조자체는 평평한 것이다. 실제로 바이트 코드의 내용을 보면 깨어진 내용중에 java/lang/String과 같이 완전한 한정이름을 class이름으로 사용됨을 알 수 있다.
     

    4.1.2 "compiler 가 인식하는 class검색 순서(소스코드내 클래스가 발견될 경우 그 클래스의 위치를 찾는 순서)" #

    1. 그 class자신
    2. 단일형식으로 임포트된 class
    3. 동일한 패키지에 존재하는 다른 class
    4. 온디멘드 형식(..* 형태) 임포트 선언된 class

    4.2 access 제어 #

    public은 다른 package에서 참조 가능하고, 무지정할 경우 동일한 package내에서만 참조 가능하다.
     

    4.2.1 "interfacde member의 access 제어" #

    interface의 member field/method는 모두 public이다. interface member에 protected나 private을 지정할 수는 없다. 또한 public을 지정할 필요도 없다. 지정해도 상관없지만 JLS 9.4에서는 다음과 같이 명시되어 있다.

    "interface의 method에 대하여 public 수식자를 지정하는 것이 허용되고 있지만, style로서는 전혀 권장할 수 없다."

    즉, interface member는 모두 public이라 되어 있는 것이다. 또한 James Gosling도 집필에 참가한 '프로그래밍 언어 Java 3판'에서는 다음과 같이 기술되어 있다고 한다.

    "public이 아닌 member를 interface에게 갖게 하는 것은 아무런 의미가 없다. interface의 member에 대한 access제어에 interface 자신의 access 제한을 적용하는 것이므로 이것은 아무런 의미가 없다."

    4.2.2 그렇다면 interface를 다른 package에 대하여 숨기고 싶으면 어떻게 하는가? #

    그것은 interface 자체 선언에 public이 아닌 것을 적용하면 되는 것이다. member별로 제어할 수 없어 불편한 면도 있지만, 나름대로 그럴 듯한 규칙이다. 하지만 이것은 정말 이상한 논리가 아닐수 없다. public이 아닌 interface에 public method가 무슨 의미가 있는지 알 수 없기 때문이다. 이 interface를 구현한 클래스에서도 method는 모두 public이 되어야 하는데, 이것도 아무래도 이상하다.

    5 기타 Java 기능 #

    5.1 Thread #

    5.1.1 "Multi Thread에서는 흐름은 복수이지만 data는 공유될 수 있다." #

    Multi processing에서는 흐름은 복수이지만 data는 독립되어 있다. 하지만 Multi Thread에서는 Heap과 static영역에 관한 한 2개 이상의 Thread 사이에 공유가 이루어 진다. 따라서 2개 이상의 Thread에서는 동일한 static field를 참조할 수 있고, 동일한 객체에 접근할 수도 있다. 그러나 stack만은 Thread별로 독립되어 있다. stack은 method에 들어가는 시점에 확보되고 빠져 나오는 시점에 확보되고 빠져 나오는 시점에 Free 되므로 2개 이상의 Thread에서 공유할 수는 없는 것이다.
     

    5.1.2 "Thread는 객체와 직교하는 개념이다." #

    Multi Thread는 어디까지나 Thread라는 처리 흐름이 여러 개 존재할 수 있다는 의미이다. 요약하면 다음 3가지 이다.
    1. Multi Thread에서는 Thread라는 처리 흐름이 2개 이상 존재할 수 있다.
    2. 어떤 Thread에서 움직이기 시작한 method가 다른 method를 호출 했을때 호출된 측의 method는 호출한 측의 method와 동일한 Thread에서 동작한다.
    3. Thread의 경계와 객체의 경계는 전혀 관계가 없다. 즉, Thread와 객체는 직교하고 있다.
       

    5.1.3 "Synchronized 의 이해" #

    Multi Thread 기반의 programming시에 synchronized를 놓쳐 자주는 일어나지 않으나 뭔가 잘못되어 가는것을 경험한 적이 있다. 즉, 이것이 원인이 되어 버그가 발생한 경우 그 버그는 재현성이 지극히 낮아지기 때문에 꽤 고생을 하게 된다. 이런 사태가 발생하게 되면 버그의 원인을 찾기가 어렵게 되고 해당 application은 언제 발생할지도 모르는 오류가 있는 상태 그대로 운영되기 때문에 심각성이 내포되어 있다고 할 수 있다.

    이러한 사태를 방지하기 위해서는 critical section을 2개 이상의 Thread가 동시에 실행되지 않도록 '배타 제어'를 해야한다. 그 키워드가 바로 synchronized이다.

    synchronized에는 synchronized(obj){} 형태와 method에 synchronized 를 붙이는 두가지 방법이 있는데, 이 둘은 범위만 같다면 같은 의미이다. 예를 들어 설명하면, 아래의 소스에서 method1()과 method2()는 동일하다.

    synchronized void method1(){
                ...
            }
            
            void method2(){
                synchronized(this){
                    ...
                }
            }

    이렇게 동일한 의미를 두가지로 만든것은 method단위로 synchronized를 걸 일이 그만큼 많다는 것을 의미한다. 많이들 오해하고 있는 부분이 위의 소스에서 알수 있듯이 method에 synchronized를 사용한다는 것은 '그 객체에 해한 조작은 동시에 하나의 Thread라는 것이지 method 호출이 하나의 Thread가 아닌것이다'

    그렇다면, Thread A가 obj의 Lock을 설정하고 있는 상태에서 다시 한번 Thread A 자신이 obj의 Lock을 설정하면 어떻게 될까? 이 경우 Thread A는 이미 이 obj에 대하여 Lock을 보유하고 있으므로 기다리지는 않아도 된다. 위의 소스에서 method1에서 method2를 호출한다면?

    method1에서 이미 obj의 Lock을 보유 했으므로 method2의 synchronized(this) 부분에서는 Lock을 기다리지 않아도 된다.

    즉, Lock의 기준이 특정Thread에 있어서 Lock의 기준이 method가 아닌 object인 것이다. 이 규칙 덕분에 synchronized method도 재귀호출이 가능해지고, synchronized method가 동일한 instance의 synchronized method를 호출할 수 있는 것이다.

    주의할 점은 static method에 synchronized가 있다면 static은 this참조가 없다고 위에서 설명하였으므로, 이 클래스의 Class 객체를 Lock하게 된다. 기준이 xx.Class가 되는 것이다.
     

    5.1.4 "Thread 사용법의 정석은?" #

    Thread 사용법에는 다음 두가지의 정석이 있다.
    1. Runnable을 implements하고 Thread의 참조를 보유(composition) 하는 방법. 이경우는 단지 Runnable만 implement함으로서 해결되는 경우가 대부분이긴 하지만, 그 class 내에서 해당 class의 Thread를 조작하게 된다면 composition한 Thread 객체에 delegation하면 된기 때문이다.
    2. Thread class를 상속하는 방법. JDK의 소스를 보면 Thread class에는 Runnable을 implements 하고 있다. 그리고 run method는 native method이다. 따라서 Thread를 상속한 모든 클래스는 사실 Runnable을 implements하고 있는 것이다. run method는 abstract가 아니므로 구현되어 있고 우리는 이를 오버라이드하여 사용하고 있다. 이 방식을 사용하면 Thread의 method를 안팍으로 자유롭게 호출할 수 이지만, 이미 다른 class를 상속하고 있다면 이 방식을 사용할 수는 없다.
       
    JDK API Reference의 Runnable에 과한 설명중에 다음과 같은 내용이 있다.

    "Thread class의 method중 run method만을 오버라이드하여 사용하는 경우는 Runnable interface만 implements하여 사용하면 된다. 왜냐하면, class의 기본적인 동작을 수정 또는 확장하지 않는한 그 class를 sub class화 하는 것은 바람직하지 않기 때문이다."

    그렇다면 위에서 언제나 1)번 방식을 사용하면 되는 것 아닌가 라는 의문이 생기게 된다. 왜 귀찮게 2)의 방법을 고민하는 것인가, 극단적이긴 하지만 만일에 사태에 이 클래스가 다른 클래스를 상속받게 되는 경우도 있을수 있는데.

    하지만 이것은 아닐것이다. 만약 이렇다면 Thread class가 Runnable을 implements할 필요가 없었을 것이기 때문이다. 또한 Thread는 생성자의 인수로 Runnable의 reference를 취득한 후 계속해서 그것을 보유한다는 것도 이상하다. Thread에 있어 Runnable이 필요한 것은 start() 때 뿐이므로 start()의 인수로 Runnable을 건네줘도 좋을 것이다.

    그럼에도 불구하고 굳이 Thread에서 계속적으로 Runnable을 보유하고 있는 것은 Runnable객체와 Thread를 강하게 결합시키려는 의도 때문이다. 이것은 의도적으로 위의 2)의 방법을 권장하는 듯한 느낌을 받게 하는듯 하다.

    그렇다면 API Reference의 말은 단지 상속을 피하라는 의미만 있는 것인가? 마지막으로 한가지 추정이 되는 부분은 Thread에는 suspend()나 stop()등과 같은 method가 현재 모두 deprecate되었다. 또한 sleep()이나 yield()는 모두 static method이므로 굳이 Thread 객체를 보유할 필요가 없다.

    그렇다면 위의 1)의 방법에서 Thread객체를 composition할 필요가 없어진다.

    "그렇다면 Thread를 아무도 보유하지 않고 Runnable만 implements한 방식이 최선인가?"

    무엇이 정답인지 도무지 알길이 없다. ^^;
     

    5.2 Exception #

    5.2.1 "finally 절은 반드시 어떠한 경우에도 실행되는가?" #

    try ~ catch 문의 finally 절은 'loop라면 break, method라면 return 절'을 만나도 뒤에 있는 finally절은 수행된다. 하지만 다음의 경우는 그렇지 않다.

    try{
                ...
                System.exit(1);
            }catch(...){
            }finally{
                ... //이 부분은 실행되지 않는다.
            }
     

    5.2.2 "예외의 종류 3가지 (Error, RuntimeException, 그밖의 Exception)" #

    5.2.2.1 Error #
    이에 관해선 JLS 11.2.1에 다음과 같이 기술되어 있다. "체크되지 않는 예외 클래스(Error와 그 Sub class)는 프로그램안의 다양한 위치에서 발생할 가능성이 있으며, 회복이 불가능하기 때문에 컴파일시 체크되지 않는 것이다. 이러한 예외를 프로그램에서 선언한다고 해도 난잡하고 무의미한 것이 될 뿐이다."

    Java의 클래스 librury에서 Error의 sub class를 살펴봐도 AWTError, LinkageError, ThreadDeath, VirtualMachineError 등 'catch해도 소용 없을 것' 들 뿐이다. (OutOfMemoryError는 VirtualMachineError 아래에 위치한다.)
     
    5.2.2.2 RuntimeException #
    위의 Error 이외의 Exception들은 application에서 catch할 가능성이 있는 예외들이다.(버그가 없으면 발생하지 않는 예외들) 그리고 RuntimeException은 '어디서든 발생할 가능성이 있는 예외'이다. RuntimeException의 sub class로는 NullPointerException, ArrayIndexOutOfBoundException, ClassCastException 등을 들 수 있다. '이러한 예외는 버그가 없는 한 발생하지 않으므로 일일이 throws 를 작성하지 않아도 된다.'

    프로그램에 버그가 없는 한 발생할 수 없는 예외가 발생한 경우 C 언어와 같이 영역 파괴가 일어나기 쉬운 언어라면 프로그램 전체를 종료시키는 것이 정답이겠지만, Java와 같이 영역파괴가 일어나지 않도록 실행시 체크(JVM Classloader의 formal verification process)를 하고 동적으로 프로그램을 load하는 언어에서는 국소적인 NullPointerException 때문에 프로그램 전체를 중지시켜서는 안 될 것이다.

    따라서, RuntimeException은 catch하지 않는 것이 바람직하다고 볼 수 있다. 버그가 있는 프로그램은 신속히 종료시키는 것이 대부분의 경우 최선의 방책이라 생각하기 때문이다.
     
    5.2.2.3 그밖의 Exception #
    위의 RuntimeException이외의 Exception의 sub class는 사용자의 잘못된 조작 등으로 인해 프로그램에 버그가 없어도 발생할 가능성이 있고 그에 대하여 프로그램이 확실히 대응해야 하는 경우에 사용된다. 예를 들면 FileNotFoundException등이다.

    그런데 개발하다 보면 이상하고 의아한 것이 하나 있다. 숫자 부분에 문자를 넣었을때 발생하는 NumberFormatException이다. 이것은 이상하게도 RuntimeException의 sub class이다. 이것은 RuntimeException이 아니었으면 하는데 NumberFormat체크는 Runtime시에만 가능한 모양이다.
     

    5.2.3 "OutOfMemoryError는 어떻게 처리해야 하는가?" #

    예전에 Swing에서 Tree구조를 이용하는 프로젝트를 한적이 있다. 이때 Tree에 branch와 node가 무수히 생기자 JVM은 OutOfMemoryError를 내뱉었다. 이에 급한 마음에 OutOfMemoryError를 catch하여 사용자에게 재시작을 요청하는 Dialog를 띄우도록 수정하였다면 이 Dialog가 과연 떳을까? 현재 메모리가 부족한 판에 Dialog를 띄울 메모리가 남아있질 않았던 것이다. 다행히 Dialog가 떴어도 작업은 계속되지 못했을 것이다. NullPointerException가 나기 때문이다.

    원인은 나중에 찾았는데, Tree구조에서 부모부터 자식들을 붙이는 순으로 Tree를 구성하는데 자식들을 줄줄이 붙여나가다가 메모리 부족현상이 발생하였고 NullPointerException은 자식이 없으니 클릭하는 순간 null을 반환하여 발생하였던 것이다.

    OutOfMemoryError의 가장 좋은 해결책은 불필요한 객체를 만들지 않는 것이었다. 그리고 Tree생성시에도 자식부터 만들고 부모를 만드는 순서로 프로그램을 수정하여 프로젝트를 정상적으로 마칠수 있었다.

    마지막에 드는 심정은 프로그램이 OutOfMemoryError를 일으키는 원인이 과연 이렇게 구성되어 발생했는지 어떻게 알수 있을까 하는 의문이다.
     

    5.3 Object Serialize #

    Java에서는 ObjectOutputStream의 writeObject() method에 데이타 구조 저장소의 참조만 건네주기만 하면 그 안에 있는 모든 객체를 1차원 stream으로 출력해 준다. (파일이나 ByteArrayOutputStream을 이용한 메모리로) 단, static field는 Serialize되지 않는데 이는 Serialize의 대상이 instance 객체뿐이기 때문이다.
     

    5.3.1 "Serialize를 위해서는 marker interface인 java.io.Serializable interface를 implements해야한다." #

    여기서 marker interface는 java.lang.Cloneable과 같이 method와 field의 정의는 없지만 객체 Type을 위한 interface이다. 예전에 Serialize를 이용하여 데이타를 유지하는 프로젝트를 한 적이 있는데 그때 생각했던것이 '모든 class들이 기본적으로 Serializable을 implements하고 있으면 편할텐데..'라는 생각이었다. 하지만 이것은 상당히 위험한 발상이었다.

    Serializable이 기본으로 implements되어 잇으면 엉뚱한 객체까지 Serialize되고 그것을 알아채지도 못하는 사태가 일어날 가능성이 높다. Serializable이 optional인 이유는 이러한 이유 때문이리라..
     

    5.3.2 "super class는 Serializable이 아닌데 sub class만 Serializable인 경우의 문제점" #

    Serialize을 이용하여 프로젝트를 할때 한번쯤 실수할 수 있는 부분이 상속된 class의 Serialize이다. 컴파일 에러도 없고 Deserialize도 잘 되었다. 하지만 키가 되는 값이 null과 0이었다. 영문을 몰라 다른곳을 헤매여도 보다가 결국 찾은 원인은 부모의 field는 Serialize되지 않는다는 것을 알게 되었다. transient와 마찬가지로 형식별 default 값으로 채워졌었다. 이는 컴파일과 실행시 아무런 오류없이 실행되어 나를 힘들게 하였기에 Java가 원망스러웠던 기분좋은 추억이다. ^^;
     

    5.3.3 "transient field의 복원(?)관련" #

    Serialize를 이용한 프로젝트를 할때는 writeObject와 readObject를 이용하여 기본적으로 제공하는 Serialize를 customizing할수있다.

    Serializable에 대한 API reference에도 다음과 같이 나와있다.

    "Serialize와 Deserialize에 대한 특별한 handling을 위해서는 다음 두개의 특별한 메소드를 구현하면 된다."

    private void writeObject(java.io.ObjectOutputStream out) throws IOException;
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

    이 두 method가 private으로 되어 있는 것을 보고 처음에는 의아해 했었던 기억이 있다. 이를 protected나 public으로 하면 제대로 동작하지 않는다. 이는 override가 이니기 때문이다. 사실은 속에서 reflectiond을 이용하여 강제적으로 호출되고 있는것이다. reflection에서는 private method까지 찾을 수 있기 때문이다.

    또한 private으로 한 가장 큰 이유는 Serialize를 객체자신이 직접 해야 안전하다는 의미도 있지 않을까 하는 생각도 든다. 다시 본론으로 들어가서 transient를 복원하는 것에 얘기를 하자면, 사실 transient는 Serialize대상에서 제외되는 것인데 복원을 할 수 있다는 말이 안된다. 하지만 프로젝트를 진행하다 보면 logic상 가능한 경우가 많이 있다.

    즉, 모든 field를 Serialize하지 않고 필요한 것만 하고 특정 field는 Serialize한 field들을 이용하여 복원하는 방법이다. 또한 Serialize당시의 객체 상태와 Deserialize시의 객체상태가 서로 다를 수 있는 field도 그것에 해당된다. cafeid만으로 나머지 field는 DB에서 읽어오게 한다면 나머지 field는 transient로 처리하고 Deserialize시 readObject()에서 복원하는 것이다.
     

    5.3.4 "Stack Overflow에 주의하라!" #

    Serialize를 하다보면 참조로 연결된 객체를 recursive하게 거슬러 올라가며 이것이 너무 깊어지면 Stack Overflow가 발생한다. 가령 linked list같은 경우이다. 이것을 Serialize하면 그 요소수만큼 recursive 호출이 발생한다. 과거(JDK1.3.0시절) 프로젝트 당시 JVM이 5111에서 Stack Overflow가 발생했던 기억이 있다.

    물론 실행시 java option에 -Xss 를 이용하여 statck 크키를 조절할 수 있지만 이것은 개발자가 아닌 실행하는 사람들에게 부담이었다. JDK의 LinkedList class의 소스를 보면 writeObject()와 readObject()를 다음과 같이 변경하고 있다.

    private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
                s.defaultWrtieObject(); //이 코드는 무조건 들어가게 되는데 이곳 소스의 System.arraycopy()에서 overflow발생한다.
                
                s.writeInt(size);   //이부분이 실제 추가되어 Stack Overflow를 예방한다.
                
                for(Entry e = ...)
                    s.writeObject(e.element);
                }
                ...
            }
            
            //readObject()도 이와 같은 개념으로 변경되어 있다.

    5.4 "nested class / inner class / 중첩클래스" #

    5.4.1 "중첩클래스의 개념" #

    개인적으로 중첩클래스를 어떠한 경우는 사용하지 않으려 한다. 사용하기가 만만치 않고 코드 읽기가 힘들어 지기때문이다. 하지만 '어떤 클래스 내에서 은폐할 목적으로 사용하는 클래스가 있다면 이것을 사용해야 한다' 실제로 Java의 AWT 클래스 Event Handler를 비롯하여 많은 클래스에서 중첩클래스를 사용하고 있다. 또한 내부 class는 그것을 둘러싸는 class의 instance(enclosing object라고 하는)의 field를 참조 할수 있는것도 장점이다. 하지만 이는 내부클래스가 아닐경우 부부 클래스를 new해서 사용하는것과 별반 다를께 없지 않은가.

    5.4.2 "내부클래스는 부모의 참조를 몰래 보유하고 있다." #

    내부 클래스의 instance는 부모의 instance에 대한 참조를 몰래 보유하고 있기 대문에 위에서 얘기한 부모의 field를 참조할 수 있는 것이다. 그러므로 static method에서는 내부클래스를 생성할 수 없다. 다음 예를 보면 바로 알수 있다.

    class Test{
                class InnerClass {
                    int i;
                    ...
                }
                
                public static void main(String[] args){
                    InnerClass icls = new InnerClass();
                    ...
                }
            }

    이 소스를 compile하면 다음의 오류가 발생한다. "non-static variable this cannot be referenced from a static context..." main method는 static이므로 this를 참조할수 없다는 것이다. 이는 InnerClass가 new 되면서 외부 클래스 Test의 this를 보유해야 하는데 여기서 static을 만나니 오류를 표출시킨것이다. 물론 일반 instance method에서는 오류가 나지 않는다.
     

    5.4.3 "local inner class에 대하여" #

    local inner class라 함은 method내에서 선언된 inner class이다.

    public class OuterClass {
                public int get(){
                    int i = 9;
                    int id = 99;
                    int id2 = 99;
                    final int id3 = 100000;
                    
                    class LocalInnerClass {
                        int id = 100;
                        
                        LocalInnerClass(){
                            System.out.println("LocalInnerClass");   
                        }
                        
                        int getId(){
                            return id3 + id;
                        }
                    }   
                    
                    LocalInnerClass lic = new LocalInnerClass();
                    return id + lic.getId();
                }   
                
                public static void main(String[] args){
                    OuterClass outer = new OuterClass();
                    System.out.println("id = " + outer.get());  
                    //결과 값은 "100000(id3) + 100(LocalInnerClass.id) + 99(OuterClass.get())" 인 100199가 나온다.
                }
                
            }

    위 소스의 LocalInnerClass는 get() 이라는 method에서만 보이는 class이다. 그리고 특이할 만한 부분이 OuterClass의 get() method에서 final로 선언된 id3이 LocalInnerClass에서 참조 가능해 진다. id2를 참조하면 compile error가 나지만 final로 선언된 것은 오류가 나지 않는다.

    이는 local variable은 method에서 나오는 순간 사라지는데, local inner class는 local variable보다 수명이 조금더 길기 때문에 final만 허용한 것이다.
     

    5.4.4 "anonymous class(무명클래스)에 대하여" #

    무명 클래스는 말그대로 이름이 없는 클래스이다.

    class AnonymousTest {
                private interface Printable {
                    void print();
                }
                
                static void doPrint(Printable p){
                    p.print();
                }
                
                public static void main(String[] args){
                    doPrint( new Printable(){
                                public void print(){
                                    System.out.println("this is new Printable print()");
                                }
                             });
                }
            }

    위 소스의 "doPrint( new Printable(){" 부분이 무명클래스 이다. compile을 수행하면 AnonymousTest$Printable.class, AnonymousTest$1.class, AnonymousTest.class 세개의 클래스가 생긴다. 여기서 AnonymousTest$Printable.class는 Printable interface이고 AnonymousTest$1.class이 무명클래스이다.

    이 소스를 보면 처음에 드는 의심이 Printable interface를 new 했다는 것이다. 여기서 굳이super class(이 소스에서는 interface)를 저정해야 하는 이유는 아무것도 상속하지 않는 무명 클래스의 instance를 만들어 봐야 의미가 없기 때문에 이렇게 한듯하다.

    "무명클래스는 어떤 class나 interface를 상속/구현 해야만 그 instance를 사용할 수 있는 것이다"
     
    이처럼 무명 클래스를 사용하면 어떤 절차(수행)를 다른 method의 인수로 건네줄 수 있게 된다. 하지만 간단한 로직만 구현처리해야 한다.

    "무명클래스는 조금만 복잡해져도 급격히 소스의 가독성이 떨어지게 되므로 남용하지 않는 것이 바람직하다"
     

    6 이래도 Java가 간단한가? #

    6.1 method overload 에서의 혼란? #

    6.1.1 "overload란 이름이 가고 인수가 다른 method에 compiler가 다른 이름을 붙이는 기능" #

    overload를 구현하면 bytecode로 변환시 다른 이름으로 method가 변환되어 별개의 method로 처리된다. 이를 JVM에서 method descripter라 하여 Oolong asembler로 변화시 다른 형태의 method가 된다. 예를 들어 "void get(double d, long l)" 은 "get(DJ)V"로 변경된다. 여기서 D는 double, J는 long, V는 void를 의미한다.

    그런데 여기서 "get(DJ)" 부분만 method 이름이므로 return type이 다른 동일 method는 overload 할 수 없다. 따라서 overload는 정적(compile시 결정)이라는 명제가 성립니다. 그래서 동적으로 사용되면 compile시 오류를 표출한다. 아래의 소스를 보자. 여기에는 IFS라는 interface와 이를 implements한 Impl1, Impl2 라는 class가 있다.

    //IFS.java
            interface IFS {
                public String getName();
            }
            
            //Impl1.java
            class Impl1 implements IFS {
                public String getName(){
                    return "Impl1";
                }
            }
         
            //Impl2.java
            class Impl2 implements IFS {
                public String getName(){
                    return "Impl2";
                }
            }   
            
            //main이 있는 OverloadTest.java
            public class OverloadTest {
        
                static void pr(int i){
                    System.out.println("pr_int : " + i);   
                }
                
                static void pr(String s){
                    System.out.println("pr_string : " + s);   
                }
                
                static void pr(IFS ifs){
                    System.out.println("pr_string : " + ifs.getName());
                }
                
                static void pr_run(Impl1 i1){
                    System.out.println("pr_run : " + i1.getName());
                }
                
                static void pr_run(Impl2 i2){
                    System.out.println("pr_run : " + i2.getName());
                }
                
                public static void main(String[] args){
                    OverloadTest test = new OverloadTest();
                    test.pr(10);
                    test.pr("Jeid");   
                    
                    IFS ifs1 = new Impl1();
                    test.pr(ifs1);
                    
                    IFS ifs2 = new Impl2();
                    test.pr(ifs2);
                    
                    //pr_run(ifs1);
                    //pr_run(ifs2);
                }
            }

    위의 소스를 수행하면 정상적으로 compile이 될것인가?

    당연히 잘 된다. pr()은 overload를 잘 구현했다. 하지만 소스 하단의 두 주석문을 풀면 어떻게 될까? 이는 compile오류를 낸다.

    OverloadTest.java:36: cannot resolve symbol
            symbol  : method pr_run (IFS)
            location: class OverloadTest
                    pr_run(ifs1);
                    ^
            OverloadTest.java:37: cannot resolve symbol
            symbol  : method pr_run (IFS)
            location: class OverloadTest
                    pr_run(ifs2);
                    ^
            2 errors

    실제 위 둘의 pr_run method는 bytecode로 변환시 "pr_run(Lpackage_name.IFS)V"로 동일하게 생성된다. 따라서 compile시에 오류를 표출한다. 이 소스를 보면 알 수 있듯이 "method overload는 정적(compile시)으로 미리 결정되며, 동적(실행시판단)으로 사용할수 없다."
     

    6.1.2 "그렇다면 overload에서 실제로 혼동되는 부분은 무엇인가?" #

    다음 소스를 보고 실제로 수행되는 method를 찾아보라.

    class OverloadTest2 {
                static int base(double a, double b){ ... }  //method A
                
                static int count(int a, int b){ ... }  //method B
                static int count(double a, double b){ ... }  //method C
                
                static int sum(int a, double b){ ... }  //method D
                static int sum(double a, int b){ ... }  //method E
            }

    • base(3,4) 를 호출했을때 수행되는 method는? => 당연히 method A (3과 4는 정수라도 double이 되므로 정상적으로 수행됨)
    • count(3,4) 를 호출했을때 수행되는 method는? => B와 C중 갈등이 생긴다. 이럴경우 JVM은 가장 한정적(more specific)한 method를 찾는다. 여기서 3과 4는 정수형에 가까우므로 method B 가 호출된다.
    • count(3, 4.0) 을 호출했을때 수행되는 method는? => 이것은 4.0 이 double이므로 method C 가 더 한정적이므로 method C 가 호출된다.
       
    • sum(3,4.0) 을 호출했을때 수행되는 method는? => 이것은 당연히 type이 일치하는 method D.
       
    • sum(3,4) 를 호출했을때 수행되는 method는?? => 이런 코드가 소스내에 있으면 다음과 같은 compile 오류를 표출한다.
    OverloadTest.java:48: reference to sum is ambiguous, both method sum(int,double)
                   in OverloadTest and method sum(double,int) in OverloadTest match
                        System.out.println("sum(3,4) = " + sum(3,4));
                                                           ^
                 1 error

    method D와 method E가 애매하다는 compile 오류이다. 이것은 둘중 어느것이 더 한정적인지 찾을 수 없으므로 bytecode 를 생성 할 수 없다는 것이다.

    "이렇듯 compiler에게 불필요한 오해(혼동)를 초래하는 overload는 사용하지 않는 것이 좋다. 개인적으로 overload를 가능한 사용하지 않으려 하고 필요하다면 인수의 개수가 다른 overload를 사용하는 편이다."
     

    6.1.3 (참고) 또다른 혼동, overload한 method를 override 하면? #

    overload란 compiler가 bytecode변환시 다른 이름을 붙이는 기능이라는 것을 위에서 설명했다. 따라서 super class에서 overload한 method를 상속하여 override하면 완전 별개의 method를 override한것처럼 JVM은 판단한다. 즉, overload와 override는 직교(전혀상관없는)하는 개념이다.

    6.2 상속/override/은폐 에서의 복잡함 #

    6.2.1 "Java class의 member 4 종류" #

    1. instance field
    2. instance method
    3. static field
    4. static method
       
    여기서 상속을 하였을 경우 runtime시 객체의 형식에 따라 선택되는 것은? 2번 instance method 뿐이다. 즉, 동명의 member를 sub class에서 선언했을 때 instance method만 override 되고 나머지는 완전 별개의 member가 된다. 따라서 위의 1,3,4는 sub class에서 동일하게 선언했을 경우 별개의 것으로 인식되며 compile시에 무엇을 access 할지 결정된다.

    즉, instance method는 override되지만 instance field/static field는 은폐된다. override는 실행시 객체의 형식에 따라 처리 할당되지만, 은폐의 경우는 compile시에 결정되고 만다.
     

    6.2.2 "override시 method 이름에 대한 함정" #

    과거에 코딩을 하던중 정말이지 어처구니 없는 경우를 당했다. override 하는 method이름을 잘못써서 황당한(?) 고생을 한적이 있다. super class의 writable()이라는 method를 writeable()이라고 override(?)하였는데 프로그램 수행 중에 writable()이 항상 false가 나오는 것이 아닌가? 그래서 소스를 추적추적 하다 몇시간을 허비했었던 기억이 있다.

    java를 접한지 얼마되지 않았고 요즘같이 eclipse같은 에디터도 없이 메모장에서 코딩하던 시절이라 더욱 고생했던것 같다. 한참 후에야 우연히 스펠링이 잘못된걸 알고 얼마나 황당했던지... 지금 생각하면 이것도 좋은 추억이리라.

    무조건 override 잘 되었을거라 생각 했던 나의 불찰도 있었지만 compile때나 runtime시 아무런 반응을 보이지 않던 Java도 원망스러웠다. 2003년도에 C#으로 프로젝트를 했는데 C#은 상속의 override에 대하여 "override void writalbe().."과 같이 정의시 override를 명시해야 된다는 것을 보고 상당히 마음에 들어 했던 기억이 있다. 가독성도 뛰어날 뿐더러 나의 몇시간동안의 헤메임도 없을 것이기 때문다. Java도 이렇게 확실한 명세였으면 정말 좋겠다.

    6.2.3 "또다른 나의(?) 실수 - 말도 안되는 오타" #

    위의 method이름을 잘못써서 고생하기 이전에 아주 비슷한 고생을 한적이 있다.

    '난 정말 바보인가'라는 생각을 들게 했던 문제였다. 초보 시절에는 왜이리도 오타가 많이 나던지... 요즘은 대충 키보드 두드려도 오타가 잘 안나는데 그 시절에 오타 때문에 느린 CPU에서 컴파일을 몇번을 했는지...
     
    기억을 되살리면 소스는 다음과 같다.

    public class Member {
                private int memberNo;
                
                public int getMemberNo(){
                    return this.memberNo;
                }
                
                public void setMemberNo(int menberNo){
                    this.memberNo = memberNo;
                }
                
                ......
            }


    위 소스의 Member에는 다른 여러가지 member field가 있는데 DB의 member table에 memberid 컬럼이 memberno로 변경되면서 Member class의 memberId를 memberNo로 변경하게 되었다. 위와 같이 수정하여 배포해놓고 테스트를 하는데 시스템이 완전히 뒤죽박죽으로 돌아버리는 것이 아닌가. 이 경우도 method 이름처럼 몇시간을 헤매었다.

    이번에 argument의 오타로 인한 어처구니 없는 실수였다. setMemberNo(int menberNo)에서 문제가 발생되었던 것이다. 인수의 memberNo를 menberNo로 잘못친것이다. 그래서 memberNo에는 해당 member의 memberno가 아닌 0이 모두 들어갔어던 것이다. 시스템은 memberno를 기준으로 도는 부분이 너무나 많았기에 오류나는 부분도 많았으며 DB에서는 제대로 된 memberno을 읽어 왔으며, compile과 runtime시 아무런 반응도 없었기에, 초보자를 그렇게도 고생시켰나 보다.

    이것도 member field면 무조건 this를 붙이도록 하던지 Java가 인수는 'm_'와 prefix를 붙이도록 Coding Style을 정의- SUN사이트의 Java Coding 규약에는 "Variable names should not start width underscore_ or dollar sign $ characters, even though both are allowed." 와 같이 명시되어 있다 - 했더라면 발생하지 않았을 문제이다.

    또한 C언어나 C#에서 처럼 compile 경고레벨을 높여놓으면 "menberNo는 어디서도 사용하지 않습니다."와 같은 메세지를 보여 줬더라면 고생을 덜 하지 않았을까?
     

    6.2.4 "static member를 instance를 경유하여 참조해서는 안 된다." #

    예를 들어 ClassA 에 public static int AA 라는 static field가 있을 경우 ClassA.AA 로 접근해야 하는데, 다음과 같이 사용하는 실수를 범한다.(물론 오류는 없지만)

    ClassA a = new ClassA(); 
            int i = a.AA;       //instance를 경유하여 접근
            int j = ClassA.AA;  //올바르게 접근

    그럼 왜 굳이 ClassA.AA와 같이 instance가 아닌 class이름을 붙여야 할까?

    static member(static field/static method)는 compile시에 이미 어느것을 호출할 지 결정하기 때문에 위의 a.AA와 같은 것은 static이 아닌것 같은 오해와 혼란만 가져오기 때문이다. 심지어 개인적으로는 동일 class 내 - 위 소스에서 ClassA의 member method - 에서 ClassA.AA라고 사용하는 편이다.

    이는 local variable과 혼동될 염려도 없을뿐더러 AA라는 변수가 static이라는 것도 확실히 알 수 있기 때문이다. 물론 private static 의 경우는 ClassA.BB 와 같이 하지 않고 BB 라고 해도 무방하겠지만 말이다.
     

    6.2.5 "super keyword는 부모의 this" #

    Java 개발자 대부분은 'super' 에 대하여 그렇게 민감하지 않을 것이다. 그거 super() 나 super.method1() 과 같이 사용되지 그 이상에 대해선 깊이 생각하지 않게 된다. super를 한마디로 정리하면 다음과 같다.

    "super keyword는 instance method등에서 this를 사용할 수 있는 곳에서만 쓸 수 있다. this의 자리에 super라고 쓰면 현재 class의 member가 참조되는 대신 부모 class의 member가 참조되는 것이다."

    6.3 상속에 관한 또 다른 문제 #

    6.4 그밖의 함정 #

    6.4.1 "생성자에 void 를 붙인다면?" #

    생성자에 void를 붙인다면 그 class가 new 될때 그 생성자(?)가 실행될까?? 아래의 'Constuctor'라는 문자열은 출력될까?

    public class ConstructorTest{
                void ConstructorTest(){
                    System.out.println("Constuctor");
                }
                .....
            }

    출력되지 않는다. 물론 compile시 아무런 경고도 없었다. 즉, void가 붙은 ConstructorTest()는 생성자가 아니라 instance method일 뿐이었고 new시에는 default constructor가 실행 되었던 것이다.

    6.4.2 "if / switch 의 함정" #

    Java 개발자라면 대부분이 초보시절에 if 조건절에 '==' 대신 '='을 써본 기억이 있을것이다. 예를 들어 "if( isListenLecture == Student.STUDENT )" 를 "if( isListenLecture = Student.STUDENT )" 로 잘못 쓴 경우이다. 여기서 Student.STUDENT는 boolean type이다. 여기서 isListenLecture는 항상 Student.STUDENT 값을 갖게 되는 버그가 생긴다. 이는 compile시에 아무런 경고도 없다. 이렇게 한번 당하고 나면 앞으로는 '=='를 정확히 쓰게 되거나 아니면 다음과 같이 쓴다.

    "if( isListenLecture )" 또는 "if( !isListenLecture )" 라고 말이다. 이것이 더욱 간결하고 의미도 분명해 지기 때문이다. 또한 다음 소스와 같은 오류도 범하는 경우가 있다. 이는 잘못된 indentation으로 빚어지는 초보의 함정이다.

    이글을 읽는 분께 한가지 당부드리고 싶은것은 여기서 초보라고 다 그런건 아니라는 것이다.

    ....
            if( a < 5 )
                b = 3;
                c = 10;   //이부분은 나중에 추가된 라인이다.
                
                
            if( isStudent )
                if( isFemale )
                    sayHello("Hi~~");
            else
                sayHello("Hello Professor~");

    위의 소스중 c = 10; 이 if( a < 5 )의 참일때 수행된다고 오해할 수도 있고, sayHello("Hello Professor~"); 부분이 if( isStudent )의 else 부분이라고 오해 할 수도 있다. 이것은 전적으로 indentation(들여쓰기)의 불찰로 개발자가 잘못 읽을 수 있는 부분이다. Java Coding Style에서는 if문 다음에 한줄의 코드가 있더라도 {} 를 사용하길 권고한다. 그러면 첫번째 if문과 같은 오류를 방지할 수 있고 두번째 if문에서도 보다 가독성이 생길 것이다.

    이와 유사한 것으로 switch문의 case 절에서 break를 쓰지 않아 항상 동일하게 처리되는 버그도 경험해 보았을 것이다.

    7 Java 기능 적용 몇가지 #

    7.1 대규모 개발에서 interface 분리하기 #

    7.1.1 "interface 분리의 필요성" #

    Java와 같은 객체지향언어에서는 공개해야 할 method만을 public으로 하고, 공개할 필요가 없는 것은 private으로 하여 class의 상세한 내용을 은폐할 수 있게 되어 있다. 그런데 private 부분이 은폐되어 있는것 처럼 보이는가?

    소스를 보면 훤히 들여다 보이는데?

    대규모 개발은 하부 class부터 bottom-up으로 진행하는 것이 이상적인 형태일 것이다. 그런 형태로 개발하면 임의의 시점에서 테스트를 할 수도 있다. 그러나 현실적으로 단기간에 많은 수의 개발자가 붙어서 단시간에 개발을 진행하는 경우가 많다. 또한 서로 호응하는 관계에 있는 class들은 어느쪽이 하부인지 정의하기가 난감할때가 많다. 이런경우 우리는 흔히 package단위로 나누어 개발한다. 하지만 이럴경우 어느정도 코딩이 종료될때까지 테스트하기가 상당히 힘들어 진다. Java에서는 private member와 method 구현까지 하나의 파일에 코딩하는데 개발 중간에 공개하여 다른 개발자가 이용해야 하는 class를 배포할 수 없으므로 동시 개발이 까칠해 진다.

    이 상황에서 다른 package(개발자)에 공개해야 하는 class 부분을 interface로 공개하면 많은 부분 유연하게 된다. 이 interface를 다른 개발자는 개발을 하고 테스트가 필요하다면 TestImpl class를 만들어 하면된다. RMI나 CORBA에서도 Stub은 이런식으로 IDL을 정의한다.

    7.2 Java에서의 열거형 #

    Java에서는 열거형-C의 구조체, 공용체-이 없다. 열거형이 왜 필요하냐고 반문하는 개발자도 있을 것이다.

    하지만 열거형이 없어 곤란을 경험한 개발자도 꽤 있으리라 본다. 최근언어(특히 객체지향 언어) - Java, Eiffel, Oberon등 - 에는 열거형은 포함되어 있지 않다. C#에는 있긴 하지만.

    이런 이유로 Java AWT의 Label class는 다음과 같이 구현되어 있다.(텍스트의 정렬값관련)

    public static final int LEFT = 0;
            public static final int CENTER = 1;
            public static final int RIGHT = 2;
            ...
            
            label.setAlignment(Label.CENTER);
            ...

    하지만 위의 소스에는 문제가 있다. setAlignment() method의 인자가 int인 것이다. 만약 위에 정의한 0, 1, 2가 아닌 다른 int 값이 들어가도 compile/runtime시 알수가 없다. 그래서 주석을 달게 되는데, 주석이라 함은 정말이지 최후의 수단이라고 봐야 한다.

    실제로 우리가 개발해 놓은 소스에도 이런부분이 있으리라 예상된다. 이 문제를 어떻게 하면 해결할 수 있을까?
     
    Java에서 열거형을 한번 만들어 보자.

    //LabelAlignment.java
            public class LabelAlignment {
                private LabelAlignment() {} //이는 생성자를 private으로 하여 다른데서는 만들지 못하도록 하기위함이다.
                
                public static final LabelAlignment LEFT = new LabelAlignment():
                public static final LabelAlignment CENTER = new LabelAlignment():
                public static final LabelAlignment RIGHT = new LabelAlignment():
            }
            
            //변형된 Label.java 의 일부..
            public synchronized void setAlignment(LabelAlignment alignment){
                if( alignment == LabelAlignment.LEFT ){
                    ...//왼쪽으로 맞추기..
                }else if( ...
                    ...
                }
            }
            ...

    위에서 작성한 소스는 잘 작동한다. 서로 다른 3개의 instance이므로 reference가 달라 '==' 연산도 가능하고, 훌륭하다.

    하지만 한가지 문제가 있다. LabelAlignment가 Serializable한 class에서 serialize되었다 deserialize 된다면?

    LabelAlignment alignment 는 새로운 instance가 되고 serialize전의 reference와 다른 참조 위치를 갖게 되어 '==' 연산은 버그를 발생시킨다.
     
    그럼 이것만 해결하면 되겠는데, 어떻게 refactoring하면 될 것인가? '==' 연산 대신 equals로 변형하면 되겠는데.

    //LabelAlignment.java
            public class LabelAlignment {
                private int flag;
                private LabelAlignment(int flag){
                    this.flag = flag;
                } 
                
                public static final LabelAlignment LEFT = new LabelAlignment(0):
                public static final LabelAlignment CENTER = new LabelAlignment(1):
                public static final LabelAlignment RIGHT = new LabelAlignment(2):
                
                public boolean equals(Object obj){
                    return ((LabelAlignment)obj).flag == this.flag;
                }
            }
            
            //변형된 Label.java 의 일부..
            public synchronized void setAlignment(LabelAlignment alignment){
                if( LabelAlignment.LEFT.equals(alignment) ){
                    ...//왼쪽으로 맞추기..
                }else if( ...
                    ...
                }
            }
            ...

    하하, Serialize까지 잘 작동한다. ^^;

    여기서 Debug를 고려한다면 0, 1, 2 대신 문자열로 "LEFT", "CENTER", "RIGHT"로 한다면 더욱 명확하지 않을까?

    (주의) 위에서처럼 LabelAlignment.LEFT 라고 쓰기 싫어서 상수 interface를 만들어 그걸 implements 하여 그냥 LEFT 라고 쓰는 것을 뿌듯해 하며 쓰는 개발자들이 있다. 물론 Swing의 소스들을 보다보면 SwingConstants라는 interface에 LEFT를 비롯하여 온갖 잡다한 상수를 집어넣어놓고 여기 저기서 implements해서 사용하고 있다. 이런 코딩 스타일은 '내 스타일이야~' 가 아니라 냄새나는 코드이다.

    LEFT라는 것이 구현한 class에 이미 있을 수 있을 수 있을뿐아니라 구현한 모든 클래스에서 LEFT를 보유하여 SwingConstants.LEFT뿐 아니라 Impl.LEFT로도 사용되게 되어 온갖 혼란을 초래하게 된다. 입력량을 줄이기 위해 interface를 implements 해서는 안되지 않을까?

    7.3 Debug write #

    C에서는 다음과 같이 pre-process로 정의하면 DEBUG라는 식별자를 #define하지 않으면 컴파일후 해당 소스의 부분이 삭제된다.

    #ifdef DEBUG
                fprintf(stderr, "error...%d\n", error);
            #endif /* DEBUG */

    그럼 Java에서는?

    Java에서는 Pre-process가 없지만 다음과 같이 작성했을때 Debug.isDebug 가 final로 선언되어 있으면 compile후 아래 3줄 모두 삭제 된다.(단 Debug.isDebug 가 false 로 초기화 되었다면 제거된다.)

    if( Debug.isDebug ){
                System.out.println("error..." + error);
            }

    Java는 compile시 byte code 생성시 final은 정적으로 판단하여 미리 정의하기 때문에 위의 3줄은 삭제될 수 있다. if문과 함께 없어지게 되므로 처리 속도에 피해를 주지 않는다. 단, 주의해야 할 점은 Debug.isDebug 값이 변경되면 이 것을 사용하고 있는 측도 모두 함께 다시 compile해야 한다. bytecode를 다시 만들어야 하기 때문이다.

    그런데, 이 소스를 Debug.write()와 같이 static 으로 하여 이 method내에서 판단하게 하면 편리할텐데. 그리고 class별로 ON/OFF 처리를 할 수 있으면 좋을텐데, 어찌 하면 가능할 것인가?

    그럼 먼저 호출한 쪽의 class이름을 찾아보자. 접근은 Exception의 printStackTrace()로 부터 시작되었다. 하지만 이 소스에는 Exception 객체를 new한 시점에 결정되어 있다. 그래서 부모인 Throwable의 생성자를 확인해 보니 fillInStackTrace() 로 되어있는데 이 method는 native method였다.

    API Reference를 보면 Thread class에서는 dumpStackTrace()라는 method가 있었다. 소스를 보니, 그것도 생성시점이었다. 아무래도 예외방면에서 찾는건 무리인듯 했다.

    그래서 class의 호출계층을 나타내는 java.lang.SecurityManager의 getClassContext() method로 접근하였다. sample 소스는 다음과 같다.

    // 1. GetCallerSecurityManager.java
            public final class GetCallerSecurityManager extends SecurityManager {
                public Class[] getStackTrace(){
                    return this.getClassContext();   
                }
            }
            
            // 2. GetCallerClass.java
            public final class GetCallerClass {
                private static GetCallerSecurityManager mgr;
                
                static{
                    mgr = new GetCallerSecurityManager();
                    System.setSecurityManager(mgr);
                }
                
                public static void writeCaller(String str){
                    Class[] stk = mgr.getStackTrace();
                    int size = stk.length;
                    for(int i = 0; i < size; i++){
                        System.out.println("stk[" + i + "] = " + stk[i]);   
                    }   
                    
                    String className = stk[2].getName();
                    
                    System.out.println("className is " + className + " : " + str);
                }
            }
            
            // 3. GetCallerClassMain1 : 호출하는 클래스 예제 1
            public class GetCallerClassMain1 {
                public static void main(String[] args){
                    GetCallerClass.writeCaller(", real is 1.");
                }
            }
            
            // 4. GetCallerClassMain1 : 호출하는 클래스 예제 2
            public class GetCallerClassMain2 {
                public static void main(String[] args){
                    GetCallerClass.writeCaller(", real is 2.");
                }
            }

    위의 3번 주석과 4번 주석 부분을 수행하면 다음과 같은 결과가 나온다.

    className is GetCallerClassMain1 : , real is 1.
        className is GetCallerClassMain2 : , real is 2.

    정확히 호출한 클래스를 표현하고 있다. 이것을 비교해서 클래스별 ON/OFF를 구현하면 된다.
     

    8 Java 5.0 Tiger 에 대하여 #

    Tiger에서는 새로운 개념의 적용이 많은 부분 시도 되었다. 이중 가장 기본이 되는 몇가지를 살펴보자.
     

    8.1 Working with java.util.Arrays #

    Tiger에서는 무엇보다도 Collection class들에 대해 많은 부분 정비하였다. 예를 들면 for/in 구문 지원과 Generic Type member와 Arrays Utility class 등이다. 그럼 Collection에 대한 static method들을 담고 있는 Arrays 에 대해 다음 example로 한눈에 살펴보자.

    package com.jeid.tiger;
    
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    
    public class ArraysTester {
    
    	private int[] arr;
    
    	private String[] strs;
    
    	public ArraysTester(int size) {
    		arr = new int[size];
    		strs = new String[size];
    		for (int i = 0; i < size; i++) {
    			if (i < 10) {
    				arr[i] = 100 + i;
    			} else if (i < 20) {
    				arr[i] = 1000 - i;
    			} else {
    				arr[i] = i;
    			}
    			strs[i] = "str" + arr[i];
    		}
    	}
    
    	public int[] getArr() {
    		return this.arr;
    	}
    
    	public String[] getStrs() {
    		return this.strs;
    	}
    
    	public static void main(String[] args) {
    		int size = 50;
    		ArraysTester tester = new ArraysTester(size);
    
    		int[] testerArr = tester.getArr();
    		int[] cloneArr = tester.getArr().clone();
    		String[] testerStrs = tester.getStrs();
    		String[] cloneStrs = tester.getStrs().clone();
    
    		// clone test
    		if (Arrays.equals(cloneArr, testerArr)) {
    			System.out.println("clonse int array is same.");
    		} else {
    			System.out.println("clonse int array is NOT same.");
    		}
    
    		if (Arrays.equals(cloneStrs, testerStrs)) {
    			System.out.println("clonse String array is same.");
    		} else {
    			System.out.println("clonse String array is NOT same.");
    		}
    
    		// 2부터 10까지 값 셋팅
    		Arrays.fill(cloneArr, 2, 10, new Double(Math.PI).intValue());
    
    		testerArr[10] = 98;
    		testerStrs[10] = "corea";
    		testerStrs[11] = null;
    
    		List<String> listTest = Arrays.asList(testerStrs);
    		System.out.println("listTest[10] = " + listTest.get(10));
    
    		System.out.println("------- unsorted arr -------");
    		System.out.println("Arrays.toString(int[]) = " + Arrays.toString(testerArr));
    		System.out.println("Arrays.toString(String[]) = " + Arrays.toString(testerStrs));
    
    		Arrays.sort(testerArr);
    		// Arrays.sort(testerStrs); //NullPointerException in sort method..(null이 없더라도 길이에 대한 크기 체크는 못함)
    		Arrays.sort(testerStrs, new Comparator<String>() {
    			public int compare(String s1, String s2) {
    				if (s1 == null && s2 == null) {
    					return 0;
    				} else if (s1 == null && s2 != null) {
    					return -1;
    				} else if (s1 != null && s2 == null) {
    					return 1;
    				} else if (s1.length() < s2.length()) {
    					return -1;
    				} else if (s1.length() > s2.length()) {
    					return 1;
    				} else if (s1.length() == s2.length()) {
    					return 0;
    				} else {
    					return s1.compareTo(s2);
    				}
    			}
    		});
    
    		System.out.println("------- sorted arr -------");
    		System.out.println("Arrays.toString(int[]) = " + Arrays.toString(testerArr));
    		System.out.println("Arrays.toString(String[]) = " + Arrays.toString(testerStrs));
    		
    		System.out.println("------------------------------------------------");
    
    		String[][] mstrs1 = { { "A", "B" }, { "C", "D" } };
    		String[][] mstrs2 = { { "a", "b" }, { "c", "d" } };
    		String[][] mstrs3 = { { "A", "B" }, { "C", "D" } };
    
    		System.out.println("Arrays.deepToString(mstrs1) = " + Arrays.deepToString(mstrs1));
    		System.out.println("Arrays.deepToString(mstrs2) = " + Arrays.deepToString(mstrs2));
    		System.out.println("Arrays.deepToString(mstrs3) = " + Arrays.deepToString(mstrs3));
    		
    		if( Arrays.deepEquals(mstrs1, mstrs2)) {
    			System.out.println("mstrs1 is same the mstrs2.");
    		}else {
    			System.out.println("mstrs1 is NOT same the mstrs2.");
    		}
    		
    		if( Arrays.deepEquals(mstrs1, mstrs3)) {
    			System.out.println("mstrs1 is same the mstrs3.");
    		}else {
    			System.out.println("mstrs1 is NOT same the mstrs3.");
    		}
    		
    		System.out.println("mstrs1's hashCode = " + Arrays.deepHashCode(mstrs1));
    		System.out.println("mstrs2's hashCode = " + Arrays.deepHashCode(mstrs2));
    		System.out.println("mstrs3's hashCode = " + Arrays.deepHashCode(mstrs3));
    	}
    
    }

    8.2 Using java.util.Queue interface #

    Queue를 이용하여 First In First OutOrdering한 Queue를 구현 가능하다.

    package com.jeid.tiger;
    
    import java.util.LinkedList;
    import java.util.PriorityQueue;
    import java.util.Queue;
    
    public class QueueTester {
    	public static void main(String[] args) {
    		System.out.println("---------- testFIFO ----------");
    		testFIFO();
    		System.out.println("---------- testOrdering ----------");
    		testOrdering();
    	}
    
    	private static void testFIFO() {
    		Queue<String> q = new LinkedList<String>();
    		q.add("First");
    		q.add("Second");
    		q.add("Third");
    
    		String str;
    		while ((str = q.poll()) != null) {
    			System.out.println(str);
    		}
    	}
    
    	private static void testOrdering() {
    		int size = 10;
    		Queue<Integer> qi = new PriorityQueue<Integer>(size);
    		Queue<String> qs = new PriorityQueue<String>(size);
    		for (int i = 0; i < size; i++) {
    			qi.offer(10 - i);
    			qs.offer("str" + (10 - i));
    		}
    		
    		for (int i = 0; i < size; i++) {
    			System.out.println("qi[" + i + "] = " + qi.poll() + ", qs[" + i + "] = " + qs.poll());
    		}
    	}
    }

    8.3 java.lang.StringBuilder 사용하기 #

    StringBuffer가 synchronize하지 않은 method들로 구성된 듯한 StringBuilder를 사용하므로 성능 향상을 도모할수 있다. 사용법은 StringBuffer와 동일하다.

    package com.jeid.tiger;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class StringBuilderTester {
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		list.add("str1");
    		list.add("str2");
    		list.add("str3");
    
    		String ret = appendItems(list);
    		System.out.println("ret = " + ret);
    	}
    
    	private static String appendItems(List<String> list) {
    		StringBuilder sb = new StringBuilder();
    		for (Iterator<String> iter = list.iterator(); iter.hasNext();) {
    			sb.append(iter.next()).append(" ");
    		}
    		return sb.toString();
    	}
    }

    8.4 Using Type-Safe Lists #

    Collection에 type을 명시하여 type-safe 하게 처리 가능. 아래에서 type을 명시하지 않을 경우 compile error가 남을 보여준다. tip으로 Number를 이용하여 byte, short, int, long, double, float 동시 사용하는 부분 참조.

    package com.jeid.tiger;
    
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    
    public class ListTester {
    	public static void main(String[] args) {
    		List<String> list = new LinkedList<String>();
    		list.add("str1");
    		list.add("str2");
    		list.add(new Integer(123));  // <-- String이 아니므로 compile error!!
    		
    		//Iterator에 String type을 명시하므로 정삭작동됨.
    		for (Iterator<String> iter = list.iterator(); iter.hasNext();) {
    			String str = iter.next();
    			System.out.println("srt = " + str);
    		}
    		
    		//Iterator에 String type을 명시하지 않았으므로 아래 A 부분에서 compile 오류 발생!!
    		for (Iterator iter = list.iterator(); iter.hasNext();) {
    			String str = iter.next(); //A
    			System.out.println("srt = " + str);
    		}
    		
    		//byte, short, int, long, double, float 동시 사용
    		List<Number> lstNum = new LinkedList<Number>();
    		lstNum.add(1);
    		lstNum.add(1.2);
    		for (Iterator<Number> iter = lstNum.iterator(); iter.hasNext();) {
    			Number num = iter.next();
    			System.out.println("num = " + num);
    		}
    	}
    }

    8.5 Writing Generic Types #

    class 나 interface keyword에 type을 명시하여 동일 타입 명시 가능. 주의 할 점은 any type은 static 일 수 없다.(동적으로 type이 정해지므로)

    class AnyTypeList<T> {
    //class AnyTypeList<T extends Number> {  // <-- 이는 Number를 상속한 type은 허용하겠다는 의미.
    	private List<T> list;
    	//private static List<T> list;  // <-- 이는 정적이므로 compile error 발생!!! 
    	public AnyTypeList(){
    		list = new LinkedList<T>();
    	}
    	
    	public boolean isEmpty(){
    		return list == null || list.size() == 0;
    	}
    	
    	public void add(T t){
    		list.add(t);
    	}
    	
    	public T grap(){
    		if (!isEmpty() ) {
    			return list.get(0);
    		} else {
    			return null;
    		}
    	}
    }

    8.6 새로운 static final enum #

    예제를 통해 알아보자.

    package com.jeid.tiger;
    
    import com.jeid.BaseObject;
    import com.jeid.MyLevel;
    
    public class EnumTester extends BaseObject {
    	private static long start = System.currentTimeMillis();
    	
    	public static void main(String[] args) {
    		try {
    			test();
    			enum1();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		printEllapseTime();
    	}
    	
    	private static void test() throws Exception {
    		byte[] b = new byte[0];
    		System.out.println(b.length);
    	}
    
    	private static void enum1() {
    		//enum TestEnum { A, B };  //enum cannot be local!!!
    		
    		for(MyVO.TestEnum te: MyVO.TestEnum.values()){
    			System.out.println("Allow TestEnum value : " + te);
    		}
    		System.out.println("---------------------------------------");
    		
    		MyVO vo = new MyVO();
    		vo.setName("enum1");
    		vo.setLevel(MyLevel.A);
    		System.out.println(vo);
    		System.out.println("isA = " + vo.isA() + ", isGradeA = " + vo.isLevelA()+ ", isValueOfA = " + vo.isValueOfA());
    		System.out.println("getLevelInKorean = " + vo.getLevelInKorean());
    	}
    
    	private static void printEllapseTime() {
    		System.out.println("==> ellapseTime is " + (System.currentTimeMillis() - start) + " ms.");
    	}
    }
    
    
    package com.jeid.tiger;
    
    import com.jeid.BaseObject;
    import com.jeid.MyLevel;
    
    public class MyVO extends BaseObject {
    	enum TestEnum {
    		A, B
    	}; // this is same public static final
    
    	private int id;
    
    	private String name;
    
    	private MyLevel grade;
    
    	// private List<T> list;
    
    	public MyLevel getLevel() {
    		return grade;
    	}
    
    	public void setLevel(MyLevel grade) {
    		this.grade = grade;
    	}
    
    	public boolean isA() {
    		return "A".equals(this.grade);
    	}
    
    	public boolean isValueOfA() {
    		return MyLevel.valueOf("A").equals(grade);
    	}
    
    	public boolean isLevelA() {
    		return MyLevel.A.equals(this.grade);
    	}
    
    	//A,B,C..대신 0,1,2... 도 동일함.
    	public String getLevelInKorean() {
    		switch(this.grade){
    		case A:
    			return "수";
    		case B:
    			return "우";
    		case C:
    			return "미";
    		case D:
    			return "양";
    		case E:
    			return "가";
    		default:
    			return "없음";
    		}
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    }

    8.7 Using java.util.EnumMap #

    java.util.Map과 동일하나 key가 enum type이어 한다. 예제로 살펴보자.

    package com.jeid.tiger;
    
    import java.util.EnumMap;
    
    public class EnumMapTester {
    
    	private enum MyEnum {
    		A, B, C
    	}; // this is same the static final..
    
    	public static void main(String[] args) {
    		MyEnum[] enums = MyEnum.values();
    		System.out.println("MyEnum is " + enums[0] + ", " + enums[1] + ", " + enums[2]);
    
    		EnumMap<MyEnum, String> em = new EnumMap<MyEnum, String>(MyEnum.class);
    		em.put(MyEnum.A, "수");
    		em.put(MyEnum.B, "우");
    		em.put(MyEnum.C, "미");
    		em.put(MyEnum.B, "가"); //key 중복은 HashMap과 동일하게 overwrite임.
    
    		for (MyEnum myEnum : MyEnum.values()) {
    			System.out.println(myEnum + " => " + em.get(myEnum));
    		}
    	}
    }

    8.8 Using java.util.EnumSet #

    java.util.Set과 동일하나 value가 enum type이어 한다. 예제로 살펴보자.

    package com.jeid.tiger;
    
    import java.util.EnumSet;
    
    public class EnumSetTester {
    
    	private enum MyEnum {
    		A, B, C, a, b, c
    	}; // this is same the static final..
    
    	public static void main(String[] args) {
    		MyEnum[] enums = MyEnum.values();
    		System.out.println("MyEnum is " + enums[0] + ", " + enums[1] + ", " + enums[2]);
    
    		EnumSet<MyEnum> es1 = EnumSet.of(MyEnum.A, MyEnum.B, MyEnum.C);
    		EnumSet<MyEnum> es2 = EnumSet.of(MyEnum.a, MyEnum.b, MyEnum.c);
    		EnumSet<MyEnum> es3 = EnumSet.range(MyEnum.a, MyEnum.c);
    		if (es2.equals(es3)) {
    			System.out.println("e2 is same e3.");
    		}
    
    		for (MyEnum myEnum : MyEnum.values()) {
    			System.out.println(myEnum + " contains => " + es1.contains(myEnum));
    		}
    	}
    }

    8.9 Convert Primitives to Wrapper Types #

    int, short, char, long, double등 primitive와 이들의 Object Wrapper 인 Integer, Shrt, Char등 간의 converting에 있어 자동으로 처리해주는 boxing과 unboxing이 지원 됨에 따라 type에 대한 유연한 처리가 가능해졌다. 예제로 살펴보자.

    package com.jeid.tiger;
    
    public class AutoBoxingTester {
    
    	public static void main(String[] args) {
    		int i = 0;
    		Integer ii = i; // boxing. JDK 1.4에서는 incompatible type error가 발생 했었으나 Tiger에서는 괜찮다.
    		int j = ii; // unboxing
    
    		for (ii = 0; ii < 5; ii++) { // Integer인데도 ++ 연산자 지원.
    		}
    
    		i = 129;
    		ii = 129;
    		if (ii == i) {
    			System.out.println("i is same ii.");
    		}
    
    		// -128 ~ 127 사이의 수는 unboxing이 되어 == 연산이 허용되지만,
    		// 그 범위 외의 경우 Integer로 boxing된 상태므로 equals를 이용해야함.
    		// 이는 버그가 발생했을 경우 찾기 쉽지 않은 단점도 내포하고 있다.!!
    		checkIntegerSame(127, 127); // same
    		checkIntegerSame(128, 128); // Not same
    		checkIntegerEquals(128, 128); // equals
    		checkIntegerSame(-128, -128); // same
    		checkIntegerSame(-129, -129); // Not same
    		checkIntegerEquals(-129, -129); // equals
    		
    		System.out.println("--------------------------------------------");
    		Boolean arriving = false;
    		Boolean late = true;
    		String ret = arriving ? (late ? "도착했지만 늦었네요." : "제시간에 잘 도착했군요.") : 
    								(late ? "도착도 못하고 늦었군요." : "도착은 못했지만 늦진 않았군요.");
    		System.out.println(ret);
    		
    		StringBuilder sb = new StringBuilder();
    		sb.append("appended String");
    		String str = "just String";
    		boolean mutable = true;
    		CharSequence chSeq = mutable ? sb : str;
    		System.out.println(chSeq);
    	}
    
    	private static void checkIntegerSame(Integer ii, Integer jj) {
    		if (ii == jj) {
    			System.out.println("ii = " + ii + ", jj = " + jj + " ==> jj is same ii.");
    		} else {
    			System.out.println("ii = " + ii + ", jj = " + jj + " ==> jj is NOT same ii!!");
    		}
    	}
    
    	private static void checkIntegerEquals(Integer ii, Integer jj) {
    		if (ii.equals(jj)) {
    			System.out.println("ii = " + ii + ", jj = " + jj + " ==> jj is equals ii.");
    		} else {
    			System.out.println("ii = " + ii + ", jj = " + jj + " ==> jj is NOT equals ii!!");
    		}
    	}
    
    }

    8.10 Method Overload resolution in AutoBoxing #

    int가 127을 초과할 경우 boxing이 이루어 질듯 하지만, method overload에 있어서는 boxing이 이루어 지지 않아 JDK1.4와 동일한 결과를 얻는다. 예제로 살펴보자.

    package com.jeid.tiger;
    
    public class OverloadTester {
    	public static void main(String[] args) {
    		double d = 10;
    		Integer ii = new Integer(10);
    		doSomething(10);
    		doSomething(1000);
    		doSomething(ii);
    		doSomething(d);
    	}
    
    	private static void doSomething(Integer ii) {
    		System.out.println("This is doSomething(Integer)");
    	}
    
    	private static void doSomething(double d) {
    		System.out.println("This is doSomething(double)");
    	}
    }

    8.11 가변적인 argument 개수 ... #

    인수가 가변적일 경우 인수의 개수가 없는것 부터 다수개까지 모두 지원. 예제로 살펴보자.

    package com.jeid.tiger;
    
    public class VarArgsTester {
    	public static void main(String[] args) {
    		setNumbers(1, 2);
    		setNumbers(1, 2, 3, 4);
    		setNumbers(1);
    		// setNumbers(); //해당 되는 method가 없어 compile error!!
    		System.out.println("==============================================");
    		setNumbers2(1, 2, 3, 4);
    		setNumbers2(1);
    		setNumbers2();
    	}
    
    	// this is same setNumbers(int first, int[] others)
    	private static void setNumbers(int first, int... others) {
    		System.out.println("-----------setNumbers()----------- : " + first);
    		for (int i : others) {
    			System.out.println("i = " + i);
    		}
    	}
    
    	// this is same setNumbers(int[] others)
    	private static void setNumbers2(int... others) {
    		System.out.println("-----------setNumbers2()----------- : "
    				+ (others != null && others.length > 0 ? others[0] : "null"));
    		for (int i : others) {
    			System.out.println("i = " + i);
    		}
    	}
    }

    8.12 The Three Standard Annotation #

    @Override - sign the override from superclass.

    //정상적인 사용
        @Override
        public int hashCode(){
            return toString().hashCode();
        }
        
        //스펠링이 틀려 compile error!!
        @Override
        public int hasCode(){   //misspelled => method does not override a method from its superclass error!!
            return toString().hashCode();
        }

    @Deprecated deprecated 주석과 동일하나 부모의 method가 deprecated되면 자식의 method를 사용해도 deprecated로 나온다.

    package com.jeid.tiger;
    
    public class AnnotationDeprecateTester {
    	public static void main(String[] args){
    		DeprecatedClass dep = new DeprecatedTester();
    		dep.doSomething(10);    //deprecated
    	}
    }
    
    class DeprecatedClass {
    	@Deprecated
    	public void doSomething(int ii){    //deprecated
    		System.out.println("This is DeprecatedClass's doSomething(int)");
    	}
    	
    	public void doSomethingElse(int ii){
    		System.out.println("This is DeprecatedClass's doSomethingElse(int)");
    	}
    }
    
    class DeprecatedTester extends DeprecatedClass {
    	@Override
    	public void doSomething(int ii){
    		System.out.println("This is DeprecatedTester's doSomething(int)");
    	}
    }

    @SuppressWarnings SuppressWarnings에 인자는 String[] type으로 여러개를 배열형태로 쓸수 있다.

    package com.jeid.tiger;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class AnnotationSuppressWarningsTester {
    	@SuppressWarnings({"unchecked", "fallthrough"} )
    	private static void test1(){
    		List list = new ArrayList();
    		list.add("aaaaaa");
    	}
    	
    	@SuppressWarnings("unchecked")
    	private static void test2(){
    		List list = new ArrayList();
    		list.add("aaaaaa");
    	}
    	
    	//warning이 없는 소스.
    	private static void test3(){
    		List<String> list = new ArrayList<String>();
    		list.add("aaaaaa");
    	}
    }

    8.13 Creating Custom Annotation Types #

    나만의 annotation을 정의할 수 있는데 키워드는 @interface이 각 method정의가 member라고 보면 된다. 간단한 예를 보면 다음과 같다.

    package com.jeid.tiger;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Documented
    @Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
    	String columnName();
    	String methodName() default "";
    }
    
    //사용하는 쪽..
    public class AnnotationTester {
    
    	@MyAnnotation(columnName = "test", methodName = "setTest")
    	private String test;
    
    	@MyAnnotation(columnName = "grpid")
    	public String grpid;
    
        ....
    }
    
    //위의 test 멤버의 경우 다음과 같이 접근 가능하다.
    Field testField = cls.getDeclaredField("test");
    if (testField.isAnnotationPresent(MyAnnotation.class)) {
        Annotatioin anno = testField.getAnnotation(MyAnnotation.class);
        System.out.println(anno.columnName() + ", method = " + anno.methodName());
    }

    9 The for/in Statement #

    9.1 for/in 의 자주 사용되는 형태 #

    for/in은 무엇보다 다양한 유형의 예제를 보는것이 제일 빠를것이다. 형태별 사용 예제를 살펴보면 다음과 같다.

    //1. 가장 단순한 형태인 배열(array)
    String[] strs = { "aaa", "bbb", "ccc" };
    for (String str : strs) {
        System.out.println(str);
    }
    
    //2. List by using Iterator
    List<Number> lstNum = new LinkedList<Number>();
    lstNum.add(1);
    lstNum.add(1.2);
    for (Iterator<Number> iter = lstNum.iterator(); iter.hasNext();) {
    	Number num = iter.next();
    	System.out.println("num = " + num);
    }
    
    //3. List를 바로 사용
    List<String> lst = new LinkedList<String>();
    lst.add("aaaaa");
    lst.add("bbbbb");
    lst.add("ccccc");
    lst.add("ddddd");
    for (String str : lst) {
    	System.out.println("str = " + str);
    }
    
    // 4. List of List
    List[] lists = { lst, lst };
    for (List<String> l : lists) {
    	for (String str : l) {
    		System.out.println("str = " + str);
    	}
    }

    10 Static Import #

    10.1 static member/method import #

    Tiger에서는 다른 클래스의 member와 method를 import 할수 있다. 단, static 일 경우만 가능하다.

    //예를 들어 System.out.println() 이라는 것을 사용하기 위해서는 다음의 import 문이 필요하다.
    
    import java.lang.System;   //물론 java.lang 이기에 import 문이 필요없지만 예를 들자면 그렇다는 것이다.&^^
    //허나, Tiger에서는 다음과 같이 사용할수 있다.
    import static java.lang.System.out;
    ...
    out.println(...);
    
    // method를 import 한다면..
    import static java.lang.System.out.println;
    ...
    println(...);

    11 References #


    from: http://www.twelo.com/96
    Posted by 1010
    01.JAVA/Java2009. 12. 21. 18:10
    반응형

    Properties configProps = new Properties();
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    InputStream inputStream = classLoader.getResourceAsStream(propertyFilePath);
    configProps.load(inputStream);


    ------------------------------------------------



    // 메일보낼사람 정보 가져오기
      // db.properties 가져오기 (* db.properties file은 DBCheckThread.class와 같은곳에  존재...)
      Properties properties = new Properties();
      InputStream inputstream = getClass().getResourceAsStream("/senduser.properties");
      properties.load(inputstream);
      String sendUser = properties.getProperty("mailuser");

      String toUser = "hanho9@nate.com";

    Posted by 1010
    01.JAVA/Java2009. 12. 21. 18:06
    반응형
    String[] a = {"a", "b"};
    System.out.println(Arrays.asList(a));
    Posted by 1010
    반응형

    Spring - Java/J2EE Application Framework

    Reference Documentation

    Rod Johnson

    Juergen Hoeller

    Alef Arendsen

    Colin Sampaleanu

    Darren Davison

    Dmitriy Kopylenko

    Thomas Risberg

    Mark Pollack

    Rob Harrop

    Version 1.2.2

    Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

    (Work in progress)


    Table of Contents

    서문
    1. 소개
    1.1. 개요
    1.2. 사용 시나리오
    2. 배경 지식
    2.1. 제어의 역행 / 의존 주입(Inversion of Control / Dependency Injection)
    3. Beans, BeanFactory 그리고 ApplicationContext
    3.1. 소개
    3.2. BeanFactory 와 BeanDefinitions - 기초
    3.2.1. BeanFactory
    3.2.2. BeanDefinition
    3.2.3. bean 클래스
    3.2.3.1. 생성자를 통한 Bean 생성
    3.2.3.2. 정적 factory메소드를 통한 Bean 생성
    3.2.3.3. 인스턴스 factory메소드를 통한 Bean 생성
    3.2.4. bean 구분자 (id 와 name)
    3.2.5. 싱글톤이나 비-싱글톤(non-singleton)
    3.3. 프라퍼티, 협력자(collaborators), autowiring 과 의존성 체크
    3.3.1. bean프라퍼티와 협력자(collaborators) 셋팅하기
    3.3.2. 생성자의 인자 분석
    3.3.2.1. 생성자의 인자 타입 대응(match)
    3.3.2.2. 생성자의 인자 인덱스
    3.3.3. bean프라퍼티와 상세화된 생성자의 인자
    3.3.3.1. value와 ref 간략화한(shortcut) 폼
    3.3.4. 메소드 삽입
    3.3.4.1. 룩업(Lookup) 메소드 삽입
    3.3.4.2. 임의의 메소드 교체
    3.3.5. depends-on 사용하기
    3.3.6. Autowiring 협력자
    3.3.7. 의존성을 위한 체크
    3.4. bean의 성질을 커스터마이징하기.
    3.4.1. Lifecycle 인터페이스
    3.4.1.1. InitializingBean / init-method
    3.4.1.2. DisposableBean / destroy-method
    3.4.2. 당신이 누구인지 알고 있다.(Knowing who you are)
    3.4.2.1. BeanFactoryAware
    3.4.2.2. BeanNameAware
    3.4.3. FactoryBean
    3.5. 추상 그리고 자식 bean정의
    3.6. BeanFactory와 상호작동하기
    3.6.1. BeanFactory의 생성물이 아닌 FactoryBean 얻기
    3.7. BeanPostprocessors로 bean 커스터마이징하기
    3.8. BeanFactoryPostprocessors를 가진 bean factory커스터마이징하기
    3.8.1. PropertyPlaceholderConfigurer
    3.8.2. PropertyOverrideConfigurer
    3.9. 추가적인 사용자지정 PropertyEditors 등록하기
    3.10. 존재하는 bean을 위한 별칭을 추가하기 위한 별칭 요소 사용하기.
    3.11. ApplicationContext에 대한 소개
    3.12. ApplicationContext에 추가된 기능
    3.12.1. MessageSource 사용하기
    3.12.2. events 전파하기
    3.12.3. Spring내에서 자원(resources) 사용하기
    3.13. ApplicationContext내에서 사용자정의 행위
    3.13.1. ApplicationContextAware 표시자(marker) 인터페이스
    3.13.2. BeanPostProcessor
    3.13.3. BeanFactoryPostProcessor
    3.13.4. PropertyPlaceholderConfigurer
    3.14. 추가적인 사용자정의 PropertyEditors 등록하기
    3.15. 프라퍼티 표현에서 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
    3.16. 필드값으로부터 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
    3.17. 다른 메소드를 호출하고 선택적으로 반환값을 사용한다.
    3.18. 하나의 파일로부터 다른것으로 bean정의를 끌어오기
    3.19. 웹 애플리케이션으로부터 ApplicationContext생성하기.
    3.20. Glue 코드와 좋지않은 싱글톤
    3.20.1. SingletonBeanFactoryLocator 와 ContextSingletonBeanFactoryLocator을 사용하기
    4. PropertyEditors, data binding, validation and the BeanWrapper
    4.1. 소개
    4.2. Binding data를 사용한DataBinder
    4.3. Bean 조작(manipulation)과 BeanWrapper
    4.3.1. Setting 과 getting 기본과 내포된 설정들
    4.3.2. 내장 PropertyEditors, 변환 타입들(Built-in PropertyEditors, converting types)
    4.3.3. 언급할 가치가 있는 다른 기능들.
    5. Spring AOP: Spring을 이용한 Aspect 지향적인 프로그래밍
    5.1. 개념
    5.1.1. AOP 개념
    5.1.2. Spring AOP의 기능과 대상
    5.1.3. Spring 내 AOP 프록시
    5.2. Spring내 Pointcuts
    5.2.1. 개념
    5.2.2. pointcuts에서의 작업(operation)
    5.2.3. 편리한 pointcut 구현물
    5.2.3.1. 정적 pointcuts
    5.2.3.2. 동적 pointcuts
    5.2.4. Pointcut 수퍼클래스(superclasses)
    5.2.5. 사용자지정 pointcuts
    5.3. Spring 내 Advice타입들
    5.3.1. Advice 생명주기
    5.3.2. Spring내 Advice 타입들
    5.3.2.1. Interception around advice
    5.3.2.2. 전(Before) advice
    5.3.2.3. Throws advice
    5.3.2.4. advice를 반환한 후(after returning advice)
    5.3.2.5. Introduction advice
    5.4. Spring내 Advisors
    5.5. AOP프록시를 생성하기 위한 ProxyFactoryBean사용하기
    5.5.1. 기본
    5.5.2. 자바빈 프라퍼티
    5.5.3. 프록시 인터페이스
    5.5.4. 프록시 클래스
    5.5.5. 'global' advisor 사용하기
    5.6. 편리한 프록시 생성
    5.6.1. TransactionProxyFactoryBean
    5.6.2. EJB 프록시
    5.7. 간결한 프록시 정의
    5.8. ProxyFactory로 프로그램적으로 AOP프록시를 생성하기.
    5.9. advised 객체 조작하기.
    5.10. "autoproxy" 기능 사용하기
    5.10.1. autoproxy bean정의
    5.10.1.1. BeanNameAutoProxyCreator
    5.10.1.2. DefaultAdvisorAutoProxyCreator
    5.10.1.3. AbstractAdvisorAutoProxyCreator
    5.10.2. 메터데이타-지향 자동 프록시 사용하기.
    5.11. TargetSources 사용하기
    5.11.1. 핫 스왑가능한 대상 소스
    5.11.2. 풀링 대상 소스
    5.11.3. 프로토 타입 대상 소스
    5.11.4. ThreadLocal 대상 소스
    5.12. 새로운 Advice 타입을 정의하기
    5.13. 추가적으로 읽을거리와 자원들
    5.14. 로드맵
    6. AspectJ 통합
    6.1. 개요
    6.2. Spring IoC를 사용하여 AspectJ 설정하기.
    6.2.1. "싱글톤" aspects
    6.2.1.1. 예제
    6.2.1.2. 정렬 이슈
    6.2.2. 싱글톤 형식이 아닌 aspect
    6.2.3. Gotchas
    6.3. 목표 Spring advice를 위한 AspectJ 포인트컷(pointcut) 사용하기
    6.4. AspectJ를 위한 Spring aspect
    7. 트랜잭션 관리
    7.1. Spring 트랜잭션 추상화
    7.2. 트랜잭션 전략
    7.3. 프로그래밍적인 트랜잭션 관리
    7.3.1. TransactionTemplate 사용하기
    7.3.2. PlatformTransactionManager 사용하기
    7.4. 선언적 트랜잭션 관리
    7.4.1. BeanNameAutoProxyCreator, 또 다른 선언적 접근방법
    7.5. 프로그래밍적/선언적 트랜잭션 관리 중 선택하기
    7.6. 트랜잭션 관리를 위한 어플리케이션 서버가 필요한가?
    7.7. 공통적인 문제
    8. 소스 레벨 메타데이타 지원
    8.1. 소스-레벨 메타데이타
    8.2. Spring의 메타데이타 지원
    8.3. Jakarta Commons Attributes과 통합
    8.4. 메타데이타와 Spring AOP 자동 프록시
    8.4.1. 기초
    8.4.2. 선언적인 트랜잭션 관리
    8.4.3. 풀링(Pooling)
    8.4.4. 사용자정의 메타데이타
    8.5. MVC 웹티어 설정을 최소화하기 위한 속성 사용하기
    8.6. 메타데이타 속성의 다른 사용
    8.7. 추가적인 메타데이타 API를 위한 지원 추가하기
    9. DAO support
    9.1. 소개
    9.2. 일관된 예외 구조
    9.3. DAO지원을 위한 일관된 추상클래스
    10. JDBC를 사용한 데이터 접근
    10.1. 소개
    10.2. 기본적인 JDBC처리와 에러 처리를 위한 JDBC Core클래스 사용하기
    10.2.1. JdbcTemplate
    10.2.2. DataSource
    10.2.3. SQLExceptionTranslator
    10.2.4. Statements 실행하기
    10.2.5. 쿼리문 실행하기
    10.2.6. 데이터베이스 수정하기
    10.3. 데이터베이스에 연결하는 방법을 제어하기
    10.3.1. DataSourceUtils
    10.3.2. SmartDataSource
    10.3.3. AbstractDataSource
    10.3.4. SingleConnectionDataSource
    10.3.5. DriverManagerDataSource
    10.3.6. DataSourceTransactionManager
    10.4. 자바 객체처럼 JDBC작업을 모델링 하기.
    10.4.1. SqlQuery
    10.4.2. MappingSqlQuery
    10.4.3. SqlUpdate
    10.4.4. StoredProcedure
    10.4.5. SqlFunction
    11. 객체-관계 연결자(O/R Mappers)를 이용한 데이터 접근
    11.1. 소개
    11.2. Hibernate
    11.2.1. 자원 관리
    11.2.2. 애플리케이션 컨텍스트내에서 자원 정의
    11.2.3. Inversion of Control: Template and Callback
    11.2.4. 탬플릿 대신에 AOP인터셉터 적용하기.
    11.2.5. 프로그램의 트랜잭션 구분(Demarcation)
    11.2.6. 선언적인 트랜잭션 구분
    11.2.7. 트랜잭션 관리 전략
    11.2.8. 컨테이너 자원 대 로컬 자원
    11.2.9. 샘플들
    11.3. JDO
    11.4. iBATIS
    11.4.1. 1.3.x and 2.0 사이의 개요와 차이점
    11.4.2. iBATIS 1.3.x
    11.4.2.1. SqlMap을 셋업하기
    11.4.2.2. SqlMapDaoSupport 사용하기
    11.4.2.3. 트랜잭션 관리
    11.4.3. iBATIS 2
    11.4.3.1. SqlMap 셋업하기
    11.4.3.2. SqlMapClientDaoSupport 사용하기
    12. 웹 MVC framework
    12.1. 웹 MVC framework 소개
    12.1.1. 다른 MVC구현물의 플러그인 가능성
    12.1.2. Spring MVC의 특징
    12.2. DispatcherServlet
    12.3. 컨트롤러
    12.3.1. AbstractController 와 WebContentGenerator
    12.3.2. 간단한 다른 컨트롤러
    12.3.3. MultiActionController
    12.3.4. CommandControllers
    12.4. Handler mappings
    12.4.1. BeanNameUrlHandlerMapping
    12.4.2. SimpleUrlHandlerMapping
    12.4.3. HandlerInterceptors 추가하기
    12.5. view와 view결정하기
    12.5.1. ViewResolvers
    12.5.2. ViewResolvers 묶기(Chaining)
    12.6. 로케일 사용하기.
    12.6.1. AcceptHeaderLocaleResolver
    12.6.2. CookieLocaleResolver
    12.6.3. SessionLocaleResolver
    12.6.4. LocaleChangeInterceptor
    12.7. 테마(themes) 사용하기
    12.8. Spring의 multipart (파일업로드) 지원
    12.8.1. 소개
    12.8.2. MultipartResolver 사용하기
    12.8.3. 폼에서 파일업로드를 다루기.
    12.9. 예외 다루기
    13. 통합 뷰 기술들
    13.1. 소개
    13.2. JSP & JSTL
    13.2.1. 뷰 해결자(View resolvers)
    13.2.2. 'Plain-old' JSPs 대(versus) JSTL
    13.2.3. 추가적인 태그들을 쉽게 쓸수 있는 개발
    13.3. Tiles
    13.3.1. 의존물들(Dependencies)
    13.3.2. Tiles를 통합하는 방법
    13.3.2.1. InternalResourceViewResolver
    13.3.2.2. ResourceBundleViewResolver
    13.4. Velocity & FreeMarker
    13.4.1. 의존물들(Dependencies)
    13.4.2. 컨텍스트 구성(Context configuration)
    13.4.3. 생성 템플릿들(Creating templates)
    13.4.4. 진보한 구성(Advanced configuration)
    13.4.4.1. velocity.properties
    13.4.4.2. FreeMarker
    13.4.5. 바인드(Bind) 지원과 폼(form) 핸들링
    13.4.5.1. 바인드(bind) 매크로
    13.4.5.2. 간단한 바인딩
    13.4.5.3. 폼 input 생성 매크로
    13.4.5.4. HTML회피를 오버라이드하고 XHTML호환 태그를 만든다.
    13.5. XSLT
    13.5.1. 나의 첫번째 단어
    13.5.1.1. Bean 정의
    13.5.1.2. 표준적인 MVC 컨트롤러 코드
    13.5.1.3. 모델 데이터를 XML로 변환하기
    13.5.1.4. view프라퍼티 정의하기
    13.5.1.5. 문서 변형
    13.5.2. 요약
    13.6. 문서 views (PDF/Excel)
    13.6.1. 소개
    13.6.2. 설정 그리고 셋업
    13.6.2.1. 문서 view정의
    13.6.2.2. 컨트롤러 코드
    13.6.2.3. Excel view를 위한 하위클래스 만들기
    13.6.2.4. PDF view를 위한 하위클래스 만들기
    13.7. JasperReports
    13.7.1. 의존성
    13.7.2. 설정
    13.7.2.1. ViewResolver 설정하기
    13.7.2.2. View 설정하기
    13.7.2.3. 리포트 파일에 대해
    13.7.2.4. JasperReportsMultiFormatView 사용하기
    13.7.3. ModelAndView 활성화하기
    13.7.4. 하위-리포트로 작동하기
    13.7.4.1. 하위-리포트 파일 설정하기
    13.7.4.2. 하위-리포트 데이터소스 설정하기
    13.7.5. 전파자(Exporter) 파라미터 설정하기
    14. 다른 웹 프레임워크들과의 통합
    14.1. 소개
    14.2. JavaServer Faces
    14.2.1. DelegatingVariableResolver
    14.2.2. FacesContextUtils
    14.3. Struts
    14.3.1. ContextLoaderPlugin
    14.3.1.1. DelegatingRequestProcessor
    14.3.1.2. DelegatingActionProxy
    14.3.2. ActionSupport 클래스들
    14.4. Tapestry
    14.4.1. 아키텍쳐
    14.4.2. 구현체
    14.4.2.1. 샘플 어플리케이션 컨텍스트
    14.4.2.2. Tapestry pages에서 빈들을 얻어오기
    14.4.2.3. 어플리케이션 컨텍스트를 Tapestry에 드러내기
    14.4.2.4. Component 정의 파일들
    14.4.2.5. abstract accessors 추가하기
    14.4.3. 개요
    14.5. WebWork
    15. JMS
    15.1. 소개
    15.2. 도메인 단일화(unification)
    15.3. JmsTemplate
    15.3.1. ConnectionFactory
    15.3.2. 트랜잭션 관리
    15.3.3. 목적지(destination) 관리
    15.4. JmsTemplate 사용하기
    15.4.1. 메시지 보내기
    15.4.2. 동기적으로 받기(Receiving)
    15.4.3. 메시지 변환기(converter) 사용하기
    15.4.4. SessionCallback 과 ProducerCallback
    16. EJB에 접근하고 구현하기
    16.1. EJB에 접근하기
    16.1.1. 개념
    16.1.2. local SLSBs에 접근하기
    16.1.3. remote SLSB에 접근하기
    16.2. Spring의 편리한 EJB구현물 클래스를 사용하기.
    17. Spring을 사용한 원격(Remoting)및 웹서비스
    17.1. 소개
    17.2. RMI를 사용한 서비스 드러내기
    17.2.1. RmiServiceExporter를 사용하여 서비스 내보내기
    17.2.2. 클라이언트에서 서비스 링크하기
    17.3. HTTP를 통해 서비스를 원격으로 호출하기 위한 Hessian 이나 Burlap을 사용하기.
    17.3.1. Hessian을 위해 DispatcherServlet을 묶기.
    17.3.2. HessianServiceExporter를 사용하여 bean을 드러내기
    17.3.3. 클라이언트의 서비스로 링크하기
    17.3.4. Burlap 사용하기
    17.3.5. Hessian 이나 Burlap을 통해 드러나는 서비스를 위한 HTTP 기본 인증 적용하기
    17.4. HTTP호출자를 사용하여 서비스를 드러내기
    17.4.1. 서비스 객체를 드러내기
    17.4.2. 클라이언트에서 서비스 링크하기
    17.5. 웹 서비스
    17.5.1. JAX-RPC를 사용하여 서비스를 드러내기
    17.5.2. 웹 서비스에 접근하기
    17.5.3. Register Bean 맵핑
    17.5.4. 자체적인 핸들러 등록하기
    17.5.5. XFire를 사용하여 웹 서비스를 드러내기
    17.6. 자동-탐지(Auto-detection)는 원격 인터페이스를 위해 구현되지 않는다.
    17.7. 기술을 선택할때 고려사항.
    18. Spring 메일 추상 계층을 사용한 이메일 보내기
    18.1. 소개
    18.2. Spring 메일 추상화 구조
    18.3. Spring 메일 추상화 사용하기
    18.3.1. 플러그인할 수 있는 MailSender 구현클래스들
    18.4. JavaMail MimeMessageHelper 사용하기
    18.4.1. 간단한 MimeMessage 를 생성하고 보내기
    18.4.2. 첨부파일들과 inline 리소스들을 보내기
    19. Quartz 혹은 Timer 를 사용한 스케쥴링
    19.1. 소개
    19.2. OpenSymphony Quartz 스케쥴러 사용하기
    19.2.1. JobDetailBean 사용하기
    19.2.2. MethodInvokingJobDetailFactoryBean 사용하기
    19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기
    19.3. JDK Timer support 사용하기
    19.3.1. 임의의 timers 생성하기
    19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기
    19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기
    20. JMX 지원
    20.1. 소개
    20.2. 당신의 bean을 JMX로 내보내기(Exporting)
    20.2.1. MBeanServer 생성하기
    20.2.2. 늦게 초기화되는(Lazy-Initialized) MBeans
    20.2.3. MBean의 자동 등록
    20.3. 당신 bean의 관리 인터페이스를 제어하기
    20.3.1. MBeanInfoAssembler 인터페이스
    20.3.2. 소스레벨 메타데이타(metadata) 사용하기
    20.3.3. JDK 5.0 Annotations 사용하기
    20.3.4. 소스레벨 메타데이타 타입들
    20.3.5. AutodetectCapableMBeanInfoAssembler 인터페이스
    20.3.6. 자바 인터페이스를 사용하여 관리 인터페이스 정의하기
    20.3.7. MethodNameBasedMBeanInfoAssembler 사용하기
    20.4. 당신의 bean을 위한 ObjectName 제어하기
    20.4.1. Properties로 부터 ObjectName 읽기
    20.4.2. MetadataNamingStrategy 사용하기
    20.5. JSR-160 연결자(Connectors)로 당신의 bean을 내보내기
    20.5.1. 서버측 연결자(Connectors)
    20.5.2. 클라이언트측 연결자
    20.5.3. Burlap/Hessian/SOAP 곳곳의 JMX
    20.6. 프록시를 통해서 MBean에 접속하기
    21. Testing
    21.1. 단위 테스팅
    21.2. 통합 테스팅
    21.2.1. 컨텍스트 관리와 캐슁
    21.2.2. 테스트 클래스 인스턴스들의 의존성 주입
    21.2.3. 트랜잭션 관리
    21.2.4. 편리한 변수들
    21.2.5. 예시
    21.2.6. 통합 테스트 실행하기
    A. spring-beans.dtd
    Posted by 1010
    반응형
    // addMethod - By John Resig (MIT Licensed)
    function addMethod(object, name, fn){
        var old = object[name];
        object[name] = function(){
            if (fn.length == arguments.length)
                return fn.apply(this, arguments);
            else
                if (typeof old == 'function')
                    return old.apply(this, arguments);
        };
    }

    // Now setup the methods
    function Users(){
        addMethod(this, "find", function(){
            // Find all users...
        });
        addMethod(this, "find", function(name){
            // Find a user by name
        });
        addMethod(this, "find", function(first, last){
            // Find a user by first and last name
        });
    }

    // Now use the methods
    var users = new Users();
    users.find(); // Finds all
    users.find("John"); // Finds users by name
    users.find("John", "Resig"); // Finds users by first and last name
    users.find("John", "E", "Resig"); // Does nothing



    출처 : http://openframework.or.kr/Wiki.jsp?page=JavaScriptMethodOverloading
    Posted by 1010
    01.JAVA/Java2009. 12. 21. 16:31
    반응형

    중급 자바 개발자의 난관, 클래스 로딩 문제 분석하기



    필자 | 장동수, 한컴 씽크프리 모바일 오피스 개발팀 dsjang@thinkfree.com

    2007년 12월 11일

    초급을 넘어 중급 단계로 들어서는 자바 개발자에게 몇 가지 난관 중 하나가 클래스 로딩 문제입니다.

    적당한 jar 파일을 찾아 적당한 디렉터리에 넣어주면 해결되는 단순한 문제부터, 커스텀 클래스 로더가 필요한 경우까지 다양한 원인과 해결책이 있겠지만, 원리를 이해하지 않고 검색 엔진의 힘에 의지해 문제를 해결하다 보면 더 큰 문제를 만들게 됩니다.

    이 연재에서는 클래스 로딩의 작동 원리에서 시작해, 개발하는 과정에서 발생할 수 있는 다양한 클래스 로딩 관련 문제에 대한 원인과 해결책을 4회에 걸쳐 설명하고 있습니다.

    또한 직접 확인해 볼 수 있는 풍부한 예제와 실습을 통해, 이 연재에서 설명하지 않은 다른 형태의 클래스 로딩 문제를 만나더라도 신속하게 원인을 규명하고 해결책을 찾을 수 있는 응용 능력을 습득할 수 있을 것입니다.

    • 1회: 클래스 로딩과 디버깅 툴 소개(한글)

    • 첫 번째 연재에서는 클래스 로더 위임 모델과 각각의 클래스 로더를 제어하기 위한 JVM의 명령어 옵션을 알아봅니다. 그리고 클래스 로딩 과정을 단계별로 설명합니다. 마지막으로 IBM의 JDK에 내장된 도구들을 사용해 이러한 과정들을 직접 확인하는 방법을 설명합니다.

    • 2회: 기본적인 클래스 로딩 예외(Exception)(한글)

    • 흔히 볼 수 있는 ClassNotFoundException, NoClassDefFoundError 같은 예외부터 ClassCircularityError, ExceptionInInitializerError 같은 평생 한 번 보기도 힘든 예외까지 클래스 로딩과 관련된 거의 모든 예외와 에러(error) 들에 대해 자세히 알아봅니다.

    • 3회: 특이한 클래스 로딩 문제 해결(한글)

    • 클래스패스 끝에 붙은 '/' 문자 때문에 발생하는 사소한 문제부터, 클래스 로더 위임 모델과 클래스 가시성 때문에 발생하는 문제, 커스텀 클래스 로더를 만들 때 부주의로 발생하는 문제, 그리고 클래스로 로더가 가비지 컬렉션되면서 발생하는 문제까지, 클래스 로딩시 일어날 수 있는 더 복잡한 문제들에 대해 자세히 설명합니다.

    • 4회: 교착 상태와 제약 조건(한글)

    • 마지막으로 클래스 로더 교착 상태, 클래스 로더 제약 조건 위반 같은 쉽게 발생하지 않지만 일단 발생하면 해결하기 어려운 문제들에 대해 자세히 설명합니다.
    Posted by 1010
    반응형

    출처 : http://www.javajigi.net/display/OSS/Spring+MVC

    Table of Contents

    Spring Introduction

    Spring에 대한 전반적인 내용은 아래를 참조하기 바란다.
    http://wiki.javajigi.net/pages/viewpage.action?pageId=280
    http://martinfowler.com/articles/injection.html

    Spring MVC


    Spring's web MVC framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for upload files.
    The default handler is a very simple Controller interface, just offering a ModelAndView handleRequest(request,response) method.
    This can already be used for application controllers, but you will prefer the included implementation hierarchy, consisting of, for example AbstractController, AbstractCommandController and SimpleFormController.
    Application controllers will typically be subclasses of those. Note that you can choose an appropriate base class: If you don't have a form, you don't need a FormController. This is a major difference to Struts.

    A flexible MVC web application framework, built on core Spring functionality.
    This framework is highly configurable via strategy interfaces, and accommodates multiple view technologies like JSP, Velocity, Tiles, iText, and POI. Note that a Spring middle tier can easily be combined with a web tier based on any other web MVC framework, like Struts, WebWork, or Tapestry.

    Spring의 웹 MVC framework는 설정가능한 핸들러 맵핑들, view분석, 로케일과 파일 업로드를 위한 지원같은 테마분석과 함께 핸들러에 요청을 할당하는 DispatcherServlet을 기반으로 디자인되었다.
    디폴트 핸들러는 ModelAndView handleRequest(request,response)메소드를 제공하는 매우 간단한 컨트롤러 인터페이스이다.
    이것은 이미 애플리케이션 컨트롤러를 위해 사용될수 있지만 AbstractController, AbstractCommandController그리고 SimpleFormController같은 것들을 포함한 구현 구조를 포함한것을 더 선호할것이다.
    애플리케이션 컨트롤러들은 전형적으로 이것들의 하위 클래스가 된다. 당신이 선호하는 빈 클래스를 선택하도록 하라.
    만약에 당신이 form을 가지지 않는다면 당신은 FormController가 필요없다. 이것이 Struts와 가장 큰 차이점이다.

    유연한 MVC 웹 어플리케이션 프레임웍은 Core Spring과 상호작용하여 만들어진다.
    이 프레임웍은 전략적인 인터페이스를 거쳐 높은수준으로 만들어진다. 그리고, JSP, Velocity, Tiles, iText, POI처럼 다양한 view 기술을 수용한다.
    Spring middle tier는 Struts, WebWork, Tapestry와 같은 다른 웹 MVC 프레임워크를 기반으로 한 웹티어와 결합할수 있다.
    Spring프레임웍에서 Model-View-Controller를 지원하는 기능이다.

    강좌가 올라와있네요...
    Spring MVC에서 클라이언트 요청의 처리 과정을 먼저 살펴보시면 이해가 되실거 같습니다.
    http://wiki.javajigi.net/pages/viewpage.action?pageId=1133

    참 고 : http://www.springframework.org/docs/reference/
    번역본 : http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/

    설정 방법

    Spring MVC를 사용하기 위해서는

    • web-app/WEB-INF/lib 아래로 spring.jar 또는 spring-webmvc.jar파일을 복사한다.
    • web-app/WEB-INF/web.xml에 DispatcherServlet를 설정한다.
    • <web.xml>
       
      
      <servlet>
      	<servlet-name>action</servlet-name>
      	<servlet-class>
      		org.springframework.web.servlet.DispatcherServlet</servlet-class>
      	<load-on-startup>1</load-on-startup>
      </servlet>	
      <servlet-mapping>
      	<servlet-name>action</servlet-name>
      	<url-pattern>*.do</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
      	<servlet-name>action</servlet-name>
      	<url-pattern>*.html</url-pattern>
      </servlet-mapping>
      

      .do, .html 로 끝나는 모든 요청은 DispatcherServlet에서 담당한다.

    • DispattcherServlet이 초기화 할 때 디폴트로 사용하는 빈 설정파일의 이름은 "서블릿 이름-servlet.xml"이 된다.
    • web-app/WEB-INF/web.xml에서 설정한 <servlet-name>인 action-servlet.xml 파일을 생성한다.

    즉 spring.jar 와 web.xml에 DispatcherServlet 을 사용한다는 선언 그리고 서블릿 이름-servlet.xml 을 작성하면 사용할 수 있다.

    다양한 예제들

    Spring MVC를 사용한 초간단 예제

    링크된 강좌를 살펴보고 대략적인 것을 파악한다면 Spring MVC 사용하기 위해서 개발자들이 만들어야 하는 것은 Controller 라는 것을 알게 될 것이다. 물론 화면에 보여주는 jsp(여기선 jsp 를 연결) 파일, 빈 설정 파일을 만드는 것은 당연한 것이다.

    Controller를 생성하고 빈 설정파일에 맵핑 하는 방법 등의 관계를 보여주기 위한 데이터를 가져와서 List 형태로 보여주는 예제이다

    • Controller
       
      package com.tmax.spring.web;
      
      import java.util.List;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import com.tmax.spring.service.iface.BbsServiceIface;
      
      public class BbsController implements Controller {
      	
      	private BbsServiceIface bbsService;
      	
      	public void setBbsService(BbsServiceIface newBbsService){
      		this.bbsService = newBbsService;
      	}
      	
      	public ModelAndView handleRequest(HttpServletRequest arg0,
      			HttpServletResponse arg1) throws Exception {
      	    
              List list = bbsService.findBbsList();
              
              return new ModelAndView("bbs/list","bbsList", list);
         }
      
      }
      

    위에서 보면 직접적으로 Controller 인터페이스를 구현해서 ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; 를 구현한다. Spring MVC에서 제공하는 다양한 Controller 들은 모두 Controller 인터페이스를 구현하기 때문에 가장 기본적인 구조라고 생각할 수 있다.

    Service 객체는 setter Injection을 통해 생성된 것이다. 이것은 이미 앞에서 다 이해했을 것이다.
    handleRequest(... ) 안에서 실제 메소드를 호출한 뒤 ModelAndView 객체를 통해 리턴을 하였다.

    링크된 Spring MVC의 요청에 대한 생명주기를 보게 되면 비지니스 계층에서 전달된 모델 데이타와 클라이언트에게 보여줄 뷰화면에 대한 정보를 ModelAndView 클래스에 담아서 반환을 하게 되어 있다. 즉 전달 데이타 모델, 화면이름, 화면에서 사용될 객체 이름 등을 담아서 ModelAndView객체를 생성, 리턴한다.

    ModelAndView 객체의 View정보가 논리적인 view이름(String) 인 경우 빈 설정 파일에 정의되어 있는 ViewResolver클래스를 이용하여 클라이언트에세 출력할 화면이름을 만들어 낼 수 있다. ModelAndView의 첫번째 인자인 "bbs/list"가 view 이름이기 때문에 설정파일의 ViewResolver를 만나서 리턴되는 화면의 전체이름이 완성되는 것이다.

    • action-servlet.xml
        
          <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
      	
          <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
      
          <!-- View Resolver for JSPs -->
          <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            		<property name="prefix" value="/jsp/"/>
                        <property name="suffix" value=".jsp"/>
          </bean>
      

    설정파일을 보면 ViewResolver로 InternalResourceViewResolver클래스를 사용한다.
    그리고 JSTLView를 사용해서 jsp 로 화면을 출력할 것이라는 것을 알 수 있다. 위에 설정된 것과 ModelAndView객체에 넘긴 view이름을 조합해 보면 /jsp/bbs/list.jsp 가 되는 것이다. (prefix+view 이름+suffix )

    설정파일을 보면 BeanNameUrlHandlerMapping 클래스를 사용해서 Controller와 URL을 맵핑해주는 것을 알 수 있다.
    만일 빈 설정파일에 HandlerMapping이 설정되어 있지 않으면 default 로 BeanNameURLHandlerMapping을 설정한다.
    맵핑 방법에는 BeanNameURLHandlerMapping, SimpleUrlHandlerMapping,CommonsPathMapHandlerMapping 등이 있다.

    • BeanNameURLHandlerMapping
      빈 이름과 URL을 mapping 하는 방법이다.
          <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
       

    즉 요청이 ..../bbs/listBbs.do가 오면 BbsController 클래스를 호출하겠다는 의미이다. name에는 <bean name="/bbs/listBbs.do /bbs/listBbs2.do".. /> 로 여러 개를 기술할 수 있다.

    • SimpleUrlHandlerMapping
      매핑에 대한 정보를 각각의 Controller에서 설정하는 것이 아니라 하나의 저장소에서 관리하는 것이다.
      Controller를 개발하는 개발자들은 빈을 정의하기만 하고 이 Controller가 어떻게 맵핑되어서 사용하는지에 대해서는 몰라도 된다.
      위의 설정파일을 SimpleUrlHandlerMapping으로 바꾸어 보면
         
          <bean id="bbsController" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
          
         <bean id="urlMapping" 
              class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
              <property name="mappings">
                  <props>
                      <prop key ="/bbs/listBbs.do">bbsController</prop>
      	   </props>
              </property>
          </bean>
      
       

    실제 Controller에 대한 정의는 일반 빈을 정의 할 때와 다르지 않게 된다. 맵핑에 대한 정보는 별도의 빈(urlMapping)에서 관리되고 있다. 만일 더욱 많은 Controller가 생성되고 맵핑을 해 줘야 할 때 모든 매핑 정보는 한곳에서 관리를 할 수 있게 되는 것이다. Controller 마다 맵핑정보까지 나열할 필요가 없게 된다.

    urlMapping 빈에서 Controller와 URL을 맵핑할 때 <props/>를 사용해서 기술할 수 있다. 또 다른 방법은 <props/>를 사용하지 않고 별도의 Properties 파일에서 관리 할 수도 있다.

    • CommonsPathMapHandlerMapping

    Spring MVC를 사용하여 다중요청 처리하기

    개발자들이 Controller 만 만들면 되지만 개발하면서 페이지, 기능이 추가 될 때마다 새로운 Contoller를 만드는 것은 굉장히 짜증이 나는 작업이다. 게다가 점점 관리해야 하는 파일의 수가 많아져서 빈 설정파일도 점점 복잡해질 것이다.이 같은 상황을 해결하기 위하여 Spring MVC는 MultiActionController를 제공한다. MultiActionController은 하나의 Controller를 구현함으로써 다수의 요청을 처리하는 것이 가능하다.

    아래 예는 회원가입,수정을 통해 MultiActionController 구현을 보여준다.

    Controller 객체인 MemberController.java

    MemberController.java
    package net.javajigi.member.web;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import net.javajigi.member.model.Member;
    import net.javajigi.member.service.MemberService;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
    
    public class MemberController extends MultiActionController{
    	
    	private MemberService memberService = null;
    	public void setMemberService(MemberService memberService){
    		this.memberService = memberService;
    	}
    	
    	public ModelAndView add(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
    		
    		Member command = new Member();
    		bind(request,command);
    		
                      memberService.addMember(command);
    		
    		return new ModelAndView("main");		
    		
    	}
    	
    	public ModelAndView update(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
    		
    		Member command = new Member();
                      bind(request,command);
    		memberService.updateMember(command);
    		
    		return new ModelAndView("list");
    		
    		
    	}
    
    }
    

    위의 예제는 add(가입), update(수정)을 구현하기 위해 MultiActionController를 상속받은 Controller 이다.
    각각의 메소드에 로직이나 서비스 호출 부분들의 소스를 구현하다.
    add 메소드를 보면 화면에서 입력받은 내용을 bind()를 사용해 domain 객체에 넘기고 있다.(이 내용은 나중에..)
    그 뒤 서비스의 addMember()를 호출한다.
    처리 후 리턴될 페이지를 설정한다.(add->main.jsp, update->list.jsp)

    Controller를 작성 후 webapps/WEB-INF/action-servlet.xml 파일에 빈을 매핑한다.

    action-servlet.xml
    <bean id="memberService" class="net.javajigi.member.service.MemberService"/>
    
    <bean name="/member/addMember.do /member/updateMember.do" class="net.javajigi.member.web.MemberController">
    	<property name="methodNameResolver">
    		<ref local="memberControllerMethodNameResolver"/>
    	</property>	
    	<property name="memberService">
    		<ref local="memberService"/>
    	</property>			
    </bean>	
    
    <bean id="memberControllerMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
    	<property name="mappings"> 
    		<props>
    			<prop key="/member/addMember.do">add</prop>
    			<prop key="/member/updateMember.do">update</prop>
    		</props>
    	</property>
    </bean>
    

    MemberController는 두 개의 URL(/member/addMember.do,/member/updateMember.do)에 맵핑이 되었다.각각의 URL이 호출될 때 MemberController 안의 어떤 메소드가 호출될 지는 "memberControllerMethodNameResolver"빈에서 PropertiesMethodNameResolver를 통해 매핑이 된다. 결국 /member/addMember.do 가 호출되면 MemberController의 add 메소드가 /member/updateMember.do가 호출되면 update 메소드가 호출된다.

    결국 위의 예는 빈 설정파일에서 URL을 다르게 함으로써 메소드에 접근할 수 있도록 설정한 것이다. (PropertiesMethodNameResolver 클래스를 통해서)

    <bean name="/member/member.do" class="net.javajigi.member.web.MemberController">
    	<property name="methodNameResolver">
    		<ref local="methodNameResolver"/>
    	</property>
    </bean>
    
    <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
    	<property name="paramName"> 
    		<value>method</value>
    	</property>
    	<property name="defaultMethodName"> 
    		<value>add</value>
    	</property>
    </bean>
    

    위의 예를 보면 MemberController는 하나의 URL에 맵핑이 되었다. 그럼 어떻게 MemberController의 다른 메소드를 호출할 수 있을까? 그것은 "methodNameResolver" 빈의 "ParameterMethodNameResolver" 를 이용해서 호출이 가능하다.
    URL을 통해 전달되는 인자중에 "paramName"인 method라는 이름을 가지는 인자의 값에 해당하는 메소드를 호출해 주는 것이다.
    즉 /member/member.do?method=add 를 하게 되면 MemberController의 add()를 호출하는 것이다. /member/member.do?method=update를 하면 update()를 호출하게 될 것이다. 만일 아무 값도 없다면 default 으로 add()를 호출하게 된다. 호출하는 URL은 같지만 뒤 인자의 인자값을 변경해 줌으로써 다른 메소드를 호출할 수 있다.

    ParameterMethodNameResolver를 이용하면 MultiActionController를 상속받은 Controller 에 메소드가 추가되더라도 설정파일에 추가적인 설정이 필요 없을 것이다. 하지만 호출할 때 추가된 메소드 이름을 알아야 한다.

    데이터 바인딩(Data Binding)
    웹 애플리케이션 개발에서 입력, 수정과 관련된 기능을 구현하기 위해서 입력하는 값에 대한 유효성 체크와 입력 값을 도메인 모델로 바인딩 하는 기능이 필요하다.그 동안 개발자들은 유효성 체크를 위해서 화면단에는 많은 자바스크립트를 데이타 바인딩을 위해서는 HttpServletRequest에 담긴 인자를 꺼내서 다시 도메인 모델로 set하는 코드들이 난무했을 것이다.

    Spring MVC 에서는 입력화면(또는 수정화면)에서 전달되는 입력 데이터를 비즈니스 계층에 전달하기 위하여 HttpServletRequest에 담긴 인자와 도메인 모델의 속성을 자동적으로 연결하는 데이터 바이팅을 지원하고 있다.

    위에 나온 bind()가 그 역할을 하는 것이다.

    bind(request,command);

    Bind request parameters onto the given command bean
    @param request request from which parameters will be bound
    @param command command object, that must be a JavaBean
    @throws Exception in case of invalid state or arguments

    대신 클라이언트가 입력한 데이터를 도메인 모델과 데이터 바인딩 하기 위해서는 입력 화면에서 사용한 속성이름과 도메인 모델의 속성이름이 같아야 한다.

    • 회원가입 입력폼인 memberEntry.jsp
      <%--
      작성자 : 난다
      일자 : 2006.02.13
      설명: 회원가입
      --%> 
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
      
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
        <head>    
          <title>사용자 관리 - 회원 가입</title>     
          <link rel="stylesheet" type="text/css" href="../css/style.css" media="screen">
        </head>  
       <body>
       <div id="member">
       <form name="thisform" method="post" action="${pageContext.request.contextPath}/member/addMember.do">   
      	<table summary="회원가입">
      		<caption>회원 정보 </caption>
      		<tr>
      			<th id="id">* 아이디</th>
      			<td headers="id">
      				<input type="text" id="id" name="id" />
      			</td>
      		</tr>
      		<tr>
      			<th id="password">* 비밀번호</th>
      			<td headers="password">
      				<input type="text" id="password" name="password" />
      			</td>
      		</tr>
      		<tr>
      			<th id="passwordRepeat">* 비밀번호</th>
      			<td headers="passwordRepeat">
      				<input type="text" id="passwordRepeat" name="passwordRepeat" />
      			</td>
      		</tr>	
      		<tr>
      			<th id="password_hint">* 비밀번호 힌트 </th>
      			<td headers="passwordHint">
      				<input type="text" id="passwordHint" name="passwordHint" />
      			</td>
      		</tr>
      		<tr>
      			<th id="passwordAnswer">* 비밀번호 답 </th>
      			<td headers="passwordAnswer">
      				<input type="text" id="passwordAnswer" name="passwordAnswer" />
      			</td>
      		</tr>
      		<tr>
      			<th id="email">* e-mail</th>
      			<td headers="email">
      				<input type="text" id="email" name="email" />
      			</td>
      		</tr>						
      	</table>
      	<center>	
      		<input type="submit" value="확인" tabindex="4" />
      	</center>
      </form>
      </div>
      </body>
      </html>
      
      
    • 도메인 모델 Member.java
    Member.java
    package net.javajigi.member.model;
    
    import org.apache.commons.lang.builder.ToStringBuilder;
    
    public class Member {
    	private String userId = null;
    	private String password = null;
    	private String passwordRepeat = null;
    	private String passwordHint = null;
    	private String passwordAnswer = null;
    	private String email = null;
    	
             변수들에 대한 getter,setter 메소드...
                  .
                  .
                  .
            public String toString() {
              return ToStringBuilder.reflectionToString(this);
            }
    	
    }
    

    memberEntry.jsp의 입력필드의 이름은 id,password,passwordRepeat,passwordHint,passwordAnswer,email로 정의 되어 있다.
    그리고 바인딩되는 도메인 모델 Member도 id,password,passwordRepeat,passwordHint,passwordAnswer,email 가 정의되어 있다.
    즉 같은 속성이름에 대해서는 가져올 수 있지만 만일 다르다면 가져오지 못하게 된다.

    위의 예제에서 사용한 MultiActionController는 입력데이터에 대한 유효성 체크 기능을 지원하지 않는다. 만일 유효성 체크도 원한다면 MultiActionContorller를 상속받아서는 사용할 수 없다.
    유효성 체크와 바인딩 기능까지 모든 기능을 사용하길 원한다면 AbstractCommandControllder 클래스를 상속받아야 한다.

    AbstractCommandControllder를 상속받은 Controller

    MemberAbstractCommandController.java
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.validation.BindException;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.AbstractCommandController;
    
    import com.tmax.spring.domain.Member;
    import com.tmax.spring.service.app.MemberService;
    
    public class MemberAbstractCommandController extends AbstractCommandController{
        private static transient Log log = LogFactory.getLog(MemberAbstractCommandController.class);
    	
        private MemberService memberService = null;
    	
        public void setMemberService(MemberService memberService){
    	this.memberService = memberService;
        }
    	
        public MemberAbstractCommandController(){
    	setCommandClass(Member.class);
        }
    	
        protected ModelAndView handle(HttpServletRequest request, 
    		HttpServletResponse response, Object command, BindException errors) 
                     		throws Exception {
    	Member member = (Member)command;
    		
    	//memberService.addBbs(command);
    	log.error("가져오는지..."+member.toString());
    		
    	return new ModelAndView("../index");	
         }
     }
    

    AbstractCommandController를 상속받아 구현 하려면 바인딩하는 클래스를 디폴트 생성자 안에 setCommandClass()를 통해서 설정해야 한다. 위의 예를 보면

    public MemberAbstractCommandController()

    Unknown macro: { setCommandClass(Member.class); }

    그러면 들어오는 입력 값들과 설정한 클래스와의 바인딩이 이전에 보았던 bind() 라는 메소드 없이 Casting 만으로 이루어 진다.

    하지만 AbstractCommandController는 요청에 대해 각각의 Controller를 만들어야 한다.결국 데이터 바인딩 및 데이터에 대한 유효성 체크 기능을 지원하고 있지만 AbstractCommandController를 상속받은 Controller로는 다수의 요청을 처리 할 수 없는 것이다.즉 Controller 를 이용하면 요청에 따라 추가되고 빈 설정 정보또한 수정해 주어야 한다.

    결국 큰 주제로 했던 다수의 요청을 한 Controller에서 처리하는 방법은 아니지만 좀 더 손쉽게 유효성체크와 데이터 바인딩을 할 수 있다는 점에서는 유용하다. 간단한 CRUD 같은 작업은 MultiController를 구현해 한 Controller에서 사용할 수도 있고( 물론 유효성체크는 스크립트나 다른 방법을 통해) 복잡하고 많은 입력 필드를 가지고 있는 화면이라면 AbstractCommandController를 사용해 좀 더 손쉽게 처리 할 수 있을 것이다.

    이것은 각각의 프로젝트, 요구사항등에 맞추어 결정하면 될 것이다.

    action-servlet.xml 추가사항
    	<bean name="/member/addMemberWithAbstract.do"   class="com.tmax.spring.web.MemberAbstractCommandController">
    	  <property name="memberService">
    		<ref bean="memberService" />
    	 </property>	
            </bean>
    
    • 실제로직을 담당하는 비지니스 객체인 MemberService.java 파일을 생성한다.
    MemberService.java
    package net.javajigi.member.service;
    
    import net.javajigi.member.model.Member;
    
    public class MemberService {
    	
    	public void addMember(Member member){
    		//로직처리
    	}
    	
    	public void updateMember(Member member){
    		//로직처리
    	}
    }
    
    

    하나의 Controller만으로 입력,수정처리하기

    <ation-servlet.xml>
     
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    	<property name="mappings">
    			<props>				
    				<prop key="/login.do">loginFormController</prop>				
    			</props>
    		</property>
    	</bean>	
    	
    	<bean id="loginFormController" class="spring.LoginFormController">
    		<property name="formView"><value>/login</value></property>
    		<property name="successView"><value>/main</value></property>
    	</bean>
    		
    	<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="viewClass">
    			<value>org.springframework.web.servlet.view.JstlView</value>
    		</property>
    		<property name="cache"> 
    			<value>false</value>
    		</property>
    		<property name="prefix">
    			<value>/</value>
    		</property>
    		<property name="suffix">
    			<value>.jsp</value>
    		</property>		
    	</bean>
    </beans>

    Controller를 URL과 매핑해주어야 한다.
    login.do에 대한 요청을 loginFormController가 담당하며, 이 Controller는 로그인을 위한 로직처리를 포함하고, 사용자가 직접 작성해야한다.
    입력화면은 /login 이며, 로직을 무사히 처리한후에는 /main으로 이동한다.
    JSTL pages , jsp Page를 사용하기위해 JstlView를 선언하였다.

    • web-app/login.jsp 로그인 페이지를 생성한다.
      <login.jsp>
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ page language="java" import="java.util.*" %>
      <html>
        <head>
          <title>My JSP 'login.jsp' starting page</title>    
        </head>  
        <body>
        <form name="myForm" method="post" action="/spring/login.do">
         	<table>
         		<tr>
         			<td>
         				ID	: <input type="text" name="userId" value="1">   				
         			</td>
         		</tr>
         		<tr>
         			<td>
         				PASSWORD	: <input type="password" name="password" value="2">   				
         			</td>
         		</tr>
         		<tr>
         			<td>
         				<input type="submit" name="login" value="로그인"/>
         			</td>
         		</tr>
         	</table>
         </form>
        </body>
      </html>
      
    • web-app/main.jsp 로그인성공후 이동할 메인 페이지를 생성한다.
      <main.jsp>
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ page language="java" import="java.util.*" %>
      
      <html>
        <head>
          <title>My JSP 'main.jsp' starting page</title>   
        </head>  
        <body>
        	Login Success~!!
        </body>
      </html>
      
    • web-app/WEB-INF/net.javajigi.user.web.LoginFormController.java 파일을 생성한다.
    LoginFormController.java
    package net.javajigi.user.web;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import net.javajigi.user.model.Authenticate;
    import org.springframework.validation.BindException;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.SimpleFormController;
    
    public class LoginFormController extends SimpleFormController {	
    	
    	public LoginFormController() {
                      // bean 객체
    		setCommandName("authenticate");
    		setCommandClass(Authenticate.class);
    	}
    	
    	public ModelAndView onSubmit(HttpServletRequest request,
    			HttpServletResponse response, Object command, BindException errors)
    			throws Exception {
    		
    		if (request.getParameter("login") != null) {
    			Authenticate auth = (Authenticate) command;
    
    			try {
    				//로직처리 
                                              //boolean bResult = UserLoginProc.login(auth);
    
                                        //View 객체 리턴 
    				return new ModelAndView(getSuccessView());
    				
    			} catch (Exception e) {
    				request.setAttribute("errorMessage", e.getMessage());
    
    				return new ModelAndView(getFormView());
    			} 
    			
    		} else {
    			HttpSession session = request.getSession();
    
    			if (session.getAttribute("loginUser") != null) {
    				return new ModelAndView("decorators/logout");
    			} else {
    				return new ModelAndView("decorators/login");
    			}
    		}
    	}
    }
    

    SimpleFormController 을 상속받은 LoginFormController.java 파일을 생성한다.

    • web-app/WEB-INF/net.javajigi.user.model.Authenticate.java 파일을 생성한다.
    Authenticate .java
    package net.javajigi.user.model;
    
    
    public class Authenticate {
        private String userId = null;
    
        private String password = null;
        
    	public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getUserId() {
            return userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    }
    
    

    LoginFormController.java에서 호출되는 bean객체를 생성한다.

    참고문헌

    Posted by 1010
    반응형
  • jpetstore 설치하기 : Spring2.0 샘플에 포함된 jpetstore를 설치하고 테스트 해 본다.
  • jpetstore 에서의 Spring 초기 설정과 IoC : jpetstore 에서의 웹 애플리케이션을 위한 Spring 초기 설정과 간단한 IoC에 대한 설명
  • jpetstore 에서의 Spring MVC 설정 : jpetstore 에서의 Spring MVC 설정을 어떻게 하는지 알아본다. 그리고 간략하게 Spring MVC에서 HTTP Request를 처리하는 과정에 대해서 셋팅하는 것과 함께 살펴본다.
  • jpetstore 에서의 HandlerMapping : Spring MVC에서 HandlerMapping을 어떻게 사용하는지 jpetstore을 통해서 알아본다.
  • jpetstore 에서의 InternalResourceViewResolver : Spring MVC에서 InternalResourceViewResolver을 어떻게 사용하는지 jpetstore을 통해서 알아본다.
  • jpetstore 예제로 살펴보는 Spring MVC와 iBatis 연동 : "shop/viewCategory.do" 요청을 예제로 하여 Spring MVC(Controller)의 전체적인 처리 과정과 iBatis를 사용하여 연동한 Spring framework의 과정을 살펴본다.
  • 참고문헌

  • 출처 : http://www.javajigi.net/pages/viewpage.action?pageId=6962

    Posted by 1010
    반응형

    출처 : http://dogfeet.tistory.com/159

    Ioc.java

    01.import java.util.Calendar;
    02.import org.springframework.beans.factory.BeanFactory;
    03.import org.springframework.beans.factory.xml.XmlBeanFactory;
    04.import org.springframework.core.io.ClassPathResource;
    05.  
    06.public class Ioc {
    07.  
    08.    private Calendar myCalendar;
    09.      
    10.    public void setMyCalendar(Calendar cal) {
    11.        myCalendar = cal;
    12.    }
    13.      
    14.    @Override
    15.    public String toString() {
    16.        return "Ioc Class with " + myCalendar;
    17.    }
    18.      
    19.    public static void main(String[] args) {
    20.        System.out.println("Spring Study!");
    21.        BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
    22.        System.out.println(bf.getBean("ioc"));
    23.        System.out.println(bf.getBean("date"));
    24.    }
    25.}

    beans.xml

    01.<?xml version="1.0" encoding="UTF-8"?>
    04.       xsi:schemaLocation="
    07.  
    08.  
    09.    <bean id="calendar" class="java.util.GregorianCalendar" />
    10.      
    11.    <bean id="date" class="java.util.Date" />
    12.  
    13.    <bean id="ioc" class="Ioc">
    14.        <property name="myCalendar" ref="calendar" />
    15.    </bean>
    16.      
    17.</beans>

    library files

    • commons-logging.jar
    • org.springframework.beans-3.0.0.RC1.jar
    • org.springframework.core-3.0.0.RC1.jar

    execution

    1.$ javac Ioc.java -cp org.springframework.beans-3.0.0.RC1.jar:org.springframework.core-3.0.0.RC1.jar
    2.$ java -cp .:org.springframework.core-3.0.0.RC1.jar:org.springframework.beans-3.0.0.RC1.jar:commons-logging.jar Ioc
    3.Spring Study!
    4.Nov 10, 2009 5:49:14 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    5.INFO: Loading XML bean definitions from class path resource [beans.xml]
    6.Ioc Class with java.util.GregorianCalendar[time=1257842954691,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Seoul",offset=32400000,dstSavings=0,useDaylight=false,transitions=14,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2009,MONTH=10,WEEK_OF_YEAR=46,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=314,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=49,SECOND=14,MILLISECOND=691,ZONE_OFFSET=32400000,DST_OFFSET=0]
    7.Tue Nov 10 17:49:14 KST 2009
    Posted by 1010
    반응형
    이 글은 번역한 것입니다. 원문은 여기에서 확인하실 수 있습니다. 겸손한 자바스크립트를 구현하기엔 jQuery가 가장 적절하고 바람직하다는 생각에 옮겨봅니다. 저도 Quark씨처럼 당장의 편리함을 따른 것보다 jQuery의 철학을 따르는 것이 더 바람직하다고 생각합니다. 항상 디커플링을 지향하는 것이 생산성 문제를 개선하고 미래에 적응하는 것을 가능케 하기 때문입니다.

     오역된 부분이 있을 지도 모르겠습니다.-_-;; 교과서 영어가 아니라-_-;; 나름대로 의역을 하긴 했는데 어렵네요.

    ----여기부터는 번역입니다.

     jQuery는 겸손한(unobtrusive) 자바스크립트를 위한 자바스크립트 라이브러리다. jQuery는 태생적으로 Behavior driven development 방법론을 지향하고 CSS 셀렉터를 사용하여 HTML 문서를 훓는 방법(traversing)을 방법에 기반한다. 반면에 Prototype은 태생적으로 Class driven development를 지향하고 쉽게 자바스크립트 개발할 수 있도록 해준다. Prototype 라이브러리는 Ruby on Rails에서 아주 잘 지원되고 많은 헬퍼 함수들을 가지고 있다.

     세달 전에 마음을 jQuery로 굳히기까지 나는 항상 Prototype 라이브러리를 사용해왔다. jQuery은 나를 매우 고무시겼다. 여기에 그 이유가 있다.

      1. BDD(Behavior driven development)
      jQuery를 사용하면 CSS로 HTML 엘리먼트의 스타일을 정의하는 것 처럼 HTML 엘리먼트의 행동들을 분리하여 정의할 수 있다. 클릭했을 때 경고창을 띄우는 예제를 보자.

    1.$(element).click(function(){
    2.  alert("warning");
    3.});
      이제 복잡한 예제를 살펴보자. speciallinks 클래스들의 모든 엘리먼트에 다음과 같은 행위를 집어넣자.
        1. href를 "javascript:void(0);"로 변경하고
        2. 클릭시 로그를 남기고
        3. onhover시(mouse over시) 배경색을 변경한다.

    1.$("div.speciallinks").attr("href","javascript:void(0)")
    2.  .click(function() {console.log("div.speciallinks clicked");})
    3.  .hover(function(){$(this).addClass("hovered");},
    4.       function(){$(this).removeClass("hovered");});

      2. MVC + J
        MVC 프레임웍은 웹 개발 환경을 Model-View-Controller로 나누었다. MVC 프레임워크에서 뷰는 HTML, CSS, Javascript의 부분으로 나누어지고 GUI 개발은 이 세부분이 하나로 결합하여 완성된다. 게다가 Ruby on Rails에서의 Prototype 라이브러리는 HTML과 JavaScript도 함께 합쳐버린다(meshup). 뷰를 개발하면서 JavaScript를 분리하는 BDD 방법론을 따르려 한다면 jQuery가 매우 적절하다. 나는 Ajax에서 MVC + J가 매우 강력하다는 것을 깨닭았다.

        나는 JQuery를 사용하여 나의 HTML 파일을 매우 깨끗하게 만들었고 모든 JavaScript 코드를 .js에 넣음으로써 HTML 엘리먼트의 행위를 모두 .js 파일에 정의하였다.

      3. 액션 체인(Chaining of actions)
        액션 체인은 DRY 원칙을 따를 뿐만 아니라 JavaScript 코드의 가독성을 증가시킨다. 만약 엘리먼트에 오퍼레이션들을 삽입하려면 다음과 같이 할 수 있다.

    1.$("div.message").show()
    2.  .append("<P>Action has been executed successfully")
    3.  .addClass("flash");
    4.// 각 함수는 라인으로 구분하여 사용한다.
    5.</P>

        이 것은 jQuery의 모든 메소드가 query 객체를 반환하기 때문에 가능하다. 그리고 뒤따르는 메소드들이 선택된 HTML 엘리먼트에 체인 형식으로 적용된다.

       4. CSS 셀렉터 문제(CSS Selector rocks)
          CSS 셀럭터 HTML DOM을 사용하기에 매우 강력한 도구이다. jQuery는 HTML 문서의 엘리먼트를 식별하기 위해 CSS 셀러터를 이용하고 이를 위해 HTML 태그의 id 속성을 관리해야 하는 지루한 작업을 회피하게 해준다. 대부분의 id 속성은 CSS 셀렉터를 올바르게 사용하여 회피할 수 있다. Prototype의 $$ 함수는 CSS 셀렉터를 지원하지만 CSS 셀렉터의 능력을 모두 이끌어내지 못한다. 나는 Prototype에서 id 속성을 사용하는 것이 최선이라는 것을 알게 됐다.

       5. 엘리먼트의 존재 유무를 검사할 필요가 없다.
         프로토타입으로 액션으로 적용하기 전에 항상 엘리먼트가 존재하는지 확인해야 했다. 예를 들어 나는 사용자가 로그인 했을 때만 사용자가 명시한 내용을 보여주고 싶었다. 사용자가 로그인 했다면 렌더링된 페이지에 div{id='user-box'}같은 엘리먼트가 있다는 것을 검사해야 했다. 프로토타입에서는 그렇게 해야 했다.

    1.if ($('user-box')!=null) {
    2.    // jQuery에서는 이 if 블럭이 필요없다.
    3.    $("user-box").style.backgroundColor = "red";
    4.}


       6. Aids development process
         jQuery를 사용하면 HTML 코드가 간결해지고 거의 건드릴 필요가 없다. 나의 웹 디자이너는 쉽게 html과 stylesheet를 수정할 수 있고 Prototype 라이브러리를 몰라도 된다.

    예제들:
      여기에는 jQuery와 프로토타입의 차이점을 강조하는 몇 가지 예제가 있다(물론, 이 코드가 최상의 코드는 아니다).

    Example1:
     이 Tab 예제는 tabs 클래스에 속하는 3 개의 tab이 있고 id는 각각 tabs1, tabs2, tabs3이다(ids=>["tabs1","tabs2","tabs3"]). 사용자가 "show first tab" 링크를 클릭했을 때 #tab1만 보여진다.

     jQuery의 경우

    1.$("div.tabs").hide();
    2.$("div#tabs1").show();


        
      Prototype의 경우

    1.$$("div.tabs").invoke("hide");  // 이거
    2.$$("div.tabs").each(function(x){
    3.  Element.hide(x);
    4.});
    5.// 그리고 다음과 같은 코드로 마무리한다.
    6.$("tabs1").show();


    Example2:
    엘리먼트의 CSS를 수정하는 예제

      jQuery의 경우

    1.$("#user-box").css("background-color","red")


      Prototype의 경우    

    1.var a = $("user-box");
    2.a.style.cssText += "background-color:red;";

    Remy Sharp의 발표자료에서 다른 예제들도 볼 수 있다. http://ajaxian.com/archives/prototype-and-jquery-a-code-comparison.

    Prototype이 좋은 이유
      1. Ajax 요청에서 div의 내용이 부분적으로 변경돼야 할때 JavaScript 행위는  activated/reactivated돼야 한다. jQuery에서는 이를 위한 많은 코드를 만들지 않아도 된다. 하지만 나는 Ajax로 로드된 이 부분을 항상 마음속으로만 새겨야 한다. Prototype에서는 로드된 HTML 엘리먼트들에 관련된 JavaScript 코드가 포함된다.
      2. Ruby on Rails의 Prototype 헬퍼들은 정말 대단하고 JavaScript와 Ajax를 만들때 많은 부분에서 매우 편리하다.
      3. Prototype은 자바스크립에서도 Ruby같은 문법을 사용할 수 있게 해준다. 개발을 쉽고 빠르게 할 수 있도록 배열, 객체 등의 기능을 변경해준다.
      4. jQuery에서는 JavaScrpt 파일이 모두 다운로드되고 DOM 생성된 후에만 엘리먼트의 행위를 사용할 수 있다. 연결이 느릴 때는 기다려야만 하기 때문에 매우 고통스럽다.

    결론:
      jQuery와 Prototype은 둘 다 매우 굉장하다. 나는 더 적은 코드로 더 많은 일을 하면서 직관적이고 겸손하게 유지해야 한다는 jQuery의 철학이 큰 차이를 만든다고 생각한다. 그러나 나는 Prototype이 매우 그립다. jquery-rails가 통합되길 기다리고 있다. 결국 이 고지는 점령될 것이다. 나는 사람들이 이 변화를 감지하고 있다는 소식을 들을 때마다 기쁘다.

    ps> 이 두 라이브러리를 함께 사용하려면 여기를 보라.


    출처 : http://dogfeet.tistory.com/29
    Posted by 1010
    01.JAVA/Java2009. 12. 18. 15:11
    반응형

    package com.jisan.common.util.page;

    public class Paging {

     private int totalCount;  
     private int currentPage;
     private int listLimit;
     private int pageLimit;

     private int totalPage;
     private int startPage;
     private int endPage;
     private int startNum;
     private int endNum;

     private String btnStart="";
     private String btnEnd="";
     private String btnListPrev="";
     private String btnListNext="";
     private String btnPageList="";
     private String btnListTenPrev= "";
     private String btnListTenNext= "";
     

     public Paging(int totalCount, int currentPage, String queryOption) {
      this.totalCount = totalCount;
      this.currentPage = currentPage;
      this.listLimit = 15;
      this.pageLimit = 5;
     }
     public Paging(int totalCount, int currentPage, String queryOption, int listLimit, int pageLimit) {
      this.totalCount = totalCount;
      this.currentPage = currentPage;
      this.listLimit = listLimit;
      this.pageLimit = pageLimit;
     }

     public PagingBean pagingProcess() throws Exception {

      PagingBean pb = new PagingBean();

      totalPage = (totalCount - 1) / listLimit + 1;
      startPage = ( (currentPage - 1) / pageLimit) * pageLimit + 1;
      endPage = startPage + pageLimit - 1;

      if (totalPage < endPage) {
      endPage = totalPage;
      }
      if (totalCount == 0) {
       totalPage = 0;
      }

      startNum = (currentPage - 1) * listLimit;
      endNum = listLimit;

      //제일 처음 페이지로.
      if (currentPage > 1) {
       btnStart = "<a href=\"javascript:go_Page('1')\"><img src=\"/spi/images/board/pag_rr.jpg\" border=\"0\" class=\"m_lr2\" /></a>&nbsp;";
      } else{
       btnStart = "<a href=\"#\"><img src=\"/spi/images/board/pag_rr.jpg\" border=\"0\" class=\"m_lr2\" /></a>&nbsp;";
      }


      //바로 이전 페이지 목록으로. (예: 이전 10 페이지로)
      if (currentPage > pageLimit) {
       int prevListPage = startPage - 1;
       btnListPrev = "<a href=\"javascript:go_Page('"+prevListPage+"')\"><img src=\"/spi/images/board/pag_r.jpg\" border=\"0\" class=\"m_lr5\" /></a>&nbsp;";
      } else{
       btnListPrev = "&nbsp;";
      }

      //페이지 목록
      btnPageList = "";
      for (int i = startPage; i <= endPage; i++) {
       if (currentPage == i) {
        btnPageList += "<b><a href=\"#\" align=\"center\" valign=\"middle\" class=\"text_05\">" + i + "</a></b>";
       } else {
        btnPageList += "<a href=\"javascript:go_Page('"+i+"')\" align=\"center\" valign=\"middle\" class=\"text_05\"> " + i + "</a>";
       }
       if (i < endPage) {
        btnPageList += "<img src=\"/spi/images/board/icon_pagingdot.jpg\" class=m_lr2>";
       }
      }

      //바로 다음 페이지 목록으로. (예: 다음 10 페이지로)
      int nextListPage = endPage + 1;
      if (nextListPage <= totalPage) {
       btnListNext = "&nbsp;<a href=\"javascript:go_Page('"+nextListPage+"')\"><img src=\"/spi/images/board/pag_f.jpg\" border=\"0\" class=\"m_lr5\" /></a>&nbsp;";
      } else {
       btnListNext = "&nbsp;";
      }

      //제일 마지막 페이지로.
      if (currentPage < totalPage) {
       btnEnd = "&nbsp;<a href=\"javascript:go_Page('"+totalPage+"')\"><img src=\"/spi/images/board/pag_ff.jpg\" border=\"0\" class=\"m_lr2\" /></a>";
      } else {
       btnEnd = "&nbsp;<a href=\"#\"><img src=\"/spi/images/board/pag_ff.jpg\" border=\"0\" class=\"m_lr2\" /></a>";
      }

      StringBuffer pageInfo = new StringBuffer();

      pb.setPageInfo(pageInfo.toString());

      StringBuffer pageHtml= new StringBuffer();


      pageHtml.append("<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
      pageHtml.append(" <tr>");
      pageHtml.append("  <td align=\"center\">" );
      pageHtml.append(   btnStart);
      pageHtml.append(   btnListPrev);
      pageHtml.append(   btnPageList);
      pageHtml.append(   btnListNext);
      pageHtml.append(   btnEnd);
      pageHtml.append("  </td>");
      pageHtml.append(" </tr>");
      pageHtml.append("</table>");

      pb.setPageHtml(pageHtml.toString());

      //페이징 처리 내용을 넘기기 위해 PagingBean에 처리한 값들을 담는다.
      pb.setTotalCount(totalCount);
      pb.setCurrentPage(currentPage);
      pb.setTotalPage(totalPage);
      pb.setStartPage(startPage);
      pb.setEndPage(endPage);
      pb.setStartNum(startNum);
      pb.setEndNum(endNum);
      pb.setBtnStart(btnStart);
      pb.setBtnEnd(btnEnd);
      pb.setBtnListPrev(btnListPrev);
      pb.setBtnListNext(btnListNext);
      pb.setBtnPageList(btnPageList);

      return pb;
     }

     public PagingBean pagingProcess2() throws Exception {

      PagingBean pb = new PagingBean();
     
     
     

      totalPage = (totalCount - 1) / listLimit + 1;
      startPage = ( (currentPage - 1) / pageLimit) * pageLimit + 1;
      endPage = startPage + pageLimit - 1;
     

      if (totalPage < endPage) {
      endPage = totalPage;
      }
      if (totalCount == 0) {
       totalPage = 0;
      }

      startNum = (currentPage - 1) * listLimit;
      endNum = listLimit;  
     

      //제일 처음 페이지로.
      if (currentPage > 1) {
       btnStart = "<a href=\"javascript:go_Page('1')\"><img src=\"/spi/images/board/button_start_be.gif\" border=\"0\" class=\"m_lr2\" /></a>&nbsp;";
      } else{
       //btnStart = "<a href=\"#\"><img src=\"/spi/images/board_img/button_start.gif\" border=\"0\" align=\"middle\" /></a>&nbsp;";
      }
     
     

      //이전 10 페이지
      int pretenpage = (currentPage/pageLimit-1)*10+1;
      if (pretenpage > 0) {
       btnListTenPrev += "<a href=\"javascript:go_Page('"+pretenpage+"')\"><img src=\"/spi/images/board/pag_rr.jpg\" border=\"0\" class=\"m_lr2\" /></a>&nbsp;";
      } else{
       //btnStart = "<a href=\"#\"><img src=\"/spi/images/board_img/button_start.gif\" border=\"0\" align=\"middle\" /></a>&nbsp;";
      } 


      //바로 이전 페이지 목록으로.
      //if (currentPage > pageLimit) {
      if (totalPage > 1) {
       int prevListPage = currentPage - 1;
       if(prevListPage < 1){
        //btnListPrev = "<a href=\"#\"><img src=\"/spi/images/board_img/button_pre.gif\" border=\"0\" align=\"middle\" /></a>&nbsp;&nbsp;&nbsp;&nbsp;";
       }else{
        btnListPrev = "<a href=\"javascript:go_Page('"+prevListPage+"')\"><img src=\"/spi/images/board/pag_r.jpg\" border=\"0\" class=\"m_lr5\" /></a>&nbsp;";
       }

      } else{
       btnListPrev = "&nbsp;";
      }

      //페이지 목록
      btnPageList = "";
      for (int i = startPage; i <= endPage; i++) {
       if (currentPage == i) {
        btnPageList += "<a href=\"#\" align=\"center\" valign=\"middle\" ><b>" + i + "</b></a>";
       } else {
        btnPageList += "<a href=\"javascript:go_Page('"+i+"')\" align=\"center\" valign=\"middle\">" + i + "</a>";
       }
       if (i < endPage) {
        btnPageList += "&nbsp;<img src=\"/spi/images/board/icon_pagingdot.jpg\" class=\"m_lr2\">";
       }
       if(endPage==1){
       }else{
        btnPageList += "&nbsp;";
       }
      } 

     
      //바로 다음 페이지 목록으로. (예: 다음 10 페이지로)  
      int nextListPage = currentPage + 1;
      if (nextListPage <= totalPage) {
       btnListNext = "<a href=\"javascript:go_Page('"+nextListPage+"')\"><img src=\"/spi/images/board/pag_f.jpg\" border=\"0\" class=\"m_lr5\" /></a>&nbsp;";
      } else {
       //btnListNext = "&nbsp;";
      }
     
      //다음 10 페이지
      int nexttenpage = (1+currentPage/pageLimit)*10+1;
      if (totalPage > nexttenpage) {
       btnListTenNext += "<a href=\"javascript:go_Page('"+nexttenpage+"')\"><img src=\"/spi/images/board/pag_ff.jpg\" border=\"0\" class=\"m_lr2\" /></a>&nbsp;";
      } else{
       //btnStart = "<a href=\"#\"><img src=\"/spi/images/board_img/button_start.gif\" border=\"0\" align=\"middle\" /></a>&nbsp;";
      } 

      //제일 마지막 페이지로.
      if (currentPage < totalPage) {
       btnEnd = "<a href=\"javascript:go_Page('"+totalPage+"')\"><img src=\"/spi/images/board/button_end_last.gif\" border=\"0\" class=\"m_lr2\" /></a>";
      } else {
       //btnEnd = "&nbsp;<a href=\"#\"><img src=\"/spi/images/board_img/button_end.gif\" border=\"0\" align=\"middle\" /></a>";
      }

      StringBuffer pageInfo = new StringBuffer();

      pb.setPageInfo(pageInfo.toString());

      StringBuffer pageHtml= new StringBuffer();


      pageHtml.append("<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
      pageHtml.append(" <tr>");
      pageHtml.append("  <td align=\"center\" >" );
      pageHtml.append(   btnStart);
      pageHtml.append(   btnListTenPrev);
      pageHtml.append(   btnListPrev);
      pageHtml.append(   btnPageList);
      pageHtml.append(   btnListNext);
      pageHtml.append(   btnListTenNext);
      pageHtml.append(   btnEnd);
      pageHtml.append("  </td>");
      pageHtml.append(" </tr>");
      pageHtml.append("</table>");


      pb.setPageHtml(pageHtml.toString());

      //페이징 처리 내용을 넘기기 위해 PagingBean에 처리한 값들을 담는다.
      pb.setTotalCount(totalCount);
      pb.setCurrentPage(currentPage);
      pb.setTotalPage(totalPage);
      pb.setStartPage(startPage);
      pb.setEndPage(endPage);
      pb.setStartNum(startNum);
      pb.setEndNum(endNum);
      pb.setBtnStart(btnStart);
      pb.setBtnEnd(btnEnd);
      pb.setBtnListPrev(btnListPrev);
      pb.setBtnListNext(btnListNext);
      pb.setBtnPageList(btnPageList);

      return pb;
     }
    }

    Posted by 1010
    98..Etc/Tomcat2009. 12. 9. 13:28
    반응형

    보통 톰캣으로 서비스 구축할 때 안정적으로 서비스하려면 한 서버에 톰캣을 4개 정도 구동시켜야 한다고 하던데 ㅎㅎ
    http://www.ibm.com/developerworks/kr/li ··· 61017%2F

    웹 개발자에게 있어 톰캣은 JSP를 배우거나 간단한 테스트를 하는 정도의 웹 컨테이너로 생각하는 경우가 많다. 하지만 근래 들어 기업 및 대형 포탈에서 상용 서비스를 위한 웹 컨테이너로서 톰캣을 선택해, 성공적으로 적용한 사례들이 늘고 있다. 톰캣에서 안정적인 웹 서비스를 제공하기 위해서 지원하는 기능은 5가지가 있다. 아파치 웹서버와 연동, 로드밸런싱, 세션 클러스터링, 데이터베이스 처리, 모니터링 및 관리 등이 그것이다.
    이 문서에서는 로드밸런싱과 세션 클러스터링 위주로 설명을 할 것이며, 다음에 기회가 된다면 다른 부분에 대해서도 자세히 알아보도록 하겠다.

    아파치 웹 서버와 톰캣의 연동

    일반적으로 정적인 페이지를 서비스할 때는 웹서버가 훨씬 더 좋은 성능을 발휘한다. 또한 이렇게 역할 분담을 함으로 톰캣에 가중되는 부하를 줄여주는 효과도 얻을 수 있다. 아파치웹서버와 톰캣을 연동하는 것을 일반적으로 ‘커넥터(Connector)'라고 부르며, 여기에는 WARP 커넥터, JK 커넥터 그리고 JK2 커넥터가 있다. 이중에서 WARP와 JK2는 공식 커넥터에서 제외되었고 현재 남아 있는 것은 JK 커넥터뿐이다. 그럼 먼저 JK 커넥터를 이용해서 아파치 웹서버와 톰캣을 연동해 보도록 하겠다.
    아파치 웹사이트에서 바이너리 혹은 소스 코드를 다운로드 받도록 하자. 유닉스 혹은 리눅스는 mod_jk.so이며 윈도우용은 mod_jk.dll이다. 이 파일을 아파치 웹서버의 modules 디렉토리에 저장한다(주의, 아파치 웹서버를 컴파일해서 사용하는 경우는 컴파일시에 DSO 기능이 가능하도록 설정해줘야 한다). 저장을 한 후에 아파치 웹서버에 해당 모듈을 인식시켜야 하며 아파치 웹서버의 httpd.conf 파일에 다음 내용을 추가하도록 하자.

    리스트 1. httpd.conf

            ...
    LoadModule jk_module modules/mod_jk.so    # 모듈 추가
    JkWorkersFile "conf/workers.properties"   # JK 설정 파일 위치 및 이름
     
    JkLogFile "logs/mod_jk.log"               # JK에 대한 로그 파일 위치
    JkLogLevel info                           # 로그 레벨 지정
    JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"   # 로그 시간 포맷 지정
    JkRequestLogFormat "%w %V %T"             # 로그 내용 포맷
    JkMount /* loadbalancer                   # URL 링크 -> 모든 요청을 톰캣으로 지정
    JkMount /servlet/* loadbalancer           # URL 링크 -> servlet 요청을 톰캣으로 지정
    ...

    위와 같은 설정을 하게 되면 아파치 웹서버로 들어온 모든 요청을 톰캣으로 재전송 하게 된다. 만일 JSP와 서블릿만 톰캣에서 서비스를 하고 나머지는 아파치 웹서버에서 서비스 하고자 한다면 다음과 같이 수정하면 된다.

      
    JkMount /*.jsp loadbalancer                # URL 링크 -> *.jsp 요청을 톰캣으로 지정 
    JkMount /servlet/* loadbalancer           # URL 링크 -> servlet 요청을 톰캣으로 지정 

    httpd.conf에는 위의 내용이 전부이다. 그럼 이제 workers.properties 파일을 작성해 보도록 하겠다. 이 파일이 실제 로드밸런싱을 위한 설정이 되겠다.


    위로

    라운드 로빈 방식의 로드밸런싱 설정

    톰캣에서 제공하는 로드밸런싱은 정확히 톰캣 자체에서 제공하는 것이 아니라 아파치 웹서버와 연동되는 커넥터에 의해서 제공된다(로드밸런싱은 JK, JK2 커넥터에서만 제공된다). 현재는 라운드 로빈(Round Robin) 방식만이 제공되며 로드밸런싱에 대한 설정은 workers.properties 파일에서 정의하게 된다.

    리스트 2. workers.properties

      
    worker.list=tomcat1, tomcat2, loadbalancer
     
    worker.tomcat1.type=ajp13
    worker.tomcat1.host=localhost
    worker.tomcat1.port=11009
    worker.tomcat1.lbfactor=100
     
    worker.tomcat2.type=ajp13
    worker.tomcat2.host=localhost
    worker.tomcat2.port=12009
    worker.tomcat2.lbfactor=200
     
    worker.loadbalancer.type=lb
    worker.loadbalancer.balanced_workers=tomcat1,tomcat2

    worker라는 개념은 톰캣의 프로세스로 보면 된다. 즉 worker를 설정하는 구성 요소는 JK 커넥터를 연결하는 방식(JK는 ajp13을 이용한다), 톰캣이 실행되어 있는 IP 혹은 도메인, ajp13 서비스 포트, 그리고 작업 할당량이다. 여기서 주의 깊게 볼 것이 작업 할당량인데 로드밸런싱 시에 lbfactor라는 작업량의 비율을 보고 라운드 로빈 방식의 서비스를 제공하게 된다. 여기서는 tomcat1과 tomcat2를 1대 2의 비율로 작업량을 할당한 것이다.
    그럼 이제 남은 작업은 2개의 톰캣 프로세스를 실행시키는 것이다. 톰캣 프로세스를 여러 개 띄우는 방법은 2가지가 있다.

    • 톰캣을 2개 설치해서 기동시킨다. 이때 포트 충돌을 피하기 위해 서버 포트, AJP13과 HTTP 1.1 커넥터 포트 2개를 충돌되지 않게 재정의 한다.
    • 하나의 톰캣에 2개의 서비스를 정의하고 톰캣을 기동시킨다. 이때 AJP13과 HTTP1.1 커텍터 포트 2개를 충돌되지 않게 재정의 한다.

    먼저 2개의 바이너리를 설치했다고 가정하면 각각의 톰캣은 다음과 같은 형태의 server.xml 파일로 적용해 준다.

    리스트 3. server.xml

    리스트 4. server.xml

    리스트 3은 tomcat1의 환경 설정이고 리스트 4는 tomcat2의 환경 설정이다. 두 환경 설정의 차이는 3개의 포트번호와 태그의 jvmRoute 속성이다. 태그의 포트는 톰캣을 종료 시키는 작업을 할 때 사용하는 것이며 태그의 포트들은 아파치를 통하지 않고 직접 톰캣에 접속할 경우와 아파치와 연계하여 JK 커넥터와 연동할 때 사용하는 포트이다. 마지막으로 태그는 JK 커넥 터에서 로드밸런싱을 수행할 때 해당 값을 구분자로 활용하게 되는데 이 값을 반드시 다른 톰캣 프로세스와 다른 이름으로 지정해야 한다. 지금까지의 환경 설정은 하나의 아파치 웹서버와 두 개의 톰캣 간의 연계를 위한 것이며 톰캣은 동일한 하드웨어 장비에 설치되어 있다는 가정하에 적용한 것이다. 만일 각각의 톰캣이 서로 다른 하드웨어에 존재한다면 jvmRoute명만 다르게 하고 포트명은 동일해도 상관이 없다. 하지만 만일 하나의 장비에 4개의 톰캣 프로세스를 실행시키고 로드밸런싱을 하려고 한다면 어떻게 될까? 톰캣을 4번 설치하고 각각의 환경 설정 파일을 수정해 주어야 할까? 만일 필요한 환경 설정 내용이 변경된다면(예를 들어 JNDI Resource 정보) 모두 운영자가 환경 설정 파일을 수정해 주어야 할까? 다행히도 톰캣에서는 하나의 바이너리에 여러 개의 프로세스가 뜨도록 할 수 있다. 톰캣의 server.xml 태그는 다음과 같은 구조를 가지고 있다.

    --> --> --> -->

    여기서 Server 태그는 유일해야 하며 Server 태그 밑에는 여러 개의 태그가 올 수 있다. 여기서 말하는 태그가 바로 하나의 톰캣 프로세스가 된다. 만일 2개의 태그를 정의했다면 2개의 프로세스가 구동되는 것이다. 리스트 5는 이렇게 구현한 환경 설정 파일이다.

    리스트 5. server.xml

    리스트 5는 하나의 톰캣 바이너리를 통해 2개의 프로세스를 실행시키는 것이다. 이렇게 하면 환경 설정의 편리성을 가져올 수 있지만 특정 서비스만 실행하거나 종료 시키는 것은 아직 지원되지 않는다. 즉 모든 서비스가 동시에 실행되거나 혹은 동시에 종료되는 것을 의미한다. 이런 점을 잘 판단해서 두 가지 형태의 환경 설정 중 하나를 선택하면 되겠다.
    지금까지는 로드밸런싱에 대해 알아보았다. 위의 환경설정을 가지고 테스트를 하다 보면 한가지 문제가 발생한다. 예를 들어 어떤 사용자가 tomcat1을 이용해서 쇼핑몰 서비스를 받고 있다가 tomcat1이 비정상 종료를 하게 되었다. 이때 사용자가 웹 페이지를 요청하게 되면 아파치 웹서버는 tomcat1이 종료된 것을 인지하고 그 이후부터 서비스를 tomcat2로 요청하게 된다. 하지만 tomcat1에 저장되어 있던 쇼핑바구니 정보 즉 세션 정보는 사라진 상태다. 즉, 서비스는 유지되지만 사용자는 다시 이유도 모르게 처음부터 쇼핑 항목들을 등록해야 하는 문제를 가지게 된다. 이제부터는 이런 문제를 해결할 수 있는 톰캣 프로세스 간의 세션 정보 공유에 대해서 알아보겠다.


    위로

    세션 클러스터링 설정

    클러스터링은 톰캣 5.x 버전부터 지원이 되고 있지만 아직은 초기 단계이고 세션 클러스터링만이 제공되고 있는 수준이다. 기능이 많이 부족하긴 하지만 로드밸런싱과 더불어 사용할 경우에는 좀 더 안정적인 서비스를 제공할 수 있다. 작업을 해주어야 할 것은 다음과 같다.

    • server.xml에 태그 정의
    • 웹 어플리케이션의 web.xml에 태그 추가
    그럼 server.xml에 설정해 보자. 태그는 태그의 하위에 정의해 주면 된다. 즉 여러 개의 호스트(예를 들어 가상 호스트) 를 설정했다면 각각의 경우에 맞게 설정해 주어야 한다. 여기서는 tomcat1과 tomcat2가 동일한 하드웨어에 별도의 바이너리 형태로 설치되어 있다고 가정하고 진행하겠다.

    리스트 6. server.xml

    ... ...

    리스트 6은 tomcat1의 server.xml에 적용한 태그이다. 이 내용은 태그 사이에 위치하게 된다. 태그 사이에는 , , 라는 3개의 태그가 위치하는데 은 멤버 그룹을 정의하는 것으로 해당 값이 동일한 모든 톰캣 프로세스는 클러스터로 묶이게 된다. 는 클러스터 그룹에서 보내오는 메시지와 세션 정보 등을 받아오는 것이며 는 자신의 세션 정보 및 메시지를 전송하는 것이다. 위의 내용을 tomcat2의 server.xml에 Receiver 태그의 tcpListenPort 값을 4002로 변경해서 적용하도록 하자.


    위로

    웹 어플리케이션 작성을 통한 테스트

    먼저 테스트를 위해서 간단한 웹 어플리케이션을 작성하도록 하겠다. 여기서 웹 어플리케이션 이름은 lbtest라고 하겠다.

    리스트 7. index.jsp

    <%@ page contentType="text/html; charset=euc-kr" %>

    세션 JSP 테스트

    <% Integer ival = (Integer)session.getAttribute("sessiontest.counter"); if(ival==null) { ival = new Integer(1); } else { ival = new Integer(ival.intValue() + 1); } session.setAttribute("sessiontest.counter", ival); %> 당신은 이곳을 <%= ival %> 번 방문 했습니다.

    여기를 클릭하세요. 여기

    request 객체와 관련된 세션 데이터
    요청된 세션 ID : <%= request.getRequestedSessionId() %>
    쿠키로 부터 요청된 세션 ID 인가? : <%= request.isRequestedSessionIdFromCookie() %>
    URL로부터 요청된 세션 ID 인가? : <%= request.isRequestedSessionIdFromURL() %>
    유효한 세션 ID 인가? : <%= request.isRequestedSessionIdValid() %>

    작성된 웹 애플리케이션을 tomcat1과 tomcat2에 배포한다. 이때 가장 중요한 것이 web.xml에 반드시 이라는 항목을 넣어 야 한다. 그럼 이제 테스트를 해보도록 하자. 먼저 아파치 웹서버, tomcat1, tomcat2를 차례로 실행시켜 보자. 그리고 http://ipaddress/lbtest/index.jsp 접속하여 세션 객체를 생성해보자. 결과 화면은 그림 1과 같다. 여기서 요청된 세션ID를 보면 뒤에 어떤 톰캣에 접속한 상태인지를 알 수 있다. 이 화면상에서는 tomcat2에 접속한 세션이다. 그럼 tomcat2를 강제로 종료시켜 보도록 하자. 종료 후 화면에 보이는 “여기”를 계속 눌러 보자. 분명히 tomcat2가 종료되었음에도 불구하고 세션 값이 유지되고 있음을 알 수 있다. 이젠 반대로 tomcat2를 다시 실행시킨 후에 tomcat1을 종료시켜 보도록 하자. 역시 마찬가지로 세션 정보가 유지되는 것을 확인할 수 있을 것 이다.

    test 결과화면
    그림 1. 테스트 결과 화면

    이상으로 톰캣을 이용한 로드밸런싱과 세션 클러스터링에 대해서 알아보았다. 일반적으로 로드밸런싱과 클러스터링은 성능 향상이라는 측면과 안정성 확보에 그 목적을 가지고 있다. 물론 고가의 상용 웹 어플리케이션 서버에 비하면 많이 부족하고 하드웨어를 이용한 로드밸런싱과 클러스터링에 비하면 안정성이 떨어질 수도 있지만 저렴한 비용으로 최대의 안정성과 성능을 얻고자 한다면 한번쯤 시도해 볼만한 좋은 기능이라고 할 수 있다. 아무쪼록 리눅스, 아파치, 톰캣 그리고 오픈소스를 통해 즐거운 웹 어플리케이션 개발 환경을 느껴보기 바란다.


    위로

    참고 자료
    The Apache Tomcat Connector: http://tomcat.apache.org/connectors-doc/
    Clustering/Session Replication HOW-TO: http://tomcat.apache.org/tomcat-5.5-doc/cluster-howto.html


    출처 : http://tellus.tistory.com/63?srchid=BR1http%3A%2F%2Ftellus.tistory.com%2F63

    Posted by 1010
    반응형

    데이터 Validator 체크를 위해서 공통클래스를 만들곤 하는데 이미 만들어진걸

    씀으로서 수고를 덜 수 있다.  그게 apache 에서 제공하는 common-validator jar 이다.

    이것을 잘 활용한다면 많은 수고를 덜수 있다. 무식하게 만든다고 장땡이는 아니다.

    있는걸 활용해서 시간을 업무에 집중할수 있는것도 중요하다.

    http://commons.apache.org/validator/index.html

    현재 1.3.1 까지 나와있는 상태이다. 그대로 활용해도 되지만

    jar 내부에 제공하는 클래스들을 상속받아 좀더 구체적인 로직을 구현해서 사용하는것도

    확장성 면에서 고려해볼만하다. 1.3. 에선 xml 검증 엔진도 추가되어있다.


    routines 패키지 내에 보면 구체적으로 Validator 체크를 할수 있도록 클래스가 제공된다.

    클래스 이름만 봐도 어떤 종류의 데이터를 체크할수 있을지 감이 올것이다.

    그리고 제일 위 3개는 Abstract 라는 이름이 붙어있다. 이것은 추상클래스를 나타내며

    3개를 제외한 클래스들에서 상속받아 구체적으로 구현한것이다. 그러므로 3개를 제외한

    클래스들을 쓰면된다.

     

    common-validator API 를 보면 http://commons.apache.org/validator/api-1.3.1/

    설명에도 나와있지만 어떤 특정데이타를 다룰때 그 데이타에 해당하는 validator

    체크할수 있도록 클래스가 분류 되어있는 것을 볼수있다. 


    데이타에 대한 validator 가 아닌 일반적인 체크는 아래 클래스들을 사용하면되는데

    보통 Genericvalidator 를 많이 사용한다.


    함수예제를 몇가지 추려보면 다음과 같다.

    public static boolean isBlankOrNull(String value) : blank or null 체크

    public static boolean isByte(String value) : Byte 변환가능한지 여부

    public static boolean isShort(String value) : Short 변환가능한지 여부

    public static boolean isInRange(byte value, byte min, byte max)

    범위지정한 곳에 속하는지에 대한 여부 등등이 있다.

     

    함수들을 사용해서 구현한 예제이다.

    private static final int ID_CHECK_ID = 2;

       public void validateId(String id){

       if (!GenericValidator.minLength(id, ID_CHECK_ID)){

           throw new IllegalArgumentException("ID 길이가 너무 작습니다");

       }

    }


    출처 : http://mainia.tistory.com/336?srchid=BR1http%3A%2F%2Fmainia.tistory.com%2F336

    Posted by 1010
    01.JAVA/Java2009. 12. 9. 11:40
    반응형

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.PrintStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.UnsupportedEncodingException;
    import java.text.DecimalFormat;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.StringTokenizer;
    import java.util.regex.Pattern;

    import org.apache.commons.lang.StringUtils;


    /**
     * 공통 String Util
     * org.apache.commons.lang.StringUtils 상속후 필요 메소드 추가
     * 자세한 기타 자세한 스펙은 org.apache.commons.lang.StringUtils 참조
     * (url :
    http://jakarta.apache.org/commons/lang/api-release/org/apache/commons/lang/StringUtils.html)
     */
    public class StringUtil extends StringUtils {
     
     public static String nullToZero(String value){
      ;
      if(value == null || value.equals("")){
       value   =   "0";
      }
      return value;
     }
     
     
     
     /**
      * 문자열 좌측의 공백을 제거하는 메소드
      * @param  str 대상 문자열
      * @return trimed string with white space removed from the front.
      */
     public static String ltrim(String str){
      int len = str.length();
      int idx = 0;

      while ((idx < len) && (str.charAt(idx) <= ' '))
      {
       idx++;
      }
      return str.substring(idx, len);
     }

     /**
      * 문자열 우측의 공백을 제거하는 메소드
      * @param  str 대상 문자열
      * @return trimed string with white space removed from the end.
      */
     public static String rtrim(String str){
      int len = str.length();

      while ((0 < len) && (str.charAt(len-1) <= ' '))
      {
       len--;
      }
      return str.substring(0, len);
     }


     /**
      * String을
      * @param str
      * @return
      */
     public static String changeMoney(String str) {
      DecimalFormat df = new DecimalFormat("###,###");

      return df.format(parseInt(str));
     }

     /**
      * 파라미터로 넘어오는 String을 , 를 제거해준다.
      *
      * @param s java.lang.String
      * @return java.lang.String
      */
     public static String removeComma(String str) {
      String rtnValue = str;
      if ( isNull(str) ) {
       return "";
      }

      rtnValue = replace(rtnValue, ",", "");
      return rtnValue;
     }

     /**
      * 숫자 0이 넘어오면 ""로 대치
      * @param  int 대상 숫자
      * @return java.lang.String
      */
     public static String isOneNull(int num){
      if (num == 0) return "";
      else return Integer.toString(num);
     }
     
     /**
      * str이 null 이거나 "", "    " 일경우 return true
      * @param str
      * @return
      */
     public static boolean isNull(String str) {

      return (str == null || (str.trim().length()) == 0 );
     }

     public static boolean isNull(Object obj) {
      String str = null;
      if( obj instanceof String ) {
       str = (String)obj;
      } else {
       return true;
      }

      return isNull(str);
     }


     /**
      * null이 아닐때.
      * @param str
      * @return
      */
     public static boolean isNotNull(String str) {
      /**
       * isNull이 true이면 false
       * false이면 true
       */
      if( isNull(str) ){
       return false;

      } else {
       return true;
      }
     }

     /***
      * 널체크
      * @param obj
      * @return
      */
     public static boolean isNotNull(Object obj) {
      String str = null;
      if( obj instanceof String ) {
       str = (String)obj;
      } else {
       return false;
      }

      return isNotNull(str);
     }

     /**
      * 파라미터가 null 이거나 공백이 있을경우
      * "" 로 return
      * @param value
      * @return
      */
     public static String replaceNull(String value) {
      return replaceNull(value, "");
     }

     /**
      * Object를 받아서 String 형이 아니거나 NULL이면 ""를 return
      * String 형이면 형 변환해서 넘겨준다.
      * @param value
      * @return
      */
     public static String replaceNull(Object value) {
      Object rtnValue = value;
      if( rtnValue == null || !"java.lang.String".equals(rtnValue.getClass().getName())) {
       rtnValue = "";
      }

      return replaceNull((String)rtnValue, "");
     }

     /**
      * 파라미터로 넘어온 값이 null 이거나 공백이 포함된 문자라면
      * defaultValue를 return
      * 아니면 값을 trim해서 넘겨준다.
      * @param value
      * @param repStr
      * @return
      */
     public static String replaceNull(String value, String defaultValue) {
      if (isNull(value)) {
       return defaultValue;
      }

      return value.trim();
     }

     /**
      * Object를 받아서 String 형이 아니거나 NULL이면 defaultValue를 return
      * String 형이면 형 변환해서 넘겨준다.
      * @param value
      * @param repStr
      * @return
      */
     public static String replaceNull(Object value, String defaultValue) {
      String valueStr = replaceNull(value);
      if ( isNull(valueStr) ) {
       return defaultValue;
      }

      return valueStr.trim();
     }

     /**
      * Method ksc2asc.
      * 8859-1를 euc-kr로 인코딩하는 함수
      * @param str - String
      * @return String
      */
     public static String ksc2asc(String str) {
      String result = "";

      if (isNull(str)) {
       result = "";
      } else {
       try {
        result = new String( str.getBytes("euc-kr"), "8859_1" );
       } catch( Exception e ) {
        result = "";
       }
      }

      return result;
     }

     /**
      * Method asc2ksc.
      * euc-kr을 8859-1로 인코딩하는 함수
      * @param str - String
      * @return String
      */
     public static String asc2ksc(String str) {
      String result = "";

      if (isNull(str)) {
       result = "";
      } else {
       try {
        result = new String( str.getBytes("8859_1"), "euc-kr" );
       } catch( Exception e ) {
        result = "";
       }
      }

      return result;
     }

     /**************************************************************************************/
     /* parse method start */


     /**
      * String을 int형으로
      * @param value
      * @return
      */
     public static int parseInt(String value) {
      return parseInt(value, 0);
     }
     /**
      * Object를 int형으로
      * defaultValue는 0이다.
      * @param value
      * @return
      */
     public static int parseInt(Object value) {
      String valueStr = replaceNull(value);
      return parseInt(valueStr, 0);
     }
     /**
      * Object를 int형으로
      * Object가 null이면 defaultValue return
      * @param value
      * @param defaultValue
      * @return
      */
     public static int parseInt(Object value, int defaultValue) {
      String valueStr = replaceNull(value);
      return parseInt(valueStr, defaultValue);
     }
     /**
      * String을 int형으로
      * String이 숫자 형식이 아니면 defaultValue return
      * @param value
      * @param defaultValue
      * @return
      */
     public static int parseInt(String value, int defaultValue) {
      int returnValue = 0;

      if( isNull(value) ) {
       returnValue = defaultValue;
      } else if( !isNumeric(value) ) {
       returnValue = defaultValue;
      } else {
       returnValue = Integer.parseInt(value);
      }

      return returnValue;
     }

     /**
      * String을 long형으로
      * defaultValue는 0이다.
      * @param value
      * @return
      */
     public static long parseLong(String value) {
      return parseLong(value, 0);
     }

     /**
      * String을 long형으로
      * 잘못된 데이타 일시 return은 defaultValue
      * @param value
      * @return
      */
     public static long parseLong(String value, long defaultValue) {
      long returnValue = 0;

      if( isNull(value) ) {
       returnValue = defaultValue;
      } else if( !isNumeric(value) ) {
       returnValue = defaultValue;
      } else {
       returnValue = Long.parseLong(value);
      }

      return returnValue;
     }

     /**
      * Exception을 String으로 뽑아준다.
      * @param ex
      * @return
      */
     public static String stackTraceToString(Throwable e) {
      try {
       StringWriter sw = new StringWriter();
       PrintWriter pw = new PrintWriter(sw);
       e.printStackTrace(pw);
       return "------\r\n" + sw.toString() + "------\r\n";
       }catch(Exception e2) {
        return StringUtil.stackTraceToString2(e);
       }
     }
     /**
      * Exception을 String으로 뽑아준다.
      * @param ex
      * @return
      */
     public static String stackTraceToString2(Throwable e) {
      ByteArrayOutputStream b = new ByteArrayOutputStream();
      PrintStream p = new PrintStream(b);
      e.printStackTrace(p);
      p.close();
      String stackTrace = b.toString();
      try {
       b.close();
      } catch (IOException ex) {
       ex.printStackTrace();
      }

    //  return convertHtmlBr(stackTrace);
      return stackTrace;
     }

     /**
      * Html 코드에서 &#60;br&#62; 태크 제거
      * @param comment
      * @return
      */
     public static String convertHtmlBr(String comment) {
      String rtnValue = "";
      if( isNull(comment) ) {
       return "";
      }

      rtnValue = replace(comment, "\r\n", "<br>");

      return rtnValue;
     }


     /**
      * String 배열을 List로 변환한다.
      * @param values
      * @return
      */
     public static List changeList(String [] values) {
      List list = new ArrayList();

      if( values == null ) {
       return list;
      }
      for(int i=0,n=values.length; i<n; i++) {
       list.add(values[i]);
      }

      return list;
     }


     public static String[] toTokenArray(String str, String sep){

      String[] temp = null;

      try{
       StringTokenizer st = new StringTokenizer(str, sep);
       temp = new String[st.countTokens()];
       int index = 0;
       while(st.hasMoreTokens()){
        temp[index++] = st.nextToken();
       }
      }catch(Exception e){
       e.printStackTrace();
      }

      return temp;
     }
        public static String strip(String str, String str1){

         if(str == null || "".equals(str.trim())) return "";

         String temp = str;
      int pos = -1;
      while((pos = temp.indexOf(str1, pos)) != -1) {
       String left = temp.substring(0, pos);
       String right = temp.substring(pos + 1, temp.length());
       temp = left + "" + right;
       pos += 1;
      }
      return temp;
        }

        /**
      * Method ksc2asc.
      * euc-kr을 euc-kr로 인코딩하는 함수
      * @param str - String
      * @return String
      */
     public static String ksc2utf8(String str) {
      String result = "";

      if (isNull(str)) {
       result = "";
      } else {
       try {
        result = new String( str.getBytes("euc-kr"), "utf-8" );
       } catch( Exception e ) {
        result = "";
       }
      }

      return result;
     }

     /**
      * string에 있는 ', ", \r\n 를 HTML 코드로 변환한다.
      * @param str
      * @return
      */
     public static String changeQuotation(String str) {
      String rtnValue = str;
      rtnValue = replaceNull(rtnValue);
      rtnValue = replace(replace(replace(rtnValue, "'", "&#39;"), "\"", "&#34;"), "\r\n", "<br>");

      return rtnValue;
     }
     public static String changeQuotation(Object obj) {
      if( isStringInteger(obj) ) {
       return changeQuotation(String.valueOf(obj));
      }

      return "";
     }

     /**
      * 해당 Object가 String or Integer 이면 true
      * 아니면 false
      * @param obj
      * @return
      */
     public static boolean isStringInteger(Object obj) {

      boolean flag = false;

      if( obj instanceof String || obj instanceof Integer ) {
       flag = true;
      }

      return flag;
     }

     /**
      * 백분율을 구한다.
      * %는 빼고 값만 리턴
      * @param value
      * @param total
      * @return
      */
     public static String percentValue(int value, int total) {
      double val = Double.parseDouble(String.valueOf(value)) / Double.parseDouble(String.valueOf(total)) * 100;

      DecimalFormat df = new DecimalFormat("##0.0");
      return df.format(val);
     }

     


     /**
      *  XSS(Cross Site Scripting) 취약점 해결을 위한 처리
      *
      * @param sourceString String 원본문자열
      * @return String 변환문자열
      */
     public static String replaceXSS(String sourceString){
      String rtnValue = null;
      if(sourceString!=null){
       rtnValue = sourceString;
       if(rtnValue.indexOf("<x-") == -1){
        rtnValue = rtnValue.replaceAll("< *(j|J)(a|A)(v|V)(a|A)(s|S)(c|C)(r|R)(i|I)(p|P)(t|T)", "<x-javascript");
        rtnValue = rtnValue.replaceAll("< *(v|V)(b|B)(s|S)(c|C)(r|R)(i|I)(p|P)(t|T)", "<x-vbscript");
        rtnValue = rtnValue.replaceAll("< *(s|S)(c|C)(r|R)(i|I)(p|P)(t|T)", "<x-script");
        rtnValue = rtnValue.replaceAll("< *(i|I)(f|F)(r|R)(a|A)(m|M)(e|E)", "<x-iframe");
        rtnValue = rtnValue.replaceAll("< *(f|F)(r|R)(a|A)(m|M)(e|E)", "<x-frame");
        rtnValue = rtnValue.replaceAll("(e|E)(x|X)(p|P)(r|R)(e|E)(s|S)(s|S)(i|I)(o|O)(n|N)", "x-expression");
        rtnValue = rtnValue.replaceAll("(a|A)(l|L)(e|E)(r|R)(t|T)", "x-alert");
        rtnValue = rtnValue.replaceAll(".(o|O)(p|P)(e|E)(n|N)", ".x-open");
        rtnValue = rtnValue.replaceAll("< *(m|M)(a|A)(r|R)(q|Q)(u|U)(e|E)(e|E)", "<x-marquee");
        rtnValue = rtnValue.replaceAll("&#", "&amp;#");
       }
      }

      return rtnValue;
        }


     /**
      * 특정문자를 HTML TAG형식으로 변경하는 메소드.
      *
      * <xmp>
      * & --> &amp;
      * < --> &lt;
      * > --> &gt;
      * " --> &quot;
      * ' --> &#039;
      *-----------------------------------------------------------------
      * <option type=radio  name=r value="xxxxxxxx"> yyyyyyy
      * <input  type=hidden name=h value="xxxxxxxx">
      * <input  type=text   name=t value="xxxxxxxx">
      * <textarea name=msg rows=20 cols=53>xxxxxxx</textarea>
      *-
      * 위와 같은 HTML 소스를 생성할 때, xxxxxxx 부분의 문자열 중에서
      * 아래에 있는 몇가지 특별한 문자들을 변환하여야 합니다.
      * 만약 JSP 라면 미리 변환하여 HTML 전체 TAG를 만들거나, 혹은 아래처럼 사용하세요.
      *-
      * <option type=radio  name=r value="<%= StringUtil.translate(s) %>"> yyyyyyy
      * <input  type=hidden name=n value="<%= StringUtil.translate(s) %>">
      * <input  type=text   name=n value="<%= StringUtil.translate(s) %>">
      * <textarea name=body rows=20 cols=53><%= StringUtil.translate(s) %></textarea>
      *-
      * 또 필요하다면 yyyyyyy 부분도  translate(s)를 할 필요가 있을 겁니다.
      * 필요할 때 마다 사용하세요.
      *-
      * </xmp>
      *
      * @return the translated string.
      * @param str java.lang.String
      */
     public static String translate(String str){
      if ( str == null ) return null;

      StringBuffer buf = new StringBuffer();
      char[] c = str.toCharArray();
      int len = c.length;

      for ( int i=0; i < len; i++){
       if      ( c[i] == '&' ) buf.append("&amp;");
       else if ( c[i] == '<' ) buf.append("&lt;");
       else if ( c[i] == '>' ) buf.append("&gt;");
       else if ( c[i] == '"' ) buf.append("&quot;"); // (char)34
       else if ( c[i] == '\'') buf.append("&#039;"); // (char)39
       else buf.append(c[i]);
      }
      return buf.toString();
     }

       /**
        * String 앞 또는 뒤를 특정문자로 지정한 길이만큼 채워주는 함수    <BR>
        * (예) pad("1234","0", 6, 1) --> "123400"   <BR>
        *
        * @param    src  Source string
        * @param    pad  pad string
        * @param    totLen     total length
        * @param    mode     앞/뒤 구분 (-1:front, 1:back)
        * @return   String
        */
     public static String pad(String src, String pad, int totLen, int mode){
      String paddedString = "";

      if(src == null) return "";
      int srcLen = src.length();

      if((totLen<1)||(srcLen>=totLen)) return src;

      for(int i=0; i< (totLen-srcLen); i++){
       paddedString += pad;
      }

      if(mode == -1)
       paddedString += src; // front padding
      else
           paddedString = src + paddedString; //back padding

      return paddedString;
     }

     /**
      * 주어진 길이(iLength)만큼 주어진 문자(cPadder)를 strSource의 왼쪽에 붙혀서 보내준다.
      * ex) lpad("abc", 5, '^') ==> "^^abc"
      *     lpad("abcdefghi", 5, '^') ==> "abcde"
      *     lpad(null, 5, '^') ==> "^^^^^"
      *
      * @param strSource
      * @param iLength
      * @param cPadder
      */
     public static String lpad(String strSource, int iLength, char cPadder){
      StringBuffer sbBuffer = null;

      if (!isEmpty(strSource)){
       int iByteSize = getByteSize(strSource);
       if (iByteSize > iLength){
        return strSource.substring(0, iLength);
       }else if (iByteSize == iLength){
        return strSource;
       }else{
        int iPadLength = iLength - iByteSize;
        sbBuffer = new StringBuffer();
        for (int j = 0; j < iPadLength; j++){
         sbBuffer.append(cPadder);
        }
        sbBuffer.append(strSource);
        return sbBuffer.toString();
       }
      }

      //int iPadLength = iLength;
      sbBuffer = new StringBuffer();
      for (int j = 0; j < iLength; j++){
       sbBuffer.append(cPadder);
      }
      return sbBuffer.toString();
     }

     /**
      * 주어진 길이(iLength)만큼 주어진 문자(cPadder)를 strSource의 오른쪽에 붙혀서 보내준다.
      * ex) lpad("abc", 5, '^') ==> "abc^^"
      *     lpad("abcdefghi", 5, '^') ==> "abcde"
      *     lpad(null, 5, '^') ==> "^^^^^"
      *
      * @param strSource
      * @param iLength
      * @param cPadder
      */
     public static String rpad(String strSource, int iLength, char cPadder){
      StringBuffer sbBuffer = null;
       if (!isEmpty(strSource)){
       int iByteSize = getByteSize(strSource);
       if (iByteSize > iLength){
        return strSource.substring(0, iLength);
       }else if (iByteSize == iLength){
        return strSource;
       }else{
        int iPadLength = iLength - iByteSize;
        sbBuffer = new StringBuffer(strSource);
        for (int j = 0; j < iPadLength; j++){
         sbBuffer.append(cPadder);
        }
        return sbBuffer.toString();
       }
      }
      sbBuffer = new StringBuffer();
      for (int j = 0; j < iLength; j++){
       sbBuffer.append(cPadder);
      }
      return sbBuffer.toString();
     }

     /**
      *  byte size를 가져온다.
      *
      * @param str String target
      * @return int bytelength
      */
     public static int getByteSize(String str){
      if (str == null || str.length() == 0)
       return 0;
      byte[] byteArray = null;
       try{
       byteArray = str.getBytes("UTF-8");
      }catch (UnsupportedEncodingException ex){}
      if (byteArray == null) return 0;
      return byteArray.length;
     }
          /**
         * 긴 문자열 자르기
         * @param srcString      대상문자열
         * @param nLength        길이
         * @param isNoTag        테그 제거 여부
         * @param isAddDot        "..."을추가 여부
         * @return
         */
        public static String strCut(String srcString, int nLength, boolean isNoTag, boolean isAddDot){  // 문자열 자르기
            String rtnVal = srcString;
            int oF = 0, oL = 0, rF = 0, rL = 0;
            int nLengthPrev = 0;

            // 태그 제거
            if(isNoTag) {
                Pattern p = Pattern.compile("<(/?)([^<>]*)?>", Pattern.CASE_INSENSITIVE);  // 태그제거 패턴
                rtnVal = p.matcher(rtnVal).replaceAll("");
            }
            rtnVal = rtnVal.replaceAll("&amp;", "&");
            rtnVal = rtnVal.replaceAll("(!/|\r|\n|&nbsp;)", "");  // 공백제거
            try {
             byte[] bytes = rtnVal.getBytes("UTF-8");     // 바이트로 보관
             // x부터 y길이만큼 잘라낸다. 한글안깨지게.
             int j = 0;
             if(nLengthPrev > 0) while(j < bytes.length) {
              if((bytes[j] & 0x80) != 0) {
               oF+=2; rF+=3; if(oF+2 > nLengthPrev) {break;} j+=3;
                 } else {if(oF+1 > nLengthPrev) {break;} ++oF; ++rF; ++j;}
             }

             j = rF;

             while(j < bytes.length) {
              if((bytes[j] & 0x80) != 0) {
               if(oL+2 > nLength) {break;} oL+=2; rL+=3; j+=3;
                 } else {if(oL+1 > nLength) {break;} ++oL; ++rL; ++j;}
             }

             rtnVal = new String(bytes, rF, rL, "UTF-8");  // charset 옵션

             if(isAddDot && rF+rL+3 <= bytes.length) {rtnVal+="...";}  // ...을 붙일지말지 옵션
            } catch(UnsupportedEncodingException e){
             e.printStackTrace();
             return srcString;
            }

         return rtnVal;
        }
       
        /**
         * total과 success 로 % 구하고 소수점 1자리까지 계산
         * @param int success     
         * @param int total      

         * @return String %
         */
        public static String calculatePercent(int success,int total){
         String result   =  "0";
         
         if(total == 0){
           
         
         }else{
         
            Double tempSuccess  = new Double(success+".0");
            Double tempTotal    = new Double(total+".0");
            Double tempPercent   = new Double(100+".0");
         
            double cal =  tempSuccess.doubleValue()*tempPercent.doubleValue()/tempTotal.doubleValue();
         
          result = new java.text.DecimalFormat("#.#").format(cal);
         
         }
         
         return result;
        }

    }


    출처 : http://shonm.tistory.com/177?srchid=BR1http%3A%2F%2Fshonm.tistory.com%2F177

    Posted by 1010
    02.Oracle/DataBase2009. 12. 9. 11:34
    반응형
    오라클 데이타딕셔너리

    All_all_tables : user가 access할수있는 모든 Table
    All_catalog : user가 access할수있는 모든 Table, Views, synonyms, sequence
    All_clusters : user가 access할수있는 모든 clusters
    less..
    All_col_comments : user가 access할수있는 모든 Table,Views에 대한 칼럼comments
    All_col_privs : user에게 또는 Public에게 허용된 모든 칼럼에 대한 권한.
    All_col_privs_made : user가 부여한 칼럼에 대한 권한.
    All_col_privs_recd : user에게 또는 Public에게 허용된 모든 칼럼에 대한 권한.
    All_coll_types : user가 access 할수 있는 모든 collection type
    All_cons_columns : 제약조건에 관련된 칼럼, access할수 있는 대한 정보
    All_constraints : access할수 있는 테이블에 대한 제약조건.
    All_db_links : user가 access 할수 있는 데이터베이스 link
    All_def_audit_opts : 오브젝트가 생성될때 적용될수있는 default오브젝트감사내용.
    All_dependencies : user가 access할수있는 오브젝트간의 dependencies(참조,link)
    All_directories : user가 access할 수 있는 모든 directories (owner 는 항상 sys)
    All_errors : user가 access할수있는 모든 objects(view,procedure,package, function,
    packagebody) 에 대한 에러.
    All_ind_columns : user가 access할수있는 테이블에 대한 인덱스의 칼럼.
    All_ind_partitions : user가 access할수있는 인덱스partition, partition에 대한
    storage매개변수, Analyze명령에 의해 결정된 partition통계.
    All_indexes : user가 access할수있는 테이블의 인덱스.
    이 view의 통계를 수집하기위해, Analyze명령을 사용한다.
    병렬partition인텍스탐색을 지원한다.
    All_labels : system labels 에 대한 Oracle server view.
    All_libraries : user가 access할 수 있는 모든 libraries.
    All_lobs : user가 access할 수 있는 모든테이블에 포함된 LOBs.
    All_method_params : user가 access할 수 있는 method와 그method의 parameter.
    All_method_results :
    All_nested_tables : user가 access할수있는테이블내의 Nested Table
    All_object_tables : user가 access할수있는테이블의모든정보.
    All_objects : user가 access할수있는objects(index partition,table partition,package,
    package body, trigger)
    All_part_col_statistics : user가 access할 수 있는 테이블partition에 대한 칼럼통계와
    막대그래프화된 정보.
    All_part_histograms : user가 access할수있는 테이블partition의 histograms에 대한
    histogram정보.
    All_part_indexes : user가 access할수있는 모든partition된 index의 partition정보.
    All_part_key_columns :user가 access할수있는 partition된 objects의 partition key
    칼럼에 대한정보
    All_part_tables : user가 access할수있는partition된 Table에 대한 partition정보.
    All_refresh : user가 access할수있는모든 refresh groups.
    All_refresh_children : user가 access할 수 있는 refresh groups 안의 모든objects
    All_refs : user가 access할 수 있는 칼럼중 REF칼럼과, REF속성.
    All_registered_snapshots : 모든 등록된 snapshots.
    All_sequences : user가 access할수있는 sequences.
    All_snapshot_logs : 모든 snapshot logs.
    All_snapshot_refresh_times : 모든 snapshot refresh times.
    All_snapshots : user가 acces할수있는 모든 snapshots.
    All_source : user가 access할수있는 모든 stored objects의 text source.
    All_synonyms : user가 access할수있는 모든 synonyms.
    All_tab_col_statistics : 'User_tab_columns' view안의 정보에대한 칼럼통계와 그래프정보
    All_tab_columns : user가 access할수있는모든 table, views, clusters에 대한 칼럼.
    이view를 활용하기위해서는 Analyze명령어를 사용한다.
    All_tab_comments : user가 access할 수 있는 모든 table, views에 대한 comments.
    All_tab_histograms : user가 access할수있는table, views에 대한 histograms.
    All_tab_partitions : user가 access할수 있는 각각의 테이블partition에 대한
    partition정보, storage parameter, Analyze명령에 의한

    통계정보등을 서술한다.
    All_tab_privs : user혹은 PUBLIC가 부여받은 오브젝트권한.
    All_tab_privs_made : user가 부여한 user권한과 오브젝트권한.
    All_tab_privs_recd : user 또는 PUBLIC이 부여받은 오브젝트권한.
    All_tables : user가 access할 수 있는 모든 테이블.
    Analyze명령으로 이 view의 통계를 얻을 수 있다.
    All_triggers : user소유의 trigger, user소유테이블의 trigger, 또는 user가
    CREATE ANY TRIGGER 권한을 갖고있다면, 모든 트리거에 대한 정보.
    All_trigger_cols : user소유의 trigger, user소유테이블의 trigger, 또는 user가
    CREATE ANY TRIGGER 권한을 갖고있다면, 모든 트리거에 대한 칼럼정보.
    All_type_attrs : user가 access할 수 있는 type의 attributes.
    All_type_methods : user가 access할수있는type의 methods.
    All_types : user가 access할 수 있는 type.
    All_updatable_columns : join view에서 update가능한 칼럼에 대한 정보.
    All_users : 데이터베이스의 모든 user에 대한 정보.
    All_views : user가 access할수있는view의 텍스트.
    Audit_actions : 감사추적type코드 정보.
    catalog : Oracle 5.0 version과의 호환정보를 포함한다.
    이view의 사용은 추천할만하지 못하다.
    cat : user_catalog 에 대한 synonym.
    chained_rows : ANALYZE LIST CHAINED ROWS 명령에 대한 default table.
    clu : user_clusters 테이블의 synonym.
    code_pieces : dba_object_size 와 user_object_size view를 create 시에 사용됨.
    code_size : dba_object_size 와 user_object_size view를 create 시에 사용됨.
    col : Oracle 5.0version 호환정보를 포함하고 있다.
    cols : user_tab_columns view 의 synonym.
    column_privileges : user가 부여한권한,부여받은권한, owner인권한,
    또는 PUBLIC에게 부여받은 권한에 대한 칼럼정보.
    Dba_2pc_neighbors : 진행중인 트랜잭션에 대한 연결 및 종료에 대한 정보.
    Dba_2pc_pending : recovery를 기다리는 분산된트랜잭션에 대한 정보.
    Dba_all_tables : 데이터베이스내의 모든테이블(object table, relational table).
    Dba_audit_exists : "AUDIT NOT EXISTS" and "AUDIT EXISTS"에 의해 생성된
    감사추적요소.
    Dba_audit_object : 시스템내의 모든 object에 대한 감사추적기록.
    Dba_audit_session : 세션연결과 종료에 관련된 모든 감사 추적기록.
    Dba_audit_statement : GRANT, REVOKE, AUDIT, NOAUDIT, ALTER SYSTEM
    관련된 감사추적기록.
    Dba_audit_trail : 모든 감사추적요소.
    Dba_blockers : 누군가가 스스로 걸지않은 lock이 해제되기를 기다리는 session정보.
    Dba_catalog : 모든 데이터베이스 table, views, synonyms 과 sequence에 대한 정보.
    Dba_clu_columns : cluster칼럼과 table칼럼의 mapping정보.
    Dba_clusters : 데이터베이스내에 있는 모든 cluster.
    Dba_col_comments : 데이터베이스내의 모든 table, views의 칼럼에대한 comments.
    Dba_col_privs : 데이터베이스내의 칼럼에 대한 모든권한.
    Dba_coll_types : 데이터베이스내의 모든 collection type, VARRAYs, nested tables,
    object tables 등에 대한 정보.
    Dba_constraints : 모든테이블에 대한 constraint(primary, check, unique,
    referential integrity, with check option on a view,

    with read only on a view) 정보.
    Dba_cons_columns : constraint 정의안에 있는 access가능한 칼럼에 대한 정보.
    Dba_data_files : 데이터베이스파일에 관한 정보.
    Dba_db_links : 데이터베이스내의 모든 Link.
    Dba_Ddl_locks : 데이터베이스내의 모든 DDL lock과 DDL lock이 현저하게
    요구되는 사항에 관한정보.
    Dba_dependencies : object 에 대한 Dependence.(REF, HARD)
    Dba_directories : 데이터베이스내의 모든 directory objects.
    Dba_Dml_locks : 데이터베이스내에 구성된모든 DML lock과 DML lock이
    현저하게 요구되는사항에 관한정보.
    Dba_errors : 데이터베이스내의 저장된 object에 대해 가장최근에 발생된 error.
    Dba_exp_files : export파일에 대한 정보.
    Dba_exp_objects : 점진적으로 export 되고있는 object에 대한 정보.
    Dba_exp_version : 가장최근에 export된 session에 대한 version 정보.

    Dba_extents : 데이터베이스내의 모든 세그먼트를 이루는 extents에 대한 정보.
    Dba_free_space : 모든 테이블스페이스내의 free extents의 정보.
    Dba_free_space_coalesced : 테이블스페이스내의 합쳐진 공간에 대한 통계정보.

    Dba_indexes : 데이터베이스내의 모든 index. 통계정보를 얻기위해 Analyze를 사용.
    Dba_ind_columns : 모든테이블과 클러스터에서 인덱스를 구성하는 칼럼에 대한정보.
    Dba_ind_partitions : 각각의 index파티션에 대해서, 파티션정보, 파티션에대한
    storage 매개변수, Analyze에 결정된 파티션통계자료.
    Dba_jobs : 데이터베이스에 있는 모든 Jobs.
    Dba_jobs_running : 데이터베이스내에 현재 실행중인 모든 Jobs.
    Dba_libraries : 데이터베이스내의 모든 libraries.
    Dba_lobs : 모든 테이블에 포함된 LOBs.
    Dba_locks : 데이터베이스내에 생성된 모든 lock, latch과 lock,latch가
    현저하게 요구되는 사항에 대한 정보.
    Dba_method_params : 데이터베이스내에 type에 대한 method 매개변수.
    Dba_method_results : 데이터베이스내에 type에 대한 method results.
    Dba_nested_tables : 모든테이블내에 포함된 nested table에 대한 정보.
    Dba_object_size : PL/SQL object에 대한 size, bytes.
    Dba_object_tables : 데이터베이스내에 모든 object tables.
    Dba_objects : 데이터베이스내에 모든 objects.(index partition, table partition,
    package,package_body,trigger)
    Dba_obj_audit_opts : 모든 table, view에 대한 감사 option.
    Dba_part_col_statistics : 모든 table 파티션에 대한 칼럼통계와 그래프정보.
    Dba_part_histograms : 모든 table 파티션의 histogram에 대한 데이터(endpoint).
    Dba_part_indexes : 모든 partition index에 대한 정보.
    Dba_part_key_columns : 모든 partition된 object에 대한 분할키칼럼정보.
    Dba_part_tables : 모든 partition된 table에 대한 정보.
    Dba_priv_audit_opts : 시스템과 user에 의해 감사를 받고있는 시스템 privileges.
    Dba_profiles : 모든 profiles과 해당 profile의 limit을 나타냄.
    Dba_queue_schedules : 메시지를 전달하는 schedule.
    Dba_queue_tables : 데이터베이스내에 생성된 모든 queue테이블의
    queue type의 name과 type.
    Dba_Queus : 데이터베이스내의 모든 queue에 대한 동작특성.
    Dba_rchild : refresh group 안의 모든 children object.
    Dba_refresh : 모든 refresh group 에 대한 정보.
    Dba_refresh_children : refresh group 안의 모든 object에 대한 정보.
    Dba_refs : 데이터베이스내의 모든 테이블의 REF칼럼과, REF 속성을 가진 칼럼.
    Dba_refistered_snapshot_groups : 모든 snapshot 사본 그룹.
    Dba_registered_snapshots : 지역테이블의 원격snapshot 에 대한 정보.
    Dba_rgroup : 모든 refresh group.
    Dba_roles : 모든 데이터베이스내에 존재하는 roles.
    Dba_role_privs : user와 role에 부여된 role에 대한 정보.
    Dba_rollback_segs : rollback segments 에 대한 정보.
    Dba_segments : 모든 데이터베이스 segment에 대한 할당된 storage에 대한 정보.
    Dba_sequences : 모든 데이터베이스내의 sequences 에 대한 정보.
    Dba_snapshot_logs : 모든 데이터베이스내의 snapshot_logs.
    Dba_snapshot_refresh_times : snapshot refresh 한 시간.
    Dba_snapshots : 모든 데이터베이스내의 snapshots.
    Dba_source : 모든 데이터베이스내의 저장object 의 source를포함.
    Dba_stmt_audit_opts : system, user에 의한 현재의 감사option에 대한 정보.
    Dba_synonyms : 데이터베이스내의 모든 synonyms
    Dba_sys_privs : user에게 부여된 system privilege와 role.
    Dba_tab_col_statistics : Dba_tab_columns view에 있는정보에 대한 칼럼통계와
    그래프정보
    Dba_tab_columns : 모든 table, view, cluster에 대한 칼럼정보. Analyze명령어사용.
    Dba_tab_comments : 데이터베이스내의 모든 table, view에 대한 주석.
    Dba_tab_histograms : 모든 table의 칼럼에 대한 histogram.
    Dba_tab_partitions : 각각의 table partition에 대해서, partition level의 partition정보와,
    partition의 storage매개변수 ,Analyze 에의해 결정된 여러 partition통계정보.
    Dba_tab_privs : 모든 데이터베이스내의 object에 부여된 권한.
    Dba_tables : 모든 데이터베이스내의 관계형테이블에 관한정보.Analyze로
    통계정보를 얻을수 있다.
    Dba_tablespaces : 모든 테이블스페이스에 관한정보.
    Dba_triggers : 모든 데이터베이스내의 trigger 정보.
    Dba_trigger_cols : 모든 trigger에서 사용된 칼럼정보.
    Dba_ts_quotas : 모든 user에게 할당된 tablespace.
    Dba_type_attrs : 데이터베이스내의 type에 대한 속성.
    Dba_type_methods : 데이터베이스내의 모든 type에 대한 methods.
    Dba_types : 데이터베이스내의 모든 추상적데이터type.
    Dba_updatable_columns : join view에서 데이터베이스관리자가
    update할수있는칼럼정보.
    Dba_users : 데이터베이스내의 모든 user정보.
    Dba_views : 모든 데이터베이스내의 view의 text.
    Dbms_alert!_info : 등록된 alert!정보.
    Dbms_lock_allocated : 사용자에게 할당된 lock정보.
    Deptree : utldtree.sql 에의해 생성되며, object의 dependency tree정보를 포함함.
    'Sys' user인 경우. 이 object에 관련된 공유커서를 나타내고,
    다른 user인 경우공유커서이외의 object를 나타낸다.
    다른 user는 공유커서정보를 얻기위해, Sys.deptree를 access할수있다.

    Dictionary : data dictionary table, view에 대한 정보.
    Dict_columns : data dictionary table, view에 대한 칼럼.
    Error_size : Dba_obejct_size 와 user_obejct_size view를 create 할때 사용된다.
    Exceptions : 무결성제약조건에 위배되는 정보를 포함. utlexcpt.sql 로 생성.
    File_lock : 병렬서버view. 초기화파라미터 GC_FILE_TO_LOCKS 에 명시된,
    데이터파일에 PCM lock의 mapping정보.
    File_ping : 병렬서버view.각데이타파일에 할당된 block의 수.
    GC_FILES_TO_LOCKS 최적값을 구하기 위해 현존하는 데이터파일의
    access방법을 결정하는데 이 정보를사용할 수 있다.
    FILEXT$ : DBA_DATA_FILES 와 동일. (DBA_DATA_FILES의 사용을 추천)
    GLOBAL_NAME : 현제 데이터베이스의 유일한 이름.
    HS_ALL_CAPS : 모든 비 Oracle Data store (FDS) 와 관련된 특성에 관한정보.
    HS_ALL_DD : 모든 비 Oracle Data store(FDS)에 대한 Data dictionary.
    HS_ALL_INITS : 비 Oracle Data store(FDS)에 대한 초기화 매개변수.
    HS_BASE_CAPS : 비 Oracle Data store(FDS)에 대한 기본특성에 관한정보.
    HS_BASE_DD : 비 Oracle Data store(FDS)에 대한 Data dictionary.
    HS_CLASS_CAPS : 비 Oracle Data store(FDS)에 포함된 class-specific 특성정보.
    HS_CLASS_DD : 비 Oracle Data store(FDS) class_specific data dictionary.
    HS_CLASS_INIT : 비 Oracle Data store(FDS) class-specific 초기화 매개변수.
    HS_EXTERNAL_OBJECT_PRIVILEGES : user에게 부여된 object권한.
    HS_EXTERNAL_OBJECTS : oracle server에서 access가능한 external obejct.
    HS_EXTERNAL_USER_PRIVILEGES : 어느 특정object에 국한되지않은 모든
    부여된권한
    HS_FDS_CLASS : 비 oracle (FDS) class 에 관한 정보.
    HS_FDS_INST : 비 oracle (FDS) instance에 관한정보.
    HS_INST_CAPS : instance-specific 특성정보.
    HS_INST_DD : 비 oracle (FDS) instance-specific data dictionary 변경정보.
    HS_INST_INIT : 비 oracle (FDS) instance-specific 초기화 매개변수정보.
    IDEPTREE : UTLDTREE.sql 로 생성하고, 관련tree를 나타냄.
    Deptree의 자동정렬버젼.
    INDEX_HISTOGRAM : Analyze index...validate structure 명령에 대한정보.
    INDEX_STATS : 마지막 Analyze index..validate structure 명령에 대한정보.
    NLS_DATABASE_PARAMETERS : 데이터베이스의 NLS 매개변수.
    NLS_INSTANCE_PARAMETERS : instance의 NLS 매개변수.
    NLS_SESSION_PARAMETERS : user session의 NLS 매개변수.

    OBJ : user_objects 의 synonym.
    PARSED_PIECES : Dba_object_size, User_object_size view를 생성시에 필요.
    PARSED_SIZE : Dba_obejct_size, User_object_size view를 생성시에 필요.
    Plan_table : explain plan의 결과에 대한 table. utlxplan.sql로 생성.
    Product_component_version : Oracle 제품군의 버전과 상태설명.


    Pstubtbl : Pstub utility에 의해 생성된 stub에 관한정보.
    Publicsyn : public synonym 에 관한 정보.
    Public_dependency : object와 관련된 dependencies.(parent object)
    Resource_cost : 각각의 resource에 대한 cost.
    Resource_map : 각각의 resource에 대한 정보.(resource name, resource number)
    Role_role_privs : 다른 role에 부여된 role정보.(user가 access가능한 role에 한해)
    Role_sys_privs : 다른 role에 부여된 system role정보(user가 access가능한role에 한해)
    Role_tab_privs : 다른 role에 부여된 table privileges정보.
    (user가 access가능한role에 한해)

    SEQ : user_sequences 의 synonym.
    Session_privs : 현재 user에게 사용가능한 권한.
    Session_roles : 현재 user에게 사용가능한 roles.
    Source_size : Dba_object_size, User_object_size view를 생성시 필요.
    Stmt_audit_option_map : 감사 option type code정보.
    Syn : user_synonyms 에 대한 synonym.
    Synonyms : Oracle ver 5.와 호환성을 포함. not recommend
    Syscatalog : Oracle ver 5.와 호환성을 포함. not recommend
    Sysfiles : Oracle ver 5.와 호환성을 포함. not recommend
    Syssegobj : Oracle ver 5.와 호환성을 포함. not recommend
    System_privilege_map : system privilege code에 대한 정보.
    Sys_objects : object ID와 object type 그리고 segment data block주소를 매핑하는정보.
    Tab : Oracle ver 5.와 호환성을 포함. not recommend
    Table_privileges : user가 부여한, 부여받은, 소유한, 그리고 PUBLIC으로
    부여된 object 권한정보. Oracle ver 6.과 호환성을 포함. not recommend.
    Table_privilege_map : access 가능한 권한code/권한명칭 정보.
    Tabs : User_tables 의 synonym.
    Tabquotas : Oracle ver 5.와 호환성을 포함. not recommend
    Trusted_servers : 분산환경에서 서버가 신뢰할만한지를 나타냄.
    Tp_pitr_check : catpitr.sql 에 의해 생성. 테이블스페이스의 point-in-time복구를
    방해할지도 모르는 dependencies혹은 restriction에 관한 정보제공.
    Ts_pitr_objects_to_be_dropped : 테이블스페이스의 point-in-time복구수행의 결과
    손실된 object에 대한 정보. (point-in-time recovery의 경우만 해당).
    User_all_tables : user가 사용가능한 테이블(object table, relational table)정보.
    User_arguments : user가 access가능한 object의 매개변수정보.
    User_Audit_object : cataudit.sql로 생성. object에 관련된 감사추적기록.
    User_Audit_session : cataudit.sql로 생성. user의 연결/종료에 관련된 감사추적기록.
    User_Audit_statement : cataudit.sql로 생성. user에 의해 실행된 GRANT,REVOKE,
    AUDIT, NOAUDIT, ALTER SYSTEM 명령에 대한 감사추적기록.


    User_Audit_trail : user와 관련된 전반적인 사항의 감사추적기록.
    User_catalog : user 소유의 table, views, synonyms, sequences 의 이름과 type.
    User_clusters : user소유의 cluster.
    User_clu_columns : user table 의 칼럼과 cluster칼럼과의 매핑테이블.
    User_col_comments : user 의 table, view의 칼럼에 대한 주석.
    User_col_privs : user 가 소유한, 부여한, 부여받은 칼럼에 대한 권한.
    User_col_privs_made : user 소유 object의 칼럼에 대한 권한.
    User_col_privs_recd : user가 부여받은 칼럼에 대한 권한.
    User_coll_types : user가 명명한 collection type정보.
    User_constraints : user소유 테이블의 제약조건정의.
    User_cons_columns : user소유 제약조건에 정의된 칼럼에 대한정보.
    User_db_links : user소유 데이터베이스링크에 대한정보.
    User_dependencies : user소유 object에 대한 dependencies.
    User_errors : user소유 저장 object에 대한 현재의 에러.
    User_extents : user소유 object에 속하는 세그먼트의 extent 정보.
    User_free_space : user가 access가능한 테이블스페이스내의 free extent 정보.
    User_indexes : user 소유의 indexes. Analyze명령을 사용해야함. 병렬서버를 지원.
    User_ind_columns : user소유 index 또는 user소유 table 의 칼럼정보.
    User_ind_partitions : user소유의 index partition각각에 대한설명과, partition정보,
    partition의 storage 매개변수, Analyze명령으로 결정된 여러partition통계
    User_jobs : user소유의 모든 job.(export/import!, execution)
    User_libraries : user소유의 모든 libraries .
    User_lobs : user소유의 table에포함된 LOBs정보.
    internal LOBs( BLOBs, NCLOBs) 만해당, external LOBs(i.e, BFILES)은 아님.
    User_method_params : user type의 method 매개변수.
    User_method_results : user type의 method 의 results.
    User_nested_tables : user소유 테이블에 포함된 nested tables.
    User_object_tables : user가 사용가능한 object table.
    User_objects : user소유의 object.(index partition, table partition, package,
    packagebody, trigger)
    User_object_size : user소유의 PL/SQL object.
    User_obj_audit_opts : cataudit.sql로 생성. user소유의 table,view에 대한 감사option
    User_part_col_statistics : user소유의 tablepartition정보에 대한 칼럼통계와 그래프정보.
    User_part_histograms : user가 access할수있는 table partition의 histogram에 대한
    그래프데이터(end-pointer).
    User_part_key_columns : user소유의 partition object의 partition key칼럼에 대한정보.
    User_part_indexes : 모든 user소유의 partition index의 partition정보.
    User_part_tables : user소유의 partition table에 대한 object 레벨의 partition정보.


    User_password_limits : user에게 적용된 password profile parameter.
    User_queue_tables : user소유 스키마에 생성된 queue table내부의 queues정보.
    User_Queues : user스키마의 모든 queue에 대한 동작 특성을 나타냄.
    User_refresh : 모든 refresh group.
    User_refresh_children : user가 소유한 refresh group 내부의 object에 관한정보.
    User_refs : user소유테이블의 object type칼럼중 REF칼럼, REF속성.
    User_resource_limits : 현재 user의 resource 한계.
    User_role_privs : user에게 부여된 roles.
    User_segments : user오브젝트에 포함된 데이터베이스 segments의 storage할당정보.
    User_sequences : user 소유의 sequences.
    User_snapshots : user 가 볼수있는 snapshots.
    User_snapshot_logs : user 소유의 모든 snapshot logs.
    User_source : user소유 저장 objects 의 모든 text source.
    User_snapshot_refresh_times : snapshot refresh time.
    User_synonyms : user소유의 synonym.
    User_sys_privs : user에게 부여된 system 권한.
    User_tab_col_statistics : user_tab_columns view에 대한 칼럼통계와
    그래프정보를 나타냄.
    User_tab_columns : user소유의 table, view, cluster의 칼럼정보.(Analyze명령사용)
    User_tab_comments : user소유의 table, view에 대한 주석.
    User_tab_histograms : user소유 table의 칼럼에 대한 histogram.
    User_tab_partitions : user소유 table partition에 대한, partition 레벨의 분할정보와,
    partition의 storage매개변수, Analyze에 의해 집계된 여러통계정보.
    User_tab_privs : user가 소유한, 부여한, 부여받은 object에 대한 권한 정보.
    User_tab_privs_made : user가 소유한 object에 관한 모든 권한.
    User_tab_privs_recd : user가 부여받은 object 권한정보.
    User_tables : user소유의 relational table에 대한 정보. (Analyze명령사용)
    User_tablespaces : user가 access 가능한 tablespaces에 대한 설명.
    User_triggers : user가 소유한 triggers 정보.
    User_trigger_cols : user가 소유한 또는 user테이블에 있는 trigger안의 column 정보.
    User_ts_quotas : user에게 할당된 tablespace quotas 정보.
    User_types : 테이블안의 user소유의 type.
    User_type_attrs : user type의 속성을 나타냄.
    User_type_methods : user type의 methods를 나타냄.
    User_updatable_columns : join view에서 사용자에게 update가 허용된 칼럼정보.
    User_users : 현재 user에 관한 정보.
    User_views : user 소유의 view에 대한 text.




    FILEXT$ : 데이터파일의 AUTOEXTEND를 ON으로 변경했을 때 처음 생성.
    V$ACCESS : 현재 데이터베이스내의 lock이걸린 object와 그 object를
    access 하려는 session id.
    V$ACTIVE_INSTANCES : 현재 데이터베이스내의 Mount된
    모든 인스턴스에대하여 인스턴스 이름과, 번호를 매치.
    V$AQ : 데이터베이스내의 모든 Queue에 대한 통계.
    V$ARCHIVE : Archive에 필요한 redo log file에 대한 정보.
    각각의 행은 하나의 thread에 대한 정보이다. V$LOG도 동일한정보.
    V$ARCHIVE_DEST : 현재의 instance에서, 모든 archive log destination,
    현재값, mode, status.
    V$ARCHIVED_LOG : archive log 이름을 포함하는 controlfile에 대한 archive log 정보,
    archive log 기록은 online중 redo log가 성공적으로 저장되었거나,
    clear(log가 clear되면, name칼럼은 null이 된다)된후 insert된다.
    V$BACKUP : 모든 online 데이터파일의 backup 상태를 나타낸다.
    V$BACKUP_CORRUPTION : 데이터파일의 backup 중 에러정보를 나타낸다.
    에러들은 control 파일과 achived log backup 에 포함되지 않는다.
    V$BACK_DATAFILE : control 파일에서 datafile과 controlfile 의 backup정보를 보여줌.
    V$BACK_DEVICE : 지원되는 backup 디바이스정보.
    V$BACK_PIECE : controlfile에서 backup piece에 대한 정보를 포함.
    각각의 backup set 은 하나 또는 그이상의 backup piece로 구성된다.
    V$BACKUP_REDOLOG : controlfile에서 backup set의 저장된 log에 대한 정보.
    Online redo logs는 곧바로 backup 되지 않는다: 먼저 disk에 저장된후 backup
    된다. 저장된 log backup set 은 하나 또는 그이상의 logs들로 구성된다.
    V$BACKUP_SET : controlfile에서 backupset 정보를 보여줌.
    backup set 행은 backup set이 성공적으로 완료되었을 때 insert된다.
    V$BGPROCESS : 백그라운드 프로세스 정보.
    V$BH : 병렬서버 view이다.
    SGA내의 모든 버퍼에 대한 ping의 상태와 수를 나타낸다.
    V$BUFFER_POOL : 인스턴스내에서 사용가능한 모든 버퍼풀에 대한정보.
    V$CACHE : 병렬서버 view이다.
    특정데이타베이스object에 관련된 현재의 인스턴스의
    SGA내부의 각각의 block에 대한 block header에 대한 정보.
    V$CACHE_LOCK : 병렬서버view. platform-specific lock manager 식별자를 제외하면,
    V$CACHE와 유사하다.
    V$CIRCUIT : 가상 circuit에 관한 정보이며, 가상circuit란 dispatcher와 server를
    통한 데이터베이스와의 user 연결을 말한다.
    V$CLASS_PING : 각각blockclass마다 ping된 블록의 수를나타낸다.
    다른class블록의 충돌을 비교하기위해 사용.
    V$COMPATIBILITY : 이전버전으로 downgrade를 방지하기위해
    데이터베이스인스턴스에 의해 사용된특성들을 설명.
    다른 인스턴스가 갖고있는 특성에 영향을 미치지 않으며,
    데이터베이스가 완전히 정지한이후에도 존재하지 않는 일시적인
    비호환성들을 포함할수도 있다.
    V$COMPATSEG : 이전버전으로 되돌아가는 것을 막기위한 데이터베이스에서
    사용되는 영구적인 특성들.
    V$CONTROLFILE : 컨트롤파일의 이름과 상태.
    V$CONTROLFILE_RECORD_SECTION : 컨트롤파일의 record에 대한 정보.
    V$COPY_CORRUPTION : 컨트롤파일로부터 데이터파일의 복사불량에 대한 정보.
    V$CURRENT_BUCKET : 캐쉬내의 버퍼의 수가 감소할때 발생할 수 있는
    캐쉬손실의 경우수를 예상하는데 유용.
    V$DATABASE : control file 로부터 데이터베이스정보를 포함.
    V$DATAFILE : 컨트롤파일로부터데이타파일에대한 정보를 포함.
    V$DATAFILE_COPY :컨트롤파일로부터 데이터파일의 복사에 대한 정보를포함.
    V$DATAFILE_HEADER : 데이터파일헤더에 대한 정보.

    V$DBFILE : 데이터베이스를 구성하는 모든 데이터파일.
    대신에 V$DATAFILE 추천한다.
    V$DBLINK : 세션에 의해 open된 데이터베이스링크에 대한 설명.
    이 데이터베이스링크들은 닫히기전에 commit되거나 rollback되어야만 한다.

    V$DB_OBJECT_CACHE : library cache에 cach된 데이터베이스오브젝트를 나타냄.
    V$DB_PIPES : 데이터베이스내에 현재 운영중인 pipe에 대한 설명.
    V$DELETED_OBJECT : 삭제된 archived 로그, 데이터파일 copy,
    컨트롤파일에서 백업piece 에 대한 정보. 이뷰의 목적은 복구목록의
    재동조작업을 최적화하는 것이다. archived 로그나, 데이터파일 copy,
    백업piece 등이 삭제될때는 해당하는 행이삭제되었음이 표시된다.
    V$DISPATCHER : dispatcher 프로세스에 관한 정보.
    V$DISPATCHER_RATE : dispatcher 프로세서에 관련된 확률통계.
    V$DLM_CONVERT_LOCAL : lock 변환작업에 대한 경과시간.
    V$DLM_CONVERT_REMOTE : 원격 lock변환작업에 대한 경과시간.
    V$DLM_LATCH : DLM 잠금에 대한 통계.
    각각의 잠금에 대한 통계보다는, 각 타입에 대한 총계를 포함.
    개념적으로 IMM_GETS/TTL_GETS 값은 1에 가깝게 된다.
    V$DLM_LOCKS : 병렬서버 view이다. 블록화되었거나, 다른 것을
    블록화하고있는 lock manager에 알려진 모든 lock에 대한 정보.
    V$DML_MISC : 잡다한 DLM 통계에 대한 정보.
    V$ENABLEDPRIVS:사용가능한 권한에 대한정보, 이들권한은
    SYS.SYSTEM_PRIVILEGES_MAP테이블에 존재해야만 한다.
    V$ENQUEUE_LOCK : 큐에 대기상태인 오브젝트에의해 소유된 모든 lock이
    view의 칼럼은 V$LOCK의 칼럼과 동일하다.
    자세한 것은 V$LOCK을 참고.
    V$EVENT_NAME : wait event 에 대한 정보.
    V$EXECUTION : 병렬 질의 실행에 대한 정보.
    V$EXECUTION_LOCATION : 병렬 질의 실행 트리의 위치에 대한 자세한 정보.
    V$FALSE_PING : 병렬서버view. ping에 실패지도 모르는 버퍼에 대한 정보.
    즉, 10회이상ping된 다른 버퍼와
    동일한 lock으로 잠겨있는 버퍼를 말한다.
    ping이 실패로 판명된 버퍼는 lock충돌을 감소시키기위해
    1-44페이지의 "GC_FILES_TO_LOCK"에 다시 매핑된다.
    V$FILE_PING : 데이터파일마다 ping된 블록수를 보여줌. 이정보는 현존하는
    데이터파일에 대한 access패턴을 결정하는데 and,
    데이터파일블록을 PCM lock에 새로 매핑하는것을 결정하는데 사용된다.
    V$FILESTAT : 파일 read/write 통계.
    V$FIXED_TABLE : 데이터베이스내의 모든 동적실행테이블, views, 유도테이블.
    실제테이블을 참조하는 약간의 V$테이블은 리스트에 없다.
    V$FIXED_VIEW_DEFINITION : (V$로 시작하는)고정view에 대한 설명.
    유의해서 사용해야한다.
    V$GLOBAL_TRANSACTION : 현재 활동중인 트랜잭션에 대한 설명.
    V$INDEXED_FIXED_COLUMN : index된 동적실행테이블(X$ table)의 칼럼에
    대한 설명. X$ table은 경고없이 변경할수있다.
    이view는 보다 효과적으로 고정뷰(V$view)에 대한
    V$INSTANCE : 현재의 인스턴스의 상태를 나타냄.
    V$INSTANCE의 버전은 V$INSTANCE의 초기버전과 호환성이 없다.
    V$LATCH : 하위 잠금에 대한 통계와 상위 잠금에 대한 요약통계.
    즉, 상위잠금에 대한 통계는 그 하위잠금에 대한 각각의 통계를 포함한다.
    V$LATCHHOLDER : 현재잠금에 대한 정보.
    V$LATCHNAME : V$LATCH 에 있는 잠금에 대한 디코드된 잠금이름에 대한
    정보. V$LATCHNAME의 행들은 V$LATCH의 행들과 1:1로 대응된다.

    V$LATCH_CHILDREN : 하위잠금에 대한 통계를 포함.
    V$LATCH의 칼럼에 child# 칼럼이추가되었다. LATCH#칼럼이 서로
    동일하다면, 하위잠금이 동일한 상위잠금을 갖는 것이다.
    V$LATCH_MISSES : 잠금을 획득하는데 실패한 시도에 대한 통계.
    V$LATCH_PARENT : 상위잠금에 대한 통계.
    V$LATCH_PARENT 칼럼은 V$LATCH칼럼과 동일하다.
    V$LIBRARYCACHE : library cache의 실행과 활동통계.
    V$LICENSE : license 한계에 대한 정보.
    V$LOADCSTAT : 직접적재하는동안 컴파일된 SQL*loader 통계정보.
    이테이블에대한 어떤 Select 문도 "no rows returned" 결과가 나오는데,
    왜냐면, 동일한 시간에 데이터를 적재하면서, 쿼리를 날릴수 없기 때문이다.

    V$LOCK : 현재 Oracle 서버에 의해 확립된 잠금에 대한 정보나 lock또는
    latch에 대한 두드러진요청
    V$LOCK_ACTIVITY : 병렬서버view이다. V$LOCK_ACTIVITY는 현재의
    인스턴스의 DLM잠금동작을 나타낸다.
    각각의 행은 잠금동작의 타입과 일치된다.
    V$LOCK_ELEMENT : 병렬서버view이다. 버퍼캐쉬에 의해사용된 각각의
    PCM잠금에 대해 v$LOCK_ELEMENT 에 한행이다.
    잠금요소에 대응되는 PCM잠금의 이름은 'BL',indx,class등이다.
    V$LOCKED_OBJECT : 시스템안의 모든 트랜잭션에 걸린 잠금을 나타낸다.
    V$LOCKED_WITH_COLLISIONS : 병렬서버view이다.
    여러버퍼를 보호하는 lock을 찾는데 사용되며, 그 버퍼들은 최소한
    10회이상 각각 강제로 쓰여지거나, 강제로 읽혀진 버퍼들이다.
    V$LOG : 컨트롤파일로부터 log 파일정보를 포함한다.
    V$LOGFILE : redo log 파일정보. redo log 그룹과 멤버 파일명.
    V$LOGHIST : 컨트롤파일로부터 log history정보를 포함. 지속적인 호환성을
    포함하고 있다. 대신에 V$LOG_HISTORY의 사용을 권장한다.
    V$LOG_HISTORY : 컨트롤파일로부터 log history 정보를 포함한다.
    V$MLS_PARAMETERS : Oracle Server의 확정된 초기화파라미터를 나타냄.
    V$MTS : multi-threaded server의 성능향상을위한 정보를 포함.
    V$MYSTAT : 현재 세션에 대한 통계값포함.
    V$NLS_PARAMETERS : 현재의 NLS 매개변수의 값들을 포함.
    V$NLS_VALID_VALUES : 유효한 NLS 매개변수값.
    V$OBJECT_DEPENDENCY : 현재 공유풀에 적재되어있는 package, procedure,
    cursor등에 관련되어있는 object를 결정하는데 사용된다.
    예를들면, V$SESSION, V$SQL등과 조인하면, 현재 어떤 user가
    실행중인 SQL문에서 어떤 테이블이 사용되었는지를 알아낼수가 있다.
    V$OFFLINE_RANGE : 컨트롤파일로부터 offline된 datafile을 보여준다.
    DATAFILE행에 저장되어있는 각각의 데이터파일의 최종offline 간격을
    보여줌. offline 간격은 테이블스페이스가 처음 offline normal,
    또는 Read Only로 변경되고난이후 다시 online 또는 read-write로
    변경된다음에 확정된다.
    데이터파일이 스스로 Offline로 변경되거나 테이블스페이스가
    OFFLINE IMMEDIATE로 변경되면, offline간격은 확정되지 않는다.

    V$OPEN_CURSOR : 각각 user 세션이 열렸있거나, 정지되어있는 cursor를 보여준다.
    V$OPTION : Oracle Server와 같이 설치된 선택사항들.
    V$PARAMETER : 초기화 파라미터에 대한 설명이다.
    V$PING : 병렬서버view이다.
    최소한 1번이상 ping된 블록만을 보여준다는 것을 제외하고 V$CACHE view와
    동일하다.특정 데이터베이스 object와 관련된 현재의 인스턴스내의 SGA에
    있는 각각의 블록에대한 block header정보를 포함하고 있다.
    V$PQ_SESSTAT : 병렬쿼리에 대한 session 통계를 포함.
    V$PQ_SLAVE : 인스턴스내에 실행중인 parallel 쿼리서버에 대한 통계.
    V$PQ_SYSSTAT : 병렬쿼리에 대한 시스템통계.


    V$PQ_TQSTAT : 병렬쿼리 동작의 통계를 포함. 통계는 질의가 완료된후에
    컴파일되며 세션이 살아있는동안 계속 남아있는다.
    V$PROCESS : 현재 작업중인 프로세스에 대한 정보.
    LATCHWAIT 칼럼은 프로세스잠금이 무엇을 기다려야하는가를 나타내며,
    LATCHSPIN 칼럼은 프로세스잠금이 동작되는 것을 나타낸다.
    멀티프로세서의 경우 Oracle 프로세스는 잠금을 기다리기전에 실시한다.
    V$PWFILE_USERS : password 파일로부터 유도해낸
    SYSDBA, SYSOPER 권한을 부여받은 user.
    V$QUEUE : 멀티쓰레드 메시지큐에 대한 정보.
    V$RECENT_BUCKET : 대용량 캐쉬실행을 평가하기에 유용한 정보.
    V$RECOVER_FILE : media 복구에필요한 파일의 상태를 나타냄.
    V$RECOVERY_FILE_STATUS : 각각의 RECOVER명령에 대한 각 데이터파일에
    대한 정보를 한행씩 포함.
    Oracle프로세스가 복구를 수행하는데 유용한 정보임.
    recover manager는 서버프로세스에 직접 복구를수행하도록 했을 때,
    recovery manager가 이 view에서 관련된정보를 참고할 수 있다.
    다른user들에게는 유용하지 않다.
    V$RECOVERY_LOG : 완벽한 media복구에 필요한 archived logs에 관한 정보.
    이정보는 log history view인 V$LOG_HISTORY에서 유도된 것이다.
    V$RECOVERY_PROGRESS : 데이터베이스복구작업이 중간에 멈추지않도록하는데
    사용되며, 복구작업을 완료하는데 요구되는 시간을 측정하는데 사용된다.
    V$RECOVERY_STATUS : 현재의 복구진행상태를 나타낸다. 단지 복구를 수행하는
    Process 에대한 정보만이유용하다. 복구관리자가 서버프로세스에게 복구를
    수행하라고 지시할때에, 복구관리자는 이view에서
    관련정보를 참조할 수 있다. 다른 user에게는 불필요하다.
    V$REQDIST : MTS dispatcher의 응답시간에 대한 그래프통계를 나타내며,
    time range는 버킷 number의 지수함수로 증가한다.
    V$RESOURCE : 자원(resource)의 이름과 주소정보를 포함.
    V$RESOURCE_LIMIT : System 자원의 부분적인 사용에 대한 정보. 자원의
    소비를 모니터링함으로서 낭비를 방지하는데 사용된다.

    V$ROLLNAME : 모든 online중인 rollback segments의 이름.
    데이터베이스가 open시에만 조회가능.
    V$ROLLSTAT : 롤백세그먼트통계정보.
    V$ROWCACHE : 자료사전활동에 대한 통계.
    각각의 행은 하나의 자료사전cache 통계를 포함.
    V$SESSION : 현재 open된 세션에 대한 정보.
    V$SESSION_CONNECT_INFO : 현재의 세션에 대한 network 연결에 대한 정보.
    V$SESSION_CURSOR_CACHE : 현재의 세션에 대한 cursor 사용에 대한 정보.
    SESSION_CACHED_CURSORS 초기화파라미터에 대한 효율을 측정하지는
    않는다.
    V$SESSION_EVENT : 세션의 event 대기에 관한정보.
    V$SESSION_LONGOPS : 장시간실행되는 작업에 대한 상태. SOFAR,
    TOTALWORK칼럼은 진행상태를 제공한다. 예를들어
    다음요소(hach cluster creations, backup, recovery) 에 대한
    작동상태를 모니터링할 수 있다.
    V$SESSION_OBJECT_CACHE : 로칼서버의 현재사용중인
    user세션의 object, cache통계정보.
    V$SESSION_WAIT : 활동중인 세션이 대기하고있는 자원또는 이벤트이다.
    V$SESSTAT : user세션 통계이다. 통계number(statistic#)에 해당하는
    통계name을 찾으려면, V$STATNAME를 참고하면 된다.
    V$SESS_IO : 각각의 user세션에 대한 I/O 통계이다.
    V$SGA : System Global Area 에대한 간략한 정보.(name, size)
    V$SGASTAT : System Global Area에 대한 자세한 정보.(name, bytes, pool)

    V$SHARED_POOL_RESERVED : Shared Pool내에 예약풀과 공간을
    바꾸고자할 때 도움이 되는통계.
    V$SHARED_SERVER : Shared Server processes 에 대한 정보를 포함.
    V$SORT_SEGMENT : 주어진 인스턴스내의 각 sort세그먼트에 대한 정보.
    테이블스페이스가 Temporary 타입일때만 update된다.

    V$SORT_USAGE : sort 사용에 대해 기술한다.
    V$SQL : Group by절이없는 공유sql영역에대한 통계이며 입력된
    원래 sql문장의 각 child의 row를 포함.
    V$SQL_BIND_DATA : 데이터가 이 서버에서 추출가능하다면 이 view를
    조회하는 세션에 소유된 각 커서안에 있는 각각의 원격bind변수에 대한
    클라이언트에 의해 보내진 데이터.
    V$SQL_BIND_METADATA : 이view를 조회하는 세션에 소유된 각커서안에 있는
    각각의 원격bind변수에 대해 클라이언트에의해 제공되는 bind metadata.

    V$SQL_CURSOR : 이 view를 조회하는 세션과 관련된 각 cursor에 대한 디버깅정보.
    V$SQL_SHARED_MEMORY : 메모리 스냅샷에 공유된 커서에 대한 정보.
    공유풀에 저장된 각SQL문은 관련된 하나또는 그이상의 하위object를
    가지고 있다.
    V$SQLAREA : 공유SQL영역에 대한 통계를 가지고있으며, Sql 문자열마다
    한행을 포함한다. 메모리내에 존재하는, parse된, 실행을 대기하고있는
    SQL문장에 대한 통계를 제공한다.
    V$SQLTEXT : SGA내부의 공유SQL 커서에 속해있는 SQL문장을 포함.
    V$SQLTEXT_WITH_NEWLINES : 가독성이 증가되고, 공백을 포함한 SQL문장안에
    newline과 tabs을 대체하지 않는다는 것을 제외하고는 V$SQLTEXT view와
    동일하다.
    V$STATNAME : V$SESSTAT와 V$SYSSTAT테이블에서 나타난 statistics에 대한 이름.
    V$SUBCACHE : 현재 라이브러리 캐쉬메모리에 적재된 하위 캐쉬에 대한 정보.
    모든 라이브러리캐쉬에 대해 언급하고있으며, 각 라이브러리 캐쉬object마다
    각 적재된 하위 캐쉬에 대해 한행을 나타낸다.
    V$SYSSTAT : 시스템 통계이다. 각 statistic number(statistic#)와 관련된 statistic의
    이름을 찾기위해서는, "V$STATNAME"를 보시오.
    V$SYSTEM_CURSOR_CACHE : 시스템 전반적인정보라는 것을 제외하고,
    V$SESSION_CURSOR_CACHE와 유사한 정보를 나타낸다.
    V$SYSTEM_EVENT : 이벤트에 대한 총 wait정보. TIME_WAITED,
    AVERAGE_WAIT칼럼은 급속메커니즘을 지원하지 않는 플랫폼에서
    0값을 포함할 것이다. 이런 플랫폼에서 DB를 운영중이고,
    이칼럼이 wait time을 줄여주기를 원한다면, 파라미터파일의
    TIMED_STATISTICS를 TRUE로 세팅하면된다.
    단지 이렇게 하면, 시스템 성능에 약간의 마이너스효과를 가져올 것이다.
    V$SYSTEM_PARAMETER : System parameter에 대한 정보.
    V$TABLESPACE : 컨트롤파일로부터 테이블스페이스 정보를 나타내준다.
    V$THREAD : 컨트롤파일로부터 thread 정보를 가져온다.
    V$TIMER : 1/100 초로 나타낸 경과시간. 시간은 epoch가 시작된이후부터
    측정되며, epoch는 OS의 특성이며, 값이 4bytes(약 497일)를 넘을때마다
    0근처의 값이 된다.
    V$TRANSACTION : 시스템내의 활동중인 트랜잭션.
    V$TRANSACTION_ENQUEUE : 트랜잭션 오브젝트에 의해 소유된 lock를 나타냄.
    V$TYPE_SIZE : 데이터블록용량을 측정하는데 사용되는 여러
    데이터베이스컴포넌트들의 SiZe.
    V$VERSION : Oracle Server의 core 라이브러리 컴포넌트의 Version수이다.
    각 컴포넌트에 한 row가 있다.
    V$WAITSTAT : 블록점유에 대한 통계. 통계가 사용가능한 시간에만 갱신된다.

    출처 : Tong - 늘푸른 소나무(松)님의 오라클-SQL통

    Posted by 1010
    01.JAVA/Java2009. 12. 8. 20:16
    반응형
    /*

    Defining the Table: Oracle and MySql


    create table MyPictures (
       id INT PRIMARY KEY,
       name VARCHAR(0),
       photo BLOB
    );
    */

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.OutputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;

    import oracle.jdbc.OracleResultSet;

    public class Main {
      public static void main(String[] args) throws Exception {
        Connection conn = getOracleConnection();
        int rows = 0;
        FileInputStream fin = null;
        OutputStream out = null;
        ResultSet rs = null;
        Statement stmt = null;
        oracle.sql.BLOB photo = null;
        conn.setAutoCommit(false);
        stmt = conn.createStatement();
        String id = "001";
        String binaryFileName = "fileName.dat";

        rows = stmt.executeUpdate("insert into my_pictures(id, photo ) values ('" + id
            + "', empty_blob() )");
        System.out.println(rows + " rows inserted");

        rs = stmt.executeQuery("select photo from  my_pictures where id = '" + id
            + "' for update nowait");
        rs.next();
        photo = ((OracleResultSet) rs).getBLOB(1);

        fin = new FileInputStream(new File(binaryFileName));
        out = photo.getBinaryOutputStream();
        // Get the optimal buffer size from the BLOB
        byte[] buffer = new byte[photo.getBufferSize()];
        int length = 0;
        while ((length = fin.read(buffer)) != -1) {
          out.write(buffer, 0, length);
        }
        conn.commit();

        out.close();
        fin.close();
        rs.close();
        stmt.close();
        conn.close();

      }

      private static Connection getHSQLConnection() throws Exception {
        Class.forName("org.hsqldb.jdbcDriver");
        System.out.println("Driver Loaded.");
        String url = "jdbc:hsqldb:data/tutorial";
        return DriverManager.getConnection(url, "sa", "");
      }

      public static Connection getMySqlConnection() throws Exception {
        String driver = "org.gjt.mm.mysql.Driver";
        String url = "jdbc:mysql://localhost/demo2s";
        String username = "oost";
        String password = "oost";

        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, username, password);
        return conn;
      }

      public static Connection getOracleConnection() throws Exception {
        String driver = "oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:oracle:thin:@localhost:1521:databaseName";
        String username = "userName";
        String password = "password";

        Class.forName(driver); // load Oracle driver
        Connection conn = DriverManager.getConnection(url, username, password);
        return conn;
      }

    }


    Posted by 1010
    01.JAVA/Java2009. 12. 8. 20:15
    반응형
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;

    public class Main {
      public static void main(String[] args) throws Exception {

        Class.forName("oracle.jdbc.driver.OracleDriver");

        String url = "jdbc:oracle:thin:@localhost:1521:javaDemo";
        String username = "username";
        String password = "welcome";

        String sql = "CREATE TABLE books (id NUMBER(11), title VARCHAR2(64))";
        Connection connection = DriverManager.getConnection(url, username, password);
        Statement statement = connection.createStatement();
        statement.execute(sql);
        connection.close();
      }
    }
     

    Posted by 1010
    01.JAVA/Java2009. 12. 8. 20:14
    반응형
     
    import
    java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    public class DemoResultSetOracle {

      public static Connection getConnection() throws Exception {
        String driver = "oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:oracle:thin:@localhost:1521:databaseName";
        String username = "username";
        String password = "password";

        Class.forName(driver); // load Oracle driver
        Connection conn = DriverManager.getConnection(url, username, password);
        return conn;
      }

      public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
          conn = getConnection();
          System.out.println("conn=" + conn);
          // prepare query
          String query = "select id, name, age from employees";
          // create a statement
          stmt = conn.createStatement();
          // execute query and return result as a ResultSet
          rs = stmt.executeQuery(query);
          // extract data from the ResultSet
          while (rs.next()) {
            String id = rs.getString(1);
            String name = rs.getString(2);
            int age = rs.getInt(3);
            System.out.println("id=" + id);
            System.out.println("name=" + name);
            System.out.println("age=" + age);
            System.out.println("---------------");
          }
        } catch (Exception e) {
          e.printStackTrace();
          System.exit(1);
        } finally {
          // release database resources
          try {
            rs.close();
            stmt.close();
            conn.close();
          } catch (SQLException e) {
            e.printStackTrace();
          }
        }
      }
    }
               
             
    Posted by 1010
    반응형
    Posted by 1010