IBatis

 | JSP
2012. 5. 8. 18:15

 

기본_구조(1).jpg

아이바 티스 기본구조 이다. 

최초 실행 해야 할것은 ibatis-2.3.4.726.jar  파일과 ojdbc14.jar 을 빌더 패치 하여 프로젝트 에 Jars Import 시켜야만 한다.

그리고 프로젝트에서 new Folder 생성후 폴더에서 마우스 오른쪽 bulder path -> use as source Folder  클릭하여 소스폴더로 변경 한다.

소스폴더로 변경된 conf 폴더에 SqlMapConfig.properties 과 SqlMapConfig.xml 파일을 생성 한다. 생성되어 있는것을 옮겨 놓아 수정 하거나하자 .

=================================================================================================================================

 

SqmlMapConfigproperties.jpg

SqlMapConfig.properties  파일 이다.

내용을 보면 dirver 에서 jdbc 패키지를 이용 하여 오라클에 접속을 시도 하며

url 을통해 오라클서버로 접속 함을 알수 있다

@ 이후에 서버명:포트번호:오라클버전 등을 적는것을 볼수 있다.

=================================================================================================================================

 

SqmlMapConfig.jpg

 

SqlMapConfig.xml 파일의 소스 이다.

앞에서 만든 SqlMapConfig.properties 를 이용 하여 정의 됨을 알수 있다.

기본 셋팅후 JDBC 의 기본 구조를 가지고 자신을 sqlMap 화 시킴으로서 사용 할수 있도록 대기 상태화 된다.

=========================================================================================================================================

 

memberxml.jpg

member.xml 파일 이다.

보기와 같이 sqlMap 생성 과정 에서 select 문을 질의 하고 있는데 그 값을 가져 오는 쿼리 문을 id화 시켜 정의 하고 있다. getMemberName 과 getMembers 두개가 정의 됨을 알수 있다.

따라서 생성 후에 <select></select > 안에 사용 하는 쿼리 문장을 추가,수정,삭제 등을 통해 알맞게 변동 하면서 id 값을 다르게 하여 명백하게 후 개발과정에서

알아 볼수 있도록 정의 하면 좋을것 이다. 이에 추가 하여 다이나믹화 시킬수 있는데 그것은 따로. 셀렉트 안에 <dynamic> 문을 추가 하여 조건에 따른 셀렉문장 지정

========================================================================================================================================================================

 

IbatisMain.jpg

 

Ibatise 의 메인 클래스다 실제 JSP 사용중에 이렇게 만들지는 않지만 잘 만들어져있는지 테스트 하기 위해 만들어져 있다. 실행 환경및 소스를 분석해 보자.

먼저 IbatisService 를 getInstance ghk 화 하여 service라는 이름으로 변수화 하였다. 이후.

MemberBean 을 제너레이션 하여 ArrayList 를 생성 하며 기본 값을 null 을 주었다.

이후 a001 의 param 을 생성 하며 조건 값을 주었다. 즉 오라클의 member 테이블의 id 값이 "a001" 인 것을 결과 값으로 내놓기 위한 것이다. 이하 서비스를 설명 하기 위해 IbatisService를 확인 해보자

===================================================================================================================================================================================

IbatisService.jpg

IbatisService.java 이다.

소스 분석을 해보자. 

소스 윗부분부터 보면 알수 있듯이 IbatisServie 의 경우 생성을 위해 기본적으로 getInstace 화 하여 (IbatisService.getInstace()) 생성 하게 만들었는데

그 이유는 그냥 만들어도 되지만 메모리의 효율을 위해 싱클톤 패턴으로 생성 한것이다. Ibatis의 기본생성자는 private 화 되어 보통 만들듯이 new IbatisServise 로는 만들수 없다.

반드시 IbatisService 의 getInstance() 메서드를 사용 하염 만들게 되는대 이 메서드의 클래스 내의 service 생성자를 보유 할수 있게 되며 주소 값이 공유 되게 되어 있다.

즉 최초 만들시에는 service 생성이 안되어 있음으로 service 가 새로 생성되어 보내 주고. 후에 생성될 경우 service 는 static 화 되어 있음으로 모두 공유 하게 되어 service 는

null이 아니며 최초 만들었던 service 를 돌려 주게 되며 실제 사용 할때는 변수명의 차이는 있을 지언정 같은 메서드를 사용 하게 됨으로서 같은 기능의 메서드를 구지

여러개 만들지 않게 되어 메모리를 절약 할수 있다.  이하 IbatisDao 에서도 같은 방식으로 코딩을 하게 됨으로 참고 하자. 내부 메서드는 구조만 보면 될듯 하다.

===========================================================================================================================================================

IbatisDao.jpg

IbatisDao 이다. 이 장에서 싱클톤 분야는 제외 하겠따. 중요 한것은 메서드의 중복 성을 뛰며 자체 적으로 생성한 클래스를 이용 하여 건너 뛰며 실행 하게 되는것이다 즉

main - > service -> dao->sqlMapClient(SqlMapConfig.xml)->JDBC->오라클 형식으로 만드는것은 어찌 보면 효율이 없어 보인다. 단순히 main과 sqlMapClient 는 제쳐

두고라도 service dao 등의 클래스 건너 뛰기는 일견 효율성이 떨어져 보이지만. 만약 가져온 데이터의 가공 또는 연산등이 필요 한 경우

dao 는 sql 과 jdbc 등의 파일과 연결 됨으로 그 파일을 수정 하기보다는 service 라는 클래스의 완충 장치를 통해 수정 이 가능 하며 dao ~ Jdbc 사이의 가져오는 연산 량을

범위를 높인후 또다른 service2 와 연동 하여 각자 원하는 메서드를 통해 값을 가져오는 것도 가능 하다 . 이 모든 기능은 각기 같은 메모리 값을 가지고 실행 되기 때문에 효율적이다. 

 

=======================================================================================================================================================================

 

BuilderSqlMapClient.jpg

 

BuildedSqlMapClient  클래스 이다. 소스 에서 sqlMapConfig.xml 등을 통해 JDBC 에 접근 함을 알수 있다. 여기서 Ibatis.jar 의 SqlMapClient 에 접근 하며 만들어 진다.

이상 2012년 3월 15일 Ibatise 의 기본 학습을 마치며 미진한 부분은 추가로 지속해서 수정 해야 겠다;;;

 

 

다음으로 신세계 마이바 티스를 공부해 보자.

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

