'필터'에 해당하는 글 2건

필터를 통해 서블릿이 실행되기 전에 특정 액션을 하게 만들 수 있다고 했다.
그런데 서블릿이 실행되고 나서 특정 액션을 하게 만들려면 어떻게 해야 할까?
간단하게 생각해 보면 필터의 doFilter()메소드 안에 있는 chain.doFilter(request, response) 를 마치고 나서 작업하면 될 것 같다.
하지만, 서블릿에 넘겨주는 response 객체를 서블릿이 사용하게 되면, 필터를 거치지 않고 바로 클라이언트로 response 하게 된다.
따라서 doFilter를 통해 response를 넘길 때 새로운 응답 객체(HttpServletResponse를 구현한 객체)를 만들어 넘기는 방법을 써야 한다.

하지만, HttpServletResponse는 간단한 클래스가 아니므로, 썬에서 구현해 둔 다음 4가지를 이용하면 된다.

ServletRequestWrapper
HttpServletRequestWrapper
ServletResponseWrapper
HttpServletResponseWrapper

위 4가지 클래스를 상속한 후 필요한 메소드만 재정의 해서 사용하면 된다.

response를 할 때 내용을 압축해서 보내주는 예제를 통해서 Response Filter를 어떻게 사용하는지 확인해 볼 수 있다.

package com.example.web;

import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CompressionFilter implements Filter {
    private ServletContext ctx;
    private FilterConfig cfg;
   
    @Override
    public void init(FilterConfig cfg) throws ServletException {
        // TODO Auto-generated method stub
        this.cfg = cfg;
        ctx = cfg.getServletContext();
        ctx.log(cfg.getFilterName() + " initialized.");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain fc) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
       
        String valid_encodings = request.getHeader("Accept-Encoding");
        if(valid_encodings.indexOf("gzip") > -1) {
            CompressionResponseWrapper wrappedResp
                        = new CompressionResponseWrapper(response);
            wrappedResp.setHeader("Content-Encoding", "gzip");
            ctx.log("before chain...");
            fc.doFilter(request, wrappedResp);
           
            GZIPOutputStream gzos = wrappedResp.getGZIPOutputStream();
            gzos.finish();
           
            ctx.log(cfg.getFilterName() + ": finished the request.");
        } else {
            ctx.log(cfg.getFilterName() + ": no encoding performed.");
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        cfg = null;
        ctx = null;
    }
}


package com.example.web;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class CompressionResponseWrapper extends HttpServletResponseWrapper {
    private GZIPServletOutputStream servletGzipOS = null;
    private PrintWriter pw = null;

    public CompressionResponseWrapper(HttpServletResponse response) {
        super(response);
    }
   
    public void setContentLength(int len) {}
   
    public GZIPOutputStream getGZIPOutputStream() {
        return this.servletGzipOS.internalGzipOS;
    }
   
    private Object streamUsed = null;
   
    public ServletOutputStream getOutputStream() throws IOException {
        if((streamUsed != null) && (streamUsed != pw)) {
            throw new IllegalStateException();
        }
       
        if(servletGzipOS == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            streamUsed = servletGzipOS;
        }
        return servletGzipOS;
    }
   
    public PrintWriter getWriter() throws IOException {
        if((streamUsed != null) && (streamUsed != servletGzipOS)) {
            throw new IllegalStateException();
        }
       
        if(pw == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            OutputStreamWriter osw = new OutputStreamWriter(servletGzipOS, getResponse().getCharacterEncoding());
           
            pw = new PrintWriter(osw);
            streamUsed = pw;
        }
        return pw;
    }
}


package com.example.web;

import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;

public class GZIPServletOutputStream extends ServletOutputStream {
    GZIPOutputStream internalGzipOS;
   
    GZIPServletOutputStream(ServletOutputStream sos) throws IOException {
        // TODO Auto-generated constructor stub
        this.internalGzipOS = new GZIPOutputStream(sos);
    }
   
    @Override
    public void write(int param) throws IOException {
        // TODO Auto-generated method stub
        internalGzipOS.write(param);
    }

}

압축과 관련된 부분은 다루고자 하는 범위를 넘어서는 듯 하며, Response를 어떻게 변형하여 넘겨주는지를 확인해 보면 된다.
CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(response); 와 같이 HttpServletResponseWrapper를 확장한 클래스를 통해 만들어진 랩퍼 클래스로부터 변경된 response 객체를 리턴받아 사용한다.
response 객체는 super(response);와 같이 상위 클래스에서 알아서 처리하도록 하며, 나머지 압축 관련 부분만 처리해 준다.


신고

'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
모든 서블릿 앞단에서 공통적으로 처리해야 할 내용이 있을 경우 필터를 사용해서 해당 내용을 처리할 수 있다.
필터는 자신만의 고유 API가 있으며, 컨테이너가 이 API를 알고 호출해 준다.
해당 API는 init(), destroy(), doFilter()이다.
그리고 호출해야 할 필터는 DD(web.xml)에서 설정하게 된다.

하나의 예제로 필터를 만들어 보면 다음과 같이 할 수 있다.(Head & First 예제)

BeerRequestFilter.java

package com.example.web;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class BeerRequestFilter implements Filter {
    private FilterConfig fc;
   
    public void init(FilterConfig config) throws ServletException {
        this.fc = config;
    }
   
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpReq = (HttpServletRequest) req;
       
        String name = httpReq.getRemoteUser();
       
        if(name != null) {
            fc.getServletContext().log("User " + name + "is updating");
        }
       
        chain.doFilter(req, resp);
    }
   
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
       
    }
}

모든 필터는 Filter 인터페이스를 구현해야 한다.
또한 doFilter()의 인자로 HttpServletRequest, HttpServletResponse가 아니라 ServletRequest, ServletResponse 가 온다는 점도 주의해야 한다.
그리고 필터는 순차적으로 진행될 수 있는데 chain.doFilter를 통해 다음 필터의 doFilter를 호출할 수 있다.

web.xml

  <servlet>
      <servlet-name>ListenerTester</servlet-name>
      <servlet-class>com.example.ListenerTester</servlet-class>
      <security-role-ref>
          <role-name>Manager</role-name>
          <role-link>Admin</role-link>
      </security-role-ref>
  </servlet>
  <servlet-mapping>
      <servlet-name>ListenerTester</servlet-name>
      <url-pattern>/ListenTest.do</url-pattern>
  </servlet-mapping>

  <filter>
      <filter-name>BeerRequest</filter-name>
      <filter-class>com.example.web.BeerRequestFilter</filter-class>
      <init-param>
          <param-name>LogFileName</param-name>
          <param-value>UserLog.txt</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>BeerRequest</filter-name>
      <url-pattern>*.do</url-pattern>
  </filter-mapping>

위와 같이 web.xml을 설정하게 되면 기존에 설정한 /ListenTest.do url을 호출하게 되면 BeerRequestFilter.java의 doFilter 메소드가 자동으로 호출되게 된다.
물론 호출 시에는 init() 메소드가 먼저 실행 되게 되고, 마지막에는 destroy() 메소드가 실행 되게 된다.

url-pattern 대신에 servlet-name이 대신 와도 된다.
우선 순위는 url-pattern이 먼저이고, 해당 패턴에 맞는 필터가 모두 실행되고 나서 servlet-name에 맞는 내용이 실행된다.

필터가 여러개일 경우에는 DD(web.xml)에 설정한 순서대로 실행 되며, 실행 과정은 stack에 관련 필터들이 쌓였다가 제거 되는 모습을 상상해 보면 된다.

필터를 클라이언트가 Request 했을 경우 뿐 아니라, 다른 경우에도 실행할 수 있다.
그럴 경우에는 다음과 같이 DD(web.xml)에 적어주면 된다.

  <filter>
      <filter-name>BeerRequest</filter-name>
      <filter-class>com.example.web.BeerRequestFilter</filter-class>
      <init-param>
          <param-name>LogFileName</param-name>
          <param-value>UserLog.txt</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>BeerRequest</filter-name>
      <url-pattern>*.do</url-pattern>
      <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

기본 값은 REQUEST이며, 그 대신에 INCLUDE, FORWARD, ERROR 를 사용할 수 있다.
선언하는 내용 그대로 include 시, forward시, error 시에 작동한다.

신고

'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 궁금한게있는데요 여기서 맨위에BeerRequestFilter.java파일에서
    if(name != null) {
    fc.getServletContext().log("User " + name + "is updating");
    }
    있는데 이걸 어떻게 확인하나요??
secret