servlet의 개념 정리

Program_Language/Etc. 2012. 5. 14. 16:29 Posted by Request

1. Servlet 이란?

 - 자바 플랫폼에서 컴포넌트 기반으로 웹 애플리케이션을 개발 할수 있는 기술.

ex) 이클립스에서 new > 자바(class)를 생성 하듯이 new > servlet 파일(java파일을 생성하지만..)을 생성 할수 있다.

 

2.컴포넌트란?

-특정 기능을 수행하기 위한 응용시스템을 구축하기 위해 사용되는 s/w단위

 

3.서블릿 생명주기

 

     1)init()   초기화 이벤트 : 처음 한번만 실행 (컨테이너는 해당 서블릿을 메모리에서 있는지 확인하여 첫 실행된다.)

 

     2)service()

   

         doGet()

         doPost()

 

 

     3)destory()     : 종료 이벤트

인터페이스 문법은 알고 있지만 막상 왜 사용 되어야 하는지 개념을 잡고자 정리를 한다.

 

 

1.먼저 interface는 다른 클래스의 멤버들과 달리 다음과 같은 제약 사항을 가지고 있다.

-모든 멤버 변수는 public static final 이어야 하며, 이를 생략할수 있다.

-모든 메서드는 public abstract이어야 하며, 이를 생략할수 있다.

 

2.인터페이스의 추상메서드의 몸통을 구현하는 자손 클래스를 작성해야하는데 이때 인터페이스를 구현한다고 하며 키워드는 implements를 사용한다.

 

3.인터페이스 이름은 주로 able로 (~를 할수 있는 ) 의미로 파일(인터페이스명)으로 한다.(권장)

ex) playable

 

4.인터페이스를 구현한 자손 클래스는 인터페이스의 멤버 접근자보다 넓은 범위의 접근제어자를 지정해야 한다.

-interface의 멤버 void a(int a, int b)   //이넘을 자손 클래스에서 구현할때 private void a() 이런식으로 하면 에러가난다.

 

 

 

 

 

class Tv{

void autoPlay(ITV i){

i.play();

}

}

interface ITV {

public abstract void play();

}

class TV_1 implements ITV{

pbulic void play(){

System.out.println("play TV_1 class");

}

}

class TV_2 implements ITV{

public void Play(){

System.out.println("play TV_2 class");

}

}

public class InterfaceTest {

public static void main(String[] args){

Tv tv = new Tv();

tv.autoPlay(new TV_1());

tv.autoPlay(new TV_2());

}

}

 

 

※ 인퍼페이스를 사용 하는 이유는

확장성코드의 유연함이 주 목적

 

 

 출처 : http://lng1982.tistory.com/17

Spring에서 DispatcherServlet은 그 자체가 Servlet이기 때문에 1개 이상의 DispatcherServlet을 설정하는 것이 가능합니다.

공통빈 설정 예)

<context-param>

           <param-name>contextConfigLocation</param-name>

           <param-value>/WEB-INF/applicationContext*.xml</param-value>

</context-param>

<listener>

           <listener-class>

           org.springframework.web.context.ContextLoaderListener

           </listener-class>

</listener>

 

ContextLoaderListener가 생성한 컨텍스트가 root컨텍스트가 되고, DispatcherServlet이 생성한 인스턴스는 root컨텍스트를 부모로 사용하는 자식 컨텍스트가 된다.

 

이때 자식컨텍스트들은 root컨텍스트가 제공하는 빈을 사용 할 수 있기 때문에

ContextLoaderListener를 이용하여 공통빈을 설정하는 것이다.

 

참고로..

ContextLoaderListenerContextConfigLocation을 명시하지 않으면

/WEB-INF/applicationContext.xml을 설정파일로 사용한다. 이때, 설정파일이 존재 하지 않으면 에러가 발생하니 주의해야 한다.

 

출처 : http://blog.naver.com/minis24?Redirect=Log&logNo=80097770192

 

equal과 equalsIgnoreCase 의 차이

Program_Language/Java 2012. 5. 2. 10:53 Posted by Request

영어 대소문자를 구분한다와 둘다 포함한 것을 구분한다라 생각하면 된다.

 

예로 들자면,

 

if(input.equal("a") || input.equal("A"))      // equal은  대소문자 를 모두 써야 한다.

 

if(input.equalsIgnoreCase("a")          //equalsIgnoreCase일 경우 한가지만 써주면 된다.

SqlMapConfig.xml 셋팅

Program_Language/Etc. 2012. 5. 2. 10:03 Posted by Request

 <!-- settings
  cacheModelsEnabled="true"  < iBatis 캐싱을 사용할지 여부 지정하는데 사용, 매핑구문에서 캐시 모델 설정해야 한다.>
  enhancementEnabled="true"   < CGLIB에 최적화된 클래스를 통해 적재지연 성능을 향상시킬지의 여부지정>
  lazyLoadingEnabled="true"   <적재지연 사용 여부>
  maxRequests="32"            <비추, request는 SQL 작업을 의마형 최대 요청갯수를 지정한다. 디폴트 512개>
  maxSessions="10"     <비추, 디폴트는 128개 이며, 한번에 10개의 세션만이 가능하도록 설정>
  maxTransactions="5"    <비추, 디폴트 32개, 활성트랜잭션의 갯수>
  useStatementNamespaces="true"  <네임스페이스를 사용해야 시행하도록 한다 디폴트는 false>
  
  <
   위의 비추천 속성은 사용을 권장 하지 않는다. 수정해야 한다면, 항상 maxRequest값 > maxSession > maxTrasaction 이 되도록 한다.
   일반적으로 세 값이 동일하게 유지하게 하는것이 좋다.
   
  >
 
 <sqlMap resource="kr/co/lnj/web/sql/Board.xml"/ -->

많은 웹 애플리케이션은 상당한 분량의 정적 내용을 제공한다. 즉, 디스크에서 데이터를 읽어 응답을 위한 소켓에 써 넣는다. 이런 처리는 상대적으로 CPU를 거의 사용하지 않을 것 같지만 다소 비효율적이다. 즉, 먼저 커널이 디스크에서 해당 데이터를 읽어 커널-사용자 간 경계를 넘어 애플리케이션으로 밀어 낸다. 그러면 애플리케이션은 해당 데이터를 소켓에 써 넣기 위해 다시 커널-사용자 간 경계를 넘어 커널로 밀어 넣는다. 결과적으로 애플리케이션은 그저 디스크에서 데이터를 가져다가 소켓으로 옮기는 비효율적인 중개자인 셈이다.

데이터가 사용자-커널 간 경계를 넘나들려면 매번 복사를 해야 한다. 이 때 CPU 사이클과 메모리 대역폭이 소모된다. 다행히 무복사 기법을 사용하면 불필요한 복사를 피할 수 있다(참으로 적절한 이름이다). 무복사 기법을 사용하는 애플리케이션은 해당 데이터를 자신을 거치지 않고 커널이 직접 디스크에서 소켓으로 복사하도록 요청한다. 무복사 기법은 애플리케이션 성능을 크게 향상시키고 커널과 사용자 모드 간의 맥락 전환 수를 줄여 준다.

자바 클래스 라이브러리는 리눅스와 유닉스 시스템에서 java.nio.channels.FileChannel 클래스의 transferTo() 메서드를 통해 무복사 기법을 지원한다. transferTo() 메서드를 사용하면 메서드를 호출한 채널(channel)에서 다른 쓰기 가능한 바이트 채널로 데이터가 애플리케이션을 거칠 필요 없이 바이트들을 곧장 전송할 수 있다. 이 글에서는 먼저 기존 복사 방식으로 파일을 전송할 때 생기는 추가 부담을 보인 후, 어떻게 transferTo()를 사용한 무복사 기법이 더 나은 성능을 보이는지를 살펴 보겠다.

데이터 전송: 기존 방식

파일을 하나 읽어 네트워크를 통해 다른 프로그램에 전달하는 시나리오를 생각해 보자. 이 시나리오는 정적인 내용을 제공하는 웹 애플리케이션, FTP 서버, 메일 서버 등을 포함한 많은 서버 애플리케이션의 동작을 나타낸 것이다. 이런 동작의 핵심은 Listing 1(완전한 예제 코드가 필요하면 다운로드를 참고)의 두 호출에 있다.


Listing 1. 바이트들을 파일에서 소켓으로 복사하기

File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);

Listing 1이 개념적으로는 단순할지 몰라도 내부적으로 이 복사 작업 동안 사용자 모드와 커널 모드 간에 맥락 전환이 네 차례 필요하다. 그림 1은 내부적으로 데이터가 어떻게 파일에서 소켓으로 옮겨지는지 나타낸 것이다.


그림 1. 기존 데이터 복사 방식
기존 데이터 복사 방식

한편 그림 2는 맥락 전환을 나타낸 것이다.


그림 2. 기존 방식에서 맥락 전환
기존 방식에서의 맥락 전환