JSP 에러코드 모음  (0) 2012.05.08
include  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
Posted by 사라링
  1. 필터를 이용한 웹 프로그래밍 Part1, 필터란 무엇인가! 
    서블릿 2.3에 새롭게 추가된 필터가 무엇이며, 어떻게 구현하는지에 대해서 살펴본다. 

                          요약 
    서블릿 2.3 규약은 2.2 규약에서 지적되었던 단점을 보완하기 위해서 노력했으며 그 노력 중의 하나가 바로 필터(Filter)이다. 이 글에서는 필터가 무엇이며 필터를 어떻게 구현하는 지에 대해서 살펴본다. 

    프로바이더: 최범균 
    --------------------------------------------------------------------------------
    필터!! 


    현 재 서블릿 2.3 규약은 Proposed Final Draft 2 상태에 있다. 조만간 서블릿 2.3과 JSP 1.2 최종 규약 이 발표될 것으로 예상되며 우리는 당연히 새롭게 추가된 것들이 무엇인지에 관심이 쏠리게 된다. 서블릿 2.3 규약에 새롭게 추가 된 것 중에 필자가 가장 눈여겨 본 것은 바로 필터(Filter) 기능의 추가이다.

    그 동안 필자는 서블릿 2.2 와 JSP 1.1에 기반하여 웹 어플리케이션을 구현하는 동안 몇몇 부분에서 서블릿 2.2 규약의 부족한 면을 느낄 수 있었으 며, 특히 사용자 인증 처리, 요청 URL에 따른 처리, XSL/T를 이용한 XML 변환(Transformation) 등 개발자들 이 직접 설계해야 하는 부분이 많았었다. 하지만, 이제 서블릿 2.3 규약에 새롭게 추가된 필터(Filter)를 사용함으로써 개발 자들이 고민해야 했던 많은 부분을 덜어낼 수 있게 되었다. 이 글에서는 필터가 무엇이며 어떻게 필터를 구현하는지에 대해 살펴볼 것 이다.

    간단하게 말해서, 필터는 'HTTP 요청과 응답을 변경할 수 있는 재사용가능한 코드'이다. 필터는 객체의 형 태로 존재하며 클라이언트로부터 오는 요청(request)과 최종 자원(서블릿/JSP/기타 문서) 사이에 위치하여 클라이언트의 요 청 정보를 알맞게 변경할 수 있으며, 또한 필터는 최종 자원과 클라이언트로 가는 응답(response) 사이에 위치하여 최종 자원 의 요청 결과를 알맞게 변경할 수 있다. 이를 그림으로 표현하면 다음과 같다.

     
    그림1 - 필터의 기본 구조 
    그 림1에서 자원이 받게 되는 요청 정보는 클라이언트와 자원 사이에 존재하는 필터에 의해 변경된 요청 정보가 되며, 또한 클라이언트 가 보게 되는 응답 정보는 클라이언트와 자원 사이에 존재하는 필터에 의해 변경된 응답 정보가 된다. 위 그림에서는 요청 정보를 변 경하는 필터와 응답 정보를 변경하는 필터를 구분해서 표시했는데 실제로 이 둘은 같은 필터이다. 단지 개념적인 설명을 위해 그림1 과 같이 분리해 놓은 것 뿐이다.

    필터는 그림1에서처럼 클라이언트와 자원 사이에 1개가 존재하는 경우가 보통이지만, 여러 개의 필터가 모여 하나의 체인(chain; 또는 사슬)을 형성할 수도 있다. 그림2는 필터 체인의 구조를 보여주고 있다.

     
    그림2 - 필터 체인 
    그 림2와 같이 여러 개의 필터가 모여서 하나의 체인을 형성할 때 첫번째 필터가 변경하는 요청 정보는 클라이언트의 요청 정보가 되지 만, 체인의 두번째 필터가 변경하는 요청 정보는 첫번째 필터를 통해서 변경된 요청 정보가 된다. 즉, 요청 정보는 변경에 변경 에 변경을 거듭하게 되는 것이다. 응답 정보의 경우도 요청 정보와 비슷한 과정을 거치며 차이점이 있다면 필터의 적용 순서가 요 청 때와는 반대라는 것이다. (그림2를 보면 이를 알 수 있다.)

    필터는 변경된 정보를 변경하는 역할 뿐만 아니 라 흐름을 변경하는 역할도 할 수 있다. 즉, 필터는 클라이언트의 요청을 필터 체인의 다음 단계(결과적으로는 클라이언트가 요청 한 자원)에 보내는 것이 아니라 다른 자원의 결과를 클라이언트에 전송할 수 있다. 필터의 이러한 기능은 사용자 인증이나 권한 체크 와 같은 곳에서 사용할 수 있다.



    필터 관련 인터페이스 및 클래스 


    필터를 구현 하는데 있어 핵심적인 역할을 인터페이스 및 클래스가 3개가 있는 데, 그것들은 바로 javax.servlet.Filter 인터페이 스, javax.servlet.ServletRequestWrapper 클래 스, javax.servlet.ServletResponseWrapper 클래스이다. 이 중 Filter 인터페이스는 클라이언트 와 최종 자원 사이에 위치하는 필터를 나타내는 객체가 구현해야 하는 인터페이스이다. 그리 고 ServletRequestWrapper 클래스와 SerlvetResponseWrapper 클래스는 필터가 요청을 변경한 결 과 또는 응답을 변경할 결과를 저장할 래퍼 클래스를 나타내며, 개발자는 이 두 클래스를 알맞게 상속하여 요청/응답 정보를 변경하 면 된다.

    Filter 인터페이스

    먼저, Filter 인터페이스부터 살펴보자. Filter 인터페이스에는 다음과 같은 메소드가 선언되어 있다.

    public void init(FilterConfig filterConfig) throws ServletException 
    필터를 웹 콘테이너내에 생성한 후 초기화할 때 호출한다. 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException 
    체인을 따라 다음에 존재하는 필터로 이동한다. 체인의 가장 마지막에는 클라이언트가 요청한 최종 자원이 위치한다. 
    public void destroy() 
    필터가 웹 콘테이너에서 삭제될 때 호출된다. 
    위 메 소드에서 필터의 역할을 하는 메소드가 바로 doFilter() 메소드이다. 서블릿 콘테이너는 사용자가 특정한 자원을 요청했 을 때 그 자원 사이에 필터가 존재할 경우 그 필터 객체의 doFilter() 메소드를 호출하며, 바로 이 시점부터 필터가 작용하 기 시작한다. 다음은 전형적인 필터의 구현 방법을 보여주고 있다.


      public class FirstFilter implements javax.servlet.Filter {
      
         public void init(FilterConfig filterConfig) throws ServletException {
            // 필터 초기화 작업
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response
                              FilterChain chain)
                              throws IOException, ServletException {
            // 1. request 파리미터를 이용하여 요청의 필터 작업 수행
            // 2. 체인의 다음 필터 처리
            chain.doFilter(request, response);
            // 3. response를 이용하여 응답의 필터링 작업 수행
         }
         
         public void destroy() {
            // 주로 필터가 사용한 자원을 반납
         }
      }


    위 코 드에서 Filter 인터페이스의 doFilter() 메소드는 javax.servlet.Servlet 인터페이스 의 service() 메소드와 비슷한 구조를 갖는다. 즉 만약 클라이언트의 자원 요청이 필터를 거치는 경우, 클라이언트의 요청 이 있을 때 마다 doFilter() 메소드가 호출되며, doFilter() 메소드는 서블릿과 마찬가지로 각각의 요청에 대해서 알 맞은 작업을 처리하게 되는 것이다.

    위 코드를 보면 doFilter() 메소드는 세번째 파라미터 로 FilterChain 객체를 전달받는 것을 알 수 있다. 이는 클라이언트가 요청한 자원에 이르기까지 클라이언트의 요청이 거쳐가 게 되는 필터 체인을 나타낸다. FilterChain을 사용함으로써 필터는 체인에 있는 다음 필터에 변경한 요청과 응답을 건내 줄 수 있게 된다.

    위 코드를 보면서 우리가 또 하나 알아야 하는 것은 요청을 필터링한 필터 객체가 또 다시 응답 을 필터링한다는 점이다. 위 코드의 doFilter() 메소드를 보면 1, 2, 3 이라는 숫자를 사용하여 doFilter() 메 소드 내에서 이루어지는 작업의 순서를 표시하였는데, 그 순서를 다시 정리해보면 다음과 같다.

    request 파리미터를 이용하여 클라이언트의 요청 필터링
    1 단계에서는 RequestWrapper 클래스를 사용하여 클라이언트의 요청을 변경한다. 
    chain.doFilter() 메소드 호출
    2 단계에서는 요청의 필터링 결과를 다음 필터에 전달한다. 
    response 파리미터를 사용하여 클라이트로 가는 응답 필터링
    3 단계에서는 체인을 통해서 전달된 응답 데이터를 변경하여 그 결과를 클라이언트에 전송한다. 
    1단계와 3단계 사이에서 다음 필터로 이동하기 때문에 요청의 필터 순서와 응답의 필터 순서는 그림2에서 봤듯이 반대가 된다.

    필터의 설정

    필 터를 사용하기 위해서는 어떤 필터가 어떤 자원에 대해서 적용된다는 것을 서블릿/JSP 콘테이너에 알려주어야 한다. 서블릿 규약 은 웹 어플리케이션과 관련된 설정은 웹 어플리케이션 디렉토리의 /WEB-INF 디렉토리에 존재하는 web.xml 파일을 통해서 하 도록 하고 있으며, 필터 역시 web.xml 파일을 통해서 설정하도록 하고 있다.

    web.xml 파일에서 필터를 설정하기 위해서는 다음과 같이 <filter> 태그와 <filter-mapping> 태그를 사용하면 된다.


      <web-app>
         
         <filter>
            <filter-name>HighlightFilter</filter-name>
            <filter-class>javacan.filter.HighlightFilter</filter-class>
            <init-param>
               <param-name>paramName</param-name>
               <param-value>value</param-value>
            </init-param>
         </filter>
         
         <filter-mapping>
            <filter-name>HighlightFilter</filter-name>
            <url-pattern>*.txt</url-pattern>
         </filter-mapping>
         
      </web-app>


    여 기서 <filter> 태그는 웹 어플리케이션에서 사용될 필터를 지정하는 역할을 하며, <filter- mapping> 태그는 특정 자원에 대해 어떤 필터를 사용할지를 지정한다. 위 예제의 경우는 클라이언트가 txt 확장자를 갖 는 자원을 요청할 경우 HithlightFilter가 사용되도록 지정하고 있다.

    <init- param> 태그는 필터가 초기화될 때, 즉 필터의 init() 메소드가 호출될 때 전달되는 파라미터 값이다. 이는 서블릿 의 초기화 파라미터와 비슷한 역할을 하며 주로 필터를 사용하기 전에 초기화해야 하는 객체나 자원을 할당할 때 필요한 정보를 제공하 기 위해 사용된다.

    <url-pattern> 태그는 클라이언트가 요청한 특정 URI에 대해서 필터링을 할 때 사용된다. 서블릿 2.3 규약의 11장을 보면 다음과 같이 url-pattern의 적용 기준을 명시하고 있다.

    '/'로 시작하고 '/*'로 끝나는 url-pattern은 경로 매핑을 위해서 사용된다. 
    '*.'로 시작하는 url-pattern은 확장자에 대한 매핑을 할 때 사용된다. 
    나머지 다른 문자열을 정확한 매핑을 위해서 사용된다. 
    에를 들어, 다음과 같이 <filter-mapping> 태그를 지정하였다고 해 보자.


         <filter-mapping>
            <filter-name>AuthCheckFilter</filter-name>
            <url-pattern>/pds/*</url-pattern>
         </filter-mapping>


    이 경우 클라이언트가 /pds/a.zip 을 요청하든 /pds/b.zip 을 요청하는지에 상관없이 AuthCheckFilter가 필터로 사용될 것이다.

    <url- pattern> 태그를 사용하지 않고 대신 <servlet-name> 태그를 사용함으로써 특정 서블릿에 대한 요청 에 대해서 필터를 적용할 수도 있다. 예를 들면 다음과 같이 이름이 FileDownload인 서블릿에 대해 서 AuthCheckFilter를 필터로 사용하도록 할 수 있다.


         <filter-mapping>
            <filter-name>AuthCheckFilter</filter-name>
            <servlet-name>FileDownload</servlet-name>
         </filter-mapping>
         
         <servlet>
            <servlet-name>FileDownload</servlet-name>
            ...
         </servlet>




    래퍼 클래스 


    필 터가 필터로서의 제기능을 하기 위해서는 클라이언트의 요청을 변경하고, 또한 클라이언트로 가는 응답을 변경할 수 있어야 할 것이 다. 이러한 변경을 할 수 있도록 해 주는 것이 바로 ServletRequestWrapper 와 ServletResponseWrapper이다. 서블릿 요청/응답 래퍼 클래스를 이용함으로써 클라이언트의 요청 정보를 변경하 여 최종 자원인 서블릿/JSP/HTML/기타 자원에 전달할 수 있고, 또한 최종 자원으로부터의 응답 결과를 변경하여 새로운 응 답 정보를 클라이언트에 보낼 수 있게 된다.

    서블릿 요청/응답 래퍼 클래스로서의 역할을 수행하기 위해서 는 javax.servlet 패키지에 정의되어 있는 ServletRequestWrapper 클래스 와 ServletResponseWrapper 클래스를 상속받으면 된다. 하지만, 대부분의 경우 HTTP 프로토콜에 대한 요청/응답 을 필터링 하기 때문에 이 두 클래스를 상속받아 알맞게 구현한 HttpServletRequestWrapper 클래스 와 HttpServletResponseWrapper 클래스를 상속받는 경우가 대부분일 것이다.

    HttpServletRequestWrapper 클 래스와 HttpServletResponseWrapper 클래스는 모두 javax.servlet.http 패키지에 정의되어 있으 며, 이 두 클래스는 각각 HttpServletRequest 인터페이스와 HttpServletResponse 인터페이스에 정의되 어 있는 모든 메소드를 이미 구현해 놓고 있다. 필터를 통해서 변경하고 싶은 정보가 있을 경우 그 정보를 추출하는 메소드를 알맞 게 오버라이딩하여 필터의 doFilter() 메소드에 넘겨주기만 하면 된다. 예를 들어, 클라이언트가 전송한 "company" 파 리머터의 값을 무조건 "JavaCan.com"으로 변경하는 요청 래퍼 클래스는 다음과 같 이 HttpServletRequestWrapper 클래스를 상속받은 후에 getParameter() 메소드를 알맞게 구현하면 된 다.


      package javacan.filter;
      
      import javax.servlet.http.*;
      
      public class ParameterWrapper extends HttpServletRequestWrapper {
         
         public ParameterWrapper(HttpServletRequest wrapper) {
            super(wrapper);
         }
         
         public String getParameter(String name) {
            if ( name.equals("company") ) {
               return "JavaCan.com";
            } else {
               return super.getParameter(name);
            }
         }
      }


    오 버라이딩한 getParameter() 메소드를 살펴보면 값을 구하고자 하는 파라미터의 이름이 "company"일 경 우 "JavaCan.com"을 리턴하고 그렇지 않을 경우에는 상위 클래스(즉, HttpServletRequestWrapper 클래 스)의 getParameter() 메소드를 호출하는 것을 알 수 있다.

    이렇게 작성한 래퍼 클래스는 필터 체인을 통 해서 최종 자원까지 전달되어야 그 효과가 있을 것이다. 즉, 최종 자원인 서블릿/JSP에 서 request.getParameter("company")를 호출했을 때 ParameterWrapper 클래스 의 getParameter() 메소드가 사용되기 위해서는 ParameterWrapper 객체 가 HttpServletRequest 객체를 대체해야 하는데, 이는 Filter 인터페이의 doFilter() 내에 서 ParameterWrapper 객체를 생성한 후 파라미터로 전달받은 FilterChain의 doFilter() 메소드를 호출함 으로써 가능하다. 좀 복잡하게 느껴질지도 모르겠으나 이를 코드로 구현해보면 다음과 같이 간단한다.


      package javacan.filter;
      
      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class ParameterFilter implements Filter {
         
         private FilterConfig filterConfig;
         
         public ParameterFilter() {
         }
         
         public void init(FilterConfig filterConfig) {
            this.filterConfig = filterConfig;
         }
         
         public void destroy() {
            filterConfig = null;
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                              throws java.io.IOException, ServletException {
            // 요청 래퍼 객체 생성
            HttpServletRequestWrapper requestWrapper = 
                         new ParameterWrapper((HttpServletRequest)request);
            // 체인의 다음 필터에 요청 래퍼 객체 전달
            chain.doFilter(requestWrapper, response);
         }
      }


    응답 래퍼 클래스 역시 요청 래퍼 클래스와 비슷한 방법으로 구현된다.

    앞 에서도 언급했듯이 요청 정보의 변경 및 응답 정보 변경의 출발점은 래퍼 클래스이다. XML+XSL/T 기법이나 사용자 인증과 같 은 것들을 최종 자원과 분리시켜 객체 지향적으로 구현하기 위해서 요청/응답 래퍼 클래스를 사용하는 것은 필수적이다. 2부에서 실 제 예를 통해서 어떻게 필터와 요청/응답 래퍼 클래스를 효과적으로 사용할 수 있는 지 살펴보게 될 것이다.

    필터 체인의 순서

    앞 에서 필터는 체인을 형성할 수 있다고 하였다. 체인을 형성한다는 것은 어떤 특정한 순서에 따라 필터가 적용된다는 것을 의미한 다. 예를 들면, 여러분은 '인증필터->파라미터 변환 필터->XSL/T 필터->자원->XSL/T 필터 ->파라미터 변환 필터->인증필터'와 같이 특정 순서대로 필터를 적용하길 원할 것이다. 서블릿2.3 규약은 다음과 같 은 규칙에 기반하여 필터 체인 내에서 필터의 적용 순서를 결정한다.

    url-pattern 매칭은 web.xml 파일에 표기된 순서대로 필터 체인을 형성한다. 
    그런 후, servlet-name 매칭이 web.xml 파일에 표기된 순서대로 필터 체인을 형성한다. 


    결론 


    이 번 1 부에서는 서블릿 2.3 규약에 새롭게 추가된 필터가 무엇인지 그리고 필터를 어떻게 구현하며 또한 필터를 어떻게 서블릿이 나 JSP와 같은 자원에 적용할 수 있는지에 대해서 알아보았다. 아직 구체적으로 필터의 응용방법에 대해서 설명하지 않았기 때문 에 필터의 장점이 머리에 떠오르지 않을것이다. 다음 2 부에서는 구체적으로 필터의 예를 살펴봄으로써 필터의 활용함으로써 얻게 되 는 장점에 대해서 살펴보도록 하자.













    필터를 이용한 웹 프로그래밍 Part2, 필터의 응용! 
    서블릿 2.3에 새롭게 추가된 필터를 이용한 사용자 인증, XSL/T 변환을 구현해본다. 

    요약 
    1부에 이어 2부에서는 사용자 인증 필터와 XSL/T 변환 필터를 구현해 봄으로써 필터를 어떻게 응용할 수 있는 지에 대해서 살펴보도록 하자. 

    프로바이더: 최범균 
    --------------------------------------------------------------------------------
    필터의 응용 


    사 실 필터는 그 동안 많은 개발자들이 필요로 하는 기능이었으며, 다른 페이지로 이동하거나(forwarding) 다른 페이지를 포함하 는(include) 방법을 사용하여 서블릿 체인(또는 서블릿과 JSP의 체인) 형태로 필터링을 구현하는 경우가 많았다. 이러한 필 터링을 적용할 수 있는 분야에는 다음과 같은 것들이 있을 것이다.

    데이터 변환(다운로드 파일의 압축 및 데이터 암호화 등) 
    XSL/T를 이용한 XML 문서 변경 
    사용자 인증 
    자원 접근에 대한 로깅 
    이 외 에도 많은 활용 방법들이 존재할 수 있겠지만 여기서 제시한 네 가지 정도가 가장 많이 사용되지 않을까 생각된다. 특히, 데이터 변 환이나 XSL/T는 필터를 적용하기에 가장 알맞은 구조를 갖고 있다. 데이터 변환의 경우 데이터 압축 필터와 암호화 필터를 체인으 로 만들어 암호화한 데이터를 다시 압축하는 등의 다양한 방식이 존재할 수 있다. 또한, XSL/T를 사용할 경우 최종 자원 은 XML 문서를 생성하고, 그 문서를 다양한 포맷으로 변환해주는 필터가 존재할 수 있다. 예를 들어, 서블릿/JSP는 사용자 가 요청한 정보를 XML로 출력하고 필터는 XSL/T를 사용하여 자원의 출력 결과를 다양한 기기(웹브라우저, 모바일 폰 등)에 맞 게 변형할 수도 있을 것이다.

    이 글에서는 사용자 인증 필터, XSL/T 필터에 대해서 살펴볼 것이다. 이 두가 지 필터는 필터를 통한 흐름 제어 및 응답 데이터의 변경 방법 등을 보여주고 있기 때문에, 이 두 가지 형태의 필터가 어떤 식으 로 구현되는 지 이해한다면 여러분은 그 외의 모든 다른 종류의 필도 어렵지 않게 구현할 수 있게 될 것이다. 지금부터 차례대로 살 펴보도록 하자.



    로그인 검사 필터 


    요즘 많은 웹 사이트들이 회원제로 운영되 고 있으며 로그인을 한 이후에 컨텐츠에 접근할 수 있도록 제한한 곳도 많다. 특히 컨텐츠의 유료화 추세에 발맞추어 이처럼 사용 자 인증이 필요한 사이트는 점차적으로 증가하고 있다. 심지어 무료 사이트 조차도 사용자가 인증을 거친 이후에 컨텐츠에 접근 할 수 있도록 하고 있다. 이처럼 사용자 인증이 웹 사이트의 필수 요소로 등장하면서 개발자들은 각각의 JSP/서블릿 등의 코드 에 사용자가 로그인을 했는지의 여부를 판단하기 위한 코드를 삽입하고 있다. 여기서 각각의 JSP/서블릿은 같은 코드 블럭을 갖 게 되며 이는 회원 인증이 변할 경우 모든 페이지를 변경해주어야 한다는 문제를 일으키게 된다.

    이러한 문제는 로그인 을 검사하는 필터를 사용함으로써 말끔하게 해소할 수 있다. 1부에서도 살펴봤듯이 클라이언트의 요청은 서블릿/JSP에 전달되기 전 에 먼저 필터를 통과하게 된다. 따라서, 필터에서 조건에 따라 알맞게 흐름을 제어할 수 있다는 것을 의미한다. 먼저 소스 코드부 터 작성해보자. 어떤 형태로 구현했는지 집중적으로 관찰하기 바란다.


      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class LoginCheckFilter implements Filter {
         
         public void init(FilterConfig filterConfig) throws ServletException {
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                     throws java.io.IOException, ServletException {
            if (LoginChecker.isLogin((HttpServletRequest)request)) {
               // 로그인을 했다면 다음 필터를 실행한다.
               chain.doFilter(request, response);
            } else {
               // 로그인을 하지 않았을 경우 로그인 페이지로 이동한다.
               ((HttpServletResponse)response).sendRedirect(LOGIN_URL);
            }
         }
         
         public void destroy() {
         }
         
         private static String LOGIN_URL = "/login.jsp";
      }
      

    로 그인 여부를 검사하는 필터는 위와 같이 매우 간단하다. 여기서 LoginChecker.isLogin() 메소드는 파라미터로 전달받 은 HttpServletRequest를 사용하여 로그인 여부를 판단해주는 일종의 보조 클래스이다. 회원이 로그인을 했 을 때 LoginChecker.isLogin() 메소드가 true를 리턴한다고 가정할 경우, 위 코드와 같이 로그인을 하면 필 터 체인의 다음 필터로 이동하고 로그인을 하지 않은 상태로 판단되면 response를 사용하여 다른 페이지로 이동하면 된다. 로그 인을 하지 않았을 때 이동하는 페이지는 일반적으로 로그인 폼을 보여주는 페이지가 될 것이다.

    위 코드를 보면 알겠지 만 클라이언트의 요청이 반드시 필터 체인의 모든 필터를 통과해야 하는 것은 아니며, 어떤 필터든지 간에 다음 필터로 이동하지 않 고 중간에 체인을 끝낼 수 있도록 되어 있다. 사용자 인증 필터는 바로 그러한 특징을 이용한 것이다. 사용자 인증 필터는 거의 대 부분의 회원 서비스에서 사용될 것이며 다음과 같이 web.xml을 설정하여 사용자 인증 필터를 적용하면 될 것이다.


       <filter>
          <filter-name>LoginChecker</filter-name>
          <filter-class>LoginCheckFilter</filter-class>
       </filter>
       
       <filter-mapping>
          <filter-name>LoginChecker</filter-name>
          <url-pattern>/board/*</url-pattern>
       </filter-mapping>


    인 증 필터를 사용함으로써 얻게 되는 장점은 서블릿/JSP와 같은 최종 자원에서 일일이 로그인 여부를 판단하지 않아도 된다는 점이 다. 이는 서블릿과 JSP는 클라이언트의 요청에 알맞은 작업만을 수행하는 역할을 맡게 되고 사용자 인증을 검사하는 역할은 맡지 않 아도 된다는 것을 의미한다. 즉, 역할에 알맞게 객체가 분리되는 것이다.



    XSL/T 필터 


    필 터가 나옴으로써 객체 지향적으로 변화된 부분이 있다면 바로 XML과 관련된 부분일 것이다. 필터가 생김으로써 서블릿과 JSP 는 더 이상 XSL/T를 이용하여 XML 문서를 HTML이나 WML과 같은 다른 통신 표준으로 변경해줄 필요가 없게 되었다. 이 제, XSL/T를 이용한 변환 작업은 이제 필터가 맡게 되었으며 서블릿과 JSP는 XML 문서를 생성하는 것 이외에 다른 작업 은 할 필요가 없게 되었다.

    XSL/T 필터는 응답 데이터를 변경해주는 필터이다. 즉, 서블릿/JSP가 생성 한 XML 문서를 XSL/T를 이용하여 완전히 새로운 형태로 재구성하는 것이 XSL/T 필터의 역할이다. 서블릿/JSP의 응답 결 과인 XML 문서를 완전히 새로운 형태로 변경해주기 위해서는 서블릿/JSP가 출력한 XML 데이터를 클라이언트(웹브라우저)에 곧바 로 전송해서는 안된다. 대신, 서블릿/JSP가 출력한 데이터를 임의의 버퍼에 저장한 후, 그 버퍼에 저장된 XML 데이터를 XSL /T를 사용하여 변환해야만 한다. 이를 위해 먼저 버퍼의 역할을 할 출력 스트림을 작성해야 하며, 또한 그 출력 스트림은 서블릿 과 JSP에서 주로 사용되는 PrintWriter 타입이어야만 한다. 다음은 이 예제에서 서블릿/JSP가 출력하는 데이터를 저장해 둘 버퍼의 역할을 하는 ReponseBufferWriter 클래스이다.


      class ReponseBufferWriter extends PrintWriter {
         
         public ReponseBufferWriter() {
            super(new StringWriter(4096) );
         }
         
         public String toString() {
            return ((StringWriter)super.out).toString();
         }
      }


    특 별히 어렵지는 않으며, ResponseBufferWriter는 print(), println(), write() 등의 메소드를 통 해서 전달된 데이터를 StringWriter에 저장하는 기능을 한다. toString() 메소드는 StringWriter에 저장 된 데이터를 String 타입으로 변환해주는 역할을 한다.

    출력 버퍼를 만들었으니 그 다음으로 해야 할 일은 최 종 자원인 서블릿과 JSP가 ResponseBufferWirter를 출력 스트림으로 사용하도록 하는 응답 래퍼 클래스를 작성하 는 것이다. 이 예제에서 사용할 응답 래퍼 클래스는 다음과 같다.


      class XSLTResponseWrapper extends HttpServletResponseWrapper {
         
         private ReponseBufferWriter buffer = null;
         
         public XSLTResponseWrapper(HttpServletResponse response) {
            super(response);
            buffer = new ReponseBufferWriter();
         }
         
         public PrintWriter getWriter() throws java.io.IOException {
            return buffer;
         }
         
         public void setContentType(String contentType) {
            // do nothing
         }
         
         public String getBufferedString() {
            return buffer.toString();
         }
      }


    위 코 드를 보면 XSLTResponseWrapper 클래스가 복잡하지 않다는 것을 알 수 있다. XSLTResponseWrapper 클 래스의 getWriter() 메소드는 실제 클라이언트로의 응답에 해당하는 스크림을 리턴하는 대신 앞에서 작성 한 ResponseBufferWriter를 리턴한다. 이렇게 함으로써 ServletResponse의 getWriter() 메소드 를 호출하는 서블릿/JSP는 클라이언트로의 응답 스트림이 아닌 ResponseBufferWriter를 출력 스트림으로 사용하게 된 다. 또 하나 주의해야 할 부분이 바로 setContentType() 메소드가 아무 기능도 하지 않는다는 점인데, 이 이유에 대해 서는 뒤에서 설명하도록 하겠다.

    이제 XML 데이터를 임시로 저장할 Writer도 구현하였고 또한 응답 래퍼도 구현하였다. 이제 마지막으로 남은 것은 필터를 구현하는 것 뿐이다. 필터는 다음과 같은 4 단계로 작업을 처리한다.

    응답 래퍼(XSLTResponseWrapper)를 생성한다. 
    생성한 응답 패퍼를 체인의 다음 필터에 전달한다. 
    래퍼로부터 서블릿/JSP가 출력한 데이터를 읽어와 XSL/T를 사용하여 HTML로 변환한다. 
    변환된 결과인 HTML을 실제 응답 스트림에 출력한다. 
    이 과정을 구현한 것이 바로 XSLTFilter 클래스이다. 


      import javax.servlet.*;
      import javax.servlet.http.*;
      import java.io.*;
      
      import javax.xml.transform.TransformerFactory;
      import javax.xml.transform.Transformer;
      import javax.xml.transform.stream.StreamSource;
      import javax.xml.transform.stream.StreamResult;
      
      public class XSLTFilter implements Filter {
         
         public void init(FilterConfig filterConfig) throws ServletException {
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                     throws java.io.IOException, ServletException {
            response.setContentType("text/html; charset=euc-kr");
            PrintWriter writer = response.getWriter();
            
            XSLTResponseWrapper responseWrapper = 
                          new XSLTResponseWrapper((HttpServletResponse)response);
            chain.doFilter(request, responseWrapper);
            
            // XSL/T 변환
            try {
               TransformerFactory factory = TransformerFactory.newInstance();
               Reader xslReader = new BufferedReader(new FileReader("c:/book.xsl"));
               StreamSource xslSource = new StreamSource(xslReader);
               
               Transformer transformer = factory.newTransformer(xslSource);
               
               String xmlDocument = responseWrapper.getBufferedString();
               Reader xmlReader = new StringReader(xmlDocument);
               StreamSource xmlSource = new StreamSource(xmlReader);
               
               StringWriter buffer = new StringWriter(4096);
               
               transformer.transform( xmlSource, new StreamResult(buffer) );
               
               writer.print(buffer.toString());
            } catch(Exception ex) {
               throw new ServletException(ex);
            }
            
            writer.flush();
            writer.close();
         }
         
         public void destroy() {
         }
      }


    XSLTFilter 클 래스의 doFilter() 메소드를 차근 차근 분석해보도록 하자. doFilter() 메소드가 가장 먼저 하는 것은 응답의 컨텐 츠 타입을 text/html로 지정하는 것이다. 물론, 한글을 사용하기 때문에 뒤에 charset도 추가해주었다. 여기 서 response 객체는 클라이언트에 대한 응답을 나타내며, 클라이언트는 결과 데이터를 HTML 문서로 인식하게 된다. 앞에 서 XSLTResponseWrapper의 setContentType() 메소드에서 아무것도 하지 않았었는데, 그 이유 는 XSLTFilter의 doFilter() 메소드에서 지정한 컨텐츠 타입을 변경할 수 없도록 하기 위함이다.

    그 다 음에는 래퍼 클래스를 생성한다. 래퍼 클래스는 XSLTResponseWrapper이며, 생성된 래퍼 클래스 는 chain.doFilter()를 통해서 다음 필터에 전달된다. 필터 체인의 실행이 완료되 면 XSLTResponseWrapper 객체에는 서블릿이나 JSP가 출력한 XML 응답 데이터가 저장되며, 그 데이터 는 responseWrapper.getBufferedString() 메소드를 통해서 구할 수 있게 된다. 이렇게 해서 구 한 XML 데이터는 JAXP 1.1에서 제공하는 Transformer의 transform() 메소드를 통해서(즉, XSL/T를 통 해서) HTML 형식으로 변환된다.

    이제 XSL/T 필터와 관련된 모든 클래스의 구현은 끝이 났다. 이제 남은 것 은 XSL/T에서 사용할 XSL 문서를 작성하고 XML 문서를 생성해주는 JSP/서블릿을 프로그래밍하고 그리고 web.xml 파일 을 통해서 XSLTFilter를 필터로 등록하는 것이다. 먼저 web.xml 파일을 필터를 사용하여 지정해보자.


       <filter>
          <filter-name>XSLT</filter-name>
          <filter-class>XSLTFilter</filter-class>
       </filter>
       
       <filter-mapping>
          <filter-name>XSLT</filter-name>
          <url-pattern>/xml/*</url-pattern>
       </filter-mapping>


    이 제 /xml/로 들어오는 모든 요청은 XSLTFilter를 토?서 처리된다. 이제 XML 문서를 생성해주는 JSP 페이지를 작성해 보자. 여기서는 테스트를 위해서 다음과 같이 간단한 JSP 페이지를 사용하였다. (이 JSP를 book.jsp라 하자.)


      <?xml version="1.0" encoding="euc-kr" ?>
      
      <%@ page contentType="text/xml; charset=euc-kr" %>
      
      <list>
         
         <book>
            <title>JavaCan.com의 JSP Professional</title>
            <author>이동훈, 최범균</author>
            <price>24,000</price>
         </book>
         
         <book>
            <title>JavaCan.com의 Victory Java</title>
            <author>이동훈, 최범균</author>
            <price>30,000</price>
         </book>
         
      </list>


    위 JSP 페이지는 보다시피 XML 문서를 생성해낸다. 이 XML 문서를 HTML로 변환해주기 위해 사용되는 XSL은 다음과 같다. (여기서는 XSL에 대한 내용은 설명하지 않겠다.)


     <?xml version="1.0" encoding="euc-kr" ?>
      
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output method = "html" indent="yes" encoding="euc-kr" />
      
        <xsl:template match="list">
        <html>
        <head><title>책 목록</title></head>
        <body>
          현재 등록되어 있는 책의 목록은 다음과 같습니다.
          <ul>
          <xsl:for-each select="book">
            <li><b><xsl:value-of select="title" /></b>
                (<xsl:value-of select="price" /> 원)
                <br />
                <i><xsl:value-of select="author" /></i>
            </li>
          </xsl:for-each>
          </ul>
        </body>
        </html>
        </xsl:template>
        
      </xsl:stylesheet>


    book.jsp 를 웹어플리케이션의 /xml 하위디렉토리에 복사한 후 웹브라우저에서 book.jsp를 요청한 결과의 소스 코드를 보면 다음과 같 이 XML이 아닌 XSLTFilter 필터를 통해서 변경된 결과가 오는 것을 확인할 수 있을 것이다.


      <html>
      <head>
      <META http-equiv="Content-Type" content="text/html; charset=euc-kr">
      <title>책 목록</title>
      </head>
      <body>
           현재 등록되어 있는 책의 목록은 다음과 같습니다.
           <ul>
      <li>
      <b>JavaCan.com의 JSP Professional</b>
                 (24,000 원)
                 <br>
      <i>이동훈, 최범균</i>
      </li>
      <li>
      <b>JavaCan.com의 Victory Java</b>
                 (30,000 원)
                 <br>
      <i>이동훈, 최범균</i>
      </li>
      </ul>
      </body>
      </html>




    결론 


    여 기서 살펴본 필터의 예제는 매우 간단하게 구현되는 것들이었지만, 아마 필터를 구현하는 데 있어서 가장 기본적인 형태를 갖는 예제 가 아닐까 생각된다. 이번 필터 예제를 통해서 여러분들은 필터의 쓰임새가 많겠구나 하고 생각했을 것이다. 어쩌면 벌써부터 압축 필 터나 이미지 생성 필터 등을 생각하고 있을지도 모르겠다. 하지만 필터의 응용을 생각하기 이전에 다음과 같은 점을 염두해두었으면 한 다.

    필터는 재사용이 가능해야 한다. 즉, 필터들은 객체 지향적으로 설계되고 구현되어야 한다. 
    필터를 통과하지 않아도 JSP/서블릿/기타 자원은 알맞은 결과를 출력해야 한다. 
    필터간에 커플링(coulpling; 결합도)이 존재해서는 안 된다. 
    이러한 것들을 염두하고서 필터를 설계하고 구현한다면, 그 필터는 여러분이 개발하게 될 웹 어플리케이션 곳곳에서 사용될 것이며 그만큼 여러분의 웹 어플리케이션은 역할별로 알맞게 분리된 구조를 갖게 될 것이다.
  2. 추가 이미지
  3.  















     

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

include  (0) 2012.05.08
IBatis  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
Posted by 사라링

FileUploadRequestWrapper

 | JSP
2012. 5. 8. 18:14
  1. package kr.or.ddit.wrapper;

    import java.awt.ItemSelectable;
    import java.io.File;
    import java.io.UnsupportedEncodingException;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;

    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.FileUploadException;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;

    /**
     * multipart/form-data
     */
    public class FileUploadRequestWrapper extends HttpServletRequestWrapper {

        private boolean multipart = false;
        private Map<String, String[]> parameterMap;
        private Map<String, FileItem[]> fileItemMap;

        public FileUploadRequestWrapper(HttpServletRequest request)
                throws FileUploadException {
            this(request, -1, -1, null);
        }

        public FileUploadRequestWrapper(HttpServletRequest request, int threshold,
                int max, String repositoryPath) throws FileUploadException {
            super(request);
            parsing(request, threshold, max, repositoryPath);
        }

        private void parsing(HttpServletRequest request, int threshold, int max,
                String repositoryPath) throws FileUploadException {
            if (ServletFileUpload.isMultipartContent(request)) {
                multipart = true;
                parameterMap = new HashMap<String, String[]>();
                fileItemMap = new HashMap<String, FileItem[]>();
                DiskFileItemFactory factory = new DiskFileItemFactory();
               
                // 메모리에 저장할 사이즈, 기본사이즈 10kb
                if (threshold != -1) {
                    factory.setSizeThreshold(threshold);
                }
                // 임시 저장 경로, 기본적으로 시스템의 탬프 , 웹서버의 경로
                if (repositoryPath != null) {
                    factory.setRepository(new File(repositoryPath));
                }
               
                ServletFileUpload upload = new ServletFileUpload(factory);
                String encodingName = request.getCharacterEncoding();
                System.out.println(encodingName);
                if (encodingName == null)
                    encodingName = "utf-8";
                upload.setSizeMax(max); // 최대 업로드 사이즈 (total)

                // upload.setFileSizeMax(); // 개별 업로드 사이즈
                List<FileItem> list = upload.parseRequest(request);
                for (int i = 0; i < list.size(); i++) {
                    FileItem fileItem = list.get(i);
                    String name = fileItem.getFieldName();
                    if (fileItem.isFormField()) {
                        // 일반 필드
                        try {
                            String value = fileItem.getString(encodingName);
                            String[] values = parameterMap.get(name);
                            if (values == null) {
                                // 한글이 만약 깨진다면  아래처럼 변경, 예전 FileUpload API,
                                // values = new String[]{new String(value.getBytes("iso-8859-1"),encodingName)};
                                values = new String[] { value };
                            } else {
                                String[] tempValues = new String[values.length + 1];
                                System.arraycopy(values, 0, tempValues, 0, values.length);
                                // 한글이 깨진다면
                                // tempValues[tempValues.length - 1] = new String(value.getBytes("iso-8859-1") ,encodingName);
                                tempValues[tempValues.length - 1] = value;
                                values = tempValues;
                            }
                            parameterMap.put(name, values);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // input type="file" 인 경우  fileItemMap에 저장
                        FileItem[] items = fileItemMap.get(name);
                        if (items == null) {
                            items = new FileItem[] { fileItem };
                        } else {
                            FileItem[] tempItems = new FileItem[items.length + 1];
                            System.arraycopy(items, 0, tempItems, 0, items.length);
                            tempItems[tempItems.length - 1] = fileItem;
                            items = tempItems;
                        }
                        fileItemMap.put(name, items);
                    }
                }
            }
        }

        public boolean isMultipartContent() {
            return multipart;
        }

        public String getParameter(String name) {
            if (multipart) {
                String[] values = (String[]) parameterMap.get(name);
                if (values == null)
                    return null;           
                return values[0];
            } else
                return super.getParameter(name);
        }

        public String[] getParameterValues(String name) {
            if (multipart)
                return (String[]) parameterMap.get(name);
            else
                return super.getParameterValues(name);
        }

        public Enumeration getParameterNames() {
            if (multipart) {
                return new Enumeration() {
                    Iterator iter = parameterMap.keySet().iterator();

                    public boolean hasMoreElements() {
                        return iter.hasNext();
                    }

                    public Object nextElement() {
                        return iter.next();
                    }
                };
            } else {
                return super.getParameterNames();
            }
        }

        public Map getParameterMap() {
            if (multipart)
                return parameterMap;
            else
                return super.getParameterMap();
        }

        public FileItem getFileItem(String name) {
            if (multipart) {
                FileItem[] fileItems = fileItemMap.get(name);
                if (fileItems != null) {
                    return fileItems[0];
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }

        public FileItem[] getFileItemValues(String name) {
            if (multipart)
                return fileItemMap.get(name);
            else
                return null;
        }

        public void delete() {
            if (multipart) {
                Iterator fileItemIter = fileItemMap.values().iterator();
                while (fileItemIter.hasNext()) {
                    FileItem[] fileItems = (FileItem[]) fileItemIter.next();
                    for (FileItem item : fileItems) {
                        item.delete();
                    }
                }
            }
        }

    } // class

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

IBatis  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
Posted by 사라링

FileUpLoad

 | JSP
2012. 5. 8. 18:14

 

 

wrapper구현.png

 

http://www.servlets.com/

cos-26Dec2008.zip

 

 

 

파일업로드는 Post방식으로만 가능하다 Get 방식 불가능

Http 요청 메시지
header : get방식: http://localhost:8080/chap13/index.jsp?파일주소 << 정말 파일의 위치주소만 전송된다.
body

form  enctype="multipart/form-data" 명시를 해야 파일데이터라고 인식을함.
input type="file"

파일을 첨부해서 보낸경우 request.getParameter(무력화당함..) 로 추출을못함. => 통체로 MultipartRequest 에 넘겸줌
 
WEB-INF/lib/cos.jar
cos라이브러리는 동시에 여러 파일을 올리수있다.
maxSize : 모든파일의 합의 제한용량


WEB-INF/fileSelect.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>파일 업로드 예제</title>
</head>
<body>
<!--
method="post" : get방식은 파일업로드 못함
enctype="multipart/form-data" : 파일업로드시 반드시 명시
type="file" : 선택파일을 매치시켜 파일을 업로드
-->
<form name="fimeForm" method="post" enctype="multipart/form-data" action="fileUpload.jsp">
작성자: <input type="text" name="user"><br/>
제목:<input type="text" name="title"><br/>
파일명:<input type="file" name="uploadFile"><br/>
파일명2:<input type="file" name="uploadFile2"><br/>
<input type="submit" value="파일 올리기"><br/>
</form>
</body>
</html>



WEB-INF/fileUpload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
<!--
MultipartRequest :
DefaultFileRenamePolicy : 기존에 업로드 파일중 이름이 똑같은것이 있는경우  업로드시 이름을 변경해주는것 ex)a.jsp,a1.jsp
 -->
<%@ page import="com.oreilly.servlet.MultipartRequest"%>
<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@ page import="java.util.*"%>
<%@ page import="java.io.*"%>

<%
    String realFolder = ""; //웹 어플레케이션상의  절대경로

    //파일이 업로드되는 폴더를 지정한다
    String saveFolder = "upload";
    String encType = "utf-8";///엔코딩타입
    int maxSize = 5 * 1024 * 1024;//최대 업로드될 파일크기 5mb

    ServletContext context = getServletContext();
    //현재 jsp 페이지의 웹어플리케이션상의 절대 경로를 구한다.
    realFolder = context.getRealPath(saveFolder);
    out.println("the realpath is: " + realFolder + "<br/>");

    try {
        MultipartRequestmulti= null;

        //전송을 담당할 콘포넌트를 생성하고 파일을 전송한다.
        //전송할 파일명을 가지고 있는 객체, 서버상의 절대경로, 최대 업로드될 파일크기
        /*
        MultipartRequest(
                request, :    요청request를 통채로 넘겨줌
                realFolder, : 웹어플리케이션상의 절대경로
                maxSize, : 최대용량
                encType, : 파일타입
                new DefaultFileRenamePolicy() :같은파일명이있는경우 이름변경
        */
        multi= new MultipartRequest(request, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
       
        //Form의 파라미터 목록을 가져온다.         //getParameterNames: 파일과 관련없는 일반정보인 경우 추출방법        //Enumeration : java.util : 데이터를 여러개 보관할수 있는객체         Enumeration params= multi.getParameterNames();

        //파라미터를 출력한다.
        while (params.hasMoreElements()) {
            //전송되는 파라미터이름
            String name = (String) params.nextElement();
            //전송되는 파라미터값
            String value = multi.getParameter(name);

            out.println(name + "=" + value + "<br>");

        }
        out.println("----------------------------------------------<br/>");

        //전송할 파일 정보를 가져와 출력한다.
        //getFileNames : 파일과 관련있는 정보인 경우의 추출방법
        Enumeration files = multi.getFileNames();

        //파일 정보가 있다면
        while (files.hasMoreElements()) {

            //input태그 속성이 file 인 태그의 name속성값 : 파라미터 이름
            String name = (String) files.nextElement();

            //서버에 저장된 파일 이름
            String filename = multi.getFilesystemName(name);

            //전송전 원래의 파일이름
            String original = multi.getOriginalFileName(name);

            //전송된 파일의 내용타입
            String type = multi.getContentType(name);

            //전송된 파일 속성이  file인 태그의 name 속성값을 이용해 파일 객체 생성
            //용량을 알아내기 위해서 : file.length();
            File file = multi.getFile(name);

            out.println("파라미터 이름 : " + name + "<br/>");
            out.println("실제 파일 이름 : " + original + "<br/>");
            out.println("저장된 파일 이름 : " + filename + "<br/>");
            out.println("파일 타입 : " + type + "<br/>");

            if (file != null) {
                out.println("크기 : " + file.length());
                out.println("<br/>");
            }
        }
    } catch (IOException ioe) {
        System.out.println(ioe);
    } catch (Exception ex) {
        System.out.println(ex);
    }
%>

 

 

다중업로드

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
Posted by 사라링

엑셀파일다운로드

 | JSP
2012. 5. 8. 18:14

출처 : http://blog.naver.com/tyboss/70038694288


1. 첫번째 방법

<%
response.setHeader("Content-Disposition", "attachment; filename=diary.xls");
response.setHeader("Content-Description", "JSP Generated Data");
response.setContentType("application/vnd.ms-excel");
%>

 
 

2. 두번째 방법
<%@ page contentType="application/vnd.ms-excel;charset=euc-kr" %>
<%
response.setHeader("Content-Disposition", "attachment; filename=LIST.xls");
response.setHeader("Content-Description", "JSP Generated Data");
%>

 
 

3. 세번째 방법 (한파일에 기본 페이지와 엑셀 다운로드 설정할때)

<%

if(actionKind.equals("Search")){
response.setContentType("text/html; charset=euc-kr");
}else{
response.setHeader("Content-Disposition", "attachment; filename=diary.xls");
response.setHeader("Content-Description", "JSP Generated Data");
response.setContentType("application/vnd.ms-excel");
}
%>

 

4. 네번째 방법 (한글 제목에 브라우저별 처리)

titleName = "한글제목";

titleName = new String(titleName.getBytes("KSC5601"), "8859_1");

if(clientBrowser.indexOf("MSIE 5.5")>-1 || clientBrowser.indexOf("MSIE 6.0") > -1 ){
response.setHeader("Content-Type", "doesn/matter;");
response.setHeader("Content-Disposition", "filename="+titleName+".xls");
}else{
response.setHeader("Content-Type", "application/vnd.ms-excel;charset=EUC-KR");
response.setHeader("Content-Disposition", "attachment; filename="+titleName+".xls");
}

response.setHeader("Content-Transfer-Encoding", "binary;");
response.setHeader("Pragma", "no-cache;");
response.setHeader("Expires", "-1;");

 

 

=========================================================================================================================

=========================================================================================================================

한글깨짐

 

head 에 다음과 같은 처리를 같이 해주면 왠만하면 해결이 된다.

 

<META HTTP-EQUIVE="CONTENT-TYPE" CONTENT="TEXT/HTML; CHARSET=KSC5601">

or
response.reset(); // response 버퍼를 비우고 respose 값을 세로 세팅

=========================================================================================================================

=========================================================================================================================

 

운영서버에서는 한글이 다 깨지는데 개발서버에서는 제대로 나와서 더 해결방법 찾기가 난감했다.

jsp 소스를 운영 거로 업어치고 나서도 여전히 잘만 보이는 개발서버 쪽 엑셀.. 뭐가 문제냐. ㄱ-

DB 상의 데이터까지 운영 쪽으로 맞추니 그제서야 한글이 깨지기 시작했다.

그럼 문제는 데이터라는 소리인데.. 어떻게 봐도 한글이 깨질 건수가 안 보였다.

 

웹 상에 떠도는 한글 깨짐 방지 태그라는

<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">

이 녀석을 써 봐도 여전히 묵묵부답..

 

하지만 해결책은 간단했다.

엑셀 녀석이 데이터를 인코딩 태그로 인식하는 경우가 간혹 생기는데

데이터 타입 앞에 &nbsp;만 입력해주면 끝. 아주 깨끗하게 잘 나온다.

 

<td>&nbsp;<%= crset.getString(1) %></td> ← 이런 식으로

 

 

=========================================================================================================================

=========================================================================================================================

 

 

 

숫자형식 엑셀에서 표현하기

== 사용법 ==

<style type="text/css">

td {mso-number-format:000000;}

</style>

또는

<td align='center' style='mso-number-format:000000'>

 

000000 : 소수도 여섯자리 정수 (반올림)로 표현된다. 여섯자리 앞의 빈칸은 0으로 채워짐

1.23 => 000001, 67.67 => 000068

000.000 : 소수자리 세자리까지 (반올림) 표현된다. 앞 뒤 빈칸은 0으로 채워짐

format은 0.00 인데 숫자가 15.1 인 경우 15.10으로 표현됨

1.5678 => 001.568

\@ : 셀형식을 텍스트형으로 표현

00035.90 인 경우 셀 형식이 숫자형이라면 35.9로 표현되지만 문자형으로 하면 0을 포함하여 보이는 그대로 표현됨

 

 

 

 

그 외 mso-number-format 요소들

mso-number-format:"0"
NO Decimals
mso-number-format:"0\.000"
3 Decimals
mso-number-format:"\#\,\#\#0\.000"
Comma with 3 dec
mso-number-format:"mm\/dd\/yy"
Date7
mso-number-format:"mmmm\ d\,\ yyyy"
Date9
mso-number-format:"m\/d\/yy\ h\:mm\ AM\/PM"
D -T AMPM
mso-number-format:"Short Date"
01/03/1998
mso-number-format:"Medium Date"
01-mar-98
mso-number-format:"d\-mmm\-yyyy"
01-mar-1998
mso-number-format:"Short Time"
5:16
mso-number-format:"Medium Time"
5:16 am
mso-number-format:"Long Time"
5:16:21:00
mso-number-format:"Percent"
Percent - two decimals
mso-number-format:"0%"
Percent - no decimals
mso-number-format:"0\.E+00"
Scientific Notation
mso-number-format:"\@"
Text
mso-number-format:"\#\ ???\/???"
Fractions - up to 3 digits (312/943)
mso-number-format:"\0022£\0022\#\,\#\#0\.00"
£12.76
mso-number-format:"\#\,\#\#0\.00_ \;\[Red\]\-\#\,\#\#0\.00\"
2 decimals, negative numbers in red and signed(1.56 -1.56)

 

 

한 셀 안에서 줄바꿈

<style>

.xl24 {mso-number-format:"\@";}

br {mso-data-placement:same-cell;}

</style>




'JSP' 카테고리의 다른 글

FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
Posted by 사라링

fileDownload.jsp

 | JSP
2012. 5. 8. 18:14

경로  root/common/download.jsp 

받는 파라미터 ./ pds 의 pds_no //파라미터 네임 : file_no

  1. <%@page import="kr.or.ddit.pds.vo.PdsBean"%>
    <%@page import="kr.or.ddit.pds.dao.PdsDaoMyBatis"%>
    <%@page import="kr.or.ddit.pds.dao.IPdsDao"%>
    <%@ page language="java" pageEncoding="utf-8"  %>
    <%@ page trimDirectiveWhitespaces="true" %>
    <%@page import="java.io.FileInputStream"%>
    <%@page import="java.io.BufferedOutputStream"%>
    <%@page import="java.io.BufferedInputStream"%>
    <%@page import="java.io.File"%>
    <%
        String file_no = request.getParameter("file_no");
        // bo_id 검증 필요!!   
        if(file_no == null || file_no.compareTo("") == 0 ){
            // throw new Exception("다운로드 에러~~~~");
            response.setHeader("Content-type", "text/html");
    %>   
    <%@page import="java.sql.SQLException"%>

    <script type="text/javascript">
            <!--
                alert("너 누구야... ");
                history.back();
            //-->
            </script>
            <%
                return;
            } // if(!StringUtils.isNumeric(bo_no))

                IPdsDao pdsDao = new PdsDaoMyBatis();
                PdsBean bean = pdsDao.findByPk(Integer.parseInt(file_no));
               
                if(bean.getPds_realpath() == null || "".equals(bean.getPds_realpath())){
               
                    // throw new Exception("파일이 없습니다.~~~~");
                    response.setHeader("Content-type", "text/html");
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    %>
                    <script type="text/javascript">
                    <!--
                        alert("파일이 존재하지 않는 글입니다.");
                        history.back();
                    //-->
                    </script>
                    <%
                        return;
                }
               
                //dao.downCount(Integer.parseInt(bo_no)); // 숙제 구현        
                // 경로, 실제 저장된 파일을 읽는다
                //String upPath = application.getRealPath("/upload");
                String upPath = "c:\\upload";
               
                String fileName = bean.getPds_userfile(); // 사용자가 올렸던 이름
                File file = new File(bean.getPds_realpath());

                byte b[] = new byte[2048];           

                if (file.length() > 0 && file.isFile()) {
                    String strClient = request.getHeader("User-Agent");
                    if (strClient.indexOf("MSIE 5.5") > -1) {
                        response.setHeader("Content-Disposition", "filename="
                                + (new String(fileName.getBytes("euc-kr"), "iso-8859-1")) + ";");
                    } else {
                        response.setHeader("Content-Disposition", "attachment;filename="
                                + (new String(fileName.getBytes("euc-kr"), "iso-8859-1")) + ";");
                    }
                   
                    response.setHeader("Content-type", "application/octet-stream");
                    response.setHeader("Content-Length", "" + file.length());
                    response.setHeader("Content-Description", "Inna Site");
                    BufferedInputStream fin = new BufferedInputStream(new FileInputStream(file));
                    BufferedOutputStream outs = new BufferedOutputStream(response.getOutputStream());
                    int read = 0;
                    try {
                        while ((read = fin.read(b)) != -1) {
                            outs.write(b, 0, read);
                        }
                        // 성공적으로 내보냈으니까. 다운횟수 증가
                        //pdsDao.incrementDownHit(bean.getPds_no());
                        //pdsDao.incrementDownHit(bean.getPds_downhit());
                        pdsDao.incrementDownHit(bean.getPds_no());
                        //pdsDao.incrementDownHit(bean.getPds_no());
                       
                        outs.close();
                        fin.close();                   
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    } finally {
                        if (outs != null) outs.close();
                        if (fin != null) fin.close();
                    }
                } else {
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    response.setHeader("Content-type", "text/html");
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    %>
                    <script type="text/javascript">
                    <!--
                        alert("[<%=bean.getPds_userfile()%>]파일이 서버에 존재하지 않습니다.");
                        history.back();
                    //-->
                    </script>
                <%
                    return;
                }
    %>

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
Posted by 사라링

FileDownLoad

 | JSP
2012. 5. 8. 18:13

[ 다운로드 ]


많은 분들이 download.jsp등이나 서블릿을 이용해서 download를 구현을 합니다.
그 이유는 download를 통해서 down을 받게함으로서 직접적인 파일의 경로를
감추어 서버의 파일구조를 감추려는 보안적인 이유도 있을 것이고
또 download.jsp안에서 session에 값을 체크하여 인증절차를 거쳐야
자료를 받을 수 있게 하기 위한 것일 수도 있고

request.getHeader("referer")를 가지고 이전 페이지 url을알아내거나
HTTP Headers Information중 Host를 체크하여 무단적인 링크를 방지하기
위한 이유등 많은 이유로 처리를 하고 있을 것입니다.

저는 download를 구현함에 있어 같은 질문이 자주 올라오는 것 같아서
그런 내용들에 대해서 간단한 답변들을 적어보도록 하겠습니다.

1. 다운로드시 익스플로러 5.5 버전에서만 제대로 안되요.

이것 버전에 따라서 Header를 다르게 설정해줘야 합니다.

================================================================
중략...

  1. if( strClient.indexOf("MSIE 5.5") != -1 ) {
        response.setHeader("Content-Type", "doesn/matter;");
        response.setHeader("Content-Disposition", "filename=" + strFileName + ";");
    } else {
        response.setHeader("Content-Type", "application/octet-stream;");
        response.setHeader("Content-Disposition", "attachment;filename="+strFileName + ";");
    };
    response.setHeader("Content-Transfer-Encoding", "binary;");
    response.setHeader("Content-Length", ""+file.length());
    response.setHeader("Pragma", "no-cache;");
    response.setHeader("Expires", "-1;");

중략..
================================================================
위의 소스와 같이 HTTP Response Headers의 User-Agent를 체크해서

버전이 5.5일경우와 그외 버전일 경우 header를 다르게 설정해줘야

합니다.


2. 파일 다운로드우 이상한 문자가 앞에 추가가 됩니다.(서버에 파일은 문제없음)

이것은 jsp일때 앞부분에 page지시자를 기술할때 다른 내용이 들어가서 그문자가

파일에 추가되어 나타나는 현상입니다. 해결방법은
================================================================
<%@ page language="java" import="java.util.*,java.io.*,java.sql.*,java.text.*"%>
<%
   String file_path = "D:/resin/webapps"+request.getParameter("file_path");
중략..
================================================================
과 같이 Enter를 입력해서 벌어진 부분을
================================================================
<%@ page language="java" import="java.util.*"%><%
   String file_path = "D:/resin/webapps"+request.getParameter("file_path");
중략..
================================================================
과 같이 붙여서 만들어주면 해결할 수 있습니다.


3. 엑셀, 워드, 파워포인트 파일의 다운로드

ppt나 xls확장자같은경우 전 아래와같이 해서 다운로드를 받고 있습니다.
어디서 소스를 봤는지 기억은 잘 안나지만..아래는 jsp화일의 일부분입니다.

String type  = request.getParameter("do");
String fileurl  = request.getParameter("fileurl");
String filepath = request.getParameter("filepath");
String filename = fileurl.substring(filepath.length()+1,fileurl.length());
PrintStream printstream = new PrintStream(response.getOutputStream(), true);
try {
         File file = new File(filepath,filename);
         FileInputStream fin = new FileInputStream(file);
         
         int ifilesize = (int)file.length();
         byte b[] = new byte[ifilesize];
         
         response.setContentLength(ifilesize);
         response.setContentType("application/smnet");
         response.setHeader("Content-Disposition","attachment; filename="+filename+";");
         
         ServletOutputStream oout = response.getOutputStream();
         
         fin.read(b);
         oout.write(b,0,ifilesize);
         oout.close();
         fin.close(); 
} catch(Exception e) {  }


4. 초간단 파일다운로드 소스

간단한 파일 다운로드 프로그램임다..
링크는 download.jsp?db=db이름&table=table이름&num=번호 와 같이 하면 됩니다.

-- download.jsp --

<%@ page contentType="text/html; charset=EUC_KR" %>
<%@ page import="java.io.*, IBboard.beans.file.FileDownload" %>
<%
//응답 헤더의 Content-Type을 세팅한다.
response.setContentType("application/x-msdownload");

FileDownload filedown = new FileDownload();

//DB에서 해당 번호에 해당하는 파일의 이름을 얻어온다.
String filename = filedown.NumToFilename(request.getParameter("db"),request.getParameter("table"),Integer.parseInt(request.getParameter("num")));
//Content-Disposition 헤더에 세팅하기위해 file 이름을 코드 변환한다.
//한글파일 Download 시에 에러가 나는게 이 코드가 빠져 있어서 그런거 같은디..^^
String filename2 = new String(filename.getBytes("euc-kr"),"8859_1");
//Content-Disposition 헤더에 파일 이름 세팅.
response.setHeader("Content-Disposition","attachment; filename="+filename2);
//해당 경로의 파일 객체를 만든다.
File file = new File ("/usr/local/apache/htdocs/upload/"+filename);
//파일 스트림을 저장하기 위한 바이트 배열 생성.
byte[] bytestream = new byte[(int)file.length()];
//파일 객체를 스트림으로 불러온다.
FileInputStream filestream = new FileInputStream(file);
//파일 스트림을 바이트 배열에 넣는다.
int i = 0, j = 0;

while((i = filestream.read()) != -1) {

bytestream[j] = (byte)i;

j++;

}

// 응답 스트림 객체를 생성한다.

OutputStream outStream = response.getOutputStream();

// 응답 스트림에 파일 바이트 배열을 쓴다.

outStream.write(bytestream);

outStream.close();

%>

 


<%-- Download.jsp --%>

<%@page contentType="text/html; charset=euc-kr" %>

<%@page import="java.io.*,java.net.*,javax.servlet.*,javax.servlet.http.*"%>
<%
 String realPath = "실제화일명.txt";
 String saveFileName = "저장시사용할화일.txt";

 

/* UTF8로 인코딩된 한글 이름의 파일을 다운로드 할때 경우에 해당한다.

 * 브라우저 옵션 고급에서 "URL을 항상 UTF-8로 보냄"로 체크되어 있을 경우는

 * 화일이름을 UTF8로 다시 인코딩 해 주어야 저장화일 이름이 깨지는 것을 막을 수 있다.

*/

 

saveFileName= URLEncoder.encode(saveFileName,"UTF8");

 

/** 다운로드 구현시 주의 사항

 * 보통 어플리케이션 서버를 root계정으로 돌리는 경우가 많다.

 * 이경우 잘못 하면 해킹의 기회를 제공 하는 경우가 있다.

 * 어떤 사이트를 보면 실제화일명을 파라미터로 받아서 처리 하는 경우 보안상 위험 할 수 있다.

 * 예를 들어 dowload.jsp?file=download/attach/../../../../../../etc/passwd

 * 위와같은 경우로 해커가 파라미터를 조작하면 계정 및 패스워드 화일외에 시스템 정보를

 * 얼마든지 획득 할 수 있다. 편의를 제공하기 우해 만든 다운로드 프로그램이.

 * 판단미스로 본 사이트를 해킹하는 툴로 사용 될 수 있다는 것이다.

 *  여기에서 아래와 같이 문자를 찾아서 해킹시도를 막는다.

  1.  if(realPath.indexOf("..") != -1 || realPath.indexOf("/") != -1) {
      return;
     }
  1.   PrintStream printstream = new PrintStream(response.getOutputStream(), true);
  1.   FileInputStream fin = null;
      ServletOutputStream outs = null;
  1.  try{
  1.  //이부분에서 화일객체 생성시 해킹에 사용 될 수 있다.
  1. // 가급적이면 화일명을 파라미터가 아니 DB에 가져오도록 로직을 구현하는게 좋다.
  1. //웹상에 보여주는 화일명과 실제화일명을 DB에 넣어 숨기는 것이 좋다.
  1. //여기서는 단순 다운로드 구현을 설명 하고자 걍 사용 하기로 한다.
  1.   File file = new File("/home/test/download/" + realpath);
  1.   fin = new FileInputStream(file);
     
      int ifilesize = (int)file.length();
      byte b[] = new byte[ifilesize];
  1.   String strClient = request.getHeader("User-Agent");
  1.   response.setContentLength(ifilesize);
      response.reset() ;
      response.setContentType("application/octet-stream");
     
      if(strClient.indexOf("MSIE 5.5") > -1) {
           response.setHeader("Content-Disposition", "filename=" + new String(saveFileName.getBytes(),"ISO-8859-1") + ";");
      } else {
           response.setHeader("Content-Disposition", "attachment;
  1.               filename=" +  new String(saveFileName.getBytes("EUC-KR"),"ISO-8859-1") + ";");
      }
      response.setHeader("Content-Length", ""+ifilesize );
      outs = response.getOutputStream();
      response.setHeader("Content-Transfer-Encoding", "binary;");
      response.setHeader("Pragma", "no-cache;");
      response.setHeader("Expires", "-1;");
  1.   if (ifilesize > 0 && files.isFile()) {
         int read = 0;
  1.      while((read = fin.read(b)) != -1) {
             outs.write(b,0,read);
         }
      }
     } catch (FileNotFoundException fnfe) {
         System.out.println(fnfe.toString());
     } catch (Exception e) {
         System.out.println(e.toString());
     } finally{
        try {
            if(outs != null) outs.close();
            if(fin != null) fin.close();
        } catch (Exception e) {}
     }
  1. %>

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
ss  (0) 2012.05.08
Posted by 사라링

cubrid-dbcp설정.

 | JSP
2012. 5. 8. 16:35

 

DBCP 설정 방법

소개:아파치 DBCP(DataBase Connection Pool)을 통한 Statement 풀링(pooling) 사용 방법을 설명하고자 한다.

DBCP에서 Statement 풀링이 커넥션 별로 유지되고 있기 때문에, 응용서버(cas)에서는 질의를 컴파일 한 정보를 계속 유지하게 된다. 이 후 같은 질의에 대해서는 prepare 과정 없이 execute 단계만 반복함으로써 성능 향상을 가져올 수 있다.

이 문서에서 DBCP 설정 방법을 tomcat 사용 시와 일반 자바클래스 사용 시 두 가지 방법을 설명한다.

 

2008-10-15 기술컨설팅팀 손승일,CUBRID

적용 대상: CUBRID2008, JDK1.6.0_04 이상

DBCP Statement 풀링

DBCP Statement 풀링 기능을 사용하기 위해 브로커 파라미터에 STATEMENT_POOLING ON/OFF가 있다.

Statement 풀링이 사용되기 위해서는 DBCP 설정에서 poolPreparedStatementstrue로 설정되어야 하고, 브로커 설정에서 STATEMENT_POOLINGON으로 설정되어야 한다.

Statement 풀링을 사용할 경우는 DBCP와 브로커 둘 다 풀링을 사용하도록 설정되어 있어야 하고, 만약 어느 한 쪽만 설정한 경우는 오히려 성능이 떨어질 수 있다.

브로커 설정

CUBRID가 설치되어 있는 경로의 ~/CUBRID/conf/cubrid_broker.conf를 열어 연결에 사용될 브로커 하단에 STATEMENT_POOLING ON을 추가하고 브로커를 재 시작 하면 설정이 적용된다.

[%BROKER1]

SERVICE=ON

BROKER_PORT=33000

SESSION_TIMEOUT=300

KEEP_CONNECTION= AUTO

STATEMENT_POOLING=ON

DBCP 다운로드

DBCP를 사용하기 위해서는

commons-dbcp-1.2.2.jarhttp://commons.apache.org/downloads/download_dbcp.cgi

commons-pool-1.3.jar http://commons.apache.org/downloads/download_pool.cgi

commons-collections-3.2.1.jar http://commons.apache.org/downloads/download_collections.cgi

에서 각각의 최신 릴리스 바이너리 파일을 다운 받아 압축을 풀면 위 파일들을 얻을 수 있다.

CUBRID JDBC Driver cubrid_jdbc.jar 파일은 CUBRID가 설치되어 있는 경로인 ~/CUBRID/jdbc/에서 얻을 수 있다.

Tomcat에서

위 세 파일과 CUBRID JDBC Driver를 CALTALIA_HOMEcommonlib 아래에두고, CALTALIA_HOMEconfserver.xml을 아래와 같이 추가 설정한다.

server.xml

server.xml의 끝 부분에 존재하는 “</Host>” 위에 아래를 추가한다.

<Context docBase="C:DBTest" path="" reloadable="true">//1


<Resource name="jdbc/CUBRIDDS" auth="Container" type="javax.sql.DataSource"// 2

factory="org.apache.commons.dbcp.BasicDataSourceFactory"

driverClassName="cubrid.jdbc.driver.CUBRIDDriver"// 3

url="jdbc:cubrid:localhost:33000:demodb:::"// 4

username="public" password=""// 5

initialSize="10"

maxActive="10"

maxIdle="10"

maxWait="-1"

poolPreparedStatements="true"/>// 6

</Context>

·1: 루트 디렉토리를 설정하는 부분으로 예제에서는 C:DBTest (Unix/Linus /DBTest) 디렉토리를 생성하고 이곳을 루트 디렉토리로 설정한다. 이곳에 jsp 등 관련 파일이 위치하면 된다.

·2: JNDI name으로 임의로 사용 할 이름을 입력 한다.

·3: JDBC 드라이버 클래스 이름으로 cubrid.jdbc.driver.CUBRIDDriver를 입력하면 된다.

·4: 접속 할 데이터베이스 url로 jdbc:cubrid:IP:PORT:DB이름::: 형식으로 입력한다.

IP : 데이터베이스가 있는 서버 IP

PORT : 큐브리드 브로커의 포트(default : 33000)

DB이름 : 데이터베이스 이름

·5: 데이터베이스에 접속할 아이디.기본적으로 데이터베이스에는 DBAPUBLIC 2개의 아이디가 존재한다.ID 파라미터에 아무 것도 입력하지 않고 “”를 입력하면 기본적으로 PUBLIC 아이디로 데이터베이스에 접속한다. 앞에서 사용한 아이디에 암호가 설정되어 있다면 PASSWORD 파라미터를 입력한다.없다면 그냥 “”를 입력한다.

·6: Statement 풀링이 사용되기 위한 설정으로 true로 설정해야 한다.

간단예제

아래 소스를 CALTALIA_HOMEconfserver.xml의 <Context docBase="C:DBTest" path="" reloadable="true"> 부분에 설정한 루트 디렉토리에 작성한다.

<%@ page contentType="text/html; charset=euc-kr"%>

<%@ page import="java.util.*" %>

<%@ page import="java.sql.*" %>

<%@ page import="javax.sql.*" %>

<%@ page import="javax.naming.*" %><%//1%>

<%

Connection conn = null;

PreparedStatement pstmt = null;

ResultSet rs = null;

try {

// 큐브리드 DBConnect하기 위해서 JNDI를 통해 DataSourse를 가져온다.

Context initContext = new InitialContext();//2

DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/CUBRIDDS");//3

conn = ds.getConnection();//4

String sql = "select name from nation";

pstmt = conn.prepareStatement(sql);

rs = pstmt.executeQuery();

while(rs.next()) {

out.println("name ==> " + rs.getString(1));

out.println("<br>");

}

} catch ( SQLException e ) {

System.err.println(e.getMessage());

} catch ( Exception e ) {

System.err.println(e.getMessage());

} finally {

try{

if ( rs != null ) rs.close();

}catch( Exception e ) {}

try{

if ( pstmt != null ) pstmt.close();

}catch( Exception e ) {}

try{

if ( conn != null ) conn.close();

}catch( Exception e ) {}

}

%>

·1: Connection Pool을 이용한 DB Connection은 JNDI를 통해 DataSourse를 가져오고 DataSourse를 통해 DBConnection하게 되는데, 이 때 ContexDataSource를 사용하기 위해서 javax.naming.*javax.sql.*import 해준다.

·2: JNDI name으로 임의로 사용 할 이름을 입력 한다.

·3: JNDI를 통해 DataSource를 가져오는데 DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/CUBRIDDS"); 와 같이 server.xmlResource name에 설정된 값 앞에 java:comp/env/를 붙여서 사용한다.

·4: DataSource를 통해 DB에 Connection한다.

위 예제 파일명이 dbcp_test.jsp로 가정하고 예제 실행을 위해 웹페이지를 http://localhost:8080/dbcp_test.jsp또는 http://<설치한서버의주소>:8080/dbcp_test.jsp 로 접속하면 결과를 볼 수 있다.

Java class에서

DBCP 사용을 위한 세 파일과 CUBRID JDBC Driver를 classpath에 설정한다.

ConnectionManager.java

DBCP 를 생성하고 Connection을 제공하는 역할을 한다.

import java.sql.*;

import org.apache.commons.dbcp.BasicDataSource;// 1

public class ConnectionManager {

private BasicDataSource ds = null;

// DBCP를 생성한다.

private void setupDataSource() {

if (ds == null) {

ds = new BasicDataSource();

String url = "jdbc:cubrid:localhost:33000:demodb:::";

String className = "cubrid.jdbc.driver.CUBRIDDriver";

String userName = "public";

String passWord = "";

ds.setDriverClassName(className);// 2

ds.setUrl(url);// 3

ds.setUsername(userName);// 4

ds.setPassword(passWord);

ds.setMaxActive(10);

ds.setInitialSize(10);

ds.setMinIdle(5);

ds.setMaxWait(5000);

ds.setPoolPreparedStatements(true);// 5

}

}

// BasicDataSource로부터 connection을 얻어온다.

public Connection getConnection() throws SQLException {

setupDataSource();

return ds.getConnection();// 6

}

}

·1: BasicDataSource을 사용하기 위해 org.apache.commons.dbcp.BasicDataSource을 import 시켜준다.

·2: JDBC 드라이버 클래스 이름으로 cubrid.jdbc.driver.CUBRIDDriver를 입력하면 된다.

·3: 접속 할 데이터베이스 url로 jdbc:cubrid:IP:PORT:DB이름::: 형식으로 입력한다.

IP : 데이터베이스가 있는 서버 IP

PORT : 큐브리드 브로커의 포트(default : 33000)

DB이름 : 데이터베이스 이름

·4: 데이터베이스에 접속할 아이디.기본적으로 데이터베이스에는 DBAPUBLIC 2개의 아이디가 존재한다.ID 파라미터에 아무 것도 입력하지 않고 “”를 입력하면 기본적으로 PUBLIC 아이디로 데이터베이스에 접속한다. 앞에서 사용한 아이디에 암호가 설정되어 있다면 PASSWORD 파라미터를 입력한다.없다면 그냥 “”를 입력한다.

·5: Statement 풀링이 사용되기 위한 설정으로 true로 설정해야 한다.

·6: BasicDataSource로 부터 connection을 얻어온다.

execDBCP.java

ConnectionManager를 이용하여 connection을 얻어오고 질의를 수행한다.

import java.sql.*;

public class execDBCP{

public static void main(String args[]){

Connection conn = null;

PreparedStatement pstmt=null;

ResultSet rs =null;

String query = "select name from nation";

try {

ConnectionManager cm = new ConnectionManager();

conn = cm.getConnection();//1

pstmt = conn.prepareStatement(query);

rs = pstmt.executeQuery();

while(rs.next()){

System.out.println("result=>"+rs.getString(1));

}

rs.close();

pstmt.close();

} catch (Exception e) {

e.printStackTrace();

}finally {

if(rs != null) try { rs.close(); } catch(Exception e) {}

if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}

if(conn != null) try { conn.close(); } catch(Exception e) {}

}

}

}

·1: ConnectionManager을 생성하고 getConnection을 호출하여 DBCP로 부터 connection을 얻어온다.

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
ss  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

ERROR&cokie

 | JSP
2012. 5. 8. 15:55

 error 에러 메세지 페이지로 넘김

web.xml 파일에 추가 하자.

   <error-page>
        <error-code>404</error-code>
        <location>/error/error404.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error/error500.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.NumberFormatException</exception-type>
        <location>/error/errorNumber.jsp</location>
    </error-page> 

 

404 에러 500 에러가 가장 많이 검출 되는 에러 이며. 코더 후에 사용자(클라이언트)에 의해 발생 하는 에러를 따로 만든 에러.jsp 에 뛰어 볼수 있도록 설정 가능 하다.

 

cookie


    Cookie cookie = new Cookie("LOVE","gubook");
    response.addCookie(cookie);
   
    cookie =new Cookie("HATE",URLEncoder.encode("황지혜 "));
    cookie.setPath("/");
    response.addCookie(cookie);
%>
<a href="view.jsp">쿠키 보기</a>
<a href="../062/view.jsp">쿠키 보기</a>

를 페이지 하면 쿠기에 내용이 저장 된다.  LOVE 라는 name 값에value 로 gubook 이 들어 갔다.

이렇게 실행된 값은 response.addCookie(변수 )로 서버쪽으로 보내 진다.

view.jsp 에서 링크를 클릭 하여 보자.

 

 

view.jsp 의 소스는

<%
    Cookie[] cookies = request.getCookies();
    if(cookies!=null){
%>
<ul>
    <%
    for(int i =0;i<cookies.length;i++){
    %>
    <li>
        <%=URLDecoder.decode(cookies[i].getValue(),"utf-8")%>
    </li>
<%} %>
</ul>

<%}else { %>
    쿠키가 없네 ? = ㅠ=
<%} %>

를 포함하고 있다. 같고 올때는 request를 쓴다. 가져온 값은 배열 형태로 가져 온다. 

 

 

 


이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ss  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

ss

 | JSP
2012. 5. 8. 15:41

sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

테스트

 | JSP
2012. 5. 8. 12:51

테스트

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
ss  (0) 2012.05.08
Posted by 사라링

BLOG main image
.. by 사라링

카테고리

사라링님의 노트 (301)
JSP (31)
J-Query (41)
JAVA (24)
VM-WARE (0)
디자인패턴 (1)
스크랩 (0)
스트러츠 (3)
안드로이드 (11)
오라클 (45)
우분투-오라클 (1)
이클립스메뉴얼 (6)
스프링3.0 (23)
자바스크립트 (10)
HTML5.0 (17)
정보처리기사 (1)
기타(컴퓨터 관련) (1)
문제점 해결 (3)
프로젝트 (2)
AJAX (4)
하이버네이트 (3)
트러스트폼 (11)
Jeus (2)
재무관리(회계) (5)
정규식 (5)
아이바티스 (8)
취미 (2)
소프트웨어 보안 관련모음 (0)
정보보안기사 (6)
C언어 베이직 및 프로그램 (3)
보안 관련 용어 정리 (2)
넥사크로 (6)
Total :
Today : Yesterday :