Site Search :
Standard Enterprise XML Methodology Pattern Setting Tunning Other
Article Contributors
GuestBook
Javapattern Maven
XSourceGen Dev
JetSpeed Test
JLook Image
jLook Family Site


Servlet의 각종 Listener 사용방법 및 샘플
 
Servlet의 각종 Listener에 대한 내역을 알아보고 한가지 리스너를 대상으로 사용방법 및 샘플코드를 보여주고 소스도 공유하도록 한다. ( 2003/03/08 ) 323
Written by ienvyou - 최지웅
1 of 1
 
Servlet의 각종 Listener 사용방법 및 샘플

자, 이번 글에서는 세션에 대한 binding이 이루어졌을때 서블릿 엔진이 어떠한 방식으로
해당 세션을 control할 수 있는지에 대하여 예제와 함께 살펴보도록 하자.

일반적으로 세션이라함은 쿠키와 같은 속성으로 분류가 되어질 수 있는데 보통 생각하기는
서버측에 저장되는 데이터정보라고 들었을 것이다. 사실 엄밀하게 말하자면
세션또한 클라이언트 브라우져에 속성을 이용하게 되는 것이지만 설명하면
더 복잡해지기만 하기 때문에 그냥 서버에 있는 서버의 메모리만큼의 영역을 차지할 
수 있는 객체로서만 바라보도록 하자.

우선 Enterprise API의 리스너의 종류를 먼저 보도록 하자. 이러한 리스너들은 API의 util.EventListener를
이용하여 엔진이 실제 처리를 해주게 되는데 간단하게 설명해서 필터링 같은 서비스를 한다고 
보면 무방할 듯하다. 필터가 무엇인지 모른다면, 그냥 간단하게 정수기라고 생각하자.
수도물을 그냥 먹을 수 없기 때문에 정수기를 통하여 이물질을 걸러내는 것처럼
자바에서도 우리가 실제 코딩한 서블릿측으로 request들이 넘어오기 전에 엔진들이 무언가를 
걸러내게 할 수 있는 속성을 가지고 있다. 그 대표적인 예가 필터, 리스너이며
JDK1.2부터 설정된 event delegation속성을 이용하여 처리한다.

아래의 리스너들은 반드시 deployment descriptor에 설정되어 있어야 한다. DD가 궁금하면
서블릿 엔진의 web.xml에 설정이 되어져야 한다는 이야기이다.

▶ javax.servlet.ServletContextAttributeListener
이 리스너는 컨텍스트에 저장된 애트리뷰의 변화가 있었을 때 설정된 이벤트를 엔진이 자동으로
발생시키도록 한다. 이벤트 감지를 얻어내기 위해서는 여러분들이 직접 작성한 리스너의
implemention클래스를 통해야 하는것은 물론이며, 아래의 리스너 모두 동일하게 설정된다.

▶ javax.servlet.ServletContextListener
당신이 작성한 웹애플리케이션에서 컨텍스트를 이용하여 무언가를 저장하고 이용할 수 있는데 
이 리스너의 작동은 그러한 컨텍스트에 대한 변경이 이루어졌을 때 자동으로 엔진이 감지하고
여러분들이 작성한 클래스의 이벤트를 발생시킨다.

▶ javax.servlet.http.HttpSessionActivationListener 
request.getSession(true)같은 경우처럼 Session에 대한 내용이 새로 생성되어 activate되어졌을 때
발생하는 이벤트를 감지하는 리스너이다.

▶ javax.servlet.http.HttpSessionAttributeListener 
HttpSession에 대한 애트리뷰트의 변경시 연결되어지는 리스너이다.

▶ javax.servlet.http.HttpSessionBindingListener 
HttpSession에 대하여 클라이언트 세션정보에 대한 바인딩이 이루어졌을 경우 감지되는 리스너이며
이번 예제에서 사용될 리스너이기도 한다. HttpSessionBindingEvent객체를 실제 구현클래스로 
넘겨주게 된다.

위의 리스너 모두 servlet2.3부터 나오기 시작했으며, 현재 사용되는 거의 모든 엔진들이 지원을
하고 있으므로 사용하는데 대하여 무리가 없으리라 본다.

자, 그러면 이제 예제를 하나 만들어보도록 하자.
놀새가 만들어볼 예제는 HttpSessionBindingListener라고 하였다. 
이것을 왜 만들었는지부터 살펴보아야 하지 않을까? 이유는 간단했다. 예전 프로젝트의 경우
클라이언트 접속에 대하여 사용자정보를 세션에 담도록 하고 있는 그 담긴 세션에 대하여
정보를 얻어내려 하였다. 사용자 정보 및 저장된 세션에 대하여 collection에 현재 이용중인 
사용자 및 그 사용자 정보들을 어드민 관리차원에서 보고 싶었던 것이다.

각종 리스너들을 이용한다면 그러한 정보들을 쉽게 얻어낼 수 있는 특징을 가지고 있었다.

▶세션이 연결된걸 감지한다면 끊어지는 것도 고객이 브라우져를 닫는 순간 감지해낼수 있는가?

어떻게 생각하는가? 이론적으로도 가능할지 모르지만 실제는 불가능하다. http프로토콜의 특성을
알아야 하고 만약 숨김 애플릿같은 것으로 브라우져 이벤트를 감지하여, 서버측으로 보낸다 가정해도
당신이 직접 구현할 수 있는가?

그러면 어떻게 해야 할까? 결국 세션시간만료가 되어져 들어오는 이벤트를 감지하는 방법이 있겠지만
그렇게 한다면 세션타임아웃을 단기간으로 설정을 해야 할테고....