이 작업은 다음 과정으로 이뤄진다.

  1. read() 호출이 사용자 모드에서 커널 모드로 맥락 전환을 일으킨다(그림 2). 내부적으로는 파일에서 데이터를 읽기 위해 sys_read()(또는 같은 기능을 하는 시스템 호출)가 호출된다. 첫 번째 복사(그림 1)는 직접 메모리 접근(direct memory access, 약자로 DMA) 엔진이 한다. 디스크에서 파일 내용을 읽어 커널 주소 공간 내 버퍼에 저장한다.

  2. 요청한 분량만큼의 데이터가 1의 읽기 버퍼에서 사용자 버퍼로 복사되고, read() 호출에서 리턴한다. 이 리턴으로 인해 커널에서 다시 사용자 모드로 또 다른 맥락 전환이 일어난다. 이제 읽어 온 데이터가 사용자 주소 공간 내 버퍼에 저장됐다.

  3. send() 소켓 호출로 인해 사용자 모드에서 커널 모드로 맥락 전환이 일어난다. 해당 데이터를 다시 커널 주소 공간의 버퍼에 넣기 위해 세 번째 복사가 일어난다. 하지만 이번에는 데이터가 목적 소켓에 연관된 다른 버퍼로 복사된다.

  4. send() 시스템 호출이 리턴하고 네 번째 맥락 전환이 일어난다. 그와 무관하며 비동기적으로 DMA 엔진이 데이터를 커널 버퍼에서 프로토콜 엔진으로 넘길 때 네 번째 복사가 일어난다.

중간에 커널 버퍼를 사용하는 것이 (해당 데이터를 직접 사용자 버퍼로 보내는 것보다) 비효율적으로 보일 수도 있다. 하지만 중간 커널 버퍼는 사실 성능을 향상시키기 위해 사용한 것이다. 읽는 쪽에서 중간 버퍼를 사용하면, 애플리케이션이 커널 버퍼에 담을 수 있는 양보다 적은 데이터를 요청했을 경우, 커널 버퍼가 일종의 "다음 데이터를 미리 읽어 두는 캐시(readahead cache)"처럼 동작할 수 있게 해 준다. 이렇게 하면 요청한 데이터의 양이 커널 버퍼 크기보다 작은 경우 성능이 많이 향상된다. 또한 중간 버퍼를 쓰는 쪽에서 사용하면 데이터를 비동기적으로 써 넣을 수 있다.

불행히 요청된 데이터의 크기가 커널 버퍼보다 큰 경우에는 이 접근법 자체가 성능 병목이 될 수 있다. 그런 경우 데이터가 애플리케이션에 최종적으로 전달되기까지 디스크, 커널 버퍼, 사용자 버퍼 간에 복사가 여러 번 일어난다.

무복사 기법은 이런 불필요한 데이터 복사를 없애 성능을 높인다.


데이터 전송: 무복사 방식

기존 방식을 다시 살펴 보면 두 번째와 세 번째 복사는 실제 필요하지 않음을 알 수 있을 것이다. 애플리케이션은 그저 데이터를 캐시했다가 소켓 버퍼로 보낼 뿐 아무런 일도 하지 않는다. 여기서 대신 데이터를 읽기 버퍼에서 소켓 버퍼로 직접 전송할 수 있다. transferTo() 메서드가 정확히 그런 일을 해 준다. Listing 2는 transferTo() 메서드의 시그너처(signature)다.


Listing 2. transferTo() 메서드

public void transferTo(long position, long count, WritableByteChannel target);

transferTo() 메서드는 데이터를 파일 채널에서 주어진 쓰기 가능한 바이트 채널로 전송한다. 내부적으로는 하부 운영체제가 무복사를 지원하는지 여부에 따라 다르다. 유닉스와 각양각색 리눅스의 경우 이 호출은 Listing 3에 보인 것처럼 sendfile() 시스템 호출로 연결된다. 이 시스템 호출은 데이터를 한 파일 디스크립터(file descriptor)에서 다른 파일 디스크립터로 전송한다.


Listing 3. sendfile() 시스템 호출

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

Listing 1에서 file.read()socket.send() 호출이 행하는 작업을 Listing 4에 보인 것처럼 하나의 transferTo() 호출로 대체할 수 있다.


Listing 4. transferTo()를 이용하여 디스크 파일에서 소켓으로 데이터 복사하기

transferTo(position, count, writableChannel);

그림 3은 transferTo() 메서드를 사용할 때의 데이터 경로를 보인 것이다.


그림 3. transferTo()를 사용할 때 일어나는 데이터 복사
transferTo()를 사용할 때 일어나는 데이터 복사

그림 4에는 transferTo() 메서드를 사용할 때 일어나는 맥락 전환을 보였다.


그림 4. transferTo()를 사용할 때의 맥락 전환
transferTo()를 사용할 때의 맥락 전환

Listing 4에서와 같이 transferTo()를 사용할 때 작업이 처리되는 과정은 다음과 같다.

  1. transferTo() 메서드가 DMA 엔진을 이용, 파일 내용을 읽기 버퍼로 복사한다. 해당 데이터는 다음으로 커널에 의해 출력 소켓에 연결된 커널 버퍼로 복사된다.

  2. DMA 엔진이 1의 데이터를 커널 소켓 버퍼에서 프로토콜 엔진으로 넘기면서 세 번째 복사가 일어난다.

보는 바와 같이 개선점이 있다. 맥락 전환 횟수를 네 번에서 두 번으로 줄였고, 데이터 복사 횟수도 네 번에서 세 번으로 줄였다(게다가 이 중 단 한 번만 CPU를 사용한다). 하지만 이 정도로는 우리 목표인 무복사는 어림도 없다. 만약 네트워크 인터페이스 카드가 데이터 모으기(gather operation)를 지원한다면 커널이 행하는 데이터 복제를 더 줄일 수도 있다. 리눅스 커널 2.4 이후에서는 이런 요구를 수용하기 위해 소켓 버퍼 디스크립터가 수정되었다. 이 방식은 다수의 맥락 전환을 줄일 뿐 아니라 CPU를 사용하는 중복된 데이터 복사를 없애준다. 사용자 쪽에서 보면 변한 게 없지만 내부 동작은 바뀌었다.

  1. transferTo() 메서드가 DMA 엔진을 이용, 파일 내용을 커널 버퍼에 복사한다.

  2. 이번에는 소켓 버퍼에 데이터를 복사하지 않는다. 대신 데이터 위치와 길이에 대한 설명이 담긴 디스크립터들만 소켓 버퍼에 추가된다. DMA 엔진은 데이터를 직접 커널 버퍼에서 프로토콜 엔진으로 넘겨준다. 이로서 앞서 마지막으로 남아 있던 CPU를 이용한 복사 과정이 없어졌다.

그림 5는 transferTo()와 데이터 모으기를 이용한 데이터 복사를 보인 것이다.


그림 5. transferTo()와 데이터 모으기를 사용한 데이터 복사
transferTo()와 데이터 모으기를 사용한 데이터 복사


파일 서버 만들기

이제 파일을 클라이언와 서버 간에 전송하는 예제를 통해 무복사 기법을 실제 사용해 보자(예제 코드는 다운로드에 있다). TraditionalClient.javaTraditionalServer.javaFile.read()Socket.send()를 이용하는 기존 복사 방식을 사용한 것이다. TraditionalServer.java는 특정 포트(port)에 사용자가 접속하기를 기다리다가 일단 접속하면 소켓으로부터 한번에 4K 바이트씩 데이터를 읽는다. TraditionalClient.java는 서버에 접속해 (File.read()를 이용) 파일에서 데이터를 4K 바이트 읽어 그 내용을 (socket.send()를 사용해) 해당 소켓을 통해 서버로 보낸다.

비슷하게 TransferToServer.javaTransferToClient.java는 같은 기능을 하지만, 파일을 서버에서 클라이언트로 보내기 위해 기존 방식 대신 transferTo() 메서드를 사용한다(결과적으로 내부에서는 sendfile() 시스템 호출을 사용한다).

성능 비교

상기 예제 프로그램들을 2.6 커널을 사용하는 리눅스 시스템에서 실행해, 다양한 크기에 대해 기존 방식과 transferTo() 방식의 실행 시간을 밀리초 단위로 측정했다.


표 1. 성능 비교: 기존 방식 대 무복사 방식

파일 크기 일반 파일 전송(ms) transferTo(ms)
7MB 156 45
21MB 337 128
63MB 843 387
98MB 1320 617
200MB 2124 1150
350MB 3631 1762
700MB 13498 4422
1GB 18399 8537

여기서 보는 것처럼 transferTo() API는 기존 방식에 비해 실행 시간을 약 65% 줄여준다. 이는 웹 서버처럼 하나의 I/O 채널에서 다른 채널로 많은 양의 데이터를 복사하는 애플리케이션에 있어 상당한 성능 향상을 가져다 줄 수 있다.


요약

이 글을 통해 하나의 채널에서 데이터를 읽어 다른 채널에 써 넣는 것과 비교해 transferTo()를 사용하는 경우의 성능 이점을 보였다. 중간 버퍼 복사는 (비록 커널 내에 감춰져 있지만) 무시할 수 없는 비용을 초래한다. 채널 간에 많은 양의 데이터를 복사하는 애플리케이션에서는 무복사 기법이 상당한 성능 향상을 가져다 줄 수 있을 것이다.



