일반 XSLTFilter ... (퍼옴)

황제낙엽 2003.07.21 15:33 조회 수 : 634 추천:161

sitelink1  
sitelink2  
sitelink3  
sitelink4  
sitelink5  
sitelink6  
USING FILTERS TO MODIFY THE SERVER'S RESPONSE
서버 응답을 수정하기 위해 필터 사용하기
 

한 승 협[tedlovesmary@hotmail.com]
자바스터디 네트워크 [ http://javastudy.co.kr ] 

보기힘들면 요기 < http://javastudy.co.kr/docs/techtips/010627.html >


"서블릿 필터로 코드 재사용성 향상시키기" 팁의 예제에서 RequestBlocker는 특정 요청을 거절하기 위해 필터를 사용했다. 필터의 좀 더 중요한 사용은 서버로부터 온 응답을 수정하는 것이다. 아래 소개된 XSLTFilter 클래스는 문서를 호출자에게 돌려주기 전에 HTML로 번역함으로써 서버로부터 리턴된 XML 문서를 자동적으로 변경한다.

//class com.develop.filters.XSLTFilter
package com.develop.filters;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

public class XSLTFilter implements Filter {
  private ServletContext ctx;
  private String xslt;
  private TransformerFactory tf = TransformerFactory.newInstance();
  private Transformer xform;

  private static class ByteArrayServletStream extends ServletOutputStream {
    ByteArrayOutputStream baos;
    ByteArrayServletStream(ByteArrayOutputStream baos) {
      this.baos = baos;
    }
    public void write(int param) throws java.io.IOException {
      baos.write(param);
    }
  }

  private static class ByteArrayPrintWriter {
    private ByteArrayOutputStream baos = new ByteArrayOutputStream();
    private PrintWriter pw = new PrintWriter(baos);
    private ServletOutputStream sos = new ByteArrayServletStream(baos);

    public PrintWriter getWriter() {
      return pw;
    }
    public ServletOutputStream getStream() {
      return sos;
    }
    byte[] toByteArray() {
      return baos.toByteArray();
    }
  }

  public void init(FilterConfig filterConfig) throws ServletException {
    ctx = filterConfig.getServletContext();
    xslt = filterConfig.getInitParameter("xslt");
    ctx.log("Filter " + filterConfig.getFilterName() + " using xslt " + xslt);
    try {
      xform = tf.newTransformer(new StreamSource( ctx.getResourceAsStream(xslt)));
    } catch (Exception e) {
      ctx.log("Could not intialize transform", e);
      throw new ServletException( "Could not initialize transform", e);
    }
  }

  public String httpReqLine(HttpServletRequest req) {
    StringBuffer ret = req.getRequestURL();
    String query = req.getQueryString();
    if (query != null) {
      ret.append("?").append(query);
    }
    return ret.toString();
  }

  public String getHeaders(HttpServletRequest req) throws IOException {
    Enumeration en = req.getHeaderNames();
    StringBuffer sb = new StringBuffer();
    while (en.hasMoreElements()) {
      String name = (String) en.nextElement();
      sb.append(name).append(": ").append(req.getHeader(name)).append("n");
    }
    return sb.toString();
  }
  public void doFilter(ServletRequest servletRequest,
                       ServletResponse servletResponse,
                       FilterChain filterChain)
              throws java.io.IOException, ServletException {
    HttpServletRequest hsr = (HttpServletRequest)servletRequest;
    final HttpServletResponse resp = ( HttpServletResponse)servletResponse;
    ctx.log("Accessing filter for " + httpReqLine(hsr) +" " + hsr.getMethod());

    final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
    final boolean[] xformNeeded = new boolean[1];
    HttpServletResponse wrappedResp = new HttpServletResponseWrapper(resp) {
      public PrintWriter getWriter() {
        return pw.getWriter();
      }
      public ServletOutputStream getOutputStream() {
        return pw.getStream();
      }
      public void setContentType(String type) {
        if (type.equals("text/xml")) {
          ctx.log("Converting xml to html");
          resp.setContentType("text/html");
          xformNeeded[0] = true;
        } else {
          resp.setContentType(type);
        }
      }
    };
    filterChain.doFilter(servletRequest, wrappedResp);
    byte[] bytes = pw.toByteArray();
    if (bytes == null || (bytes.length == 0)) {
      ctx.log("No content!");
    }
    if (xformNeeded[0] == true) {
      try {
        xform.transform(new StreamSource(new ByteArrayInputStream(bytes)),
                        new StreamResult(resp.getOutputStream()));
        ctx.log("XML -> HTML conversion completed");
      } catch (Exception e) {
        throw new ServletException(
                              "Unable to transform document", e);
      }
    } else {
      resp.getOutputStream().write(bytes);
    }
  }
  public void destroy() {
    ctx.log("Destroying filter...");
  }
}
XSLTFilter 클래스는 RequestBlocker 예제보다 훨씬 더 복잡하다. 그러나 하나의 중요한 메소드는 doFilter이다. 체인에 있는 다음 필터로 응답 객체를 전달하는 대신에 XSLTFilter의 doFilter에 대한 호출은 wrappedResp라 불리는 맞춤 응답 객체를 기술한다.

응답 객체를 대체하는 것을 쉽게 하기 위해, 필터 아키텍쳐는 HttpServletResponseWrapper란 이름의 헬퍼 클래스를 제공한다. 이것은 오리지널 응답 객체를 감싸고 단순하게 모든 메소드 호출을 통해서 전달한다. XSLTFilter는 getOutputStream과 getWriter 그리고 setContentType의 세 메소드를 오버라이딩하며 HttpServletResponseWrapper의 무기명(anonymous) 서브클래스를 생성한다.

getOutputStream과 getWriter 메소드 내포된 헬퍼 클래스인 ByteArrayPritWriter(줄여 pw)의 인스턴스를 이용한다. 하부(downstream) 필터나 서블릿이 "응답"에다 쓸 때, 실제로 pw의 인스턴스에다 쓴다.

setContentType 메소드는 되돌려지는 내용이 "text/xml"인가를 확인한다. 만약 하부 서블릿이나 필터가 내용 타입을 "text/xml"로 설정하려고 하면, 오버라이드된 setContentType이 그것을 "text/html"로 대신 바꾼다. 그리고 변형이 실행될 필요를 나타내는 xformNeeded[0] 플래그를 설정한다.

doFilter를 호출한 후, XSLTFilter는 응답이 변형될 필요가 있는지를 확인한다. 만약 그렇다면, pw에서 하부 응답을 가져와서 실제 응답인 resp로 바꾼다. 물론 resp도 진짜 응답은 아닐 것이다. 왜냐하면 XSLTFilter가 아직까지는 다른 필터의 하부이기 때문이다.

변형은 XSLT를 사용해서 수행된다. XSLT는 XML 파싱용 Java API(JAXP)의 TransformerFactory 클래스를 사용하여 로드된다. 변형을 하는 것은 다른 필터 특성인 초기화 파라미터를 설명한다. XSLPFilter는 변형을 수행하도록 하는 xslt란 이름의 초기화 파라미터를 사용하여 설정되어지길 기대한다.

XSLTFilter의 동작을 보려면, 다음 단계를 수행해라.

XSLT 처리기가 필요할 것이다. 이 팁은 공개 소스 처리기인 Xalan-Java 버젼 2.1.0로 테스트 되었고, 이것은 http://xml.apache.org/xalan-j/index.html에서 다운로드 받을 수 있다.
 
Tomcat의 예제 어플리케이션 내에서 작업을 하고 있다고 가정하기 때문에, xerces.jar와 xalan.jar를 {Tomcat디렉토리}/webapps/examples/lib 에다 추가해라.
 
XSLTFilter를 클래스 패스 상의 servlet.jar와 jaxp.jar와 함께 컴파일해라. 이 두 jar파일들은 Tomcat 설치판에서 찾을 수 있다. XSLTFiler.class를 {Tomcat디렉토리}/webapps/examples/WEB-INF/classes/com/develop/filters에 복사해라.
 
필터를 설정해라. 두 개의 XML 코드조각을 {Tomcat디렉토리}/webapps/examples/WEB-INF/web.xml 에 추가해야 될 것이다.
  <filter>
    <filter-name>XSLT Filter</filter-name>
    <filter-class>com.develop.filters.XSLTFilter</filter-class>
    <init-param>
      <param-name>xslt</param-name>
      <!-- Change the param-value to the XSLT you want to use -->
      <param-value>/xform2.xsl</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>XSLT Filter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

서블릿 엔진이 XML 컨텐츠 타입에 대해 설정되어 있는지 확인해라. {Tomcat디렉토리}/conf/web.xml 파일을 편집하고 아래 XML 코드 조각을 더해야 될 것이다.
   <!-- add to the list of mime-mappings already present -->
   <mime-mapping>
     <extension>xml</extension>
     <mime-type>text/xml</mime-type>
   </mime-mapping>

XML과 XSL 파일들 설치해라. 아래 설정단계는 보이는 것처럼 xform2.xsl과 Index.xml을 사용하고 있다고 가정한다. 그 파일들을 {Tomcat디렉토리}/webapps/examples로 복사해라.
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- File Index.xml -->
<tips>
<author id="stu" fullName="Stuart Halloway"/>
<author id="glen" fullName="Glen McCluskey"/>
<tip title="Using the SAX API"
     author="stu"
     htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0627.html#tip2"
     textURL="http://java.sun.com/jdc/TechTips/txtarchive/June00_Stu.txt">
</tip>
<tip title="Random Access for Files"
     author="glen"
     htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0509.html#tip1"
     textURL="http://java.sun.com/jdc/TechTips/txtarchive/May00_GlenM.txt">
</tip>
</tips>

<!-- File Xform2.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
  <HTML><BODY><H1>JDC Tech Tips Archive</H1>
  <xsl:apply-templates/>
  </BODY></HTML>
  </xsl:template>

  <!-- list the title of a tip -->
  <xsl:template match="tip">
  <br><xsl:apply-templates select="@*"/><xsl:value-of select="@title"/></br>
  </xsl:template>

  <!-- create a link to any htmlURL -->
  <xsl:template match="@htmlURL">
  <A HREF="{.}"> HTML </A> |
  </xsl:template>

  <!-- create a link to any textURL -->
  <xsl:template match="@textURL">
  <A HREF="{.}"> TEXT </A> |
  </xsl:template>

  <!-- ignore other attributes -->
  <xsl:template match="@*"/>
</xsl:stylesheet>

Tomcat을 재시작해라. 브라우져를 사용하여 http://localhost:8080/examples/Index.xml을 입력보라. 필터는 자동적으로 이 문서를 HTML로 바꿀 것이다.
 
다른 XML과 XSL 문서도 해보아라. 필터의 초기화 파리미터를 바꾸면, Tomcat을 재시작하는 것을 잊지말자.
서블릿 필터에 대해 더 알고 싶으면, http://java.sun.com/products/servlet/index.html 에서 자바 서블릿 2.3 스펙을 참조하세요.

자바 서블릿 2.3 스펙의 개요는, http://www.java-pro.com/upload/free/Features/Javapro/2001/07jul01/kj0107/kj0107-1.asp 에서 Kevin Jones의 "Newer is Better"라는 기사를 보세요.