'Response'에 해당하는 글 2건

매일 프로그램 삽질은 하고 있지만 이번 건은 유난히 더 삽질이 길었다.
3일 동안 아무런 일도 못하고 이 일에만 매달렸다.
다른 일을 하긴 해야 하지만, 개발자 자존심에 해결하지 못하고 넘어가기에도 뭐 하고...
결국에는 해결하게 되었지만 아무것도 아닌 원인 때문에 해결하고도 짜증이 난다.

증상은 다음과 같다.




소스는 간단하다.
그냥 특정 쿠키를 생성한 다음 response에 실어 보내는 것이다.

그런데 로컬과 서버의 결과물이 달랐다.

* 로컬의 결과물
cookieKey1=bla...bla...;
cookieKey2=bla...bla...;

* 서버의 결과물
cookieKey1="bla...bla...";
cookieKey2="bla...bla...";

둘 간의 차이점은 value 앞 뒤로 쌍따옴표(")가 있느냐 없느냐이다.
큰 차이가 아닐지 모르지만 인증 관련된 작업을 다루는데 있어서 해당 값으로 인해 로그인이 되기도 하고 안되기도 하는 부분이라서 매우 골치 아픈 부분이었다.

소스를 이리도 뜯어보고 저리도 뜯어 봤지만 해결은 되지 않았고,
Googling을 아무리 해 봐도 답은 나오지 않았다.(내 능력 부족 -.-;;)

결국 회사 내 다른 분의 도움을 얻어 로컬과 서버의 tomcat 버젼의 차이때문에 생기는 것임을 알게 되었고 서버의 tomcat 버젼을 5.5.27로 downgrade해서 문제를 해결하게 되었다

관련된 내용은 http://tomcat.apache.org/tomcat-5.5-doc/changelog.html 의 46587 에서 확인해 볼 수 있다.

신고

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

트랙백이 하나이고 , 댓글이 없습니다.
secret
필터를 통해 서블릿이 실행되기 전에 특정 액션을 하게 만들 수 있다고 했다.
그런데 서블릿이 실행되고 나서 특정 액션을 하게 만들려면 어떻게 해야 할까?
간단하게 생각해 보면 필터의 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