다운로드 하십시오

설명 이름 크기 다운로드 방식
이 글의 예제 프로그램 j-zerocopy.zip 3KB HTTP

다운로드 방식에 대한 정보


 

참고자료

교육

  • "Zero Copy I: User-Mode Perspective"(Dragan Stancevic, Linux Journal, 2003년 1월): 무복사 기법과 sendfile()에 대해 더 알아보자.

  • "An Efficient Zero-Copy I/O Framework for UNIX"(Moti N. Thadani와 Yousef A. Khalidi, 썬 마이크로시스템즈, 1995년 5월): 이 논문은 애플리케이션 프로그램과 유닉스 커널 간의 버퍼 관리와 교환을 위한 무복사 프레임워크를 소개한다.

  • transferTo(): java.nio.channels.FileChannel 클래스의 transferTo() 메서드에 대한 Javadoc 문서

필자소개

Sathiskumar Palaniappan은 IBM 인도 연구소 자바 기술 센터의 시스템 소프트웨어 엔지니어다.

Pramod B. Nagaraja는 IBM 인도 연구소 자바 기술 센터의 소프트웨어 엔지니어다.

 

 

출처 : http://www.ibm.com/developerworks/kr/library/j-zerocopy/index.html

Spring + iBatis + Struts 조합 왜 쓸까?


1. Struts 의 ActionMapping(URL 매핑이나 컨텍스트 제어)이 Spring 의 MVC 보다 훨씬 간결하기 때문.


2. 1과 같은 상황에서 Spring만으로 구성한 프로젝트에 비해 환경 설정 파일등이 간소해짐으로써 생산성의 향상을기대할수 있다.


3. 앞단에서 넘어오는 Request 핸드링이나 URL 매핑은 Struts 에서 해결하고,

 나머지 영역은 Spring의 자체적인 강점을 고스란히 유지해나갈수 있다.
(즉, MVC 모델에서 Controller 유입, 유출에 대한 제어권은 설정이 간결해지고 Spring 자체의 장점을 유지되는 효과를 기대 할수 있다.)
-loC,ACP, 단위테스트, OCP 설계에서 얘끼하는 개방 폐쇄 원칙이나 SRP 같은 걸 유지할 수 있고, POJO, 기타 Spring에서 제공하는 기반 기술


Spring만으로도 프레임웍 확장하는 쪽으로(URL Mapping만 해결 해주면 될듯) 잡고 Spring + iBatis만으로 MVC 설계하는 쪽으로 하겠습니다.


 

 

java.lang.NoSuchMethodError
뜻 그대로 보자면 메소드를 찾지못해서 일어나는 에러이다..

하지만 클래스, jar도 정상적으로 올라가있고 javap 명령을 이용해 확인해봐도 메소드도 존재할때도
이런 메시지가 뜨는 경우가 발생한다

그 이유는 classpath상에 중복되는 class가 포함된 jar가 존재하기 떄문이다..헐?
WEB-INF/lib 에 있는 jar파일과 같은 파일이 java/jre/lib/ext 경로에있어 지우고 해보니 정상작동한다

출처 : http://slog2.egloos.com/3775114 

 /**
  * SHA-256 으로 암호화된 passwd 를 반환.
  * @param str
  * @return hashed string.
  */
 public static String makeSHApasswd(String str){
  
  int interNb = 1000;
  byte[] byteArray =byteArrFromStr("osungbnk");
  StringBuilder x = new StringBuilder();
  
  try {
   if(!"".equalsIgnoreCase(str)){ //
    byte[] retVal = getHash(interNb, str, byteArray);

    for(int i=0; i<retVal.length; i++){
     x.append(String.format("%x", retVal[i]));
    }
   }
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  } catch (UnsupportedEncodingException e1) {
   e1.printStackTrace();
  }
  
  return x.toString();
 }

SHA-256 패스워드 생성

Program_Language/Java 2012. 4. 4. 14:18 Posted by Request

/**
     * 입력된 password 로 SHA-256 hashCode 생성.
     * @return byte[]
     */
    public static byte[] getHash(int iterationNb, String password, byte[] salt) throws NoSuchAlgorithmException, UnsupportedEncodingException {

        MessageDigest digest = MessageDigest.getInstance("SHA-256");

        digest.reset();
        digest.update(salt);

        byte[] input = digest.digest(password.getBytes("UTF-8"));

        for (int i = 0; i < iterationNb; i++) {
            digest.reset();
            input = digest.digest(input);
        }

        return input;
    }