음, 여전히 문제로 남아있기는 하다. 그건 여러분들도 한번 생각해보시도록 하고.
사실 예전에 구현했던것은 한사람이 웹사이트에 중복으로 로그인을 하는 것을 방지하는 것을 만든적은
있다. 위의 리스너를 요리조리 이용해서 말이다.


▶  자, 이제 예제를 만들어보도록 하자.

당근 시나리오부터 살펴봐야겠다. 
1. 사용자는 로그인을 한다. 
2. 로그인서블릿이 그 내용을 받아 세션에 저장한다.
3. 링크페이지를 통해 메인페이지로 이동한다.

우선 로그인페이지부터 만들도록 하자.

<html>
<body>
<center>
<b>Login Page</b>
<hr>
<form action="/servlet/LoginServlet" method="POST">
<table border=0>
<tr><td>Name : </td><td><input type=text name=name></td></tr>
<tr><td>Password : </td><td><input type=password name=pword></td></tr>
<tr><td><input type=submit></td><td><input type=reset></td></tr>
</table>
</form>
</center>
</body>


위에서는 간단하게 name, pword라는 input type을 정의하여 로그인서블릿의 requets로 던진다.
서블릿에서 처리되는 코드를 보도록 하자.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class LoginServlet extends HttpServlet {
	private SessionListener listener;

	public void init() {
		listener = new SessionListener();
	}

	public void doPost(HttpServletRequest req, HttpServletResponse res) 
	throws ServletException, IOException {
		res.setContentType("text/html");
		
		String name=req.getParameter("name");
		String pword=req.getParameter("pword");
		String data[]={name,pword};
		HttpSession sess=req.getSession(true);
		sess.setAttribute("Listener",listener); 
        // SessionListener를 세션에 저장한다.
		sess.setAttribute("user.data",data);
		PrintWriter out = res.getWriter();
		
		out.println("Login is Successed<br>");
		out.println("<a href=/servlet/LinkPage>LinkPage</a>");
		out.close();
	}
}

위의 코드에서 눈여겨 보아야 할것은 bold(굵은글씨)로 쓰여진 부분이다.
사실 3라인의 코드에서는 리스너 2개가 동작하게 된다. 
HttpSessionActivationListener, HttpSessionBindingListener인데 그 중의 한 동작인 
HttpSessionBindingListener를 보도록 하자.

여러분들은 AWT나 Swing클래스를 작성하여 이벤트를 발생시켜 본적이 있는가?
그렇다면 아래의 코드를 이해하는 데 무리가 없을 것이고 잘 모르는 분을 위하여
코드아래에 설명을 하도록 하겠다.

import java.util.*;
import javax.servlet.http.*;

public class SessionListener implements HttpSessionBindingListener {
/**
* 세션에 대한 값이 binding되었을때 호출되는데 그값은 HttpSessionBindingEvent에
* 저장되어 있다.
*/
	public void valueBound(HttpSessionBindingEvent event) {
		HttpSession hs = event.getSession();
		System.out.println("SessionListener.valueBound() : "+event.getName());

	}
/**
* 세션에 바인딩 되어 있는 값이 없어지게 될때 자동으로 엔진에 의하여
* 호출 되어 진다.
*/
	public void valueUnbound(HttpSessionBindingEvent event) {
		HttpSession hs = event.getSession();
		System.out.println("SessionListener.valueUnbound() : "+event.getName());
	}
}

위의 코드에서 대체 이벤트는 무엇인가? 
앞서 delegate event 모델을 쓴다라고 했다.  즉 실제 들어오는 request에 대한 내용을
처리하던 도중에 엔진은 자신과 연결된 개발자가 작성한 event가 있는 지 web.xml에서
찾아보게 된다. 있다라고 하면 엔진은 HttpSessionBindingEvent라는 것을 만들게 되고
name, session, value값을 저장하여 당신이 작성한 listener클래스로 던지게 되면
그것을 처리하는 로직을 집어넣게 된다면 엔진은 그에 상응하는 처리를 하게 될것이다.

즉 모든 이벤트를 직접 여러분이 핸들링하여 작성하는 것이 아니라 엔진에 위임시켰으므로
delegation이란 용어를 쓴것이다.

자. 그러면 그 리스너를 뽑아서 어떻게 처리해야 하나? 

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class LinkPage extends HttpServlet {
	
public void doGet(HttpServletRequest req, HttpServletResponse res) 
throws ServletException,IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();

    HttpSession sess=req.getSession();
    String msg="";

    if(sess.isNew()) {
        msg="Error!!!. You must login.";
    } else {
        String[] data=(String[])sess.getAttribute("user.data");
        msg=data[0]+"!!, Welcome to this page.";
        // 세션리스너의 정보를 읽어본다.
        SessionListener listener = (SessionListener)sess.getAttribute("Listener");
    }
    
    out.println("<font size=-1 color=blue><b>"+msg+"</b></font>");
    sess.invalidate();
    out.close();
	}
}

위와 같이 처리가 되다면 Deployment descriptor를 작성하여야 한다.
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
<web-app>
<!-- START SESSION LISTENER --> 
   <listener>
         <listener-class>SessionListener</listener-class>
    </listener> 
    <!-- END SESSION LISTENER --> 

</web-app>

위와 같이 설정하면 결과값을 테스트해볼 수 있고, 세션에 값이 저장되는
시점에 데이터 처리하는 방법을 테스트할 수 있으며, 위와 같은 방법을 코딩할 수 있다면
다른 모든 리스너클래스에 대한 핸들링이 가능해질 것이다.

전체소스는 첨부된 파일을 참조하기 바란다.

Written by Carouser : 2002-04-08
 
1
References
 
http://java.sun.com/servet
This article source zip : listener.zip
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD