'52.Apache Project &...'에 해당되는 글 118건

  1. 2009.04.21 Struts action-mappings, global-forwards 의 차이점
  2. 2009.04.21 기반기술공부 - Struts1 예제 Login
  3. 2009.04.21 기술기반공부 - Struts1
  4. 2009.04.21 기반기술재공부 - MVC 패턴
  5. 2009.04.21 스트럿츠2 package(모듈)
  6. 2009.04.21 스트럿츠(struts) 내부구조
  7. 2009.04.21 기술기반공부 - Struts1
  8. 2009.04.20 struts와 ibatis 를 이용한 게시판 생성
  9. 2009.04.20 1일차 mysql 노 설치버전
  10. 2009.04.07 Log4sql 설치와 쿼리의 실제 내용을 log로 볼수 있는 방법
  11. 2009.04.07 System.out.println()은 잊어라 log4sql이 온다.
  12. 2009.03.04 Commons-beanutils version 1.8.0 - How to Download and Install on Mac OS X
  13. 2009.03.04 The BeanUtils Component
  14. 2009.03.04 아파치 common 오픈 소스 digester를 소개합니다.
  15. 2009.03.04 jakarta common util중 Beanutils사용법
  16. 2009.03.04 Commons-Digester
  17. 2009.03.04 Commons-DbUtils 2
  18. 2009.03.04 DbUtils 몇가지 예제
  19. 2009.03.04 jakarta Project BeanUtils 소개
  20. 2009.03.04 Commons BeanUtil
  21. 2009.03.04 BeanUtils
  22. 2009.03.04 ApacheCommon 의 BeanUtils 를 이용하는 초간단 예제..
  23. 2009.03.04 간단한 BeanUtils 사용하기
  24. 2008.12.15 Apache AXIS를 이용한 웹서비스 강좌
  25. 2008.12.04 LOG4J
  26. 2008.09.18 Digester를 이용한 Naver Open API Java Client 모듈
  27. 2008.08.19 아파치 로그분석툴 awstats 설치/활용 가이드 1
  28. 2008.08.19 DBUtils에서 Clob 사용하기
  29. 2008.08.19 POI의 HSLF를 이용하여 PowerPoint 문서를 읽어보자
  30. 2008.08.19 POI의 HWPF를 이용하여 MS WORD문서를 읽어보자
반응형

action-mappings

<action-mappings>설정

  • 컨트롤러가 요청을 받았을 때 어떤 Action 인스턴스를 실행할 것인가에 대한 설정 정보
  • <action-mappings>하위에 <action>을 이용해서 여러 개의 action 설정 가능
  • <action> : 특정 request URI와 대응하는 Action 매핑 정의

<action>의 주요 attribute

  • path : 확장자를 제외한 "/"로 시작하는 경로명
  • type : action클래스의 이름
  • scope : form bean이 저장되어 있는 context의 scope
  • name : action과 연결된 form bean의 name
  • role : Action 객체에 접근할 수 있는 권한을 설정
  • input : form bean에서 validation error가 발생한 경우 되돌아 가거나 상황을 표시할 수 있는 경로

다음은 <action>의 attribute들이다.
Name Description
attribute Form bean에 접근하기 위한, request-scope 또는 session-scope attribute의 name 값이다. 사용할 form bean을 다른 attribute의 이름으로 사용하고자 할 때 사용한다. Form bean이 name attribute에 기술되어 있을 때에만 기술될 수 있다.
className Action들의 configuration 정보를 담고 있을 객체이다. 반드시 org.apache.struts.config.ActionMapping 또는 이를 상속 받은 클래스여야 한다.
디폴트 값 : org.apache.struts.config.ActionMapping
forward 요청된 request를 Action 클래스 대신하여 수행할 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 나타낸다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
include 요청된 request를 Action 클래스 대신하여 수행할 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 나타낸다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
input Form bean에서 validation error가 발생했을 때, 이를 나타낼 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 가리킨다. Form bean이 name attribute에 기술되어 있을 때에만 기술될 수 있다. [required: form bean이 name attribute에 기술되어 있고 validation error들을 리턴할 경우]
name 이 action 매핑 사용하는 form bean의 이름을 나타낸다.
path Submit된 request의 상대(module-relative)경로를 나타낸다. 이 attribute는 반드시 "/"으로 시작해야 하고, filename의 확장자 없이 기술되어야 한다. 예를 들어, "/main.do"은 적절한 path attribute의 기술 방법이 아니다. 왜냐하면 이미 do라는 확장자가 action 매핑에 사용되고 있는 것을 알고 있기 때문에, "/main"이라고만 기술하는 것이 옳다. [required]
parameter Action 객체에 특별한 어떤 값을 넘겨주기 위한 설정 parameter이다. 현 Action 클래스에서는 이 attribute를 이용하지 않고 있기 때문에, 값을 넣는다 해도 처리되지 않는다. 만약 이 attribute를 사용하고자 하면, Action 클래스의 서브클래스를 만들어 구현해야 한다.
prefix Request parameter name을 form bean property name에 매치시키는 데 사용되는 prefix를 나타낸다. Form bean이 name attribute에 기술되어 있을 때에만 설정할 수 있다.
roles Action 객체에 접근할 수 있는 권한을 설정한다. 여러 role 이름들은 콤마(,)로 구분하여 쓸 수 있다. 예를 들어, "admin, master, user"라고 써주면 admin, master, user의 세 가지 권한 중 어느 한가지 권한이라도 가진 사용자는 이 action을 사용할 수 있게 된다.
scope 이 action이 사용하는 form bean이 저장되어 있는 context의 scope를 나타낸다. request 또는 session.
디폴트 값 : session
suffix Request parameter name을 form bean property name에 매치시키는 데 사용되는 suffix를 나타낸다. Form bean이 name attribute에 기술되어 있을 때에만 설정할 수 있다.
type 요청된 request를 수행할 Action 클래스를 나타낸다. 이 클래스는 org.apache.struts.action.Action의 서브클래스여야 한다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
unknown 설정 파일에 정의되지 않은 request를 처리하는 default action 매핑인지 여부를 나타낸다. 요청된 request를 수행할 action 매핑 객체가 없을 경우에, unknown이 true로 설정된 action 매핑 객체에게 이 request를 넘겨 처리하게 한다. 각각의 module마다 unknown이 true인 action 매핑은 하나만 있을 수 있다.
디폴트 값 : false
validate Form bean에서 validation을 수행할지 여부를 나타낸다. 이 값이 true이면, form bean의 validate 메소드가 실행된다.
디폴트 값 : true
cancellable Struts 1.3에 추가된 attribute로 Struts 1.2.9에서 <set-property>로 설정했던 것이 1.3부터 바뀌었다. Cancel Process를 사용하기 위해서 설정해야 한다.

Samples

다음은 struts-config-login.xml 파일에서 action-mappings 설정에 대한 예제이다.
<action-mappings>
<action
	path="/login"
	type="anyframe.sample.struts.web.action.LoginAction"
	name="userForm"
	scope="request"
	input="/basic/login.jsp">
	<exception key="error.password.mismatch" path="/basic/login.jsp" 
	type="javax.security.auth.login.FailedLoginException" />
	<forward name="success" path="/basic/main.jsp" />
</action>
	...
</action-mappings>
'/login.do' 의 request에 대해 LoginAction 이 처리하도록 매핑되어 있으며, 이 때 action에 연결된 form bean은 UserForm 이다. request scope 동안 form bean 이 유지되며 forward 경로는 Action클래스에서 "success"라는 이름으로 forward name을 세팅 했기 때문에 /basic/main.jsp로 forwarding한다. exception 발생 시 /basic/login.jsp로 돌려진다.

<action>의 작성은 개발자가 반드시 숙지해야할 부분으로, request의 처리를 담당하는 Action을 매핑하고 페이지 네비게이션을 제어하는 등 웹 어플리케이션 개발의 중요한 작업이다.

global-forwards

<global-forwards> 설정

  • 실제 forward 또는 redirect 할 수 있는 URI를 논리적인 이름으로 맵핑
  • <global-forwards>하위에 <forward>를 이용해서 여러 개의 URI 매핑 설정
  • 하나의 <forward>는 하나의 논리적인 이름을 module-relative 또는 context-relative URI 경로로 매핑함 (URI 경로를 직접 사용하는 것 보다 logic 내부적으로 정해진 이름을 사용함으로써, view로부터 controller와 model을 분리)
  • 모든 action에서 사용할 수 있는 global level의 forward를 정의
forward 의 우선순위
<forward>은 전역(global) level과 action level 에서 정의될 수 있는데 action level 에서 선언된 것이 더 우선순위가 높다.
다음은 <forward>의 attribute들이다.
Name Description
className Forward들의 configuration 정보를 담고 있을 객체이다. org.apache.struts.config.ActionForward 또는 이를 상속 받은 클래스여야 한다.
디폴트 값 : org.apache.struts.config.ActionForward
name 현재 forward의 이름이고, 다른 forward들과 구분될 수 있는 identifier이다. [required]
path Forward 또는 redirect할 resource(*.do, *.jsp 등)의 상대(module-relative or context-relative)경로를 나타낸다. [required]
redirect RequestProcessor가 이 forward에 대해 redirect할 필요가 있을 때, true로 설정한다.
디폴트 값 : false

Samples

다음은 struts-config.xml 파일에서 global-forwards 설정에 대한 예제이다.
<global-forwards>
        <forward name="login" path="/login.jsp"/>
        <forward name="main" path="/main.do" redirect="true"/>
</global-forwards>
Posted by 1010
반응형
Struts를 이용한 간단한 로그인 예제

이번엔 웹 개발의 전형적인 예로써 사용자의 ID와 Password를 입력 받아 이를 인증 후 결과 페이지로 Forward 시키는 것을 Struts Framework을 이용하여 만들어 보겠습니다.

구성요소들은  다음과 같습니다.

1.        로그인  HTML 페이지
2.        Controller&Model 역할을 하는 LoginAction
3.        ActionForm, 폼빈 역할을 하는 LoginForm
4.        로그인이 성공 이었을 때 분기하는 success.html
5.        로그인이 실패했을 경우 분기하는 fail.html

사용자는 HTML 웹페이지를 이용하여 ID/Password를 입력 후 로그인 버튼을 누릅니다. 이 요청을 Controller 와 Model의 역할을 동시에 수행 하는(이 예제에서…)LoginAction이 이를 받아 폼빈(LoginForm, 사용자가 HTML 폼에서 입력한 정보들이 들어 있다.)을 통해 사용자가 입력 한 정보를 추출 후 인증을 수행 합니다.

폼빈(LoginForm)이 존재 하므로 LoginAction에서는 요청 파라미터를 추출 하는 일은 없습니다. 최근에 발표된 RC2 버전인 경우 매번 폼빈을 생성 할 필요 없이 동적으로 파라미터를 추출 할 수 있도록 했는데 이를 동적 폼빈 이라고 하며 DynaActionForm 클래스를 이용 합니다.

주)이 예제를 이클립스에서 실행 하기 위해서 이전 강좌(Eclipse에서 Struts(HelloWorld))와 같이 struts-test 라는 Tomcat 프로젝트에서 작성을 합니다. 그리고 프로젝트에서 마우스 우측 버튼을 누른 후 속성 - Java Build Path - library - add jars를 하신 후 WEB-INF/lib 아래의 struts를 다운 받아서 푼 jar 파일들을 선택 해 주신 후 아래의 예제를 돌리셔야 합니다.

1.        로그인을 위한 login.jsp를 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)

<%@ page language="java" contentType="text/html;charset=euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:form action="/login">
        ID : < html:text property="id"/><br>
        Password : <html:password property="pwd"/><br>
        <html:submit value="로긴"/>
</html:form>

2.        로그인이 성공 했을 때 분기할 페이지인 success.html을 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)
3.        

<html>
        <head><title>로그인 성공 화면</title></head>
        <body>
                로그인이 성공 되었습니다...
        </body>        
</html>

4.        로그인이 실패 했을 경우 분기 할 페이지인 fail.html을 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)

<html>
        <head><title>로그인 실패 화면</title></head>
        <body>
                로그인 실패!!
        </body>        
</html>

5.        사용자가 요청 시 넘기는 값들을 빈으로 저장 해 놓기 위해 Request Parameter와 같은 구조를 가지는 폼빈을 만듭니다. LoginForm.java (이클립스인 경우 WEB-INF/src에서 마우스 우측 버튼을 클릭 후 new, class 하신 후 만드세요)

이 클래스는 Form빈으로서 사용자가 입력 한 값을 적절한 변수(프로퍼티)로 설정 해 주는 역할을 합니다. getXXX, setXXX 메소드등을 이용하여 적절한 변수 값으로 설정 되는 것입니다.


package login;
import org.apache.struts.action.*;

public class LoginForm extends ActionForm {
        protected String id;
        protected String pwd;
       
        public String getId() {
                return id;
        }
       
        public String getPwd() {
                return pwd;
        }
       
        public void setId(String id) {
                this.id = id;
        }
       
        public void setPwd(String pwd) {
                this.pwd = pwd;
        }       
}

6.        이번에는 MVC 모델의 컨트롤러 및 모델의 역할을 동시에 수행하는 LoginAction.java 파일을 만듭니다. 아래 파일에서 AuthUser라는 모듈을 자바빈으로 빼 낸다면 이것이 모델에 해당 하게 될겁니다. 본예제에서는 편의상 컨트롤러와 모델의 기능을 통합 시켰습니다.

이 클래스는 사용자가 입력 한 파라미터(id, pwd)를 LoginForm(폼빈)으로부터 받아 이
값을 이용하여 인증을 한 후 적절한 View를 선택 하여 포워딩을 하는 것입니다.

예제의 간결함을 위해 본 예제에서는 AuthUser 메소드는 무조건 인증이 성공됨을 알
리는 true를 리턴 합니다. 이 부분을 Oracle과 같은 DB와 JDBC등을 이용한 적절한
인증을 구현 하시는 것은 여러분들의 몫으로 넘기겠습니다.


package login;

import org.apache.struts.action.*;
import javax.servlet.http.*;
import java.io.*;

public class LoginAction extends Action {
        public ActionForward execute(ActionMapping mapping, ActionForm form,
                                              HttpServletRequest req, HttpServletResponse res) {
                String id = ((LoginForm)form).getId();
                String pwd = ((LoginForm)form).getPwd();
               
                boolean isOK = authUser(id, pwd);
               
                if (isOK) {
                        return (mapping.findForward("success"));
                }
                else {
                        return (mapping.findForward("fail"));
                }               
        }
       
        public boolean authUser(String id, String pwd) {
                //이곳에서 디비에 접속해서 ID와 Password가 맞는지 확인 한다.
                //본 예제에서는 무조건 인증이 된것으로 true를 리턴 합니다
                return true;
        }
}


7.        이번에는 web.xml을 만듭니다. (WEB-INF에서 마우스 우측 버튼을 누른 후 new, file 하신 후 만드세요)

web.xml 파일에서 추가 한 내용은 URL창에 확장자가 .do인 요청이 들어 오면 ora.apache.struts.action.ActionServlet으로 요청을 보내도록 설정을 했습니다. 그런다음 스트럿츠의 설정 파일인 struts-config.xml의 위치를 알려주고 있습니다.

<?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/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
        <!-- Struts Tag Library Descriptors -->
        <taglib>
            <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
            <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
        </taglib>
        <taglib>
            <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
            <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
        </taglib>
        <taglib>
            <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
            <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
        </taglib>    
       
        <!-- ActionServlet Congif =====================================-->
        <servlet>
                <servlet-name>action</servlet-name>
                <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
                <init-param>
                        <param-name>config</param-name>
                        <param-value>/WEB-INF/struts-config.xml</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>                
        </servlet>
       
       
        <!-- ActionServlet Mapping ====================================-->
        <servlet-mapping>
                <servlet-name>action</servlet-name>
                <url-pattern>*.do</url-pattern>
        </servlet-mapping>


</web-app>


8.        이번에는 struts 설정을 위한 struts-config.xml을 만듭니다. . (WEB-INF에서 마우스 우측 버튼을 누른 후 new, file 하신 후 만드세요)

이 파일에서는 프로그램에서 사용 할 폼빈을 LoginForm으로 설정 하고 프로그램에서 사용 할 포워딩 이름을 설정 하는데(물론 이 부분은 action 설정에서 할 수도 있지만 전역적으로 설정 하기 위해 global-forwards 로 설정을 했습니다.

그리고 실제 요청을 처리 할 action 부분을 설정 하는데 예제 에서는 URL에 “/login”이 포함되어 있는 경우에 처리하라고 지정 했는데 LoginAction이 그 일을 하는거죠… 또한 폼빈으로 LoginForm을 설정 했습니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
     
    <!-- ========== Form Bean Definitions ================================== -->
    <form-beans>
        <form-bean name="LoginForm" type="login.LoginForm">          
        </form-bean>        
    </form-beans>
   
  
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards>
        <forward name="success" path="/success.html" />
        <forward name="fail" path="/fail.html" />
    </global-forwards>
   
    <!-- ========== Action Mapping Definitions =============================== -->
    <action-mappings>
        <action          
                path="/login"
                type="login.LoginAction"
            name="LoginForm"                        
            validate="false"
        >
        </action>        
    </action-mappings>  
</struts-config>

위에서 주석 만드실 때 주의하세요… <!—다음에 줄 두개짜립니다. “====” 만약 다음과 같이 하시면 Tomcat 시작시 오류 뜹니다.<!--------------  

그리고 이클립스에서 만드시면 별도로 LoginForm.java나 LoginAction.java를 별도로 컴파일 하실 필요는 없습니다. 브라우저에서 다음과 같이 입력 하신면 로그인 화면이 나타나며 적절한 ID/PWD를 입력하시면 인증이 성공 했다는 success.html이 실행 될겁니다.

실행 후 화면을 보시면 주소창에 /login.do 라고 나타나 있을 것입니다. 이 같이 함으로써 사용자의 입장에서는 마지막 결과가 어느 페이지에서 나오는지를 알 수가 없습니다.(멋지죠^^) 당연히 직접 그 페이지로 접근 한다는 것은 불가능 합니다.

그러므로 success.jsp와 같은 결과 페이지를 만드는 사람과 Action등을 만드는 사람의 업무가 분리가 가능 하다는 이야기 입니다. 결국 이러한 장점이 MVC 모델의 장점이 되는거구요…

다음 강좌부터는 이 로그인 예제를 하나씩 기능을 추가해 가도록 하죠^^

먼저 HTML을 이용해서  Form양식을 만들어야만 한다.

Form 양식은 id와 pwd를 입력받는 일반 Web상의 login소스를 생각하면 된다.


<%@ page language="java" pageEncoding="EUC-KR"%>

<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html" %>

<html:html>

<html:form action="/login">

<table width="200" height="138" border="0" cellpadding="0" cellspacing="0">
  <tr>
    <td height="8" colspan="3"><img src="images/login_title.gif" width="200" height="40"></td>
  </tr>
  <tr>
    <td width="79" align="left" class="style36 style16"><img src="images/id.gif" width="70" height="20"></td>
    <td width="89" height="20">
   
     <html:text property="uid" value="" size="12" style="width:75px;height:17px; border: #4882B2 1px solid"/>
      
    </td>
    <td width="38" rowspan="2">
    <html:img src="images/icon/btn_left_login.gif" width="35" height="46"
     onclick="loginCheck()" onmouseover="this.style.cursor='hand'"/>

    </td>
  </tr>
  <tr>
    <td height="20" align="left" class="style37"><img src="images/pwd.gif" width="70" height="20"></td>
    <td>
      <p>
     
      <html:password property="password" value="" size="12" style="width:75px;height:17px; border: #4882B2 1px solid"/>
     
    </p></td>
  </tr>
  <tr align="left" valign="middle">
    <td height="16" colspan="3">&nbsp;&nbsp;<a href="#" class="style16 style25" onMouseOver="MM_swapImage('Image17','','images/icon_left02.gif',1)" onMouseOut="MM_swapImgRestore()"><strong><img src="images/icon/icon1.gif" width="11" height="11">&nbsp;
    <html:link action="registerAgree">
     회원가입
    </html:link>
    </strong></a></td>
  </tr>
  <tr align="left">
    <td height="8" colspan="3">&nbsp;&nbsp;<a href="#" class="style26" onMouseOver="MM_swapImage('Image18','','images/icon_left02.gif',1)" onMouseOut="MM_swapImgRestore()"><strong><img src="images/icon/icon1.gif" width="11" height="11">&nbsp;
    아이디/ 비밀번호 찾기
    </strong></a></td>
  </tr>
 
</table>

</html:form>


</html:html>



위와 같은 양식을 이용해서 Struts-config.xml파일에선 2개의 property를 생성후

parameter를 받을수 있도록 설정을 해준다.


<form-bean name="loginForm" type="com.chestnut.logins.LoginForm">
           <form-property name="uid"  type="java.lang.String" />
            <form-property name="password"  type="java.lang.String" />
</form-bean>


위의 설정이 끝났으면 Action을 설정해준다.

<action
         attribute="loginForm"
         path="/login"
         name="loginForm"
            input="/sources/user/guest/Pindex.jsp"
            type="com.chestnut.logins.LoginAction"
            validate="false"
            scope="request">
            <forward name="success" path="/home.do"/>
            <forward name="fail" path="/sources/user/guest/PregisterAgree.jsp"/>
        </action>


로그인이 완료 됬을경우 home으로 실패 할경우 회원 가입 페이지로 forward를 시켜주는 부분이다.


Form을 통한 Action처리이기 때문에 크게 3개의 클래스가 필요하다.

Form클래스 Action처리 클래스 회원 정보를 저장해서 가지고 있을 DTO명칭 대신 단순하게UserInfo라는 클래스.


Class들을 만들자.

먼저 Login Form처리 클래스이다.

/*
 * Created on 2005. 7. 23
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chestnut.logins;

/**
 * @author 한진석
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class LoginForm extends ActionForm {
 
 private String password;
 private String uid;
 
  public void reset( ActionMapping mapping, HttpServletRequest request ) {
   this.password= "";
    this.uid= "";
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

public String getUid(){
  try{
   return new String( this.uid.getBytes("ISO8859_1"), "EUC_KR" );
  }catch( Exception e){
   return new String("");
  }
 }
 public String getPassword(){
  try{
   return new String( this.password.getBytes("ISO8859_1"), "EUC_KR" );
  }catch( Exception e){
   return new String("");
  }
 }
}

getter와 setter 함수 작성시 잘 모르는 사람들은 고생을 많이 한다.

Getter와 setter작성시 항상 주의를 해야만 한다. Form에서 넘겨주는 name값이 반드시 getter와

setter에 들어가야만 한다.

위에서 보면 uid와 password로 넘겨 주고 ActionForm을 상속받은 클래스에선 getPassword(...

getUid라는 이름으로 작성을 하였다.

틀릴경우 Log파일을 보면 Error내용이 나온다. getter 또는 setter를 찾을수 없다는 내용의 ..


마지막으로 Action클래스를 작성.

/*
 * Created on 2005. 7. 23
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chestnut.logins;

import java.sql.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.chestnut.logins.LoginForm;

/**
 * @author 한진석
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class LoginAction extends Action {
 
   public ActionForward execute(
     ActionMapping mapping,
     ActionForm form,
     HttpServletRequest request,
     HttpServletResponse response)
     throws Exception {

    String forwards = "";

    String uid         = "";
    String pwd       = "";
    
    LoginForm loginform = (LoginForm)form;
    
    uid  = loginform.getUid().trim();
    pwd  = loginform.getPassword().trim();
  
    javax.sql.DataSource src = getDataSource( request, "ora");
    Connection dbcon = null;
    PreparedStatement pstmt = null;
    
    ResultSet rs = null;
    ResultSet rs1 = null;
    
    UserInfo ui = null;
    
    try{
     dbcon = src.getConnection();
     String query ="SELECT * FROM USERS WHERE " +
     " USERID='"  + uid +"'"+
   " AND PWD='" + pwd +"'";
     pstmt = dbcon.prepareStatement(query);
     rs = pstmt.executeQuery();    
     String userId ="";
    
       if( rs.next() ){
        userId = rs.getString("USERID");
        HttpSession session = request.getSession();
       
        ui =
         new UserInfo(
           userId,
       rs.getString("NAME"),
       0,
       rs.getInt("USERTYPE"),
       rs.getString("UPYUN"),
       rs.getString("ADDRESS"),
       rs.getString("PHONE"),
       rs.getString("CPHONE")
     );
                       
        String query2 ="UPDATE USERS SET LASTLOGIN=SYSDATE" +
          " WHERE USERID='"+userId+"'";

        pstmt.executeUpdate( query2 );
               
        String query3 = "select * from mileage " +
          " where userid='"+userId+"'";
              
        pstmt = dbcon.prepareStatement(query3);
        rs1 = pstmt.executeQuery();
       
        if(rs1.next()){
         ui.setMileage(rs1.getInt("MILEAGE"));
        }
        rs1.close();
       
        dbcon.commit();
       
        session.setAttribute("user", ui );
        forwards = "success";
       
       }else{
        request.setAttribute("notuser",new String("1"));
        forwards= "fail";
       }
       
  }catch( Exception e){

   System.out.println("(SQL Error( Login ERROR ): "+ e );
   
  }finally{

   if(pstmt != null){pstmt.close();}
   if(dbcon != null){ dbcon.close();}
   if(rs != null){rs.close();}


    }
 
    return mapping.findForward( forwards );
   }

 }

Posted by 1010
반응형

Struts
웹 애플리케이션을 구현할 때 사용하는 오픈 소스 프레임웍인 Struts는 대중적인 디자인 패러다임에 근거하고 있다. 이 프레임웍은 Java Servlets, JavaBeans, ResourceBundles, XML 같은 표준 기술을 바탕으로 구현되어 유연하고 확장성 있는 컴포넌트를 제공한다. Struts는 Controller 레이어를 ActionServlet 의 형태로 구현하고 JSP 태그 라이브러리를 사용하여 View 레이어를 구현할 것을 권장한다. 또한, Struts는 Action 클래스를 통해 Model 레이어 주변에 래퍼를 제공한다. 그림 1은 Model-View-Controller 디자인에 기반한 Struts 프레임웍이다.

그림 1. Struts와 MVC
Struts and MVC

Struts 컴포넌트 개관
우선 Struts 컴포넌트를 설명하고 웹 애플리케이션 개발시 역할을 설명하겠다.


Action

애플리케이션의 모든 Action 은 Struts의 org.apache.struts.action.Action를 를 확장한다. 이 Action 클래스는 애플리케이션의 Model 레이어로의 인터페이스를 제공하면서 비즈니스 로직 주변의 래퍼로서 작동한다. 각 Action 클래스는 각자의 케이스 스팩(case-specific) 구현을 perform() 메소드에 제공해야 한다. perform() 메소드는 언제나 ActionForward의 값을 리턴한다.


ActionForm

애플리케이션의 모든 ActionForm은 Struts의 org.apache.struts.action.ActionForm을 확장한다. ActionForms 은 요청 매개변수들을 캡슐화 및 유효화하는 단순한 JavaBean이다. 요청 데이터를 유효화하기 위해 ActionFormvalidate() 메소드는 케이스 스팩의 구현을 제공해야 한다. ActionForm은 요청 데이터의 캐리어로서 Action 클래스에 작용한다. JSP 객체는 각각의 ActionForm 을 합하여 애플리케이션의 View 레이어를 만든다. JSP 객체의 거의 모든 폼(form) 필드는 이에 상응하는 ActionForm의 애트리뷰트로 매핑한다.

JSP 커스텀 태그 라이브러리
JSP 커스텀 태그 라이브러리는 태그로 표현되는 액션들의 모음이다. 이것은 JSP Specification 1.1의 강력한 기능이다. 프리젠테이션 티어를 다른 애플리케이션 티어들과 분리할 수 있다. 이 라이브러리는 사용이 쉽고 XML과 같은 방식으로 읽을 수 있다. 자바 스크립틀릿의 사용을 최소화 함으로서 JSP 컴포넌트를 쉽게 관리할 수 있다. Struts가 제공하는 JSP 태그에는 HTML, 로직, 빈 태그들이 포함된다.


ActionErrors

예외 핸들링을 지원할 때 ActionError를 사용한다. ActionError는 애플리케이션 예외를 잡아 View 레이어로 보낸다. 각각은 ActionError의 모음이다. ActionError는 에러 메시지들을 캡슐화 하면서 Presentation 레이어의 </html:errors>ActionError 컬렉션의 모든 에러 메시지들로 렌더링한다.


Best Practice 1. 다중 ActionForm을 통한 데이터 재사용

Struts 컴포넌트에 익숙해졌으니 이제 프레임웍을 활용하는 방법을 설명하겠다. 우선 Struts는 모든 JSP 객체를 ActionForm과 연합할 것을 권장한다. 스크린에 나타난 데이터를 캡슐화 하는 것이다.ActionForm에서 찾은 부속 메소드를 사용하여 JSP 객체에 있는 폼(form) 데이터에 액세스한다. Listing 1은 View 레이어에서 ActionForm 의 일반적이 사용법이다.

Listing 1. JSP에서 ActionForm 사용하기
   <html:form action="/bp1">   <html:text  property="attrib1" />   </html:form >

"BP1AForm" 이라고하는ActionForm에는 attrib1 애트리뷰트를 비롯하여 게터(getter)와 세터(setter) 메소드도 포함되어 있다. 설정 파일인 struts-config.xml에서 "/bp1" 액션은 name 애트리뷰트를 사용하여 bp1AForm 으로 매핑된다. 이것은 JSP에 데이터 디스플레이를 돕는다.

Best Practice 1을 구현할 때 Struts는 다음의 두 가지를 권장한다:

  1. BP1AForm의 애트리뷰트 서브셋을 형성하고있는 애트리뷰트로 JavaBean (BP1BForm) 을 만들고 아울러 이 애트리뷰트의 게터와 세터 메소드도 만든다.
  2. 빈과 BP1AForm을 결합하여 BP1AForm 의 애트리뷰트를 BP1BForm 빈으로 대체한다. 이제 BP1BForm을 통해 BP1AForm 의 애트리뷰트 서브셋에 액세스 할 수 있다. (Listing 2)
Listing 2. JSP의 폼(form) 애트리뷰트에 액세스하기
   <html:form action="/bp1">   
<bean:define name="bp1AForm" property="bp1BForm" id="bp1B"
type="com.ibm.dw.webarch.struts.BP1BForm" />
<html:text name="bp1B" property="subsetAtt1" />
</html:form >

기억하세요!
이 Best Practice 1의 주요 장점은 애트리뷰트 세트에 액세스하기 위해 다중 ActionForm이 필요할 때 이를 사용할 수 있다는 것이다. 다음은 기억해야 할 점이다:

  • Struts는<bean:define/> 태그를 구현한다.
  • <%@ taglib uri="struts-bean.tld" prefix="bean" %> 코드가 struts-bean.tld를 가르킬때 <bean:define/> 태그는 JSP 컴포넌트에서 작동을 시작한다.
  • ActionForm을 확장하는 BP1AForm의 밸리데이션 프레임웍은 BP1BForm의 데이터를 반드시 유효화해야 한다.

애플리케이션에서 Action 클래스를 만들 때, org.apache.struts.action.Action을 직접 확장하는 대신org.apache.struts.action.Action을 확장하여 Action 클래스 (IntermediateAction)를 만든다. 다른 모든 Action 클래스들은 이 IntermediateAction 클래스를 확장한다.

Best Practice 2. Action 클래스를 사용하여 요청 핸들하기
일반적으로 Struts 프레임웍을 사용할 때 JSP 컴포넌트가 애플리케이션을 실행하기위해 요청하는 모든 액션의 경우, 애플리케이션은 Struts의 org.apache.struts.action.Action 을 확장하여 Action 클래스를 만들어야 한다. 이 개별적인 Action 클래스는 요청을 처리하면서 애플리케이션의 Model 레이어와 인터페이싱한다.

Best Practice 2를 구현할 때 Struts는 다음과 같은 절차를 권장한다:

  1. org.apache.struts.action.Action을 확장하여 BP2Action이라고 하는Action 클래스를 만든다.
  2. BP2Action 을 확장하여 웹 애플리케이션에서 다른 모든 Action 클래스들을 만든다.
  3. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException에서 그랬던 것처럼, BP2Action에서 performTask()메소드를 만든다.
  4. BP2Action에서 한 개 이상의 일반적인 메소드를 애플리케이션에 추가한다. 예를 들면 serverSideValidate()과 같은 것이다. 다음 요소들을 고려하여 메소드의 액세스 변경자를 결정할 수 있다:
    • 모든 Action 클래스들이 이 메소드를 구현한다면 이를 추상화하라.
    • 어떤 Action 클래스가 케이스 스팩 구현을 제공한다면 메소드 보호를 선언하고 여기에 디폴트 구현을 제공한다.
  5. BP2Action에서 마지막으로 perform() 메소드를 선언한다. 위에 언급된 일반 메소드를 선언하라. 이는 요청 프로세스 전에 항상 호출되어야 한다. 이제 3단계에서 만들어진 performTask() 메소드를 호출한다.
  6. BP2Action을 확장하는 모든 Action 클래스에서 performTask() 와 케이스 스팩 구현을 추가하라.

장점
Best Practice 2는 두 가지 주요 장점이 있다. 첫째, 웹 애플리케이션의 모든 Action 클래스에서 과잉의 코드를 피할 수 있다. 둘째, 하나의 Action 클래스에 작동을 중앙화 함으로서 애플리케이션의 일반적인 태스크에 대한 제어력을 높인다.

Best Practice 3. ActionForm을 세션 데이터에 작동시키기
Struts 기반의 웹 애플리케이션에서 각ActionFormorg.apache.struts.action.ActionForm을 확장한다. 이 ActionForm은 페이지 데이터를 캡슐화하고 밸리데이션 프레임웍을 제공하여 요청 매개변수의 타당성 검사를 한다.

대부분의 웹 애플리케이션들은 세션에서 데이터를 관리하여 애플리케이션을 통해 사용할 수 있도록 한다. Best Practice 3은 웹 애플리케이션 기능을 다룬다. toSession()fromSession() 메소드가 세션 데이터를 폼(form) 데이터 간 이동할 수 있도록 한다.

  1. org.apache.struts.action.ActionForm을 확장하여 추상 클래스 BP3Form을 만든다.
  2. BP3Form에서 퍼블릭 앱스트랙트void toSession(SessionData sessionData)void fromSession(SessionData sessionData)처럼 메소드와 액세스 변경자를 추가한다.
  3. 모든 ActionForm에서BP3Form을 확장하고 추상 메소드를 구현한다. 이 곳에서 폼 데이터가 세션으로(부터) 옮겨진다.
  4. 상응하는 Action 클래스는 이들 메소드가 호출되는 순서를 결정할 수도 있다. 예를 들어. actionForward가 결정되기 전에 ActionForm에서 toSession() 메소드를 호출할 수 있다.

Best Practice 3을 사용할 때는
Best Practice 3은 세션 데이터들이 하나의 객체로서 관리될 때 가장 유용하다. 또는 모든 페이지가 세션 데이터를 조작하거나 사용할 때도 유용하다.

Best Practice 4.효과적인 예외 핸들링
전통적으로 애플리케이션 예외는 Action 클래스에서 발생한다. 이 예외는 첫 번째로 기록된다. 그런 다음 Action 클래스는 ActionError를 만들고 이를 애플리케이션 범위 안에 저장한다. 이 클래스는 콘트롤을 적절한 ActionForward에 전달한다. Listing 3은 Action 클래스가 예외를 핸들하는 방법을 보여준다.

Listing 3. Action 클래스에서의 예외 핸들링
   try {   //Code in Action class   }   
catch (ApplicationException e) {
//log exception
ActionErrors actionErrors = new ActionErrors();
ActionError actionError = new ActionError(e.getErrorCode());
actionErrors.add(ActionErrors.GLOBAL_ERROR, actionError);
saveErrors(request, actionErrors); }

일반적인 예외 핸들링 절차가 모든 Action 클래스에 예외 정보를 저장하는 반면, Best Practice 4의 목적은 예외 핸들링을 하면서 과잉의 코드를 막는 것이다:

  1. org.apache.struts.action.Action을 확장하여 BP4Action라는 Action 클래스를 만든다.
  2. BP4Action을 확장하여 웹 애플리케이션에 다른 모든 Action 클래스들을 만든다.
  3. BP4Action에서, ActionErrors actionErrors = new ActionErrors(); 변수를 선언한다.
  4. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, ActionErrors actionErrors) throws IOException, ServletException처럼 BP4ActionperformTask() 메소드를 만든다.
  5. BP4Action에서 마지막으로 perform() 메소드를 선언한다. 그런 다음 일반적인 메소드를 호출한다. 이들은 항상 요청 프로세스 전에 호출되어야 한다. 이제 이전 단계에서 만들어진 performTask() 메소드를 호출할 수 있다.
  6. BP4Action을 확장하여 모든 Action 클래스에서 performTask()메소드를 구현하면서 애플리케이션 예외를 처리한다. (Listing 4)
Listing 4. ActionErrors를 효과적으로 사용하기
   try		 {   //Code in Action class   }   
catch(ApplicationException appException) {
//Log exception
//Add error to actionErrors
actionErrors.add(ActionErrors.GLOBAL_ERROR,
new ActionError(appException.getErrorCode())); }

BP4Action에서 performTask() 메소드를 호출한 후에 saveErrors(request, errors)를 사용하여 ActionErrors를 저장한다.

장점
Best Practice 4의 큰 장점은 ActionErrors를 핸들하는 Action 클래스에서 코드 과잉을 막는다.

Posted by 1010
반응형
MVC 패턴은 웹뿐 아니라 어플리케이션 개발에서도 굉장히 많이 사용하는 디자인 패턴이다.
예를들어 MFC가 그렇다.

1.1.1 Classic MVC becomes outdated

  • MVC 3개의 레이어로 구성되어 레이어별 역할이 뚜렷하기 때문에 협업이 용이하고, 디자인과 코딩의 분리가 자연스럽다.
  • JSP 페이지에 비즈니스 로직이 제외되므로 가독성이 증가하고 유지보수가 용이하다.
  • M(Model) : BO (Business Object), DO(Domain Object), VO (Value Object) ...
  • V(View) : HTML(정적인 컨텐츠) , JSP(동적인 컨텐츠)..
  • C(Controller) : (ex) Servlet

첫번째는 MVC의 가장 기본적인 패턴이다

View에 있는 Buttons을 클릭하면, Controller는 View로부터 이벤트를 받아 Model에서 로직을 수행하고
View는 Model로부터 전달된 model-change 이벤트를 받아 데이터를 갱신한다.

예를 들어 회원이 로그인을 한다를 세 파트로 나눠서 보면,
View    : 웹페이지에 떠있는 ID와 Password입력용 텍스트박스와 Log-in 버튼 정도가 되겠습니다.
Model  : ID와 Password를 입력하면 이 값을 자바에서 사용가능한 자료형으로 변환을 해서 보관을 합니다.
             이 역활을 담당해주는게 Model입니다.
Control : 실질적인 역활 수행을 하게 됩니다.
             입력된 값을 받아서 DB의 내용을 조회해보게 되겠죠.

자 다시 얘기하자면, 웹페이지에 텍스트 박스와 로그인버튼이 있는 화면을 제공하는 부분과 Log-in버튼을 눌렀을때 웹페이지의 텍스트박스안에 입력한 값을 넘겨 받는 부분 그리고 이 값을 가지고 로그인 과정을 수행한뒤 결과를 반환하는 과정. 이렇게 3부분으로 나눈게 바로 MVC패턴이 되겠습니다.

하지만 오랜 경험상 다들 아시겠지만, 변하지 않는 패턴은 죽은 패턴입니다.
이 MVC패턴도 변화하게 되는데, 다음은 그 2번째 모형입니다.



새로 추가된게 보이시나요?
Dispatcher가 추가된게 새 MVC 패턴입니다.

1.1.2 Classic MVC gets an update: the Front Controller

  • 웹 버전의 MVC는 Front Controller 패턴을 구현한다.
  • 웹 버전의 MVC에서 View는 Controller를 직접 호출하지 않고, web request기반의 URL 매핑을 이용하여 호출한다.
  • request URL을 수행될 command instance로 보내기 위해 dispatcher를 사용한다.
  • 모든 request는 dispatcher를 통해 Controller에 전달된다.
  • Model에서 View에 직접 model-change 를 notify할 수 없고, Controller를 통해서 View에 오직 최종 요청의 결과를 보내준다.


    기본적인 MVC패턴은 하나의 로직은 하나의 모델과 하나의 컨트롤을 가졌습니다.
    MVC 패턴의 목적은 M-V-C를 나눔으로서 웹디자이너와 프로그래머 사이의 분업을 보다 쉽게 하기 위함이었습니다.
    마찬가지로 어플리케이션 개발에서도 MVC패턴을 사용합니다. GUI 따로 프로그래밍 따로죠.

    여기서 문제점이 발생하게 되는데, 기본MVC모델은 프로그래머가 뭔가 바꿧을 경우...
    예를 들자면, 메소드의 이름을 하나 바꿨을 경우라던가 파일명을 바꿨다라던가 했을 때
    Control을 직접 view에서 호출하기 때문에 디자이너와의 썸씽이 불가피하게 됩니다.

    그래서 Dispatcher가 추가된 새 모델이 나오게 됩니다.
    이 Dispatcher는 말 그대로 분류해주는 역활을 하게 됩니다. WINAPI를 해보셨다면 이해가 쉬우실겁니다.

    비교해서 말하자면 기존에는 view에서 bean을 부르고 id와 password에 매치시켜준뒤 해당 메소드를 호출함으로서 결과를 얻게 되고 이 결과에 따라 웹페이지를 사용자에게 보여주면서 하나의 로직이 끝나게 됩니다.
    새 모델에서는 view에서는 일체의 bean과 연동되는 작업이 없습니다. 단지 이 Dispatcher에게 post든 get이든 값을 넘겨줄때 파라미터를 하나 더 추가해줍니다.

    "LOGIN"이라는 파라미터지요.
    네 이제 감이 잡히시나요? Dispatcher에선 이 파라미터를 분석해서 어떤 서비스를 호출할 것인가를 결정하는 겁니다.

    다시 짧게 정리하자면
    기존 모델 : view에서 직접 로직을 호출. (예) LOGIN에 관련된 메소드를 호출한다.
    새    모델 : view에서 약속된 파라미터를 추가한 값을 Dispatcher에게 넘겨주면 이 Dispatcher에서 로직을 호출.
                    (예) view에서 "LOGIN"이라는 파라미터가 추가된 값을 post or get으로 넘겨준다. Dispatcher에서
                          LOGIN이라는 값을 가지고 LOGIN에 관련된 메소드를 호출한다.

    이리하여 진정한! MVC패턴이 나타나기 시작합니다.
    프로그래머는 웹마스터에게 단지 로긴할땐 Login이란 값을 파라미터로 넘겨라 라고 주문만 하고 로긴에 관련된 메소드는
    boolean 웹마스터귀는당나귀귀(String id, String pass){...} 라고 만들어도 아무런 문제가 없게 된겁니다.



    다음은 마지막 MVC패턴 차례가 되겠습니다.

    1.1.3 MVC evolves: the Page Controller

    • 수행 될 controller를 dispatcher를 이용하여 찾지 않고 view를 직접 호출하면, view가 렌더링되기 전에 controller를 직접 호출한다.
    • Page Controller는 X,Y,Z 작업을 분리하여 좀 더 모듈화가 된 것으로 보인다.
    • WebWork에서는<webwork:action> 커스텀 태그를 이용해 Page Controller를 지원한다.
    • Struts에 익숙하다면 Front Controller 패턴이 매우 익숙하지만, 몇 몇 프레임워크는 캡슐화가 용이하기 때문에 Page Controller를 수용한다.


    예, 이번엔 Dispatcher가 사라졌군요? 그리고 조금 복잡해 보입니다.
    하지만 모든 일이 그렇듯 알고 보면 별게 아니...ㄹ겁니다.

    사실 Dispatcher가 사라진거 같이 보이지만, 그건 눈속임 입니다.
    View.jsp를 감싸고 있는 Page Controller라는게 추가가 됩니다.
    이녀석은 로그인과 로그아웃이라는 작업 형태를 Action으로 분리해서 가지고 있습니다.
    사용자가 로그인을 선택하면 로그인Action을 로그아웃을 선택하면 로그아웃Action을 호출합니다.

    어떤가요? 하는 일이 Dispatcher와 똑같죠.


    네...그럼 차이점을 한번 살펴보겠습니다.
    Front controller 1.1.2 방식에서는 파라미터를 가지고 요청의 성질을 결정했습니다.
    이런 접근 방식은 웹디자이너와 프로그래머의 일을 확실히 나누기 위함 입니다.

    Page Controller에서는 커스텀 테그를 이용하여 이 과정을 거치게 됩니다.
    즉, 태그를 사용함으로서 직관적으로 서비스와의 연동을 이룰 수 있으며 약속된 태그를 사용함으로서 분업을 이룰 수 있게 해줍니다.

    이 패턴은 한번도 적용해 본 적이 없어서 확실히 말씀드리기 어렵습니다만... 추측하기에 Struts의 그것과 비슷하지 않을까 싶습니다.

    MVC패턴은 웹에서는 굉장히 요긴한 패턴입니다. ㅇ_ㅇ/
  • Posted by 1010
    반응형
    struts2  package란 !

    Struts1의 모듈(Module)의 개념
    클라이언트 요청 처리에 필요한 것을 논리적으로 그룹핑하는데 사용한다.
    result-typles
    interceptors
    default-interceptor-ref
    default-action-ref
    global-results
    global-exception-mappings
    action

    패키지는 다른 패키지를 상속한다.
    중복된 설정을 줄여준다.
    모든 패키지는 디폴트 설정 파일인 struts-default.xml에 선언된
     struts-default 패키지를 상속해야 한다.


    1.package 선언

     <package name=“패키지명“ namespace=“/패키지명" extends="struts-default">
       </package>
    name (필수 속성)
    패키지를 구분하는 이름
    namespace
    Action의 URL 경로로 사용한다.
    http://~/contextName/패키지명/~.action
    WebContent/패키지명 폴더가 존재해야 한다.
    extends
    상속할 패키지 이름을 지정한다.
    기본적으로 struts-default 패키지를 지정해야 한다.

    2.default 패키지

    namespace  속성이 생략되거나, namespace 속성 값이 “”로 지정된 패키지를 말한다.
    Struts2는 액션을 특정 패키지에서 찾을 수 없을 경우 최종적으로 디폴트 패키지에서 액션을 찾는다.
    -root 패키지나 user패키지의 action이 없을 경우에 default 패키지로 찾아가서
    결과를 찾는다 . 오류 검사시에 반듯이 필요하게 될 것이다

    <!-- 디폴트 패키지 선언 -->
    <package name="default" namespace="" extends="struts-default">
    </package>

    3.root 패키지


    루트 패키지는 namespace 속성 값이 “/”로 지정된 패키지를 말한다.
    요청 URL이 컨텍스트 패스 바로 뒤에 액션명.action 형태로 들어 왔을 때, 사용되는 패키지이다.


    4.외부 xml 패키지 삽입
    형태 및 위치
    struts.xml 파일과 동일한 DTD 사용
    클래스 패스의 어느 곳에나 존재 가능
    목적
    기능별로 패키지화해서 독립적으로 개발하고자 할 경우 (Struts1에서의 모듈 개념)
    <include file="struts-root.xml"/>
    Posted by 1010
    반응형

    스트럿츠의 역할

    - 모델과 뷰 사이의 공백을 연결해주는 역할을 담당

    - 개발자들이 데이터베이스나 웹 페이지와 같은 가공되지 않은 재료로부터 각 요소들이 서로 긴밀히 연결된 하나의 애플리케이션을 만드는 데 도움을 주는 보이지 않는 토대들의 집합

    - 개발자가 애플리케이션과 사용자 사이의 상호 작용 방식을 구체적으로 정의하기 위한 프로그램 가능한 컴포넌트들의 집합

    *하이퍼링크: 애플리케이션에 있는 특정한 리소스(웹 페이지, 커스텀 액션)로 연결하는 일종의 경로

    스트럿츠에서는 ActionForward를 사용하여 하이퍼링크를 정의

     

     

    각 컴포넌트의 역할

     

    ActionForward

    논리적인 이름과 path프로퍼티를 가진다.

    - XMl 컨피규레이션 파일로 정의

    - 스트럿츠가 웹 애플리케이션을 로딩할 때 읽어들인다.

    - 해당 링크를 참조하는 모든 컴포넌트들을 변경하지 않고도 링크에 대한 목적지를 쉽게 변경할 수 있다.

    ActionForward 자바빈 생성 예

    <forward name =welcomepath=/pages/index.jsp”/>

     

    ActionForm

    검증과 수정 사이클을 관리하도록 몇 가지 표준 메소드들이 추가된 자바빈즈

    - 프로퍼티들을 HTML 컨트롤의 속성과 일치시키는 작업은 스트럿츠에서 자동으로 처리된다.

    - 스트럿츠 컨피규레이션은 일련의 디스크립터(<form-beans>와 <form-bean>요소)를 통해 생성된 ActionForm 클래스를 참조한다.

    - 스트럿츠 1.1에서는 각각의 프로퍼티들을 직접 정의하는 대신 java.util.Map을 사용하여 어트리뷰트 이름으로 저장할 수 있다. (DynaBean)

    ActionForm 자바빈 생성 예

    package app;

    import org.apache.struts.action.*;

    public class loginform extends actionform{

    private string username = null;

    public string getusername(){

    return (this.username);

    }

    public void setusername(string username){

    this.username = username;

    }

    <form-beans>

    <form-bean name=LoginFormtype=app.LoginForm”/>

    </form-beans>

     

    Action(커스텀 액션)

    - HTML 폼은 action 파라미터를 이용하여 폼의 데이터를 보낼 위치를 브라우저에게 알려준다.

    - 프레임워크가 알아서 ActionForm을 생성하고, 적절한 값을 설정하고, 검사한 다음 ActionForm을 Action 객체에게 전달한다.

    - Action은 ActionForward 객체를 컨트롤러에게 전달한다.

    - Action은 시스템 경로가 아닌 논리적인 이름을 사용하여 정의를 선택할 수 있게 된다.

    - 컨트롤러는 확장성을 보장하기 위해서 현재 요청과 응답 객체도 함께 전달한다.



    스트럿츠 핵심 클래스 정의

     

    • ActionServlet : MVC 컨트롤러이며 요청 디스패터의 역할을 한다. 스트럿츠 프레임워크 내에 단 하나의 서블릿 인스턴스가 존재한다.
    • ActionMapping : URL 패턴과 비즈니스 로직(Action)간의 매핑을 표현한다. 입력과 출력 빛 비즈니스 로직에 기반한 '보내지는'대상을 지정한다.
    • ActionForm : MVC모델이다. MVC뷰로부터의 입력을 표현하는 자바빈처럼 작동한다. ActionServlet은 자동적으로 빈이 인스턴스화된 이후에 프로퍼티를 지정해 주며, 빈에 validate()메소드를 가지고 있으면 사용자가 만든 Action클래스를 호출하기 이전에 이 메소드를 호출한다. ActionForm은 JSP 뷰를 표현하기 위해 확장되었다.
    • ActionForward :이동할 페이지 정보와 포워드(forward), 리다이렉트(redirect)중 어느 방식으로 다음 페이지로 이동할 것인지에 대한 정보를 담고 있다.
    • Action : 비즈니스 로직을 나타내며 MVC모델이다. 특정한 요청에 대한 비즈니스 로직을 처리하기 위해 확장된다. 예를 들어 /login URI를 위해 LoginAction을 만든다.

     ActionServlet
    처음으로 논의할 스트럿츠 프레임워크의 컴포넌트는 ActionServlet이다. org.apache.struts.action 패키지에 있는
    ActionServlet은 추상클래스인 javax.servlet.HttpServlet 추상클래스를 상속받았다.
     클라이언트로 부터 호출 받으면 가장 먼저 호출되는 클래스이다.

    • process() : doGet() 이나 doPost()로부터 요청과 응답을 넘겨받는다. process()메소드는 요청을 처리하기 위해, ActionServlet안에 있는 다른 메소드를 호출한다. process()메소드는 요청을 처리하기 위해,
      ActionServlet안에 있는 다른 메소드를 호출한다. 여기에서는 중요한 9개의 메소드를 살펴볼 것이다.

          1.pocessMultipart() : request content 타입 multipart data를 확인하여 새로운
            request Wrapper만듬

          2.processPath() : 요청 URI 인자에서 접미사나 접두사를 제거하여 URI를 추출한다.
            예를 들어,/login.do라면 /login을 반환한다.

          3.processLocale() : Locale이 요청에 설정되어 있으면 검사하고,
             없으면 하나 만들어서 세션에 저장한다.

          4.processMapping() : ActionMapping객체를 얻기 위해 path를 키로 이용한다.
            만약 path가 맞는 ActionMapping객체가 ActionMappings collection안에 있으면
            process()메소드로 반환된다.
          5.processContent() : 요청 콘텐트 차입과 인코딩 결정 디폴트 (text/html)
          6.ProcessPreprocess() : 서블릿 필터와 같은 역할 요청이 처리되기정에 할작업처리해줌
          7.processRoles() : 사용자가 요청한 request에서 처리할수 있는 role 있는지 판별
          8. processActionForm() : ActionMapping의 name속성을 이용하여 이런 종류의
             ActionForm이 이미 만들어졌는지를 확인한다. 그렇다면,
             그 ActionForm이 process()메소드로 반환되며, 그렇지 않으면 ActionMapping에
             정의된 새로운 ActionForm을 만들어서 반환한다.

          9.processPopulate() : ActionForm내에 ActionServlet의 참조를 설정하며,
            ActionForm()의 reset()메소드를 호출하여 값들을 디폴트 값으로 만든다.

          10.processValidate() : ActionForm에 확인작업이 필요한지를 검사한다.
          11.processForward() : action 태그에서 forward 속성이 설정되어 있다면
              RequestDispatcher forward()메소드 호출
          12.processInclude() : action 태그에 include속성이 설정되어있다면        
              RequestDispatcher include()메소드 호출
          13.processActionCreate() : Action 객체를 생성한다 Action 객체가
              이미 존재한다면 재사용 하게 된다.

          14.processActionPerform() : processActionCreate()에서 반환된 Action객체의
              perform()메소드가 호출된다. 비즈니스 로직을 수행한 후에, Action객체는
              ActionServlet이 요청을 보낼 타겟을 가지고 있는 ActionMapping을 반환한다.

          15.processActionForward() : RequestDispatcher나 Request.sendRedirect()를
              이용하여 원하는 뷰를 만들고,요청을 ActionServlet에 의해 완전히 처리한다.

    • initApplication() : 애플리케이션을 위해 MessageResource를 로딩한다.
    • initMapping() : ActionServlet에 pageckage.ClassforName을 등록하도록 한다.
      formBean의 디폴트 클래스는 org.apache.struts.action.ActionFormBean이다.
    • initDigester() : org.apache.struts.digester.Digester클래스를 써어
      struts-config.xml 파일로부터 설정을 읽어들인다.
    • initOter() : 초기화에 필요한 작업을 처리한다. struts-config.xml에 있는
      initOther()는 content , locale , cache를 찾는다.


    ActionMapping
    비즈니스 로직을 처리할 Action클래스에 요청을 보내기 위해 ActionServlet이 알아야 할 처리 정보를 가지고 있다.
    ActionMapping은 <action-mappings> 요소 내의 <action>정보를 이용하여 만들어진다.
    <action-mappings>

       <action name="loginForm" path="/login" type="kr.co.a.LoginAction" input="/login.jsp"> </action>

    </action-mappings>
    /login의 경로를 가진 요청을 받으면, 액션 클래스는 LoginAction이고, 입력은 loginForm이
    표현하며 입력 폼의 경로는 /login.jsp라는 것을 의미한다.

    • name : 이 액션이 사용할 struts-config.xml에 정의된 폼 빈의 이름
    • path : 이 매핑을 선택하는 데 매칭되는 요청 URI. URI는 '/'로 시작해야 한다.
    • type : 이 매핑에서 사용되는 Action 구현의 완전한 자바 클래스 이름
    • input : 입력 페이지를 매핑한다. ActionServlet이 ActionForm의 확인작업을 수행하는데,
      만일 에러가 있다면 input변수가 forward나 sendRedirect의 목표가 될 것이다.


    ActionForm
    ActionForm클래스는 struts-config.xml에 의해 정의된다.

    <form-beans> <form-bean name="loginForm" type="kr.co.a.LoginForm" /> </form-beans> 


    모든 ActionForm 구현은 <form-beans>태그의 몸체에 등록되어야 한다. ActionForm이 추상 클래스이므로,
     개발자는 이를 확장하여 사용자의 입력 데이터를 저장하는 클래스를 만들어야 한다.

    • name : ActionMapping에서 정의된 범위 안에서 이를 얻기 위해 사용된느 변수명
    • type : 사용자가 구현한 ActionForm에 대한 패키지의 올바른 참조


    Action
    어떤 URI에 특정 액션을 제공하기 위해 확장된다. 이것의 목적은 요청을 위한 비즈니스 로직을 실행하는 것이다.
    Action클래스는 ActionServlet에 의해 인스턴스로 만들어진다. Action클래스는 기본 Locale을 저장하며,
    애플리케이션에 대한 MessageResources의 참조를 저장한다.


    ActionForward
    ActionForward클래스는 struts-config.xml 파일에서 설정된다. 두 종류의 ActionForward가 있다.

     

     

    Posted by 1010
    반응형

    Struts
    웹 애플리케이션을 구현할 때 사용하는 오픈 소스 프레임웍인 Struts는 대중적인 디자인 패러다임에 근거하고 있다. 이 프레임웍은 Java Servlets, JavaBeans, ResourceBundles, XML 같은 표준 기술을 바탕으로 구현되어 유연하고 확장성 있는 컴포넌트를 제공한다. Struts는 Controller 레이어를 ActionServlet 의 형태로 구현하고 JSP 태그 라이브러리를 사용하여 View 레이어를 구현할 것을 권장한다. 또한, Struts는 Action 클래스를 통해 Model 레이어 주변에 래퍼를 제공한다. 그림 1은 Model-View-Controller 디자인에 기반한 Struts 프레임웍이다.

    그림 1. Struts와 MVC
    Struts and MVC

    Struts 컴포넌트 개관
    우선 Struts 컴포넌트를 설명하고 웹 애플리케이션 개발시 역할을 설명하겠다.


    Action

    애플리케이션의 모든 Action 은 Struts의 org.apache.struts.action.Action를 를 확장한다. 이 Action 클래스는 애플리케이션의 Model 레이어로의 인터페이스를 제공하면서 비즈니스 로직 주변의 래퍼로서 작동한다. 각 Action 클래스는 각자의 케이스 스팩(case-specific) 구현을 perform() 메소드에 제공해야 한다. perform() 메소드는 언제나 ActionForward의 값을 리턴한다.


    ActionForm

    애플리케이션의 모든 ActionForm은 Struts의 org.apache.struts.action.ActionForm을 확장한다. ActionForms 은 요청 매개변수들을 캡슐화 및 유효화하는 단순한 JavaBean이다. 요청 데이터를 유효화하기 위해 ActionFormvalidate() 메소드는 케이스 스팩의 구현을 제공해야 한다. ActionForm은 요청 데이터의 캐리어로서 Action 클래스에 작용한다. JSP 객체는 각각의 ActionForm 을 합하여 애플리케이션의 View 레이어를 만든다. JSP 객체의 거의 모든 폼(form) 필드는 이에 상응하는 ActionForm의 애트리뷰트로 매핑한다.

    JSP 커스텀 태그 라이브러리
    JSP 커스텀 태그 라이브러리는 태그로 표현되는 액션들의 모음이다. 이것은 JSP Specification 1.1의 강력한 기능이다. 프리젠테이션 티어를 다른 애플리케이션 티어들과 분리할 수 있다. 이 라이브러리는 사용이 쉽고 XML과 같은 방식으로 읽을 수 있다. 자바 스크립틀릿의 사용을 최소화 함으로서 JSP 컴포넌트를 쉽게 관리할 수 있다. Struts가 제공하는 JSP 태그에는 HTML, 로직, 빈 태그들이 포함된다.


    ActionErrors

    예외 핸들링을 지원할 때 ActionError를 사용한다. ActionError는 애플리케이션 예외를 잡아 View 레이어로 보낸다. 각각은 ActionError의 모음이다. ActionError는 에러 메시지들을 캡슐화 하면서 Presentation 레이어의 </html:errors>ActionError 컬렉션의 모든 에러 메시지들로 렌더링한다.


    Best Practice 1. 다중 ActionForm을 통한 데이터 재사용

    Struts 컴포넌트에 익숙해졌으니 이제 프레임웍을 활용하는 방법을 설명하겠다. 우선 Struts는 모든 JSP 객체를 ActionForm과 연합할 것을 권장한다. 스크린에 나타난 데이터를 캡슐화 하는 것이다.ActionForm에서 찾은 부속 메소드를 사용하여 JSP 객체에 있는 폼(form) 데이터에 액세스한다. Listing 1은 View 레이어에서 ActionForm 의 일반적이 사용법이다.

    Listing 1. JSP에서 ActionForm 사용하기
       <html:form action="/bp1">   <html:text  property="attrib1" />   </html:form >

    "BP1AForm" 이라고하는ActionForm에는 attrib1 애트리뷰트를 비롯하여 게터(getter)와 세터(setter) 메소드도 포함되어 있다. 설정 파일인 struts-config.xml에서 "/bp1" 액션은 name 애트리뷰트를 사용하여 bp1AForm 으로 매핑된다. 이것은 JSP에 데이터 디스플레이를 돕는다.

    Best Practice 1을 구현할 때 Struts는 다음의 두 가지를 권장한다:

    1. BP1AForm의 애트리뷰트 서브셋을 형성하고있는 애트리뷰트로 JavaBean (BP1BForm) 을 만들고 아울러 이 애트리뷰트의 게터와 세터 메소드도 만든다.
    2. 빈과 BP1AForm을 결합하여 BP1AForm 의 애트리뷰트를 BP1BForm 빈으로 대체한다. 이제 BP1BForm을 통해 BP1AForm 의 애트리뷰트 서브셋에 액세스 할 수 있다. (Listing 2)
    Listing 2. JSP의 폼(form) 애트리뷰트에 액세스하기
       <html:form action="/bp1">   
    <bean:define name="bp1AForm" property="bp1BForm" id="bp1B"
    type="com.ibm.dw.webarch.struts.BP1BForm" />
    <html:text name="bp1B" property="subsetAtt1" />
    </html:form >

    기억하세요!
    이 Best Practice 1의 주요 장점은 애트리뷰트 세트에 액세스하기 위해 다중 ActionForm이 필요할 때 이를 사용할 수 있다는 것이다. 다음은 기억해야 할 점이다:

    • Struts는<bean:define/> 태그를 구현한다.
    • <%@ taglib uri="struts-bean.tld" prefix="bean" %> 코드가 struts-bean.tld를 가르킬때 <bean:define/> 태그는 JSP 컴포넌트에서 작동을 시작한다.
    • ActionForm을 확장하는 BP1AForm의 밸리데이션 프레임웍은 BP1BForm의 데이터를 반드시 유효화해야 한다.

    애플리케이션에서 Action 클래스를 만들 때, org.apache.struts.action.Action을 직접 확장하는 대신org.apache.struts.action.Action을 확장하여 Action 클래스 (IntermediateAction)를 만든다. 다른 모든 Action 클래스들은 이 IntermediateAction 클래스를 확장한다.

    Best Practice 2. Action 클래스를 사용하여 요청 핸들하기
    일반적으로 Struts 프레임웍을 사용할 때 JSP 컴포넌트가 애플리케이션을 실행하기위해 요청하는 모든 액션의 경우, 애플리케이션은 Struts의 org.apache.struts.action.Action 을 확장하여 Action 클래스를 만들어야 한다. 이 개별적인 Action 클래스는 요청을 처리하면서 애플리케이션의 Model 레이어와 인터페이싱한다.

    Best Practice 2를 구현할 때 Struts는 다음과 같은 절차를 권장한다:

    1. org.apache.struts.action.Action을 확장하여 BP2Action이라고 하는Action 클래스를 만든다.
    2. BP2Action 을 확장하여 웹 애플리케이션에서 다른 모든 Action 클래스들을 만든다.
    3. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException에서 그랬던 것처럼, BP2Action에서 performTask()메소드를 만든다.
    4. BP2Action에서 한 개 이상의 일반적인 메소드를 애플리케이션에 추가한다. 예를 들면 serverSideValidate()과 같은 것이다. 다음 요소들을 고려하여 메소드의 액세스 변경자를 결정할 수 있다:
      • 모든 Action 클래스들이 이 메소드를 구현한다면 이를 추상화하라.
      • 어떤 Action 클래스가 케이스 스팩 구현을 제공한다면 메소드 보호를 선언하고 여기에 디폴트 구현을 제공한다.
    5. BP2Action에서 마지막으로 perform() 메소드를 선언한다. 위에 언급된 일반 메소드를 선언하라. 이는 요청 프로세스 전에 항상 호출되어야 한다. 이제 3단계에서 만들어진 performTask() 메소드를 호출한다.
    6. BP2Action을 확장하는 모든 Action 클래스에서 performTask() 와 케이스 스팩 구현을 추가하라.

    장점
    Best Practice 2는 두 가지 주요 장점이 있다. 첫째, 웹 애플리케이션의 모든 Action 클래스에서 과잉의 코드를 피할 수 있다. 둘째, 하나의 Action 클래스에 작동을 중앙화 함으로서 애플리케이션의 일반적인 태스크에 대한 제어력을 높인다.

    Best Practice 3. ActionForm을 세션 데이터에 작동시키기
    Struts 기반의 웹 애플리케이션에서 각ActionFormorg.apache.struts.action.ActionForm을 확장한다. 이 ActionForm은 페이지 데이터를 캡슐화하고 밸리데이션 프레임웍을 제공하여 요청 매개변수의 타당성 검사를 한다.

    대부분의 웹 애플리케이션들은 세션에서 데이터를 관리하여 애플리케이션을 통해 사용할 수 있도록 한다. Best Practice 3은 웹 애플리케이션 기능을 다룬다. toSession()fromSession() 메소드가 세션 데이터를 폼(form) 데이터 간 이동할 수 있도록 한다.

    1. org.apache.struts.action.ActionForm을 확장하여 추상 클래스 BP3Form을 만든다.
    2. BP3Form에서 퍼블릭 앱스트랙트void toSession(SessionData sessionData)void fromSession(SessionData sessionData)처럼 메소드와 액세스 변경자를 추가한다.
    3. 모든 ActionForm에서BP3Form을 확장하고 추상 메소드를 구현한다. 이 곳에서 폼 데이터가 세션으로(부터) 옮겨진다.
    4. 상응하는 Action 클래스는 이들 메소드가 호출되는 순서를 결정할 수도 있다. 예를 들어. actionForward가 결정되기 전에 ActionForm에서 toSession() 메소드를 호출할 수 있다.

    Best Practice 3을 사용할 때는
    Best Practice 3은 세션 데이터들이 하나의 객체로서 관리될 때 가장 유용하다. 또는 모든 페이지가 세션 데이터를 조작하거나 사용할 때도 유용하다.

    Best Practice 4.효과적인 예외 핸들링
    전통적으로 애플리케이션 예외는 Action 클래스에서 발생한다. 이 예외는 첫 번째로 기록된다. 그런 다음 Action 클래스는 ActionError를 만들고 이를 애플리케이션 범위 안에 저장한다. 이 클래스는 콘트롤을 적절한 ActionForward에 전달한다. Listing 3은 Action 클래스가 예외를 핸들하는 방법을 보여준다.

    Listing 3. Action 클래스에서의 예외 핸들링
       try {   //Code in Action class   }   
    catch (ApplicationException e) {
    //log exception
    ActionErrors actionErrors = new ActionErrors();
    ActionError actionError = new ActionError(e.getErrorCode());
    actionErrors.add(ActionErrors.GLOBAL_ERROR, actionError);
    saveErrors(request, actionErrors); }

    일반적인 예외 핸들링 절차가 모든 Action 클래스에 예외 정보를 저장하는 반면, Best Practice 4의 목적은 예외 핸들링을 하면서 과잉의 코드를 막는 것이다:

    1. org.apache.struts.action.Action을 확장하여 BP4Action라는 Action 클래스를 만든다.
    2. BP4Action을 확장하여 웹 애플리케이션에 다른 모든 Action 클래스들을 만든다.
    3. BP4Action에서, ActionErrors actionErrors = new ActionErrors(); 변수를 선언한다.
    4. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, ActionErrors actionErrors) throws IOException, ServletException처럼 BP4ActionperformTask() 메소드를 만든다.
    5. BP4Action에서 마지막으로 perform() 메소드를 선언한다. 그런 다음 일반적인 메소드를 호출한다. 이들은 항상 요청 프로세스 전에 호출되어야 한다. 이제 이전 단계에서 만들어진 performTask() 메소드를 호출할 수 있다.
    6. BP4Action을 확장하여 모든 Action 클래스에서 performTask()메소드를 구현하면서 애플리케이션 예외를 처리한다. (Listing 4)
    Listing 4. ActionErrors를 효과적으로 사용하기
       try		 {   //Code in Action class   }   
    catch(ApplicationException appException) {
    //Log exception
    //Add error to actionErrors
    actionErrors.add(ActionErrors.GLOBAL_ERROR,
    new ActionError(appException.getErrorCode())); }

    BP4Action에서 performTask() 메소드를 호출한 후에 saveErrors(request, errors)를 사용하여 ActionErrors를 저장한다.

    장점
    Best Practice 4의 큰 장점은 ActionErrors를 핸들하는 Action 클래스에서 코드 과잉을 막는다.


    이 글의 출처 : http://www.ibm.com/developerworks/kr/library/wa-struts/

    Posted by 1010
    반응형
    struts와 ibatis 를 이용한 게시판 생성
    **작업 순서

    1. Dynamic Web project 생성(struts)

    2. WEB-INF -> lib 에
           
            struts,ibatis,JSTL 관련 jar 파일 복사        

                antlr-2.7.2.jar
                commons-beanutils-1.8.0.jar
                commons-chain-1.2.jar
                commons-digester-1.8.jar
                commons-fileupload-1.1.1.jar
                commons-io-1.1.jar
                commons-logging-1.0.4.jar
                commons-validator-1.3.1.jar
                oro-2.0.8.jar
                struts-config.xml
                struts-core-1.3.10.jar
                struts-taglib-1.3.10.jar
                struts-tiles-1.3.10.jar

                ibatis.jar

                jstl.jar
                standard.jar

    3. WEB-INF 에

            struts-config.xml 생성(struts 설정 파일)

    4. META-INF 에

            context.xml 생성(connection pool 설정 파일)

    5. WEB-INF의
       
            web.xml에 struts 관련 설정 및
            connection pool 설정

    6. 패키지 및 jsp 파일용 폴더 생성

            패키지 : action    InsertAction, DeleteAction
                        dao       BoardDao, ibatis 설정 파일(Board.xml SqlMapConfig.xml)
                        vo         BoardVO
                        form      BoardForm
            폴더    : board    list.jsp, write.jsp, ...

    7. jsp -> action -> dao        (or        dao-> action -> jsp )

       jsp , XXXAction 생성
       Dao에 메소드 추가 순서로 작업

    8. struts-config.xml 에
        mapping 정보 추가

    9. server Restart    다시 7번부터 반복


    **실습

    1.  web-inf 에서 board 폴더를 만들고 write.jsp 파일을 생성한다.
        (web-inf 폴더 아래 있는 jsp 파일은 url 입력으로는 절대 접근 불가능하다.)

    2. lib에 board_config.xml을 추가한다.

    3. web.xml에 다음과 같이 config 값을 바꿔준다.

        <init-param>
          <param-name>config</param-name>
          <param-value>/WEB-INF/board-config.xml</param-value>
        </init-param>

    4. board-config.xml에서 다음과 같이 action-mapping 를 추가하면 외부에서 접근할수 없는 페이지에 접근 가능하다.
       
        <action-mappings>
          <action path="/write" forward="/WEB-INF/board/write.jsp"></action>
        </action-mappings>

        http://localhost/struts/write.do
        이런식으로 주소를 입력해주면 web-inf 내부에 있는 페이지로 이동되게 된다.

    5. write.jsp 에 html 코드를 사용하여 화면을 만들어준다.

    6. 또 글 목록을 보여주기 위한 list.jsp를 만들고 html 코드를 작성해 준다.

    7. 기존의 프로젝트에 이미 struts 관련 라이브러리가 있기 때문에
            jstl, ibatis 관련 라이브러리만 추가시켜준다.
       
            apache-tomcat-6.0.18\webapps\examples\WEB-INF\lib
                jstl.jar
                standard.jar

            ibatis-2.3.4.726\lib
                ibatis-2.3.4.726.jar

    8. META-INF 폴더에 context.xml 를 생성하고 connection pool 을 설정해 준다.

                <?xml version="1.0" encoding="UTF-8"?>
                <!-- Connection Pool 설정 파일  -->
                <Context>
                 <Resource name="jdbc/myoracle"
                     auth="Container"
                              type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
                              url="jdbc:oracle:thin:@localhost:1521:orcl"
                              username="scott" password="tiger" maxActive="20"
                              maxIdle="10" maxWait="-1"/>
                </Context>

    9. web.xml에서 자원참조 설정을 해준다.

             <resource-ref>
                  <res-ref-name>jdbc/myoracle</res-ref-name>
                  <res-type>javax.sql.DataSource</res-type>
                  <res-auth>Container</res-auth>
             </resource-ref>

    10. src 에서 패키지를 생성해준다.
            action, form, dao, vo

    11. 게시문의 정보를 전달하기 위한 BoardVO 를 생성한다.
       
             private int no;
             private String title;
             private String writer;
             private String content;
             private int hit;
             private int groupNo;
             private Date regdate;
             private String fileName;


    12. 기존의 테이블에서 첨부파일 이름을 저장하기 위한 속성을 추가한다.

            alter table board2
            add filename varchar2(50)

    13. BoardDao 클래스를 작성

            dao 클래스는 single tone 으로 작성한다.
            static 멤버변수로 자기 자신을 생성하고, static 함수를 통해 객체를 리턴해 주도록 작성한다.

    14. BoardDao 에 ibatis 설정을 해 준다.
           
            dao 패키지에 Board.xml 과 SqlMapConfig.xml 파일을 생성하고

            Board.xml 에 쿼리문을 추가시켜준다.
                 <select id="list" resultClass="vo.BoardVO">
                      select no, title, writer, hit, groupNo, regdate
                      from board2
                 </select>

            SqlMapConfig.xml 파일에 Connection Pool 설정을 해 준다.
                 <!-- Connection Pool 설정 -->
                 <transactionManager type="JDBC" commitRequired="false">
                   <dataSource type="JNDI">
                        <property name="DataSource" value="java:comp/env/jdbc/myoracle"/>
                   </dataSource>
                 </transactionManager>  
            dataSource type="JNDI" 으로 설정해 두명 ibatis 가 tomcat 에서 생성한 Connection Pool을 가져와서 사용한다.
            그리고 쿼리를 실행할 mapper 파일을 설정해 준다.
                    <!-- mapper 파일 등록 -->
                    <sqlMap resource="dao/Board.xml"/>

    15. BoardDao 클래스에서 쿼리를 실행할 메소드를 생성한다.

         우선 sql 을 얻어오기 위해 SqlMapClient 객체를 얻어온다.

             private SqlMapClient mapper;
     
             private BoardDao() {
              try{
                   Reader reader = Resources.getResourceAsReader("dao/SqlMapConfig.xml");
                   mapper = SqlMapClientBuilder.buildSqlMapClient(reader);
              }catch(IOException e){
                   e.printStackTrace();  
              }
       
            반환할 자료가 다수이기 때문에 queryForList() 메소드를 사용한다.
            public List<BoardVO> getList() throws SQLException{
                return mapper.queryForList("list");
             }

    16. action 패키지에 Action을 상속받는 ListAction 클래스를 만들고
            excute() 메소드를 오버라이드 해 준다.
            메소드 내부에서 BoardDao 객체를 얻어오고 getList() 메소드를 호출하여 리스트를 얻어온다.
            그리고 request 의 어트리뷰트로 리스트를 세팅 하고
            /WEB-INF/board/list.jsp 페이지로 포워드 한다.

    17. list.jsp 에서 jstl 을 이용하여 넘어온 리스트를 화면에 그려주도록 한다.

            jstl 을 사용하기 위해 taglib 를 추가시킨다.
           <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

            다음과 같이 forEach 를 사용하여 리스트를 반복하여 그려준다.

            <c:forEach var="board" items="${boardList}">
           
            <!-------------그린게시판 첫번째줄시작 번호. 제목. 작성일. 조회------------>
            <tr bgcolor="f7f7f7">
              <td height="24" align="center">${board.no }</td>
              <td><a href="freeboard_view.htm" class="purple_bbs">${board.title }</a></td>
              <td align="center">${board.writer }</td>
              <td align="center">${board.regdate }</td>
              <td align="center">${board.hit }</td>
            </tr>
            <!-------------그린게시판 두번째줄시작 ----->
            <tr bgcolor="e4e4e4">
              <td height="1" colspan="5"></td>
            </tr>
           
            </c:forEach>
           
    18. board-config.xml 에 다음과 같이 action-mapping 을 추가시켜준다.

             <action-mappings>
                  <action path="/write" forward="/WEB-INF/board/write.jsp"></action>
                  <action path="/list" type="action.ListAction"></action>
             </action-mappings>

    ListAction.java
    Board.xmlBoardDao.javaSqlMapConfig.xml
    BoardVO.java
    board-config.xml
    struts-config.xml
    web.xml
    list.jsp
    write.jsp
    Posted by 1010
    반응형

    MySQL5

    Database/MySQL 2007/08/13 04:41
    작성자 : shin-gosoo(hchshin@chol.com)
    작성일 : 2007.04.10


    새창 보기

    윈도우 자바개발환경을 위한 기본적인 Mysql 5 설치방법입니다.

    목차
    1. 데이터베이스 설치
    2. Mysql 환경설정 - 한글개발환경(euckr)일 경우
    3. Mysql 환경설정 - 다국어 개발환경(utf-8)일 경우
    1. 데이터베이스 설치
    • 설치 버전 : 5.0.37 (2007.04.10 현재 최신 버전)
    • 다운로드 URL : http://dev.mysql.com/downloads/mysql/5.0.html#win32
    • Without installer (unzip in C:\) : mysql-noinstall-5.0.37-win32.zip (45.6M) 를 선택해서 다운로드 받는다.
      개인 취향이겠지만 필자는 인스톨 버전은 싫어함.
      또한, 필자는 개발환경은 하드드라이브가 C, D로 나눠서 있을 경우 D 드라이브에 설치한다. 가끔씩 윈도우를 재설치 할 경우를 대비해서.
    • mysql-noinstall-5.0.37-win32.zip 를 풀면 mysql-5.0.37-win32 폴더가 생긴다. mysql-5.0.37로 이름변경해서 아래와 같이 설치하자.
    • 설치 예)
      • 설치디렉토리 : D:\dev\mysql-5.0.37
      • 윈도우 시스템환경변수 설정
        • Path : D:\dev\mysql-5.0.37\bin; 추가
      • 윈도우 서비스로 설정
        • 도스프롬프트 : D:\dev\mysql-5.0.37\bin> mysqld --install ( 서비스 제거는 mysqld --remvoe )
        • 제어판 - 관리도구 - 서비스를 통해 Mysql 서비스 시작
      • 도스 프롬프트에서 C:/>mysql -uroot 로 접속되면 설치 성공.
    2. Mysql 환경설정 - 한글개발환경(euckr)일 경우
    • my.ini 설정
    • C:\Windows 밑에 my.ini 파일 생성
      1. [mysql]  
      2. default-character-set = euckr 
      3.  
      4. [mysqld]  
      5. character-set-client-handshake=FALSE 
      6. init_connect="SET collation_connection = euckr_korean_ci" 
      7. init_connect="SET NAMES euckr" 
      8. default-character-set = euckr 
      9. character-set-server = euckr 
      10.  
      11. collation-server = euckr_korean_ci 
      12.  
      13. [client]  
      14. default-character-set = euckr 
      15.  
      16. [mysqldump]  
      17. default-character-set = euckr 
    • Mysql Restart
    • root 계정으로 mysql 접속후
    • mysql>status
    • 아래와 같이 나오면 설정 OK.
      1. mysql> status  
      2. --------------  
      3. mysql  Ver 14.12 Distrib 5.0.37, for Win32 (ia32)  
      4.  
      5. Connection id:          1  
      6. Current database:  
      7. Current user:           root@localhost  
      8. SSL:                    Not in use  
      9. Using delimiter:        ;  
      10. Server version:         5.0.37-community MySQL Community Edition (GPL)  
      11. Protocol version:       10  
      12. Connection:             localhost via TCP/IP  
      13. Server characterset:    euckr  
      14. Db     characterset:    euckr  
      15. Client characterset:    euckr  
      16. Conn.  characterset:    euckr  
      17. TCP port:               3306  
      18. Uptime:                 10 sec  
      19.  
      20. Threads: 1  Questions: 4  Slow queries: 0  Opens: 12  Flush tables: 1  Open tabl  
      21. es: 6  Queries per second avg: 0.400  
      22. --------------  
      23.  
      24. mysql> 
    • root 계정 초기 비밀번호 지정하기
      1. C:>mysql -uroot mysql  
      2.  
      3. mysql>update user set password=password('새비밀번호') where user='root';  
      4. mysql>flush privileges;  
      5. mysql>exit  
      6.  
      7. C:>mysql -uroot -p새비밀번호  
    • 데이터베이스 생성 및 사용자 생성
      1. C:>mysql -uroot -p비밀번호  
      2.  
      3. mysql>CREATE DATABASE myproject_kr DEFAULT CHARACTER SET euckr COLLATE euckr_korean_ci;  
      4.  
      5. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'localhost' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      6.  
      7. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'%' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      8.  
      9. mysql>FLUSH PRIVILEGES;  
      10.  
      11. mysql>exit  
      12.  
      13. C:>mysql -ujavamaster -p1234 myproject_kr  
      14.    
      4라인 : euckr 환경으로 myproject_kr 이라는 데이터베이스 생성
      6라인 : 아이디 javamaster, 비밀번호 1234로 로컬에서만 접속권한이 있는 사용자 생성
      8라인 : 아이디 javamaster, 비밀번호 1234로 원격에서도 접속권한이 있는 사용자 생성
      10라인 : 권한 적용
      14라인 : 새로 생성한 계정으로 접속

    3. Mysql 환경설정 - 다국어 개발환경(utf-8)일 경우
    • my.ini 설정
    • C:\Windows 밑에 my.ini 파일 생성
      1. [mysql]  
      2. default-character-set = utf8 
      3.  
      4. [mysqld]  
      5. character-set-client-handshake=FALSE 
      6. init_connect="SET collation_connection = utf8_general_ci" 
      7. init_connect="SET NAMES utf8" 
      8. default-character-set = utf8 
      9. character-set-server = utf8 
      10. collation-server = utf8_general_ci 
      11.  
      12. [client]  
      13. default-character-set = utf8 
      14.  
      15. [mysqldump]  
      16. default-character-set = utf8 
    • Mysql Restart
    • root 계정으로 mysql 접속후
    • mysql>status
    • 아래와 같이 나오면 설정 OK.
      1. mysql> status  
      2. --------------  
      3. mysql  Ver 14.12 Distrib 5.0.37, for Win32 (ia32)  
      4.  
      5. Connection id:          1  
      6. Current database:  
      7. Current user:           root@localhost  
      8. SSL:                    Not in use  
      9. Using delimiter:        ;  
      10. Server version:         5.0.37-community MySQL Community Edition (GPL)  
      11. Protocol version:       10  
      12. Connection:             localhost via TCP/IP  
      13. Server characterset:    utf8  
      14. Db     characterset:    utf8  
      15. Client characterset:    utf8  
      16. Conn.  characterset:    utf8  
      17. TCP port:               3306  
      18. Uptime:                 10 sec  
      19.  
      20. Threads: 1  Questions: 4  Slow queries: 0  Opens: 12  Flush tables: 1  Open tabl  
      21. es: 6  Queries per second avg: 0.400  
      22. --------------  
      23.  
      24. mysql> 
    • root 계정 초기 비밀번호 지정하기
      1. C:>mysql -uroot mysql  
      2.  
      3. mysql>update user set password=password('새비밀번호') where user='root';  
      4. mysql>flush privileges;  
      5. mysql>exit  
      6.  
      7. C:>mysql -uroot -p새비밀번호  
    • 데이터베이스 생성 및 사용자 생성
      1. C:>mysql -uroot -p비밀번호  
      2.  
      3. mysql>CREATE DATABASE myproject_utf8 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;  
      4.  
      5. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'localhost' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      6.  
      7. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'%' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      8.  
      9. mysql>FLUSH PRIVILEGES;  
      10.  
      11. mysql>exit  
      12.  
      13. C:>mysql -ujavamaster -p1234 myproject_utf8  
      14.  
      15. mysql>set names euckr;  
      16.    
      4라인 : euckr 환경으로 myproject_utf8 이라는 데이터베이스 생성
      6라인 : 아이디 javamaster, 비밀번호 1234로 로컬에서만 접속권한이 있는 사용자 생성
      8라인 : 아이디 javamaster, 비밀번호 1234로 원격에서도 접속권한이 있는 사용자 생성
      10라인 : 권한 적용
      14라인 : 새로 생성한 계정으로 접속
      16라인 : utf8 환경일 경우 도스프롬프트에서 테이블에 한글문자를 등록(insert)하거나 한글을 조회(select)시 깨져보임. set names euckr; 로 설정을 바꾸면 한글이 제대로 보임(Mysql 5일 경우)


    'Database > MySQL' 카테고리의 다른 글

    MySQL - JDBC Source  (0) 2007/10/29
    Connecting to a MySQL Database using Connector/J JDBC Driver  (0) 2007/10/29
    MySQL - create table examples  (0) 2007/10/29
    MySQL - JDBC  (0) 2007/10/29
    MySQL5  (0) 2007/08/13
    MySQL for JDBC Driver  (0) 2007/06/13
    Posted by 1010
    반응형

    Log4sql 설치와 쿼리의 실제 내용을 log로 볼수 있는 방법
    ORM 이나 Spring JDBC 등을 통해서 쿼리를 날리게 되면 로그로 쿼리의 조합된 모양을 보기가
    상당히 힘들다. 그리고 log4sql 를 중간에 설치해 쿼리의 내용을 로그로 찍어줄수가 있다.

    사이트 : http://log4sql.sourceforge.net
    한국 국기를 클릭하면 한글 사이트로 번역이 된다.
     


    다운받은 파일을 열게 되면 log4sql.jar, log4sql_conf.jsp있다.
    log4sql.jar 라이브러리 폴더에 넣으면 되고 , jsp 페이지는 설정값을 변경할수 있는 방법을 제공한다.

    다음은 프로젝트 파일에서 설정값을 변경한다.
    Jdbc driverClassName 설정값을 변경해야되는데 그 값은 value 값을
    core.log.jdbc.driver.OracleDriver 설정하면된다. Log4jsql.jar열어보면 여러종류의 드라이버가
    있으니 그에 맞는 클래스를 선택하면된다.



    Jdbc connect 정보를 설정한 곳에 보통 xml 파일에 설정이 되어있는데
    <
    property name="driverClassName" value="core.log.jdbc.driver.MssqlDriver"/>
    <property name="url" value="jdbc:microsoft:sqlserver://xxx.xxx.xxx.xxx:1433;DatabaseName=spring"/>
    <property name="username" value="xxxxx"/>
    <property name="password" value="xxxxx"/>

    아래그림과 같이 elapsed time 처럼 쿼리가 걸리는 시간도 찍히게 된다

    Posted by 1010
    반응형
    log4sql은 많이 알려지진 않았지만 상당히 유용한 sql 로거입니다. 만든 분은 송인섭이라는분인데 한국분이신것 같습니다.

     기존에 우리는 sql을 디버깅 하기 위해서 대부분 System.out.println()을 사용했습니다. log4j를 이용해서 sql을 콘솔에 찍기도 하고 jdbc관련 클래스를 만들어서 사용하는 경우에도 결국엔 System.out.println()을 사용하게 됩니다. 하지만 sql을 이렇게 콘솔로 표현하게 되면 여러가지 불편한 점들이 생깁니다.
     가장 큰 문제점이 바로 소스는 소스대로, 콘솔은 콘솔대로 가독성이 떨어지게 됩니다. 소스상에서 System.out을 사용하게되면 개발이 끝나도 운영에 들어가게되면 이 부분을 주석처리하거나 다른 설정을 하게됩니다.
     또한 콘솔에서 보여질때 sql을 소스상에서 들여쓰기를 해주지 않았다면 단순한 sql은 괜찮지만 복잡한 sql은 볼때마다 매번 들여쓰기를 해주어야 한다는 단점이 있습니다. log4sql은 이러한 문제를 해결하기 위해서 간단히 드라이버 설정만 바꿔주면 System.out.println()을 사용하지 않아도, 소스상에서 들여쓰기를 해주지 않아도 콘솔에 예쁘게 들여쓰기가 된 sql을 보여줍니다.

    log4sql이 뭔가요?

    간단하게 log4sql이 무엇인지 백번 설명을 하기보다는 한번 보는게 확실히 이해하기 쉬울꺼 같습니다.
    사용자 삽입 이미지
    저 노란색 박스안에 나타난 내용이 log4sql에서 자동으로 찍어준 내용입니다. sql이 실행된 시간과 실행된 메소드, sql이 실행되는데 소요된 시간, 실행된 sql을 자동으로 들여쓰기를 해서 보여주고 있습니다.

    log4sql의 장점

    1. 자동 들여쓰기
    일반적으로 콘솔에 들여쓰기가 된 sql을 보려면 다음과 같은 코드를 작성해야 했습니다.
    1. StringBuffer  var11 = new StringBuffer();  
    2. var11.append("SELECT * \n");  
    3. var11.append(" FROM   emp \n");  
    4. var11.append(" WHERE  deptno = '30' \n");  

    하지만 더이상 이런 부분은 신경 쓰지않아도 됩니다. log4sql을 사용한다면 말이죠.

    2. 페이지 반응속도가 느리다구요?
    log4sql은 쿼리가 실행되는데 걸린 시간을 표시해줍니다. 어느 부분이 느린지 어느 쿼리가 느린지 콘솔을 보기만 하면 알 수 있습니다.

    3. 이 쿼리가 어디서 날린 쿼리지?
    log4sql은 쿼리가 실행된 메소드도 표시해 줍니다. 하지만 쿼리를 날리는 클래스를 별도로 두고 해당 클래스를 이용해서 쿼리를 날리게 되면 log4sql도 어쩔수 없답니다.^^;;

    어떻게 사용하나요?

    간단합니다. JDBC 관련 설정에서 드라이버 설정만 바꿔주면 됩니다. 예를 들어 오라클을 사용하는 경우 기존에 'oracle.jdbc.drirver.OracleDriver' 드라이버를 사용했다면 'core.log.jdbc.driver.OracleDriver'로 바꿔주기만 하면 됩니다. 이제 log4sql을 사용할 준비가 된겁니다. 이제 WAS를 올리고 콘솔을 한번 보세요. sql이 깔끔하게 정리되어서 나타날꺼에요.^^
    다른 디비를 사용하시는 경우에는 log4sql 홈페이지에서 확인하시면 됩니다.

    다음에는 log4sql의 상세 설정에 대해서도 알아보도록 하겠습니다.



    출처 : http://westzero.net/16
    Posted by 1010
    반응형
     

    commons-beanutils  most recent diff


    version 1.8.0

      View the most recent changes for the commons-beanutils port at: commons-beanutils.darwinports.com/diff
      Scroll down toward the bottom of the page to get installation instructions for commons-beanutils.
      The raw
      portfile for commons-beanutils 1.8.0 is located here:
      http://commons-beanutils.darwinports.com/dports/java/commons-beanutils/Portfile
      Find related portfiles with the unique DarwinPorts.com search feature.
      Check for any related Fink projects here:
      pdb.finkproject.org/pdb/package.php/commons-beanutils

      The commons-beanutils Portfile 40121 2008-09-21 15:49:41Z jberry macports.org $

      PortSystem 1.0

      Name: commons-beanutils
      Version: 1.8.0

      Category: java
      Maintainers: jberry openmaintainer
      Platform: darwin

      Description: Jakarta Commons-BeanUtils
      Long Description: Commons-BeanUtils provides easy-to-use wrappers around the Java reflection and introspection APIs.
      Homepage:
      http://commons.apache.org/beanutils/

      distfiles ${distname}-src${extract.suffix}

      Master Sites: apache:commons/beanutils/source/

      Checksums: md5 1bce3cfa4ae33c94686422e78abc0792 sha1 a4af85d2cfd04a42d8de9a5bb5336a21f33e30ce

      depends_build bin:ant:apache-ant
      depends_lib bin:java:kaffe port:junit port:commons-logging port:commons-collections

      worksrcdir ${distname}-src

      use_configure no

      build.cmd ant
      build.target jar javadoc
      build.args -Dcommons-collections.jar=${prefix}/share/java/commons-collections.jar -Dcommons-logging.jar=${prefix}/share/java/commons-logging.jar -Djunit.jar=${prefix}/share/java/junit.jar

      destroot {
      xinstall -m 755 -d ${destroot}${prefix}/share/java ${destroot}${prefix}/share/doc
      xinstall -m 644 ${worksrcpath}/dist/commons-beanutils-core-${version}.jar ${destroot}${prefix}/share/java/commons-beanutils-core.jar
      xinstall -m 644 ${worksrcpath}/dist/commons-beanutils-${version}.jar ${destroot}${prefix}/share/java/commons-beanutils.jar
      file copy ${worksrcpath}/dist/docs ${destroot}${prefix}/share/doc/${name}
      }

      livecheck.check regex
      livecheck.url
      http://commons.apache.org/downloads/download_beanutils.cgi
      livecheck.regex "${name}-(\\d+\\.\\d+(\\.\\d+)?)-src.tar.gz"

    If you haven't already installed Darwin Ports, you can find easy instructions for doing so at the main Darwin Ports page.

    Once Darwin Ports has been installed, in a terminal window and while online, type the following and hit return:


      %  cd /opt/local/bin/portslocation/dports/commons-beanutils
      % sudo port install commons-beanutils
      Password:
    You will then be prompted for your root password, which you should enter. You may have to wait for a few minutes while the software is retrieved from the network and installed for you. Y ou should see something that looks similar to:

      ---> Fetching commons-beanutils
      ---> Verifying checksum for commons-beanutils
      ---> Extracting commons-beanutils
      ---> Configuring commons-beanutils
      ---> Building commons-beanutils with target all
      ---> Staging commons-beanutils into destroot
      ---> Installing commons-beanutils
    - Make sure that you do not close the terminal window while Darwin Ports is working. Once the software has been installed, you can find further information about using commons-beanutils with these commands:
      %  man commons-beanutils
      % apropos commons-beanutils
      % which commons-beanutils
      % locate commons-beanutils

     Where to find more information:

    Darwin Ports
    Posted by 1010
    반응형
    The BeanUtils Component

    대부분의 자바 개발자들은 객체 속성의 getters와 setters에 대한 자바빈즈 네이밍 패턴에 따르는 자바 클래스들을 만들곤 한다. 이러한 상호대응하는 getXxx 와 setXxx 메소드들를 호출하는 방식으로 직접 이들 메소드들에 접근함은 자연스럽다. 그러나 자바 객체 속성들에 대한 동적인 접근이 필요한 경우가 종종 발생한다.(호출되는 속성 getter 와 setter 메소드들의 compiled-in 지식없이) . 다음과 같은 경우들이 이에 포함된다.

    • 자바 오브젝트 모델과 상호 작용하는 스크립팅 언어를 개발할 때 (such as the Bean Scripting Framework).
    • 웹 프레이젠테이션과 같은 템프릿 언어 프로세스를 개발할 때 (such as JSP or Velocity).
    • JSP와 XSP 환경을 위한 커스텀 태그 라이버러리를 개발할 때 (such as Jakarta Taglibs, Struts, Cocoon).
    • XML-based 구성(configuration) 자원을 활용할 때 (such as Ant build scripts, web application deployment descriptors, Tomcat's server.xml file).

    자바 언어는 ReflectionIntrospection API들을 제공한다.(JDK Javadoc 안의 java.lang.reflect 과 java.beans 패키지를 보라). 그러나, 이들 API들은 이해하고 활용하기에 매우 복잡해질 수 있다. BeanUtils 콤프넌트는 이들의 능력을 쉽게 사용할 수 있도록 랩퍼들을 제공한다


    문서

    Release Notes 는 이 배포판에 포함되어 있는 새로운 기능과 버그 픽스를 문서로 정리하고 있다.

    JavaDoc API documents 는 online상에서 가능하다. 특히, PropertyUtils 클래스 설명서에 기술된 속성 참조 신택스 옵션들에 주의해야 한다.


    배포판



    Commons BeanUtils

    Most Java developers are used to creating Java classes that conform to the JavaBeans naming patterns for property getters and setters. It is natural to then access these methods directly, using calls to the corresponding getXxx and setXxx methods. However, there are some occasions where dynamic access to Java object properties (without compiled-in knowledge of the property getter and setter methods to be called) is needed. Example use cases include:

    • Building scripting languages that interact with the Java object model (such as the Bean Scripting Framework).
    • Building template language processors for web presentation and similar uses (such as JSP or Velocity).
    • Building custom tag libraries for JSP and XSP environments (such as Jakarta Taglibs, Struts, Cocoon).
    • Consuming XML-based configuration resources (such as Ant build scripts, web application deployment descriptors, Tomcat's server.xml file).

    The Java language provides Reflection and Introspection APIs (see the java.lang.reflect and java.beans packages in the JDK Javadocs). However, these APIs can be quite complex to understand and utilize. The BeanUtils component provides easy-to-use wrappers around these capabilities.

    BeanUtils Core And Modules

    Since the 1.7.0 release BeanUtils has distributed three jars:

    • commons-beanutils.jar - contains everything
    • commons-beanutils-core.jar - excludes Bean Collections classes
    • commons-beanutils-bean-collections.jar - only Bean Collections classes
    The main commons-beanutils.jar has an optional dependency on Commons Collections

    Bean Collections

    Bean collections is a library combining BeanUtils with Commons Collections to provide services for collections of beans. One class (BeanComparator) was previously released, the rest are new. This new distribution strategy should allow this sub-component to evolve naturally without the concerns about size and scope that might otherwise happen.

    Bean Collections has an additional dependency on Commons Collections .

    Documentation

    The User Guide is part of the package JavaDocs.

    The Release Notes document the new features and bug fixes that have been included in this release.

    The JavaDoc API documents are available online. In particular, you should note the property reference syntax options described in the PropertyUtils class description.

    Releases

    1.8.0

    BeanUtils 1.8.0 is binary compatible with version 1.7.0 and contains quite a few bug fixes and enhancements .

    BeanUtils 1.8.0 is available to download here .

    1.7.0

    BeanUtils 1.7.0 is a service release which removes the dependency upon a specific commons-collection library version. It may be safely used together with either the 2.x or 3.x series of commons-collections releases. It also introduces a number of important enhancements. It is backward compatible with the 1.6 release.

    This important service release is intended to help downstream applications solve dependency issues. The dependency on commons collections (which has become problematic now that there are two incompatible series of commons collections releases) has been factored into a separate optional sub-component plus a small number of stable and mature org.apache.commons.collections packaged classes (which are distributed with the BeanUtils core). This arrangement means that the BeanUtils core sub-component (which is the primary dependency for most downsteam applications) can now be safely included on the same classpath as commons collections 2.x, 3.x or indeed neither.

    The distribution now contains alternative jar sets. The all-in-one jar contains all classes. The modular jar set consists of a core jar dependent only on commons logging and an optional bean collections jar (containing classes that provide easy and efficient ways to manage collections of beans) which depends on commons collections 3.

    BeanUtils 1.7.0 is available to download here .

    Older Releases (Not Mirrored)

    Support

    The commons mailing lists act as the main support forum. The user list is suitable for most library usage queries. The dev list is intended for the development discussion. Please remember that the lists are shared between all commons components, so prefix your email by [beanutils].

    Issues may be reported via ASF JIRA .


    Posted by 1010
    반응형

    1. 왜 이걸 썼을까?
       bean에서 하드코딩된 값을 property 파일에 넣고 쓸려면 "key=value" 형태 밖에 안됩니다.
       좀더 복잡한 구조를 효율적으로 관리하고자 한다면 xml을 이용해야 하는데
       xml를 쉽게 처리할 수 있는게 뭐 없을까 찾아보니 digester라는 아파치 오픈소스가
       눈에 띄었습니다.
       그냥 따라하기 식으로 환경구성하고 테스트 만들어 보니 한 30분 즘...
       정말 쉽게 구현이 되었습니다.
      
    2. 필요한건 뭘까?
       url : http://jakarta.apache.org/commons/digester/
       아래 보면 digester를 이용하기 위해 관련된걸 보면 commons의 logging 1.1.x, BeanUtils 1.x, Collections 2.x/3.x 등이 필요합니다.
       다 다운받으세요^^  

    사용자 삽입 이미지
      digester : http://jakarta.apache.org/site/downloads/downloads_commons-digester.cgi
      logging : http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi
      BeanUtils : http://jakarta.apache.org/site/downloads/downloads_commons-beanutils.cgi
      Collections :  http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi


    3. 만들어 보기
        일단 간단한 형태로 구조를 잡았습니다.
    [code]
    <?xml version="1.0"?>
    <config description="kkaok">
    <database part="oracle">
    <driverclassname>oracle.jdbc.driver.OracleDriver</driverclassname>
    <url>oracle ......</url>
    <maxactive>10</maxactive>
    <maxwait>100</maxwait>
    <defaultautocommit>true</defaultautocommit>
    <defaultreadonly>false</defaultreadonly>
    <defaulttransactionisolation>2</defaulttransactionisolation>
    <username>oid</username>
    <password>opwd</password>
    </database>
    <database part="cubrid">
    <driverclassname>cubrid.jdbc.driver.CUBRIDDriver</driverclassname>
    <url>jdbc:CUBRID:localhost:33000:demodb:::</url>
    <maxactive>10</maxactive>
    <maxwait>100</maxwait>
    <defaultautocommit>true</defaultautocommit>
    <defaultreadonly>false</defaultreadonly>
    <defaulttransactionisolation>2</defaulttransactionisolation>
    <username>cid</username>
    <password>cpwd</password>
    </database>
    </config>
    [/code]
    config 라는 거 아래 database라는 노드가 반복이 되고
    database 아래에는 part, driverclassname,url,maxactive ... 등이 있습니다.

    xml을 매핑할 객체를 만들어 보겠습니다.
    [code]
    public class ConfigInfo {
        private List databaseinfo; // database가 반복되는 bean
        public ConfigInfo(){
            databaseinfo  = new ArrayList();
        }
        public void AddDatavaseInfo(DatabaseInfo dbinfo){ // database 빈에 추가하는 메소드
            databaseinfo.add(dbinfo);
        }
    }
    [/code]
    반복이 되는 패턴 등은 따로 클래스로 뽑고 그냥 값은 property로 만드시면 됩니다.
    [code]
    public class DatabaseInfo {
        private String part;
        private String driverclassname;
        private String url;
        private int maxactive;
        private long maxwait;
        private boolean defaultautocommit;
        private boolean defaultreadonly;
        private String username;
        private String password;
        private int defaulttransactionisolation;
        public int getDefaulttransactionisolation() {
            return defaulttransactionisolation;
        }
        public void setDefaulttransactionisolation(int defaulttransactionisolation) {
            this.defaulttransactionisolation = defaulttransactionisolation;
        }
        public boolean isDefaultautocommit() {
            return defaultautocommit;
        }
        public void setDefaultautocommit(boolean defaultautocommit) {
            this.defaultautocommit = defaultautocommit;
        }
        public boolean isDefaultreadonly() {
            return defaultreadonly;
        }
        public void setDefaultreadonly(boolean defaultreadonly) {
            this.defaultreadonly = defaultreadonly;
        }
        public String getDriverclassname() {
            return driverclassname;
        }
        public void setDriverclassname(String driverclassname) {
            this.driverclassname = driverclassname;
        }
        public int getMaxactive() {
            return maxactive;
        }
        public void setMaxactive(int maxactive) {
            this.maxactive = maxactive;
        }
        public long getMaxwait() {
            return maxwait;
        }
        public void setMaxwait(long maxwait) {
            this.maxwait = maxwait;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPart() {
            return part;
        }
        public void setPart(String part) {
            this.part = part;
        }
    }
    [/code]

    [code]
    public class ReadConfig {
        public static void main(String[] args){
            try {
                Digester digester = new Digester();
                digester.setValidating(false);
                 // 최상위 노드를 생성합니다. 객체는 addObjectCreate
                // addObjectCreate(xml 경로와노드명, 객체 타입)
                digester.addObjectCreate("config", ConfigInfo.class);
                // DatabaseInfo를 생성합니다.
                digester.addObjectCreate("config/database", DatabaseInfo.class);
                // DatabaseInfo의 part 지정
                //attributes는 addSetProperties(경로, attribute name, bean 매핑할 property) 메소드로 등록합니다.
                digester.addSetProperties("config/database", "part", "part");
                // node 추가, addBeanPropertySetter(경로와이름, 객체필드명)
                digester.addBeanPropertySetter("config/database/driverclassname",
                        "driverclassname");
                digester.addBeanPropertySetter("config/database/url", "url");
                digester.addBeanPropertySetter("config/database/maxactive",
                        "maxactive");
                digester
                        .addBeanPropertySetter("config/database/maxwait", "maxwait");
                digester.addBeanPropertySetter("config/database/defaultautocommit",
                        "defaultautocommit");
                digester.addBeanPropertySetter("config/database/defaultreadonly",
                "defaultreadonly");
                digester.addBeanPropertySetter("config/database/defaulttransactionisolation",
                "defaulttransactionisolation");
                digester.addBeanPropertySetter("config/database/username",
                        "username");
                digester.addBeanPropertySetter("config/database/password",
                        "password");
                // set하기
                // addSetNext(경로와이름, 추가할 메소드명);
                digester.addSetNext("config/database", "AddDatavaseInfo");

                File input = new File(xml파일);
                config = (ConfigInfo) digester.parse(input);

                // config에 xml값이 이제 다 담겼습니다. 이제 입맛대로 사용하시면 됩니다.

            } catch (Exception exc) {
                exc.printStackTrace();
            }
        }
    }
    [/code]

    4. 결과
        work flow
        1.  xml 구조 및 파일 생성
        2. 매핑할 객체 생성
        3. digester로 매핑하기
        4. 사용

        xml로 만들걸 객체로 생성하거나 객체로 구조 잡고 xml로 만들 수 있다면 나머진 digester가 알아서 해줍니다.
        약간의 규칙을 알아야 하는데 해당 사이트에 가보시면 문서가 잘 만들어 져 있으니 사용하는데 크게 어렵진 않을거 같습니다.
        소스를 보시면 알겠지만 기존 xml을 다루는 거와 비교해보면 월등히 간결합니다.
        기존 xml과 관련된 많은 것들이 보이지도 않습니다.
        한 30분 노력해서 앞으로 몇년을 써먹을 수 있다면 충분히 가치가 있다고 생각합니다.
        try it~~~~

    Posted by 1010
    반응형

    jakarta common util중 Beanutils사용법

    먼저.. 속성 접근 테스트에 쓰일 빈 두개.. Address , Employee

    -------------------------------------------------------------

    package com.joldo.test;

    /**
     * @author 카악퇴 joldo@joldo.com
     */
    public class Address {
     String post;
     String detail;
     
     public Address(){ 
     }
     public Address(String post, String detail){
      this();
      this.post = post;
      this.detail = detail; 
     }

     public String getDetail() {
      return detail;
     }

     /**
      * @return
      */
     public String getPost() {
      return post;
     }

     /**
      * @param string
      */
     public void setDetail(String string) {
      detail = string;
     }

     /**
      * @param string
      */
     public void setPost(String string) {
      post = string;
     }

    }
    -------------------------------------------------------------

    -------------------------------------------------------------

    package com.joldo.test;

    import java.util.HashMap;
    import java.util.Map;

    /**
     * @author 카악퇴 joldo@joldo.com
     */
    public class Employee {
     
     public Employee(){}
     
     
     // Simple Property
     private String firstName;
     public String getFirstName() {
      return firstName;
     }
     public void setFirstName(String string) {
      firstName = string;
     }

     private String lastName;
     public String getLastName() {
      return lastName;
     }
     public void setLastName(String string) {
      lastName = string;
     }
     
     private Address addr ;
     public Address getAddress(){
      return addr;
     }
     public void setAddress(Address addr){
      this.addr  = addr;
     }
     
     private int num;
     public int getNum() {
      return num;
     }
     public void setNum(int i) {
      num = i;
     }
     
     // Indexed Property
     private String[] books = {"java","tomcat","c++"};
     public String getBook(int index){
      return books[index];
     } 
     public void setBook(int index, String book){
      books[index] = book;
     }
     
     // Mapped Property
     private Map video = new HashMap();
     public String getVideo(String key){
      return (String)video.get(key);
     }
     public void setVideo(String key , String value){
      this.video.put(key,value);
     }
     
     


    }
    -------------------------------------------------------------



    다음 두개는 속성 카피 테스트에 쓰일 빈 두개..

    -------------------------------------------------------------

    /*
     * Created on 2003. 11. 21.
     *
     * To change the template for this generated file go to
     * Window>Preferences>Java>Code Generation>Code and Comments
     */
    package com.joldo.test;

    /**
     * @author 카악퇴 joldo@joldo.com
     *
     * 
     */
    public class FromClass {
     
     String fromStringToString;
     String fromStringToInt;
     String fromStringToBoolean;
     
     int fromIntToString;
     int fromIntToInt;
     int fromIntToBoolean;
     
     boolean fromBooleanToString;
     boolean fromBooleanToInt;
     boolean fromBooleanToBoolean;
     
     String fromExistToNon;
     
     public boolean isFromBooleanToBoolean() {
      return fromBooleanToBoolean;
     }

     public boolean isFromBooleanToInt() {
      return fromBooleanToInt;
     }

     public boolean isFromBooleanToString() {
      return fromBooleanToString;
     }

     public String getFromExistToNon() {
      return fromExistToNon;
     }

     public int getFromIntToBoolean() {
      return fromIntToBoolean;
     }

     public int getFromIntToInt() {
      return fromIntToInt;
     }

     public int getFromIntToString() {
      return fromIntToString;
     }

     public String getFromStringToBoolean() {
      return fromStringToBoolean;
     }

     public String getFromStringToInt() {
      return fromStringToInt;
     }

     public String getFromStringToString() {
      return fromStringToString;
     }

     public void setFromBooleanToBoolean(boolean b) {
      fromBooleanToBoolean = b;
     }

     public void setFromBooleanToInt(boolean b) {
      fromBooleanToInt = b;
     }

     public void setFromBooleanToString(boolean b) {
      fromBooleanToString = b;
     }

     public void setFromExistToNon(String string) {
      fromExistToNon = string;
     }

     public void setFromIntToBoolean(int i) {
      fromIntToBoolean = i;
     }

     public void setFromIntToInt(int i) {
      fromIntToInt = i;
     }

     public void setFromIntToString(int i) {
      fromIntToString = i;
     }

     public void setFromStringToBoolean(String string) {
      fromStringToBoolean = string;
     }

     public void setFromStringToInt(String string) {
      fromStringToInt = string;
     }

     public void setFromStringToString(String string) {
      fromStringToString = string;
     }

    }
    -------------------------------------------------------------

    -------------------------------------------------------------

    /*
     * Created on 2003. 11. 21.
     *
     * To change the template for this generated file go to
     * Window>Preferences>Java>Code Generation>Code and Comments
     */
    package com.joldo.test;

    /**
     * @author 카악퇴 joldo@joldo.com
     *
     * 
     */
    public class ToClass {

     String fromStringToString;
     int fromStringToInt;
     boolean fromStringToBoolean;
     
     String fromIntToString;
     int fromIntToInt;
     boolean fromIntToBoolean;
     
     String fromBooleanToString;
     int fromBooleanToInt;
     boolean fromBooleanToBoolean;
     
     String fromNonToExist;

     public boolean isFromBooleanToBoolean() {
      return fromBooleanToBoolean;
     }

     public int isFromBooleanToInt() {
      return fromBooleanToInt;
     }

     public String isFromBooleanToString() {
      return fromBooleanToString;
     }


     public boolean getFromIntToBoolean() {
      return fromIntToBoolean;
     }

     public int getFromIntToInt() {
      return fromIntToInt;
     }

     public String getFromIntToString() {
      return fromIntToString;
     }

     public boolean getFromStringToBoolean() {
      return fromStringToBoolean;
     }

     public int getFromStringToInt() {
      return fromStringToInt;
     }

     public String getFromStringToString() {
      return fromStringToString;
     }

     public void setFromBooleanToBoolean(boolean b) {
      fromBooleanToBoolean = b;
     }

     public void setFromBooleanToInt(int b) {
      fromBooleanToInt = b;
     }

     public void setFromBooleanToString(String b) {
      fromBooleanToString = b;
     }


     public void setFromIntToBoolean(boolean i) {
      fromIntToBoolean = i;
     }

     public void setFromIntToInt(int i) {
      fromIntToInt = i;
     }

     public void setFromIntToString(String i) {
      fromIntToString = i;
     }

     public void setFromStringToBoolean(boolean string) {
      fromStringToBoolean = string;
     }

     public void setFromStringToInt(int string) {
      fromStringToInt = string;
     }

     public void setFromStringToString(String string) {
      fromStringToString = string;
     }

     public String getFromNonToExist() {
      return fromNonToExist;
     }

     public void setFromNonToExist(String string) {
      fromNonToExist = string;
     }

    }
    ------------------------------------------------------




    ------------------------------------------------------

    다음 main 을 가지고 있는 테스트 클래스.

    ------------------------------------------------------

    package com.joldo.test;

    import java.util.HashMap;

    import org.apache.commons.beanutils.BasicDynaClass;
    import org.apache.commons.beanutils.BeanUtils;
    import org.apache.commons.beanutils.DynaBean;
    import org.apache.commons.beanutils.DynaProperty;
    import org.apache.commons.beanutils.PropertyUtils;
    import org.apache.commons.beanutils.WrapDynaBean;

    /**
     * @author 카악퇴 joldo@joldo.com
     *
     * 외부 의존 패키지
     * commons-beanutils
     * commons-collections
     * commons-logging
     */
    public class SimpleTest {

     public static void main(String[] args) {
     
      Employee emp = new Employee();
     
      /**
       * < PropertyUtils >
       * simple property
       */  
      emp.setFirstName("yoon");
      emp.setLastName("sangjin");
      emp.setNum( 10 );
     
      String firstName = null;
      String lastName = null;
      int num = -1;
     
      try{
       firstName = (String)(PropertyUtils.getSimpleProperty(emp,"firstName"));
       lastName =  (String)(PropertyUtils.getSimpleProperty(emp,"lastName"));
       num = ( (Integer)(PropertyUtils.getSimpleProperty(emp,"num")) ).intValue();


       System.out.println( firstName + ":" + lastName + ":" + num );
      }catch( Exception e){
       System.out.println( e );
      }
     
     
     
     
      /**
       * < PropertyUtils >
       * Indexed property
       */ 
      int index = 0;
      String name = "book";
      String value1 = null;
      String value2 = null;
      try{
       value1 = (String)(PropertyUtils.getIndexedProperty(emp , name , index ));  
       value2 = (String)(PropertyUtils.getIndexedProperty(emp , name + "[" + index + "]"  ));
     
       System.out.println( value1 + ":" + value2 );
      }catch( Exception e){
       System.out.println( e );
      }
     
     
     
      /**
       * < PropertyUtils >
       * Mapped property
       */  
      name = "video";
      String key = "oracle";
      String setValue = "this is oracle's value";
     

      try{
      
       PropertyUtils.setMappedProperty( emp , name + "(" + key + ")" , setValue);  
       String getValue = (String)( PropertyUtils.getMappedProperty(emp , name + "(" + key + ")" ) );
       System.out.println( getValue );
      }catch( Exception e){
       System.out.println( e );
      } 
      /** OR **/
      try{
       PropertyUtils.setMappedProperty( emp , name , key  , setValue);  
       String getValue = (String)( PropertyUtils.getMappedProperty(emp , name , key ) );
       System.out.println( getValue );
      }catch( Exception e){
       System.out.println( e );
      }
     
      /**
       * < PropertyUtils >
       * Nested Property
       */
      name = "address.post";
      Address address = new Address("152-093","seoul korea");
      emp.setAddress( address );
     
      try {
       String post = (String)PropertyUtils.getNestedProperty(emp , "address.post");
       System.out.println( post );  
      
      }catch( Exception e){
       System.out.println( e);
      }
     
      /**
       * < DynaBean , DynaClass >
       *
       * 동적 클래스 정의 및 인스턴스 생성
       */
      DynaProperty[] props = new DynaProperty[]{
       new DynaProperty("firstName", String.class),
       new DynaProperty("lastName",  String.class),
       new DynaProperty("num", Integer.class),
       new DynaProperty("addr", Address.class),
       new DynaProperty("books", String[].class )
      };
     
      BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);
     
      DynaBean employee;
      try {
       employee = dynaClass.newInstance();

       employee.set("firstName", "lee");
       employee.set("lastName", "sun sin");  
       employee.set("num", new Integer(10));
       employee.set("addr", new Address() );  
       employee.set("books",new String[10]);
       employee.set("books",0,"javabook");
      
       // error code
       //employee.set("books[0]","javabook");
     
       System.out.println( employee.get("firstName") );
       System.out.println( employee.get("lastName"));
       System.out.println( employee.get("num"));
       System.out.println( employee.get("books",0));

      } catch (Exception e) {
       System.out.println( e );
      }
     
     
     
     
     
     
      /**
       * WrapDynaBean
       *
       * 이미 제작되어진 빈즈를 래핑하여
       * DynaBean 인터페이스로서 접근
       *
       */
      DynaBean wrapper = new WrapDynaBean(emp);
      name = "firstName";
      firstName = null;
      firstName = (String) wrapper.get(name);
      System.out.println( name );
     
     
      /**
       * BeanUtils (property converting)
       *
       * 스트링 값을 이용
       * 특정빈의 int,boolean등 primitive 타입으로
       * 변환하여 빈즈 세팅
       */
      Employee empLim = new Employee();
      HashMap map = new HashMap();
      map.put("firstName","lim");
      map.put("lastName","kkuk jung");
      map.put("num","24");
     
      try {
       BeanUtils.populate(empLim,map);
      }catch (Exception e) {
       e.printStackTrace();
      }
     
      System.out.println(empLim.getFirstName());
      System.out.println(empLim.getLastName());
      System.out.println(empLim.getNum());
     
      /**
       * BeanUtils (property copy)
       *
       * from 객체의 getter 메소드와
       * to 객체의 setter 메소드를 추출하여
       * from 객체의 속성값으로부터 to 객체 속성값 세팅
       * 스트러츠의 ActionForm 과 모델측의 VO간의 매핑시
       * 유용하게 쓰일 듯..
       */
     
      FromClass from = new FromClass();
      ToClass to = new ToClass();
     
      from.setFromStringToString("string");
      from.setFromStringToInt("10");
      from.setFromStringToBoolean("true");
      from.setFromIntToString(10);
      from.setFromIntToInt(20);
      from.setFromIntToBoolean(30);
      from.setFromBooleanToString(true);
      from.setFromBooleanToInt(true);
      from.setFromBooleanToBoolean(true);
      from.setFromExistToNon("string"); 
     
     
      try {
       System.out.println("-- before copy --");
       System.out.println( to.getFromStringToBoolean() );
       System.out.println( to.getFromStringToInt()     );
       System.out.println( to.getFromStringToString()  );
       System.out.println( to.getFromIntToBoolean()    );
       System.out.println( to.getFromIntToInt()        );
       System.out.println( to.getFromIntToString()     );
       System.out.println( to.isFromBooleanToBoolean() );
       System.out.println( to.isFromBooleanToInt()     );
       System.out.println( to.isFromBooleanToString()  );
       System.out.println( to.getFromNonToExist()      );
      
       // clone 이 아닌
       // 다른 클래스의 인스턴스간 속성값 copy
       BeanUtils.copyProperties( to, from);

       System.out.println("-- after copy --");
       System.out.println( to.getFromStringToBoolean() );
       System.out.println( to.getFromStringToInt()     );
       System.out.println( to.getFromStringToString()  );
       System.out.println( to.getFromIntToBoolean()    );
       System.out.println( to.getFromIntToInt()        );
       System.out.println( to.getFromIntToString()     );
       System.out.println( to.isFromBooleanToBoolean() );
       System.out.println( to.isFromBooleanToInt()     );
       System.out.println( to.isFromBooleanToString()  );
       System.out.println( to.getFromNonToExist()      );


      }catch (Exception e) {
       e.printStackTrace();
      }
     }
    }


    ********************************************************************************
    http://blog.naver.com/rosekingdom/60001408076
    스타일시트나 다른 HTML 자원을 참조할 시에는 <html:link rewrite>를 사용하라

    html:link rewrite 태그는 컨텍스트 상대적인 URL을 사용할 수 있다. 또한, 필요하다면 URL을 인코딩하는 것도 가능하기 때문에 컨테이너가 보안을 관리하는 자원의 경우 이용할 수 있다.
    예제)
    <LINK REL=’stylesheet’ HREF=”’<html:rewrite page=’/path/to/CSS”/>’ TYPE=’text/css’>

    정적인 페이지 접근을 위한 액션(Action)은 어찌 할 것인가?

    모델 2 환경에서 페이지는 깔끔하지만, 처리한 내용만 보여주기 때문에 단순하다고 할 수 있다. 모델 2 환경에서는 액션을 먼저 거친 후, 페이지로 이동을 해야 한다(바로 이 부분이 모델 1에서 웹 프로그래밍 하던 사람들이 고생하는 것으로 역자 또한 그러하다). 그리고 액션은 페이지가 request, session context에 담아서 보낸 정보를 조합하여 나타낼 뷰를 결정해야 한다. 그러나 액션은 뷰의 주소를 모른다(ActionMapping이 알고 있음). 모든 페이지는 하나의 액션 핸들러를 가지고 있어야 한다. 하나의 액션은 다수의 다른 뷰 페이지를 핸들링 할 수 있으며 하나의 페이지는 여러 다른 액션에 의해서 핸들링 될 수 있다. 그러나 하나의 페이지는 적어도 하나의 액션 핸들러를 반드시 가지고 있어야 한다. 여기서 한가지 질문! 그렇다면 static한 페이지에 접근하는 경우, 모두 액션을 가지고 있어야 하는가? 100개의 페이지가 있다면 그것들 모두 액션을 가지고 있어야 하는가? 이런 경우를 대비해서 스트럿츠에는 ForwardAction이라는 액션을 제공한다. 액션 맵핑에 아래와 같이 입력해주면 된다.
    예제)
    <action    name="boardForm"
                parameter="/boardcreate.jsp"
                path="/boardcreateform"
                type="org.apache.struts.actions.ForwardAction"
                validate="false"/>

    다른 건 다 무시하고, parameter와 type만 살펴보자. Boardcreate.jsp은 새로운 글을 입력하는 페이지로, 실제로 액션이 있어도 아무런 역할을 하지 못한다. 이 경우 org.apache.struts.actions.ForwardAction를 액션 타입으로 지정하고, parameter란에 이동하고자 하는 페이지 주소를 적는다. 이 경우는 /boardcreateform.do를 (*.do으로 서블릿 매핑이 되었다고 가정) 주소창에 치면 우선은 ForwardAction을 통과해서 boardcreate.jsp로 포워드 된다. 이렇게 함으로써 모델 2에서 Action을 거쳐서 페이지로 이동해야 한다는 조건을 만족하게 된다.

    액션이 하는 일이 매우 작다면 하나의 액션을 가지고 여러 일을 처리하는 방법을 간구하라

    간단히 게시판을 만드는 중이었다. 게시물 삭제, 수정, 수정하는 폼, 등록, 답글 달기, 주석 달기 등 이렇게 하나하나 액션을 만들다 보니 관리해야 할 액션 클래스만 몇 십 개가 되어버렸다. 이렇게 하나의 단위로 묶을 만한 액션들 다수가 존재한다면, DispatchAction의 사용을 고려해봐야 한다. 곧바로 예제를 보겠다.
    예제)
    <action
                input="/board/bullcreateform.jsp"
                name="bullForm"
                parameter="method"
                path="/bull"
                scope="request"
                type="webfx.board.struts.action.BullAction"
                validate="false"/>

    다른 액션 맵칭하는 방법과 거의 다르지 않다. 여기서 특이하게 볼 내용은 parameter이다. 그리고 BullAction의 코드는 아래와 같다.

    public BullAction extends org.apache.struts.actions.DispatchAction {
     public ActionForward create(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws Exception {
     …
    };
     public ActionForward remove(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws Exception {
     ….
     }

     등등…
    }

    이 소스에는 execute라는 메소드가 없다. 단지 Method Signature가 이름을 제외하고 execute와 다른 create, remove 함수가 있다. 그리고 이 액션은 org.apache.struts.actions.DispatchAction을 상속했다. 우리가 parameter로 지정한 값은 request로 넘어올 파라미터 이름이다. 그리고 이 파라미터에 우리가 실행시키고자 하는 메소드 명을 지정하면 그 메소드가 실행된다. 예를 들어 /bull.do?method=create라고 실행시켰다면 BullAction에서 create 함수를 실행시키게 된다. 이처럼 관리하기 힘들 정도로 액션이 늘어날 경우에는 DispatchAction을 사용하는 것도 고려해봐야 한다.

    데이터와 Value Objects을 교환하기 위해서 populate 유틸리티를 사용하라

    Jakarta.apache.org 사이트에 있는 Commons에 속한 BeanUtils를 말하는 겁니다. BeanUtils.populate()와 describe() 메소드는 beans와 임의 타입 간의 데이터 전송 사용을 위해서 사용될 수 있다. 요구되는 것은 대응되는 public 형의 프로퍼티 이름들과 네이티브 타입이다. Wrapping Object 즉, Integer 같은 것들은 지원이 되지만 그 외의 타입은 지원되지 않는다(예, Date).

    describe 메소드는 소스 빈을 스트링 값을 가지고 있는 맵으로 만든다. populate 메소드는 그러한 맵을 읽어서 타겟이 되는 빈에 프로퍼티를 적용하는 역할을 한다. 만약 타겟 타입이 스티링이 아니라면 필요한 타입으로 변경이 된다. 만약 변환이 실패하면 예외가 발생한다. 다음이 두 빈(Bean) 간의 데이터 이동하는 소스이다.
    BeanUtils.populate(target, BeanUtils.describe(source));

    몇몇 경우에 필요한 만큼만 조절한 ActionBean에서 또한 조절된 값을 가지고 있는 Map을 만들기 위해서 커스터마이징한 describe 메소드가 필요할 때가 있다. 다른 값들 (즉, 지원되지 않는 타입)은 직접 세팅해 줄 수 있다.
    BeanUtils.populate(target, source.describe());
    tartget.setDate(source.getDateBinary());

    BeanUtils을 직접 이용하는 것보다 유틸클래스를 하나 만들어, 그곳에서 BeanUtils 클래스를 이용하도록 만드는 게 더 좋을 것이다. 예를 들면 아래와 같다.
    valueUtils.populate(ValueObject target, ActionForm source);
    valueUtils.populate(ActionForm target, ValuteObject source);

    공통적으로 사용되는 속성들에 대한 객체를 만들어 사용하라

    웹 애플리케이션 안에서 애플리케이션에 전체적으로 걸쳐서 사용되어야 하는 속성이 있다. 사용자 정보, 북마크, 메뉴 세팅, 다른 프로퍼티 등등이 이것에 속할 수 있다. 이때에는 이런 것들을 각각 따로 관리하는 것보다 이런 것들을 한꺼번에 래핑하는 객체를 만들어서 사용하자.

    애플리케이션을 위한 Base Action을 사용하라

    애플리케이션 액션들은 몇몇 기본적인 작업을 처리한다. 이러한 태스크를 일관적으로 구현하기 위해, 애플리케이션의 다른 액션들이 서브클래싱하여 사용할 수 있는 Base 클래스를 이용하자! 만약 개개 액션의 execute 메소드 안에서 중요하게 처리되어야 할 일이 있다면 서브 클래스들이 execute() 메소드를 통해서 실행시킬 추상 메소드를 사용하는 것이 좋은 접근 방법이다. Base 클래스는 기본적으로 자신이 해야 할 일을 하고 그것이 모두 잘 처리 되었을 때, 새로운 메소드를 실행시키고 그 결과를 리턴한다. 이 부분 때문에 새로운 메소드의 서명을 변경할 수 있다.
    Public ActionForward execute(ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
       throws IOException, ServletException {

      // Application specific bevaviour
      // if evevrything is kosher call subclass

      return (executeAction(mapping, form, request, response, myParameter))
    }

    executeAction은 Base 클래스의 추상 클래스이다.

    이중으로 submit이 일어나는 것을 막기 위해서 Action Token을 사용하라

    스트럿츠의 액션에 one-use tokens을 만들어 담는 메소드들이 있다. 폼이 작성될 때 세션에 토큰이 위치하게 되고, 이 토큰은 HTML 폼에 hidden 프로퍼티로 포함된다. 그 폼이 리턴 될 때 토큰은 validation이 된다. 만약 validation에 실패하면 벌써 그 폼은 submit이 된 것이므로 사용자는 그에 대한 알림 메시지를 받을 수 있다.
    saveToken(request)
    on the return trip
    isTokenValid(request)
    resetToken(request)
    스트럿츠를 이용한 웹 애플리케이션 개발 사이클
    화면 설계서를 개발한다. (화면 요구사항 설계)
    화면 요구사항을 만족하기 위한 Helper 클래스를 개발한다. (value object)
    워크플로우 요구사항을 개발한다. (화면 이동 등의 스토리보드 작성)
    워크플로우 요구사항을 만족시키기 위한 ActionMapping을 개발한다.
    ActionMapping을 사용하여 워크 플로우를 검증하기 위한 Stub JSP 페이지를 작성한다.
    ActionMapping에 의해 호출 되는 액션을 개발한다.
    Stub Jsp를 작성한다.


    Posted by 1010
    반응형

    Commons-Digester


    I. Digester ?

    digester는 XML파일로 저장된 정보를 java 객체에 매핑해 주는 API입니다. 하지만 그 반대기능은 되지 않습니다.

    보통은 XML파일을 parsing할때 SAX와 DOM을 이용하였지만 DOM은 성능이 좀 느리고, SAX는 DOM보다는 빠르지만 코드가 난잡해 집니다. digester는 SAX기반이지만 pattern 매칭으로 아주 쉽고 빠르게 XML 파일을 parsing합니다


    원래 digester는 struts의 struts-config.xml 정보를 로딩하기위해 개발되어 struts의 한 부분이었는데 독립적으로 commons project로 분리되었습니다.


    II. 준비물

    digester를 사용하기 위해서는 다음 4가지 준비물이 필요합니다

    commons-digester http://jakarta.apache.org/site/downloads/downloads_commons-digester.cgi

    commons-beanutils http://jakarta.apache.org/site/downloads/downloads_commons-beanutils.cgi

    commons-collections http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi

    commons-logging http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi

    만약 digester는 SAX API를 사용하기 때문에 jdk1.4 이상의 버젼이 아니라면 Xerces같은 XML parser가 필요합니다


    아래예제를 실행하려면 dbcp 관련 파일도 필요하며 이는 Commons-dbcp 편을 참조하세요~


    참고 사이트

    commons-digester API http://jakarta.apache.org/commons/digester/commons-digester-1.6/docs/api/index.html

    commons-digester Guide http://jakarta.apache.org/commons/digester/apidocs/org/apache/commons/digester/package-summary.html


    III. 시작하기 전에..

    보통 digester는 여러 설정값들을 xml에 정의해 놓고 이를 어플리케이션에서 로드하는 방식으로 많이 이용됩니다. 이 강좌에서도 데이터베이스 정보를 xml에 정의해 놓고 이를 로딩하여 데이터베이스에 연결하는 예제를 강의할 것입니다.

    시작하기 전에 XML에 대한 어느정도 기본 지식이 필요합니다.


    주요함수

    다른 함수들도 많이만 가장 많이 사용되는 다음 4가지만 딱 눈으로 익히고 갑시다


    -. addObjectCreate(element 경로, 자바빈크래스) : 어떤 element 경로를 자바빈클래스로 매칭?

    -. addSetProperties(element 속성명, 자바빈프로퍼티명) : 어떤 element 속성을 자바빈 변수에 설정?

    -. addBeanPropertySetter(element 경로, 자바빈프로퍼티명) : 어떤 element 경로를 자바빈 변수에 설정?

    -. addSetNext(element 경로, 자바빈함수) : 어떤 element 경로를 자바빈 함수에?

    대강은 이런 뜻으로 알고 넘어 갑쉬다~!

    그럼 element 경로가 먼가요? 다음에 나옵니다


    Element Matching Pattern

    XML에 element들의 path를 pattern으로 인식하는 방법만 익힙시다.


    <a>                 -- Matches pattern "a"
        <b>             -- Matches pattern "a/b"
            <c/>        -- Matches pattern "a/b/c"
            <c/>        -- Matches pattern "a/b/c"
        </b>
        <b>             -- Matches pattern "a/b"
            <c/>        -- Matches pattern "a/b/c"
            <c/>        -- Matches pattern "a/b/c"
            <c/>        -- Matches pattern "a/b/c"
        </b>
    </a>


    위 XML을 보면 element a가 최상위 루트 element 입니다.

    이것은 "a"로 매칭되며 그다음 a의 서브 element b는 "a/b" 로 매칭합니다

    그다음은 .. "a/b/c".. 

    쉽죠?

    즉 최상위만 "/"가 붙지 않으며 그 이하는 트리구조처럼 "/"를 붙여주면 됩니다


    자 그럼 좀전에 보았던 함수들과 연관지어 보면..

    ...

    digester.addObjectCreate("a/b", B.class);

    digester.addBeanPropertySetter("a/b/c", "c");

    ...

    요렇게 쓰입니다.


    IV. Digester를 이용하여 데이터베이스 커넥션 정보를 DBCP로 멀티 설정하여 웹에서 사용해 보자!

    자 이제 실질적인 예제를 봅시다~


    무엇을 하려고 하려면 mysql과 oracle jdbc정보를 xml 파일에 기록해 두고 이를 딱 한번만 읽어서 이정보를 데이터베이스 커넥션풀인 dbcp에 설정할 것입니다


     XML 파일

    다음과 같은 XML 파일이 있습니다. 이 파일은 mysql과 oracle을 연결하는 커넥션 정보를 가지고 있습니다

    이 파일이름은 C:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\config.xml입니다

    mysql과 oracle 두개의 jdbc pool을 dbcp로 설정할 것입니다.

    이 파일은 제가 임의로 정해서 만든겁니다.


    <?xml version="1.0" encoding="EUC-KR"?>


    <connection-sources>
       <description>This script is a connection description</description>


       <JDBCConnectionPool name="mysql">
          <description>Mysql Connection Source</description>
          <defaultAutoCommit>true</defaultAutoCommit>
          <defaultReadOnly>false</defaultReadOnly>
          <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
          <maxActive>10</maxActive>
          <maxIdle>10</maxIdle>
          <maxWait>10000</maxWait>
          <username>unicorn</username>
          <password>iloveyou</password>
          <url>jdbc:mysql://localhost/unicorn</url>
       </JDBCConnectionPool>


       <JDBCConnectionPool name="oracle">
          <description>Oracle Connection Source</description>
          <defaultAutoCommit>true</defaultAutoCommit>
          <defaultReadOnly>false</defaultReadOnly>
          <driverClassName>oracle.jdbc.driver.OracleDriver</driverClassName>
          <maxActive>10</maxActive>
          <maxIdle>10</maxIdle>
          <maxWait>10000</maxWait>
          <username>unicorn</username>
          <password>iloveyoutoo</password>
          <url>jdbc:oracle:thin:@localhost:1521:unicorn</url>
       </JDBCConnectionPool>


    </connection-sources>


    web.xml

    웹 배치파일에 의해 db.ConnectionInitialize.java를 초기 서블릿 컨테이너 로딩시 실행하여 XML정보를 DBCP로 세팅할 것입니다

    config.xml 파일경로를 config 파라미터에 설정합니다


    <?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/dtd/web-app_2_3.dtd">

    <web-app>

      <servlet>
         <servlet-name>connectioninitialize</servlet-name>
         <servlet-class>db.ConnectionInitialize</servlet-class>
         <init-param>
            <param-name>config</param-name>
            <param-value>C:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\config.xml</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
      </servlet>
       
    </web-app>


    db.jdbc.ConnectionSources.java

    XML 파일에서도 보면 알수있듯이 connection-sources 서브요소인 description과 JDBCConnectionPool을 저장하는 객체입니다. 변수를 눈여겨 봅시다


    package db.jdbc;
    
    import java.util.HashMap;
    
    public class ConnectionSources {
    
    	private String description;
    	private HashMap source = new HashMap();
    	
    	public void setDescription(String description) {
    		this.description = description;
    	}
    	
    	public void addSource(JDBCConnectionPool source) {
    		this.source.put(source.getName(), source);
    	}
    	
    	public String getDescription() {
    		return this.description;
    	}
    	
    	public JDBCConnectionPool getSource(String name) {
    		return (JDBCConnectionPool)this.source.get(name);
    	}
    }
    

    addSource 함수는 여러 JDBCConnectionPool 정보를 ConnectionSourcec 의 source에 저장합니다. 이 함수는 밑에서 다시 나오니 눈여겨 봅시다


    db.jdbc.JDBCConnectionPool.java

    변수에 대해 단순히 setter, getter로 이루어져 있습니다.

    XML 파일을 보면JDBCConnectionPool 의 서브 element들을 저장하는 객체이며 XML파일과  이 java 변수명들과 매칭되는것을 알수 있을겁니다.

    대강 감이 오나요? ㅡ.ㅡ? 감좌봐쓰~?


    package db.jdbc;
    
    public class JDBCConnectionPool {
    
    	private String name;
    	private String description;
    	private boolean defaultAutoCommit;
    	private boolean defaultReadOnly;
    	private String driverClassName;
    	private int maxActive;
    	private int maxIdle;
    	private int maxWait;
    	private String username;
    	private String password;
    	private String url;
    	
    //for debug public void print() { String toString = "name : "+name+"\n"+ "description : "+description+"\n"+ "defaultAutoCommit : "+defaultAutoCommit+"\n"+ "defaultReadOnly : "+defaultReadOnly+"\n"+ "driverClassName : "+driverClassName+"\n"+ "maxActive : "+maxActive+"\n"+ "maxIdle : "+maxIdle+"\n"+ "maxWait : "+maxWait+"\n"+ "username : "+username+"\n"+ "password : "+password+"\n"+ "url : "+url; System.out.println(toString); } public void setName(String name) { this.name = name; } public void setDescription(String description) { this.description = description; } public void setDefaultAutoCommit(boolean defaultAutoCommit) { this.defaultAutoCommit = defaultAutoCommit; } public void setDefaultReadOnly(boolean defaultReadOnly) { this.defaultReadOnly = defaultReadOnly; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public void setMaxWait(int maxWait) { this.maxWait = maxWait; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setUrl(String url) { this.url = url; } public String getName() { return this.name; } public String getDescription() { return this.description; } public boolean getDefaultAutoCommit() { return this.defaultAutoCommit; } public boolean getDefaultReadOnly() { return this.defaultReadOnly; } public String getDriverClassName() { return this.driverClassName; } public int getMaxActive() { return this.maxActive; } public int getMaxIdle() { return this.maxIdle; } public int getMaxWait() { return this.maxWait; } public String getUsername() { return this.username; } public String getPassword() { return this.password; } public String getUrl() { return this.url; } }

    db.ConnectionInitialize.java

    자 이제 여기가 핵심 클래스입니다. 위부분이 모두이해가 되었으면 다음 소스코드를 살펴봅시다

    web.xml에서 정의한 config.xml을 로딩하여 파싱하고 그 정보를 DBCP에 설정합니다

    mysql과 oracle 두개의 jdbc를 설정하도록 xml에 정의하였었습니다.


    package db;
    
    import java.sql.*;
    import java.util.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    import org.apache.commons.pool.ObjectPool;
    import org.apache.commons.pool.impl.GenericObjectPool;
    import org.apache.commons.dbcp.ConnectionFactory;
    import org.apache.commons.dbcp.PoolingDriver;
    import org.apache.commons.dbcp.PoolableConnectionFactory;
    import org.apache.commons.dbcp.DriverManagerConnectionFactory;
    
    import org.apache.commons.digester.Digester;
    import java.io.*;
    import org.xml.sax.SAXException;
    
    import db.jdbc.ConnectionSources;
    import db.jdbc.JDBCConnectionPool;
    
    public class ConnectionInitialize extends HttpServlet {
    
    	public void init() throws ServletException {
    
    		String config = null;
    		// web.xml의 배치스크립트의 파라미터이름을 가져온다
    		Enumeration names = getServletConfig().getInitParameterNames();
    		do {
    			if(!names.hasMoreElements())
    			break;
    			String name = (String)names.nextElement();
    			String value = getServletConfig().getInitParameter(name).trim();
    
    			System.out.println(name+" : "+value);
    
    			// config에 정의된 XML 파일을 가져온다
    			if (name.startsWith("config"))
     				config = value;
    		} while(true);
    
    		//Digester를 생성하고
    		Digester digester = new Digester();
    		//XML 유효성을 검증할것인가?
    		digester.setValidating(false);
    
    		//connection-sources 요소를 ConnectionSources.class 객체에 저장하겠다
    		digester.addObjectCreate("connection-sources",
    		                          ConnectionSources.class);
    
    		//connection-sources/description 요소를
    		//ConnectionSources.class의 description 변수에 저장하겠다
    		digester.addBeanPropertySetter("connection-sources/description",
    						"description");
    
    		//connection-sources/JDBCConnectionPool 요소를 JDBCConnectionPool.class 객체에저장하겠다
    		digester.addObjectCreate("connection-sources/JDBCConnectionPool",
    		                         db.jdbc.JDBCConnectionPool.class);
    
    		//connection-sources/JDBCConnectionPool 요소의 name 속성을
    		//JDBCConnectionPool.class의 name 변수에 저장하겠다
    		digester.addSetProperties("connection-sources/JDBCConnectionPool", "name", "name");
    
    		//connection-sources/JDBCConnectionPool/description 을
    		//JDBCConnectionPool.class 객에의 description 변수에 저장하겠다
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/description",
                                           "description");
    
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/defaultAutoCommit",
                                           "defaultAutoCommit");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/defaultReadOnly",
                                           "defaultReadOnly");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/driverClassName",
                                           "driverClassName");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxActive",
                                           "maxActive");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxIdle",
                                           "maxIdle");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxWait",
                                           "maxWait");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/username",
                                           "username");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/password",
                                           "password");
    		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/url", "url");
    
    		//connection-sources/JDBCConnectionPool을
    		//ConnectionResources.class의 addSource를 이용하여 반복적으로 추가하겠다
    		digester.addSetNext("connection-sources/JDBCConnectionPool", "addSource");
    
    		try {
    			File file = new File(config);
    			//digester 파싱 및 결과 리턴
    			ConnectionSources connectionSources =
                                                                 (ConnectionSources)digester.parse(file);
    
    			System.out.println(connectionSources.getDescription());
    			JDBCConnectionPool mysql = connectionSources.getSource("mysql");
    			mysql.print(); //디버깅
    			JDBCConnectionPool oracle = connectionSources.getSource("oracle");
    			oracle.print(); //디버깅
    
    			//이부분 이하는 Commons-dbcp 부분을 참조하기 바란다
    			setupDriver(mysql.getName(),
    				mysql.getDriverClassName(),
    				mysql.getUrl(),
    				mysql.getUsername(),
    				mysql.getPassword(),
    				mysql.getDefaultAutoCommit(),
    				mysql.getDefaultReadOnly(),
    				mysql.getMaxActive(),
    				mysql.getMaxIdle(),
    				mysql.getMaxWait());
    
    			setupDriver(oracle.getName(),
    				oracle.getDriverClassName(),
    				oracle.getUrl(),
    				oracle.getUsername(),
    				oracle.getPassword(),
    				oracle.getDefaultAutoCommit(),
    				oracle.getDefaultReadOnly(),
    				oracle.getMaxActive(),
    				oracle.getMaxIdle(),
    				oracle.getMaxWait());
    
    		} catch (FileNotFoundException filenotfoundexception) {
    			System.out.println("Config file not found");
    			filenotfoundexception.printStackTrace();
    		} catch (Exception exception) {
    			exception.printStackTrace();
    		}
    	}
    
    	public void setupDriver(String poolName,
    			String driverClassName,
    			String url,
    			String username,
    			String password,
    			boolean defaultAutoCommit,
    			boolean defaultReadOnly,
    			int maxActive,
    			int maxIdle,
    			long maxWait) throws Exception {
    		try {
    		    Class.forName(driverClassName);
    		} catch (ClassNotFoundException classnotfoundexception) {
    			System.out.println(driverClassName+" is not found");
    		    classnotfoundexception.printStackTrace();
    		    throw classnotfoundexception;
    		}
    		
    		GenericObjectPool connectionPool = new GenericObjectPool(null);
    		connectionPool.setMaxActive(maxActive);
    		connectionPool.setMaxIdle(maxIdle);
    		connectionPool.setMaxWait(maxWait);
    		
    		ConnectionFactory connectionFactory
    			= new DriverManagerConnectionFactory(url, username, password);
    		
    		PoolableConnectionFactory poolableConnectionFactory
    			= new PoolableConnectionFactory(connectionFactory,
    		                                   connectionPool,
    		                                   null,
    		                                   null,
    		                                   defaultReadOnly,
    		                                   defaultAutoCommit);
    		
    		Class.forName("org.apache.commons.dbcp.PoolingDriver");
    		PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
    		
    		driver.registerPool(poolName,connectionPool);    
    	}
    }
    

    그럼 이렇게 설정한 DBCP는 어떻게 사용할까요?

    이전강좌에서 보았던 ConnectionContext.java와 ConnectionResource.java를 다시 사용해봅시다

    package db;
    
    public interface ConnectionContext {
    	public java.sql.Connection getConnection();
    	public void rollback();
    	public void commit();
    	public void release();
    }
    

    ConectionContext를 구현한 ConnectionResource는 풀이름을 받아 처리하는것으로 수정하였습니다

    package db;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import javax.sql.DataSource;
    
    public class ConnectionResource implements ConnectionContext {
    	private Connection connection = null;
    	private boolean transaction = false;
    
        public ConnectionResource(String poolName) throws Exception {
        	init(false, poolName);	
        }
        
        public ConnectionResource(boolean transaction, String poolName) throws Exception {
        	init(transaction, poolName);
        }
        
        public void init(boolean transaction, String poolName) throws Exception {
        	this.transaction = transaction;
        	connection = DriverManager.getConnection("jdbc:apache:commons:dbcp:"+poolName);
    if (transaction) connection.setAutoCommit(false); if (connection == null) throw new Exception("fail to get connection"); } public Connection getConnection() { return connection; } public void rollback() { if (transaction) { if (connection != null) try { connection.rollback(); } catch (Exception e) {} } } public void commit() { if (transaction) { if (connection != null) try { connection.commit(); } catch (Exception e) {} } } public void release() { { if (connection != null) { if (transaction) { try { connection.setAutoCommit(true); } catch (Exception e) {} } try { connection.close(); } catch (Exception e) {} }    } }

    jsp 파일

    마지막으로 ConnectionContext는 풀이름과 함께 사용하면 되겠네요

    <%@ page contentType="text/html;charset=EUC_KR" %>
    <%@ page import="java.sql.*,db.*" %>

    <%

    ConnectionContext mysqlContext = new ConnectionResource("mysql");
    // 혹은 ConnectionContext oracleContext = new ConnectionResource("oracle");


    Connection connection = null;


    try {


        connection = mysqlContext.getConnection();
        out.println(connection);


    } catch (Exception exception) {
        exception.printStackTrace();
    } finally {
        if (connection != null) try { mysqlContext.release(); } catch (Exception exception) {}
    }

    %>


    DBCP 부분은 이전 강좌인 Commons-dbcp (http://www.jakartaproject.com/article/jakarta/1111890409958 )부분을 참조하세요~


    ps. 휴 =3 다썼당 ^_^

         흠.. 쉽게쓴다고 이리 저리 하루종일 썼는데 설명이 많이 부족한것 같네요

         그밖에 사항은 알아서 찾아보기~!

         digester II탄에서는 xml 설정파일로부터 규칙을 읽어 xml을 파싱하는 방법을 알아보겠습니다~


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

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

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

     
    Posted by 1010
    반응형

    Commons-DbUtils

     

    I. 어디서 다운을 받나요?


    http://jakarta.apache.org/site/downloads/downloads_commons-dbutils.cgi

    http://jakarta.apache.org/commons/dbutils/apidocs/index.html


    II. 설치는 어떻게 하나요?

    다운 받은 commons-beanutils.jar는 자신의 /WEB-INF/lib/ 에 복사합니다


    III. DbUtils란 무엇인가요?

    DbUtils는 JDBC 작업을 좀더 쉽게 할수있도록 해주는 작은 클래스 집합입니다.


    IV. 왜 DbUtils를 사용해야 하는가요?

    ① resource 누출에 대한 어떠한 가능성도 없습니다
      JDBC코딩을 하는데 있어서 쉽지않고 양도 만만치 않으며 지루해 지기 쉽습니다
      이러다 보면 자기도 모르게 Connection 누수를 발생시킬수 있는데 이러한 가능성을 배재해 줍니다


    ② 코드의 가독성이 높아집니다
      데이터베이스 처리하는데 필요한 코드의 양을 절대적으로 줄여야 합니다.
      남아있는 코드로 당신의 의도를 정확하게 나타내어야 합니다.


    ③ ResultSet으로 부터 JavaBean property로 세팅을 해줍니다!
      더이상 setter메소드를 이용하여 ResultSet으로부터 컬럼값을 가져오는 코딩을 하지 않아도 됩니다
      ResultSet 각각의 row는 bean instance의 에 완벽하게 전달해 줍니다


    V. 어떻게 사용하나요?

      Connection, Statement, ResultSet 의 close를 간단하게!

        이럴때는 org.apache.commons.dbutils.DbUtils 클래스를 이용하자!

        이 클래스는 모두 static 메소드들로 구성되어있습니다

       

        사용예)

        DbUtils.close(conn);  
        DbUtils.close(stmt);
        DbUtils.close(rs);
        DbUtils.closeQuietly(conn);
        DbUtils.closeQuietly(stmt);
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(conn, stmt, rs);
        DbUtils.commitAndClose(conn);

        DbUtils.commitAndCloseQuietly(conn);
        DbUtils.loadDriver("com.mysql.jdbc.Driver");
        DbUtils.rollback(conn);


        closeQuietly 메소드처럼 뒤에 Quietly라고 붙어 있는 메소드는 익셉션 처리는 자체적으로 처리합니다,

        즉 자신을 call한곳으로 throw 하지 않습니다

        commitAndCloses는 connection을 commit 후 close 하며 rollback는 connection을 rollback 합니다

        loadDriver 는 JDBC 드라이버를 로딩 합니다


     파일로 저장된 SQL을 사용하자!

        이럴 때는 org.apache.commons.dbutils.QueryLoader 클래스를 이용합니다

        이 클래스는 SingleTone 패턴의 클래스입니다

        즉 파일로 저장된 SQL을 읽어 HashMap으로 로드하는 클래스 입니다

       

        사용예)

        QueryLoader queryloader = QueryLoader.getInstance();   //싱글톤
        HashMap hashmap = queryloader.load("sql");      

        queryloader.unload("sql");


        queryloader는 싱클톤이므로 위와같이 객체를 얻어옵니다

        load 함수는 Properties 클래스를 이용하여 sql.properties 파일을 읽어

        HashMap으로 저장하여 리턴하여 줍니다

        unload는 load시 따로 메모리에 저장해 놓았던 sql 정보를 해제합니다


      Setter함수로 더이상 머리 아프지 말자!

       이럴때는 org.apache.commons.dbutils.QueryRunner 클래스를 이용합니다


       사용예)

        ...  

        BoardVO boardVO = null;

        ArrayList arraylist = new ArrayList();

     

        resultset = statement.executeQuery("SELECT * FROM board_t");

        while (resultset.next()) {

            boardVO = new BoardVO();

            boardVO.setTitle("title");

            boardVO.setContent("content");

            boardVO.setWriter("writer");

            arraylist.add(boardVO);       

        }

        ..

       와 같은 코드는 다음과 같이 간략화 됩니다


       ResultSetHandler rsh= new BeanListHandler(BoardVO.class);
       QueryRunner queryRunner = new QueryRunner();

       List list = (List)queryRunner.query(conn, "SELECT * FROM board_t", rsh);


       정말 간단해 집니다 만약 테이블에 컬럼이 30~40개가 된다면..

       select 한문장 할려면 코드수가 몇십줄 입니다. 더이상 노가다 하지 맙시다~


       QueryRunner는 다음과 같은 함수를 지원합니다

       사용예)

      QueryRunner queryrunner = new QueryRunner();
       QueryRunner queryrunner = new QueryRunner(DataSource ds);
    //datasource를 바로 이용할 수 있다

      queryRunner.query(Connection conn, String sql, ResultSetHandler rsh)
       queryRunner.query(Connection conn, String sql, Object param, ResultSetHandler rsh)
       queryRunner.query(Connection conn, String sql, Object[] params, ResultSetHandler rsh)


       여기서 말하는 Object param은 파라미터 전달시 사용됩니다

       ArrayList params = new ArrayList();
       params.add("100");

       params.add("200");

       ResultSetHandler rsh = new BeanListHandler(BoardVO.class);
       QueryRunner queryRunner = new QueryRunner();

       List list = (List)queryRunner.query(conn, "SELECT * FROM board_t WHERE boardNo > ? and boardNo < ?", params.toArray(), rsh);


       select 뿐만 아니라 update, delete역시 가능합니다

       사용예)

       QueryRunner queryRunner = new QueryRunner();

       queryRunner.update(Connection conn, String sql)
       queryRunner.update(Connection conn, String sql, Object param)
       queryRunner.update(Connection conn, String sql, Object params[])

      

       ArrayList params = new ArrayList();
       params.add(boardId);
       queryRunner.update(connection, "UPDATE board_t SET read = read + 1 WHERE boardNo = ?", params.toArray());

       와 같이 사용할 수 있습니다



    VI. 샘플코드

    public class DbUtilsExample() {

       public static void main(String[] args) {

           HashMap map = QueryLoader.getInstance().load("sql");  // (주의) load함수는 실행할때마다 파일을 읽습니다


           Connection conn = null;

           try {

               DbUtils.loadDriver("com.mysql.jdbc.Driver");

               conn = DriverManager.getConnection("jdbc:mysql://localhost/mysql", "root", "");


               ArrayList params = new ArrayList();
               params.add(args[0]);


               ResultSetHandler rsh = new BeanListHandler(BoardVO.class);

               QueryRunner qr = new QueryRunner();

               List list = (List)qr.query(conn, (String)map.get("select"), params.toArray(), rsh);


               for (int i = 0; i < list.size(); i++) {

                   BoardVO board = (BoardVO)list.get(i);

                   System.out.println(board.getTitle());

                   System.out.println(board.getContent());

                   System.out.println(board.getWriter());

              }

           } catch (Exception e) {

               System.out.println(e);

           } finally {

               DbUtils.closeQuietly(conn);

           }

       }

    }



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

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

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

    Posted by 1010
    반응형

    DbUtils 몇가지 예제


    DBUtils 기본은 다음 링크를 참조하세요

    http://www.jakartaproject.com/article/jakarta/1108193481660


    설정방법

       DB유틸 설정 방법은 특별히 없습니다  그냥 다운받은 클래스 패스 잡으시면 됩니다

       Application에서 사용시에는 환경변수나 실행시 클래스 패스를 잡으면 되고요,

       웹에서 사용한다면 해당 어플리케이션의 /WEB-INF/lib/ 에 commons-beanutils.jar 를 복사하면 됩니다


       기본적인 문서는 http://www.jakartaproject.com/article/jakarta/1108193481660 를 보세요


       Download http://jakarta.apache.org/site/downloads/downloads_commons-dbutils.cgi

       API http://jakarta.apache.org/commons/dbutils/apidocs/index.html


    SELECT 예제 (여러건)


    <%@ page contentType="text/html;charset=EUC_KR" %>
    <%@ page import="com.jakartaproject.board.vo.*,org.apache.commons.dbutils.*,java.sql.*,java.util.*, org.apache.commons.dbutils.handlers.*" %>

    <%
           Connection conn = null;

           try {

               DbUtils.loadDriver("com.mysql.jdbc.Driver");

               conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "test", "1111");


               ArrayList params = new ArrayList();
               params.add("1%");


               ResultSetHandler rsh = new BeanListHandler(BoardVO.class);

               QueryRunner qr = new QueryRunner();

               List list = (List)qr.query(conn, "SELECT boardTitle, boardContent, userNick FROM board_test_t WHERE userIp like ?", params.toArray(), rsh);


               for (int i = 0; i < list.size(); i++) {

                   BoardVO board = (BoardVO)list.get(i);

                   System.out.println(board.getBoardTitle());

                   System.out.println(board.getBoardContent());

                   System.out.println(board.getUserNick());

              }

           } catch (Exception e) {

               System.out.println(e);

           } finally {

               DbUtils.closeQuietly(conn);

           }
    %>


    SELECT 예제 (한건)

    select 처리 건수가 1건일 경우에는 MapHandler를 사용하면 됩니다

    <%@ page contentType="text/html;charset=EUC_KR" %>
    <%@ page import="org.apache.commons.dbutils.*,java.sql.*,java.util.*, org.apache.commons.dbutils.handlers.*" %>

    <%
           Connection conn = null;

           try {

               DbUtils.loadDriver("com.mysql.jdbc.Driver");

               conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "test", "1111");


               ResultSetHandler rsh = new MapHandler();

               QueryRunner qr = new QueryRunner();

               Map map = (Map)qr.query(conn, "SELECT count(*) cnt FROM board_test_t", rsh);

               System.out.println(map.get("cnt"));

               

           } catch (Exception e) {

               System.out.println(e);

           } finally {

               DbUtils.closeQuietly(conn);

           }
    %>

    핸들러에는 아래와 같이 여러 종류의 핸들러 들이 있으며,

    ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, ColumnListHandler, KeyedHandler, MapHandler, MapListHandler, ScalarHandler

    그때그때 맞춰 사용하면 됩니다



    UPDATE 예제

    이 예제는 Unicorn 소스에 있는 예입니다

    Unicorn 소스를 다운받아 /src/com/jakartaproject/admin/dao/AdminMySqlDAO.java 를 열어 보시면 Update, Insert 예제를 볼수 있습니다


    public void setBoardCommonSecurity(ConnectionContext connectioncontext,

                                                      AdminForm adminForm) throws BaseException {


            String updateQuery = "UPDATE board_common_t SET badIp=?, badId=?, badNick=?, badContent=?, inputPerMin=?, tryLogin=?";

            try {
                ArrayList params = new ArrayList();
                params.add(encode(adminForm.getBadIp()));
                params.add(encode(adminForm.getBadId()));
                params.add(encode(adminForm.getBadNick()));
                params.add(encode(adminForm.getBadContent()));
                params.add(String.valueOf(adminForm.getInputPerMin()));
                params.add(String.valueOf(adminForm.getTryLogin()));

                QueryRunner queryRunner = new QueryRunner();
                queryRunner.update(connectioncontext.getConnection(), encode(updateQuery), params.toArray());

            } catch (Exception e) {
                logger.error("Error at AdminDAO.setBoardCommonSecurity",e);
                BaseException baseException = new BaseException("errors.sql.problem");
                throw baseException;
            }

            logger.info("AdminDAO.setBoardCommonSecurity was executed");
        }

    Posted by 1010
    반응형

    1. jakarta Project BeanUtils 소개

    요즘 자주 사용하는 스트럿츠, 스프링 등의 프레임워크를 보면 BeanUtils를 자주 사용하는 걸 볼 수 있습니다.
    자바 객체의 속성들을 동적으로 파악해서 필요한 처리를 해야 하는 경우가 점차 증가하고 있는것입니다.

    기존의 Reflection와 Introspection API를 이용해서 구현할 수 있지만  API를 이해하고 활용하기가 매우 복잡하고 까다롭기도 합니다. 좀 더 쉽고 편하게
    이용할 수 없을까 하는 needs에 의해 만들어 진게 BeanUtils 컴포넌트입니다.

    이 컴포넌트를 처음 본다고 하시는 분도 계시겠지만 실제 우리 소스에서 검색해보면 사용 페이지가 제법 나오기도 하며 스프링 프레임워크가 적용되지 않은 페이지에서 쉽고 편하게 사용할 수 있는 모듈이기도 합니다.
    이클립스에서 BeanConverterUtils, BeanUtils로 검색해보세요^^

    일단 우리가 현재 이 모듈을 어떻게 사용하고 있는지 보겠습니다.
    검색을 보면 request로 넘기는 무수히 많을 값을 ReqPrdSearch 객체에 값을 자동으로
    넣어 주는 걸 볼 수 있습니다.

    검색을 하면 get 방식으로 다음처럼 값이 넘어 갑니다.
    fld=&so=0&mfr=&attr_1=&attr_2=&attr_3=&attr_4=&attr_5=&attr_6=&am1=&am2=&am3=&am4=&am5=&am6=&mfm=&cm=&sn=41&pg=20&rq=&price1=-1&price2=-1&searchColl=ALL&tq=mp3&cat0=&cat1=&cat2=&catdepth=-1&searchListType=A

    이 값들을 어떻게 처리할까요?
    그냥 useBean 쓰면 안되나요? 안됩니다.^^
    jsp가 아닌 bean에서 처리할려 하니 쓸 수가 없습니다.
    물론 스프링에서 아래처럼하면 됩니다.
    bind(request, ReqPrdSearch);

    하지만 스프링를 사용하지 않은 경우 어떻게 하나요?
    beanUtils를 이용하시면 됩니다.

    request를 객체에 담는거 이외에 또 어디에서 사용하고 있을까요?

    DB로 부터 가져온 값을 처리할 때 이용하고 있습니다.
    spring jdbc를 이용해서 DB로부터 값을 가져와서 Map(GSData)에 넣고 이용하는데
    그 값을 객체에 넣고 사용할 때가 있습니다. 소스를 추적하다 보면 값넣어 주는
    부분이 안보이는데 잘 작동이 되는 걸 볼 수 있습니다.

    알게 모르게 이미 우리 안에서 사용되고 있으며
    알면 편하고 쉽게 쓸 수 있어서 간단하게 BeanUtils를 소개합니다.

    2. reference
    http://jakarta.apache.org/commons/beanutils/
    이곳에 가시면 각종 guide문서와 최신 jar파일을 다운 받으실수 있습니다.

    http://jakarta.apache-korea.org/commons/beanutils.html
    영어에 거부 반응이 있으신분은 여기 가보시면 번역된 내용을 접하실 수 있습니다.

    http://www.jakartaproject.com/
    자카르타프로젝트라는 책을 쓴 최범균씨 홈페이지인데 좋은 정보를 얻을 수 있습니다.

    3. 주요 클래스
    3-1. BeanUtils
         객제의 정보, 속성 값 읽기, 속성 값 쓰기 등의 기능을 제공하고 있으며
         PropertyUtils와 차이점은 value를 convert 해준다는 겁니다.
    3-2. PropertyUtils
         BeanUtils와 기능은 거의 흡사합니다.
    3-3. ConvertUtils
         타입에 따라 convet 하는 기능을 수행합니다.
        
    4. 상세 내용
       BeanUtils를 사용하기 위해서는 몇가지 규칙이 있습니다.
       관련 부분은 필요시 사이트나 책을 찾아 보시면 나옵니다.

       BeanUtils, PropertyUtils, ConvertUtils는 모두 static한 메소드를 가지고 있으며
       BeanUtilsBean, PropertyUtilsBeanUtilsBean, ConvertUtilsBeanUtilsBean 이라는
       싱글톤으로 구현된 객체와 wrapping되어 있는걸 볼 수 있습니다.
       ...
       public static Object cloneBean(Object bean)
               throws IllegalAccessException, InstantiationException,
               InvocationTargetException, NoSuchMethodException {
           return BeanUtilsBean.getInstance().cloneBean(bean);
       }
       ...
       나름대로 참고할 만한 구조 같습니다. ^^
      
    4-1. BeanUtils

    // bean 복제
    public static Object cloneBean(Object bean)

    // orig에서 dest로 복제, 동일 속성명이 존재해야합니다.
    public static void copyProperties(Object dest, Object orig)

    // orig의 property를 dest로 복제
    public static void copyProperty(Object bean, String name, Object value)

    // Return the value of the specified array property of the specified bean, as a String array.
    public static String[] getArrayProperty(Object bean, String name)

    // 배열값 가져오기, property name을 'name[0]' 이런 식으로 주어야 한다. 규칙임
    public static String getIndexedProperty(Object bean, String name)

    // 배열값 가져오기 index는 몇번째
    public static String getIndexedProperty(Object bean, String name, int index)

    // mapped property 가져오기, property name을 'name(0)' 이런 식으로 주어야 한다.
    public static String getMappedProperty(Object bean, String name)
    // mapped property 가져오기
    public static String getMappedProperty(Object bean, String name, String key)

    public static String getNestedProperty(Object bean, String name)

    // bean에서 값 가져오기
    public static String getProperty(Object bean, String name)

    public static String getSimpleProperty(Object bean, String name)

    // bean에서 해당 name의 property에 value를 convert해서 넣는다.
    public static void setProperty(Object bean, String name, Object value)

    /*
     * bean 있는 값을 key, value로 map에 넣어 줍니다. 가장 많이 쓰이는 메소드 중 하나
     */
    public static Map describe(Object bean)

    /*
     * map에 있는 값을 bean에 넣어 줍니다. 가장 많이 쓰이는 메소드 중 하나
     */
    public static void populate(Object bean, Map properties)

    describe메소드를 이용해서 객체의 property와 value, type등을 쉽게 알아낼 수 있습니다.

    Map map = BeanUtils.describe(bean);
    Set set = map.keySet();
    Iterator it = set.iterator();
    while (it.hasNext()) {
        String key = (String)it.next();

        if ("class".equals(key)) {
            continue;
        }
        Object value = map.get(key);
        properties.put(key, value);
    }
                
    4-2. PropertyUtils
         BeanUtils와 거의 동일합니다.
         BeanUtils가 bean를 다루기위한거라면 PropertyUtils는 map을 처리한다고 보시면
         됩니다.

    4-3. ConvertUtils
         이 utils은 싱글톤으로 구현된 ConvertUtilsBean에서 맵에 각종 컨버터를
         등록해 놓고 lookup(Class clazz)해서 converter 얻고 그걸로 값을 처리하고
         있습니다.
         조금만 수정하면 아주 잘 써먹을 수 있는 util이길래 언급합니다.

    5. example & test

    배열, 기본형, 객체를 property로 하는 dto들을 만들어 잘 처리가 되는지 확인합니다.

    Person.java : dto안에 또 다른 dto를 넣어서 잘 처리되는지 확인하기 위해 만들었습니다.

    public class Person {
        private String name;
        private int age;

        public Person(String name, int age)
        {
            this.name = name;
            this.age = age;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }

        public String toString()
        {
            final String TAB = "    ";

            String retValue = "";

            retValue = "Person ( "
                + "name = " + this.name + TAB
                + "age = " + this.age + TAB
                + " )";

            return retValue;
        }
    }


    Employee.java : 기본형, 배열, 객체 등을 property로 생성합니다.

    public class Employee {
        private String address;
        private String firstName;
        private String lastName;
        private int age;
        private Person person;
        private Date credate;
        private List personList;
        private String[] fld1;
        private int[] fld2;

        public String[] getFld1() {
            return fld1;
        }
        public void setFld1(String[] fld1) {
            this.fld1 = fld1;
        }
        public int[] getFld2() {
            return fld2;
        }
        public void setFld2(int[] fld2) {
            this.fld2 = fld2;
        }
        public List getPersonList() {
            return personList;
        }
        public void setPersonList(List personList) {
            this.personList = personList;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public Date getCredate() {
            return credate;
        }
        public void setCredate(Date credate) {
            this.credate = credate;
        }
        public String getFirstName() {
            return firstName;
        }
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
        public String getLastName() {
            return lastName;
        }
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        public Person getPerson() {
            return person;
        }
        public void setPerson(Person person) {
            this.person = person;
        }
        public String toString()
        {
            final String TAB = "    ";

            String retValue = "";

            retValue = "Employee ( "
                + "address = " + this.address + TAB
                + "firstName = " + this.firstName + TAB
                + "lastName = " + this.lastName + TAB
                + "age = " + this.age + TAB
                + "person = " + this.person + TAB
                + "credate = " + this.credate + TAB
                + "personList = " + this.personList + TAB
                + " )";

            return retValue;
        }

    }


    BeanConvertUtils.java

    public class BeanConvertUtils {
        //map의 value을 bean에 넣어주는 메소드
     public static void mapToBean(java.util.Map properties, java.lang.Object bean)
       throws IllegalAccessException, InvocationTargetException,
       NoSuchMethodException {

      if (properties == null) {
       return;
      }

      BeanUtils.populate(bean, properties);

     }

        //bean의 value을 map에 넣어주는 메소드
     public static void beanToMap(java.lang.Object bean, java.util.Map properties)
       throws IllegalAccessException, InvocationTargetException,
       NoSuchMethodException {

      Map map = PropertyUtils.describe(bean);

      map.remove("class");
      properties.putAll(map);

     }
    }


    TestBeanConvertUtils : 테스트 케이스

    public class TestBeanConvertUtils extends TestCase {
        Employee emp;
        GSData map;
        HttpServletRequestMock request;
        public void setUp() {
            List list = new ArrayList();
            list.add(new Person("kkaok1", 23));
            list.add(new Person("kkaok2", 22));

            emp = new Employee();
            emp.setAddress("경기도");
            emp.setFirstName("kim");
            emp.setLastName("hyun");
            emp.setPerson(new Person("kkaok", 22));
            emp.setPersonList(list);
            emp.setAge(22);
            emp.setFld1(new String[]{"0","1","2"});

            map = new GSData();
            //map = new HashMap();
            map.put("address", "경기도");
            map.put("firstName", "kim");
            map.put("lastName", "hyun");
            map.put("person", new Person("kkaok", 22));
            map.put("personList", list);
            map.put("age", new Integer(22));
            map.put("fld1", new String[]{"0","1","2"});
        }

        public void testmapToBean() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
            Integer i1= new Integer(0);

            new ConvertUtilsBean().convert("0", i1.getClass());

            Employee emptest = new Employee();
            BeanConvertUtils.mapToBean(map, emptest);
            //System.out.println(emp.toString());
            //System.out.println(emptest.toString());
            assertEquals(emp.toString(), emptest.toString());
        }

        public void testbeanToMap() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
            Map maptest = new HashMap();
            BeanConvertUtils.beanToMap(emp, maptest);
            //System.out.println(map.toString());
            //System.out.println("maptest : "+maptest.toString());

            assertEquals(map.get("address"), maptest.get("address"));
            assertEquals(map.get("age"), maptest.get("age"));
        }

    }



    6. 마무리

       map에 있는 값을 bean에 넣거나
       bean에 있는 값을 map에 넣거나
       request에 있는 값을 bean에 넣거나
       동적으로 변하는 bean을 분석해야 할때 정말 편하고 쉽게 쓸 수 있는 모듈입니다.

       한번 소스를 뜯어 보시면 좋은 내용을 접하실 기회가 되실 거라 믿습니다.

    7. 참고

       eclipse에서 테스트 하실때는 commons-beanutils.jar, commons-beanutils-bean-collections.jar,  commons-beanutils-core.jar, commons-logging-1.1.jar, junit-3.8.1.jar 등을 빌드패스에 넣어 주세요

      * request의 값을 빈에 넣는 예)
      BeanUtils.populate(객체, request.getParameterMap());
      * request.getParameterMap() 이렇게 하면 map에 key, value로 값이 return 됩니다.

    출처 : Tong - avcom18님의 spring통

    Posted by 1010
    반응형

    Commons BeanUtil 에는 참 유용한게 많다..


    그중 RowSet 과 비스무리(?) 한 것이 하나 있는데..


    바로 RowSetDynaClass 이다...



    일반적 JDBC 프로그래밍에서는 아래와 같은 순서를 가진다.


    pstmt = t.prepareStatemet(query);

    pstmt.setXXX(....);

    rs = pstmt.executeQuery();


    while(rs.next()){

     ... 작업들.....

    }


    rs.close();


    이런 구조를 가지고 가게 되며, 커넥션이 닫히기 전에 resultSet 을 순차적으로 내려가면서 작업을 한다.


    Commons BeanUtils 안에는 RowSetDynaClass 라는 클래스가 있다.

    이 클래스는 JDBC 연결이 끊어진 이후에도 사용가능한 CachedRowSet 과 비슷한 기능을 제공해준다.


    이것의 사용법은 상당히 간단하다. 참조 코드는 다음과 같다.


       pstmt = t.prepareStatement(query);
       rs = pstmt.executeQuery();
       rsdc = new RowSetDynaClass(rs);
      
       rs.close();

    ....


    finally{   ... conn.close(); }


    요기까지가 끝이다.. 이후에는 rsdc 라는 녀석을 이용해서 데이터를 취득하면 된다.

    이렇게 데이터를 가져오게 되면

    rsdc 에서는 getRows() 메소드를 이용하여 List 객체를 취득할 수 있다.


    List arr = rsdc.getRows();


    또한 검색해 온 컬럼의 이름들을 Property 로 얻을 수도 있다.


     DynaProperty[] dynas = rsdc.getDynaProperties();
     
     for(int i = 0; i < dynas.length; i++){
      System.out.println(dynas[i].getName());
     }


    DynaProperty 클래스는 일반적인 Property 개념으로 보면 된다.


    또한 이렇게 얻어온 DynaProperty 배열객체를 이용하여 RowSetDynaClass 를 순환하며 내용을 참조할 수 있다.


    List 형태로 반납된 rows 들을 순환하게 되면 DynaBean 이라는 객체를 반납하게 된다.

    이 형태를 이용하여 rs.next() 작업과 동일한 결과를 얻을 수 있다.


     Iterator iter = arr.iterator();
     
     while(iter.hasNext()){
      DynaBean bean = (DynaBean)iter.next();
     
      for(int i = 0; i < dynas.length; i++){
       System.out.println(bean.get(dynas[i].getName()));
      }
     }



    사용하다 주의할 것은 테이블 컬럼명이 regiDate 와 같이 중간에 대문자가 끼어있을 경우이다

    이렇게 중간에 대문자가 끼어있는 것들은 RowSetDynaClass 를 생성하는 과정에서

    전부 소문자로 바뀌게 된다.

    SQL 상에서는 대소문자를 가리지 않지만 객체에서 값을 얻어올때는 가린다는것에 주의!!!


    그렇기 때문에 bean.get("regidate") 와 같이 프라퍼티를 전부 소문자로 적어야

    제대로 출력될 것이다.



    DAO Layer 측에서 해야할 일들이 DAO를 이용하는 Layer 에 종속적인 작업이 진행될 경우

    RowSetDynaClass 를 이용하여 Action 측에서 사용하면 각 레이어의 할일이 조금더 분명해 질수 있다.



     

    Posted by 1010
    반응형

    BeanUtils
    map에 있는 값을 bean에 넣거나  , bean에 있는 값을 map에 넣거나   , request에 있는 값을 bean에 넣거나
    동적으로 변하는 bean을 분석해야 할때 정말 편하고 쉽게 쓸 수 있는 모듈.
    객제의 정보, 속성 값 읽기, 속성 값 쓰기 등의 기능을 제공.

    eclipse에서 테스트 할 땐 commons-beanutils.jar, commons-beanutils-bean-collections.jar,  commons-beanutils-core.jar, commons-logging-1.1.jar, junit-3.8.1.jar 등을 빌드패스에 넣어야 함.





    // bean 복제
    public static Object cloneBean(Object bean)

    // orig에서 dest로 복제, 동일 속성명이 존재해야합니다.
    public static void copyProperties(Object dest, Object orig)

    // orig의 property를 dest로 복제
    public static void copyProperty(Object bean, String name, Object value)

    // Return the value of the specified array property of the specified bean, as a String array.
    public static String[] getArrayProperty(Object bean, String name)

    // 배열값 가져오기, property name을 'name[0]' 이런 식으로 주어야 한다. 규칙임
    public static String getIndexedProperty(Object bean, String name)

    // 배열값 가져오기 index는 몇번째
    public static String getIndexedProperty(Object bean, String name, int index)

    // mapped property 가져오기, property name을 'name(0)' 이런 식으로 주어야 한다.
    public static String getMappedProperty(Object bean, String name)
    // mapped property 가져오기
    public static String getMappedProperty(Object bean, String name, String key)

    public static String getNestedProperty(Object bean, String name)

    // bean에서 값 가져오기
    public static String getProperty(Object bean, String name)

    public static String getSimpleProperty(Object bean, String name)

    // bean에서 해당 name의 property에 value를 convert해서 넣는다.
    public static void setProperty(Object bean, String name, Object value)

    /*
     * bean 있는 값을 key, value로 map에 넣어 줍니다. 가장 많이 쓰이는 메소드 중 하나
     */
    public static Map describe(Object bean)

    /*
     * map에 있는 값을 bean에 넣어 줍니다. 가장 많이 쓰이는 메소드 중 하나
     */
    public static void populate(Object bean, Map properties)



    describe메소드를 이용해서 객체의 property와 value, type등을 쉽게 알아낼 수 있음.

    Map map = BeanUtils.describe(bean);
    Set set = map.keySet();
    Iterator it = set.iterator();
    while (it.hasNext()) {
        String key = (String)it.next();

        if ("class".equals(key)) {
            continue;
        }
        Object value = map.get(key);
        properties.put(key, value);
    }

    Posted by 1010
    반응형
    먼저 아래사이트에서 필요 라이브러리를 다운로드 받는다...

    http://commons.apache.org/beanutils/
    http://commons.apache.org/logging/

    필요라이브러리는 아래와 같다.
    /*현재 최신버전은 1.8.0 이다.*/
    commons-beanutils. jar
    commons-beanutils-bean-collections.jar
    commons-beanutils-core.jar

    /*현재 최신버전은 1.1.1 이다.*/
    commons-logging.jar

    테스트 Person 객체.. 코드

    public class Person {
     private String firstName;
     private String lastName;
     private Address address;
     private String phoneNumber;
     private long personId;
     public Person() {
      address = new Address();
     }
     public String getFirstName() {
      return firstName;
     }
     public void setFirstName(String firstName) {
      this.firstName = firstName;
     }
     public String getLastName() {
      return lastName;
     }
     public void setLastName(String lastName) {
      this.lastName = lastName;
     }
     public Address getAddress() {
      return address;
     }
     public void setAddress(Address address) {
      this.address = address;
     }
     public String getPhoneNumber() {
      return phoneNumber;
     }
     public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
     }
     public long getPersonId() {
      return personId;
     }
     public void setPersonId(long personId) {
      this.personId = personId;
     }
     
     
     public String toString() {
      StringBuffer buffer = new StringBuffer();
      buffer.append("firstName-->").append(this.getFirstName()).append("\n");
      buffer.append("lastName-->").append(this.getLastName()).append("\n");
      buffer.append("phoneNumber-->").append(this.getPhoneNumber()).append("\n");
      buffer.append("personId-->").append(this.getPersonId()).append("\n");
      buffer.append("address-->").append(this.getAddress().toString()).append("\n");
      return buffer.toString();
     }
    }


    public class Address {
     private String address;
     private String detailAddress;
     private String zipCode;
     private String countryCode;
     public String getAddress() {
      return address;
     }
     public void setAddress(String address) {
      this.address = address;
     }
     public String getDetailAddress() {
      return detailAddress;
     }
     public void setDetailAddress(String detailAddress) {
      this.detailAddress = detailAddress;
     }
     public String getZipCode() {
      return zipCode;
     }
     public void setZipCode(String zipCode) {
      this.zipCode = zipCode;
     }
     public String getCountryCode() {
      return countryCode;
     }
     public void setCountryCode(String countryCode) {
      this.countryCode = countryCode;
     }
     public String toString() {
      StringBuffer buffer = new StringBuffer();
      buffer.append("  address-->").append(this.getAddress()).append("\n");
      buffer.append("  detailAddress-->").append(this.getDetailAddress()).append("\n");
      buffer.append("  zipCode-->").append(this.getZipCode()).append("\n");
      buffer.append("  countryCode-->").append(this.getCountryCode()).append("\n");
     
      return buffer.toString();
     }
    }


    import java.util.HashMap;
    import java.util.Map;
    import org.apache.commons.beanutils.BeanUtils;
    public class Test {
     public static void main(String[] args) throws Exception{
      Map map = new HashMap();
      map.put("firstName", new String("블라블라"));
      map.put("lastName", new String("블랍블라블라블라"));
      map.put("phoneNumber", new String("0000-0000-0000"));
      map.put("personId", new Long(10011001));
      map.put("address.address", new String("경기도 블라블라블라블라") );
      map.put("address.detailAddress",new String("불라불라??? ???동 ????호") );
      map.put("address.zipCode",new String("000-000") );
      map.put("address.countryCode",new String("국가코드 000"));
     
      Object oClass = Person.class.newInstance();
      BeanUtils.populate(oClass, map);
      System.out.println(oClass.toString());
     }
    }


    위의 예제는 스트러츠같은곳에서 form 값을 세팅할때 사용하는것을 한번 작성해봤다.

    스트러츠에도 위와같이 폼빈을 세팅해주지 않을까 생각한다.

    리플렉션으로 만들어도 되겠지만 이왕 누군가가 만들어둔게 있으면 가져다 쓰는게 시간도 절약되고 주절주절 ㅋㅋㅋ

    오늘은 여기까지. ^^
    Posted by 1010
    반응형

    간단한 BeanUtils 사용하기


    자료형게시판을 만들다가 불현듯 잔머리가 생각이 났다.


    자료형나름대로의 컨트롤로를 두면 어떨까 ? 하는 생각이다.

    그래서 요기저기 머리를 굴리다가 MultipartRequest와 BeanUtils 를 이용해서 하기로 마음을 먹었다.


    순서는  업로드해서 Bean에 담기까지의 과정이다.

    1. 다중 파일을 을 업로드 시킨다.

    2. MultipartRequest를 이용해서 받는다.

    3. request.setAttribute 를 이용해서 request에 MultipartRequest의 내용을 담다.

    4. request.getRequestDispatcher 를 이용해서 bean에 담을 페이지(또는 서블릿)으로 이동시킨다.

    5. BeanUtils을 이용해 bean에 모든내용을 담는다.


    1~4번까지는 그리 무리없이 진행이 되었지만 역시 5번에 막힌다.

    모양이 심플해야 하기에 (본인은 복잡스러운거 싫어함) 그래서 머리를 굴리다가

    BeanUtils를 써보면 어떨까 생각이 들었다.



    일단 BeanUtils을 사용하기 위해서는 자카르타 프로젝트 홈페이지에서 다운을 받는다.

    최신버전은 commons-beanutils-1.7.0 일듯..

    그리고 이것을 사용하기 위해서 다른 패키지가 하나더 필요하다

    commons-logging-1.0.4 요놈 도 자카르타에서 다운을 받자


    위의 두개 패키지를 압축을 풀고 lib폴더에 보면 jar파일 있을거시다. 이것을 내 프로젝트의 lib로 이동시키자

    그럼 일단 준비끝이라 할 수 있다.


    그럼 소스부분을 보면..

    1번 소스는 볼필요가 없기에 패스


    2~4번 소스


     String repository  = "D://upload//";  // 저장할곳을 넣는다.

     MultipartRequest multi = new MultipartRequest(request, repository, 1024*1024*1024, "euc-kr", new DefaultFileRenamePolicy());

     java.util.Enumeration params = multi.getParameterNames();
     while( params.hasMoreElements() ) {
      String name = (String)params.nextElement();
      String value = multi.getParameter(name);
      request.setAttribute(name, value); // 요부분은 3번 부분
     }

     java.util.Enumeration files = multi.getFileNames();
     while ( files.hasMoreElements() ) {
      String name = (String)files.nextElement();
      String filename = multi.getFilesystemName(name);
      String type = multi.getContentType(name);
     }
     request.getRequestDispatcher("step2.jsp").forward(request, response); // 요부분이 4번부분


    본인은 원래 저기서 파일관련 테이블에 참조키를 이용하여 파일을 관리하기로 했다. 그부분은 각자 알아서 처리하면될듯하다.


    5번 소스


     BoardBean board = new BoardBean();
     BeanUtils beanutils = new BeanUtils();
     java.util.Enumeration params = request.getAttributeNames();

     while ( params.hasMoreElements() ) {
      String name = (String)params.nextElement();
      Object value = request.getAttribute(name);
      BeanUtils.setProperty(board , name, value ); // 이부분이 5번부분이라고 할수있다.

    }


    확인은

    out.println("<hr>board.getContent() = " + board.getContent() );
    out.println( beanutils.getProperty(board, "content") );

    이런식으로 확인을 해보면 알 수 있다.



    setProperty 를 보면

    setProperty(java.lang.Object bean, java.lang.String name, java.lang.Object value)

    이런식으로 bean과 각 bean의 멤버, 그리고 해당 value를 각각 담을 수 있게 해준다.

     

    반대로

    getProperty는

    getProperty(java.lang.Object bean, java.lang.String name)

    bean과 멤버를 이용해서 value를 얻을 수있다.


    생각보다 모양이 간단하다(?) 서블릿으로 만드는 건 각자가 하면 될 듯하다.

    근데 위에 보면

    commons-logging-1.0.4  이놈은 사용을 하지 않는 것 처럼 보이지만

    commons-logging-1.0.4 이 없으면 BeanUtils 가 에러가 나기에 꼭 lib에 추가하도록 한다.




    일반적인 request 받아서 빈에 넣기


     BeanUtils beanutils = new BeanUtils();
     GroupBean group = new GroupBean();

     java.util.Enumeration params = request.getParameterNames();

     while ( params.hasMoreElements() ) {
      String name = (String)params.nextElement();
      String value = CommonUtil.htmlNko2nulltrim(request.getParameter(name), "");
      //out.println(name+" = " + value + "<br>");
      if (!value.equals("")){
       BeanUtils.setProperty(group , name, value );
      }
     }

    Posted by 1010
    반응형
    다음까페의 프로그래머자료 카페에서 참조 했습니다.
    http://cafe251.daum.net/_c21_/bbs_search_read?grpid=19VVx&fldid=Ki0S&contentval=00008zzzzzzzzzzzzzzzzzzzzzzzzz&nenc=rZc2_1ntyEMM3NSIvBc.2w00&dataid=8&fenc=w5lwmbf1XmU0&docid=CDb9MIhS


    AXIS에 대한 자료가 많지 않은데요.. 참 정리가 잘 된 문서인 것 같습니다. 시키는 대로 테스트를 하다 보니.. 웹서비스의 개념이 어느정도 잡히는 듯 합니다. 좋은 자료라 공유해봅니다. 참고하세요~~)

     
    Apache AXIS를 이용한 웹서비스 강좌(팁)
    개요 : 톰캣(자바)환경에서 axis를 이용한 웹서비스

    Apache Axis is an implementation of the SOAP ("Simple Object! Access Protocol") submission to W3C.
    아래의 내용은 중요하므로 기본 개념이 분명이 있어야 한다.
    본 강좌(팁)은 사용법을 위주로 아주 기초적인 예제를 다룰것이므로 자세한 내용은 생략한다.
    SOAP (Simple Object! Access Protocol)
    WSDL (Web Service Description Langauge)
    WSDD (Web Service Deployment Descriptor)
    "웹서비스는 인터넷을 통해 제공되는 소프트웨어가 PC나 휴대폰과 같은 다중 기기에서 동일하게
    작동하도록 해준다. 여기에는 데이터 교환의 표준이면서 경쟁 관계에 있는 기술들이 서로 통신하고
    협동하도록 해주는 XML(Extensible Markup Language), 웹서비스 간에 어떻게 인터넷을 통해
    교신하는지를 서술하는 SOAP(Simple Object! Access Protocol), 웹서비스가 무엇이고 또 어떻게
    접근할 수 있는지를 기술하는 WSDL(Web Services Description Language), 그리고
    온라인 전화번호부의 역할을 수행하면서 기업들로 하여금 웹서비스를 등록, 홍보, 검색할 수
    있도록 하는 UDDI(Universal Description, Discovery andIntegration) 등이 포함된다."

    1. Apache Tomcat 4.1 이상 , Full Version으로 설치
    • 톰캣 5.5.4 버젼을 사용하였다. (참고로 Windows환경이다.)
      설치후 http://localhost:8080으로 확인해서 동작이 되면 정상이다.
      이것이 톰캣설치의 전부이고, jsp 및 servlet이 동작이되면 준비는 되었다.
      필요에 따라 jdbc등 기타 드라이버를 c:\tomcat\common\lib\ 에 복사 한다.
    • set CATALINA_HOME=c:\tomcat

    2. 자바 개발툴킷 설치 : JDK 1.4 버젼 이상이면 된다.

    • 참고로 jdk 1.5 버젼을 설치하였다.
    • 설치 후에 classpath를 설정한다.
      set CLASSPATH=.;c:\jdk15\lib\tools.jar;c:\tomcat\common\lib\servlet-api.jar;
      추가적으로 필요한 jdbc 및 기타 라이브러리들을 추가한다.
    • set JAVA_HOME=c:\jdk15
      set PATH=c:\jdk15\bin; 을 기존 PATH에 추가한다.

    3. ANT 설치 (옵션) : ant에 대해서는 다른 문서나 커뮤니티 사이이트에서 참고할것.

    • http://ant.apache.org에서 ant 1.6.2 버젼을 받아서 설치한다.
      설치는 다운 받은 파일을 압축을 풀어서 일정한 디렉토리에 옮겨놓으면 된다.
    • set ANT_HOME=c:\ant
      set PATH=c:\ant\bin; 을 기존 PATH에 추가한다.
    • C:\tomcat\server\lib\catalina-ant.jar 파일을 c:\ant\lib\ 디렉토리로 복사한다.
    • 추가적으로 junit 이나 xml관련 라이브러리등을 c:\ant\lib\ 에 복사하여 사용하면 된다.

    4. AXIS 설치 : http://ws.apache.org/axis/ , http://ws.apache.org/axis/releases.html

    • axis 사이트에서 최신 릴리즈를 다운받아서 압축을 푼후에, 원하는 디렉토리에 옮겨놓으면 된다.
      참고로 1.1 final version를 다운 받아서 사용중이다.
    • set AXIS_HOME=c:\axis
    • set AXIS_LIB=%AXIS_HOME%\lib
    • set AXISCLASSPATH=%AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery.jar;
      %AXIS_LIB%\commons-logging.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;
      %AXIS_LIB%\log4j-1.2.8.jar;%AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar;
      %AXIS_LIB%\wsdl4j.jar
    • set CLASSPATH=%CLASSPATH%;%AXISCLASSPATH% 으로 기존 CLASSPATH 에 추가한다.
    • 참고 :
      CLASSPATH에 있는 xml-apis.jar 파일과 xercesImpl.jar 파일은
      http://xml.apache.org/ 에서 받으면 된다.
      또한, 아래에서 보게 될 라이브러리 테스트를 통하여 추가 라이브러리를 확인할수 있고
      확인후에 필요한 다운 사이트 정보까지 나오게 되므로 정보를 확인후 다시 설정해주면 된다.

    5. axis를 톰캣에 연동하기

    • 연동에 있어서 특별한 것은 없고 단지, c:\axis\webapps\axis\ 에서 axis디렉토리를
      c:\tomcat\webapps\axis로 복사하면 된다. 즉, 톰캣 webapps디렉토리에 axis 컨텍스트가
      추가 되었다고 이해해도 되겠다.

    6. 테스트 해보기

    • 톰캣을 실행하고
    • 연결테스트를 해본다.
      http://localhost:8080/axis
    • 라이브러리 테스트
      http://localhost:8080/axis/happyaxis.jsp 에서 필요하거나 필수적인 추가 라이브러리를
      설치하라는 경고 메시지를 보여주기 때문에 이때 필요한 라이브러리를 다운받아서
      c:\tomcat\webapps\axis\lib\ 에 복사해주고 다시 테스트를 해본다.
      필요에 따라서 다운받은 라이브러리를 CLASSPATH에 설정해주어서 jdk로 컴파일할때 이용하자.

    7. SOAP 테스트

    • http://localhost:8080/axis/services/Version?method=getVersion
      다음과 같이 출력되면 정상적으로 설치가 된것이다. (내용은 환경에 따라 약간 차이가 난다.)

      - xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      -
      -
      Apache Axis version: 1.1 Built on Jun 13, 2003 (09:19:43 EDT)



    8. AdminClient 테스트

    • C:\axis\samples\stock 디렉토리로 이동하여 예제로 테스트해보자.
    • java org.apache.axis.client.AdminClient
      -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd
    • deploy가 되었는지 확인하고, 에러가 나면 거의 대부분의 원인은 클래스패스에서 나온다.
    • deploy가 제대로 되었는지 확인해보자.
      http://localhost:8080/axis/servlet/AxisServlet
      아래처럼 추가된 부분이 웹에 보인다면 성공적으로 deploy된것이다.
      urn:xmltoday-delayed-quotes (wsdl)
      test
      getQuote

    9. Client 수행 테스트

    10. 프로그램 작성 순서

    • 서버 Interface와 구현 Class를 작성한후 컴파일 한다.
    • 구현 클래스로 부터 WSDL 파일을 생성한다.
    • WSDL 파일로 부터 deploy.wsdd 파일을 생성한다.
    • deploy.wsdd 파일을 이용해서 웹서비스를 deploy하고 server-config.wsdd 파일을 생성 수정한다.
    • 구현클래스로 부터 client-side 클래스를 생성한다.

    11. 프로그램 작성 플로우

     

    12. 예제 프로그램

    • 자바 Interface와 자바 Implement 클래스 작성
      c:\test\ 에 아래 2개의 파일을 생성한다.

      //begin - HelloIF.java
      package webservice.hello;
      public interface HelloIF extends java.rmi.Remote {
      public String hello(java.lang.String name) throws java.rmi.RemoteException;
      }
      //end - HelloIF.java

      //begin - HelloImpl.java
      package webservice.hello;
      public class HelloImpl implements HelloIF {
      public HelloImpl() {
      }
      public String hello(String name) {
      System.out.println(name);
      return "hi " + name;
      }
      }
      //end - HelloImpl.java

    • 컴파일

      javac -d . HelloImpl.java
      컴파일 후 c:\test\webservice/hello 디렉토리밑에 HelloImpl.class 와 HelloIF.class가 생성.
      생성된 class파일을 C:\Tomcat\webapps\axis\WEB-INF\classes
      디렉토리에 패키지 디렉토리를 포함해서 복사한다.
      참고: 컴파일이 되지 않을경우, HelloIF.java파일을 먼저 컴파일한후에 c:\test\webservice/hello
      디렉토리에 HelloIF.class파일을 복사해놓고 다시 컴파일 해본다.

    • 구현된 클래스로 부터 WSDL 생성 하기

      java org.apache.axis.wsdl.Java2WSDL -o c:\test\webservice\hello\Hello.wsdl
      -l http://localhost:8080/axis/services/Hello -n http://hello.webservice -pwebservice.hello
      http://hello.webservice webservice.hello.HelloIF

      Java2WSDL 인자 (http://ws.apache.org/axis/java/reference.html)
      인 자
      의 미
      -o 파일경로 wsdl 생성 위치
      -l URL client가 접속할 URL
      -n 네임스페이스 WSDL의 타켓 네임스페이스
      -p패키지 네임스페이스 네임스페이스와 네임스페이스 매핑
      Target 인터페이스 이름


      [생성된 Hello.wsdl 파일]


      ...
      ...생략...
      ...







    • WSDL 파일로 부터 deploy.wsdd 파일 생성 및 client-side 자바 클래스 생성

      java org.apache.axis.wsdl.WSDL2Java -o c:\test\webservice\hello\
      -d Application -s c:\test\webservice\hello\Hello.wsdl

      c:\test\webservice\hello 디렉토리 하위에 webservice/hello
      새로운 디렉토리가 생성된것을 확인 할수 있다.
      그 디렉토리에 deploy.wsdd 파일과 새롭게 생성된 자바 파일이 존재해야 한다.
      또한, undeploy.wsdd 파일도 생성 된 것을 볼수 있는데 이파일은 서비스를
      undeploy할때 사용한다.
      인 자
      의 미
      -o 디렉토리경로 wsdd 파일과 클래스파일이 생길 디렉토리
      -d scope 설치 영역 - Application, Request , Session
      -s wsdd 파일 생성
      Target wsdl 파일 경로

    • [deploy.wsdd 파일 수정]
      wsdd 파일은 wsdl 파일만 가지고 생성되었기 때문에 실제 구현클래스가 무엇인지 제대로
      설정이 안되어 있으므로 그부분을 수정해 주어야한다.
      붉은색 부분으로 해당 부분을 수정해주면 된다.

      xmlns="http://xml.apache.org/axis/wsdd/"
      xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
      ...
      ...생략...
      ...
      webservice.hello.HelloImpl"/>
      ...
      ...생략...
      ...





    • 서비스 디플로이먼트 하기

      java org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd    <== 포트번호를 80등으로 바꿀경우는 꼭 이와같은 행태로 -l옵션을 주어서 드플로이 해야만 한다.

      [디플로이먼트 확인하기]
      http://localhost:8080/axis/servlet/AxisServlet
      'Hello' 라는 서비스가 설치된것을 확인할 수 있다.

    • 서비스 언디플로이먼트 하기

      java org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService undeploy.wsdd

      [언디플로이먼트 확인하기]
      http://localhost:8080/axis/servlet/AxisServlet
      'Hello' 라는 서비스가 삭제 된것을 확인할 수 있다.

    14. 클라이언트 구현하기

    • 이제 생성된 서비스에 클라이언트로 접근해보는 예제를 생성하고 테스트 해보자.
      아래와 같이 작성하고 c:\tomcat\webapps\axis\ 복사한다.

    • [HelloClient.jsp]
      <%@ page contentType="text/html; charset=euc-kr" language="java"
      import!="java.net.MalformedURLException,
      java.net.URL,
      java.rmi.RemoteException,
      javax.xml.namespace.QName,
      javax.xml.rpc.ServiceException,
      org.apache.axis.client.Call,
      org.apache.axis.client.Service" %>
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">






      <%
      Service service = new Service();
      Call call = (Call)service.createCall();
      call.setTargetEndpointAddress(new URL("http://localhost:8080/axis/services/Hello?wsdl"));
      call.setOperationName(new QName("http://soapinterop.org/", "hello"));
      String returnValue = (String)call.invoke(new Object![]{"! 안녕하세요.^^*"});
      out.println(returnValue);
      %>



    • http://localhost:8080/axis/hello.jsp

      hi ! 안녕하세요.^^*

    • 위에서 처럼 나오면 성공이다. HelloClient.jsp부분하고 차이가 없는것 같지만
      "! 안녕하세요" 와 "hi ! 안녕하세요" 차이가 크다.(구현하는 프로그램적으로...)
      즉, hi 라고 출력되어지는 String hello() 웹 메소드를 HelloImpl.class에서 가져와서 실행하는 것이다.

    14. 두번째 예제

    • 첫번째 방법 외에 빠른 방법이 있다.
      자바 파일을 만들고 확장자를 java가 아닌 jws로 바꾸고 실행하는 방법이다.
      참고로 c:\tomcat\webapps\axis\WEB-INF\web.xml를 한번 검토해길 바란다.
    • [ HelloWorldService.jws]

      public class HelloWorldService
      {
      public String HelloWorld(String data)
      {
      return "Hello World! You sent the string '" + data + "'.";
      }
      }

    • 위의 파일을 c:\tomcat\webapps\axis\ 에 복사하고

      http://localhost:8080/axis/HelloWorldService.jws

      그 주소에 설치된 서비스가 있다는 HTML 페이지가 나온다. 여기서 더나가 HelloWorld 메서드를 호출해보자.

      http://localhost:8080/axis/HelloWorldService.jws?method=HelloWorld&data=Hi+my+name+is+okjsp

      메서드를 호출한 결과 XML을 받았다.

    • 자바로 클라이언트 구현 하기


      [ HelloWorldClient.java]

      import! java.net.MalformedURLException;
      import! java.net.URL;
      import! java.rmi.RemoteException;
      import! javax.xml.namespace.QName;
      import! javax.xml.rpc.ServiceException;
      import! org.apache.axis.client.Call;
      import! org.apache.axis.client.Service;

      public class HelloWorldClient
      {
      public static void main(String[] args) throws ServiceException, MalformedURLException, RemoteException
      {
      Service service = new Service();
      Call call = (Call)service.createCall();
      call.setTargetEndpointAddress(new URL("http://debianbox:8080/axis/HelloWorldService.jws"));
      call.setOperationName(new QName("http://soapinterop.org/", "HelloWorld"));
      String returnValue = (String)call.invoke(new Object![]{"My name is okjsp."});
      System.out.println(returnValue);
      }
      }

    • HelloWorldClient.java 파일을 컴파일 하고 실행 해서 결과를 확인해 본다.
      참고로 컴파일 에러가 나는 경우는 라이브러리등이 CLASSPATH에 잡혀있지 않아서 이다.
      본 문서의 처음 부분에 있는 설정 부분을 확인해 보고 다시 시도 해보자.

    15. 다른 시스템과 연동하기

    SOAP로 운영되고있는 서버 시스템과 연동을 한다고 하면 먼저 해당 시스템에 wsdl을 알아야만 한다.

    SOAP서버 운영자로부터 wsdl을 볼수있는 url이나 .wsdl파일을 요청하도록한다.

    그렇게 해서 받은 wsdl파일을  첨부된pdf 파일에서 언급된 WSDL2Java 명령을 이용하여 java파일들을 생성하게 된다.

    이렇게 생성된 java파일들을 통해 바로 써버쪽에서 써비스와 연결할 수 있는 정보들을 알수있다.

    즉,서버쪽에서 AxisServlet에 등록된 모듈에 대한 인터페이스 관련 파일들인것임.

    이 java파일들을 이용해서 첨부파일 예제인 FactClientByWSDL.java파일과 같이 client 파일을 만들어 실행시키면 서버와  연결하여 데이터를 받을 수 있게된다.

    물론 client 파일 실행시에는 해당 FactClientByWSDL.java 파일과 WSDL2Java 명령으로 생성된 자바파일이 패키지화 되어 있어야 참조하여 실행이 될것이고 axis관련된 jar파일들도 classpath에 참조가 되어야 할것이다.

    Apache AXIS를 이용한 웹서비스 강좌
    Posted by 1010
    반응형
    LOG4J

    I. 들어가면서.. 그리고 log4j


    log4j는 자바 어플리케이션에서 빠르고 효과적으로 로깅 할 수 있도록 도와주는 오픈 소스 프로젝트입니다.


    로깅(logging)은 코드의 가독성을 떨어뜨리는 단점이 있지만 애플리케이션에 문제가 있을 때 개발자가 자세한 상황을 파악할 수 있도록 해 주며 테스팅시 빠질 수 없는 요소입니다.


    아마도 여러분들은 여러 어플리케이션이 추가되면서 각 개발자들만의 독특한 로깅방식이 서로 썩이고 얽혀서 화면에 나타나는것을 많이 봤을겁니다 -_-;
    즉 로깅방법을 통일할 필요가 있는것이죠. 모든 개발자가 특정 포맷에 맞추어서 로깅 한다면 한결 로깅하기도 편하겠지요


    오픈 소스 프로젝트인 Log4j는 개발자들이 매우 손쉽고 다양한 형태로 로깅을 할 수 있도록 도와줍니다. 성능또한 우수해 더이상 System.out.println을 사용할 필요가 없습니다.



    II. 다운로드


    다운로드 http://logging.apache.org/log4j/docs/download.html

    매뉴얼 http://logging.apache.org/log4j/docs/documentation.html

    API spec http://logging.apache.org/log4j/docs/api/index.html



    III. LOG4J 구조


    일단 log4j를 잘 모르지만 그 구조만 살짝 살펴보고 넘어갑시다

    log4j는 크게 3가지 요소로 구성되며 그 구조는 다음과 같습니다

    ① Logger(Category) : 로깅 메세지를 Appender에 전달합니다.

    ② Appender : 전달된 로깅 메세지를 파일에다 기록할 것인지, 콘솔에 출력할 것인지

                       아니면 DB에 저장할 것인지 매개체 역활을 합니다.

    ③ Layout : Appender가 어디에 출력할 것인지 결정했다면 어떤 형식으로 출력할 것이지

                    출력 layout을 결졍합니다.

    쉽죠?



    IV. LOG4J 로깅 레벨


    log4j는 다양한 로깅레벨을 지원합니다.


    ① FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.

    ② ERROR : 일반 에러가 일어 났을 때 사용합니다.

    ③ WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.

    ④ INFO : 일반 정보를 나타낼 때 사용합니다.

    ⑤ DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.


    만약 로깅 레벨을 WARN 으로 설정하였다면 그 이상 레벨만 로깅하게 됩니다.

    즉 WARN, ERROR, FATAL 의 로깅이 됩니다.



    V. 샘플코드 1


    jsp에서 사용하는 예제가 없어 만들어 봤습니다.


    test.jsp


    <%@ page contentType="text/html;charset=MS949"
     import="org.apache.log4j.Logger" %>

    <%!
     static Logger logger = Logger.getLogger("test.jsp");
    %>

    <%
    logger.fatal("fatal!!");

    logger.fatal("fatal2!!", new NullPointerException("널입니다요"));

    logger.error("error!", new NumberFormatException());

    logger.error("error!2");

    logger.warn("warn");

    logger.info("info");

    logger.debug("debug");
    %>


    결과 콘솔화면








    static Logger logger = Logger.getLogger("test.jsp");

    static 메소드 getLogger를 통해 logger 인스턴스를 가져옵니다.
    getLogger에는 파라미터로 스트링 혹은 클래스를 사용하는데 jsp에서는 클래스를 파라미터로 주기에는 좀 애매합니다. 그냥 스트링으로 주도록 하지요


    logger.fatal("fatal!!");
    logger.fatal("fatal2!!", new NullPointerException("널입니다요"));
      
    logger에 fatal 레벨의 메세지를 전달합니다. 다음 두가지 메소드를 지원하는군요

    fatal(Object message)

    fatal(Object message, Throwable t)

    각 레벨마다 위처럼 두가지 메소드를 지원합니다.


    지원 메쏘드
    logger.fatal(Object message) logger.fatal(Object message, Throwable t)
    logger.error(Object message) logger.error(Object message, Throwable t)
    logger.warn(Object message) logger.warn(Object message, Throwable t)
    logger.info(Object message) logger.info(Object message, Throwable t)
    logger.debug(Object message) logger.debug(Object message, Throwable t)


    VI. 샘플코드 2


    서블릿의 경우 다음과 같이 코딩하면 되겠군요

    TestServlet.java


    import javax.servlet.*;
    import javax.servlet.http.*;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;

    public class TestServlet extends HttpServlet {


        static Logger logger = Logger.getLogger(TestServlet.class);


        public void init(ServletConfig config) throws ServletException {
             super.init(config);
        }


        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

             try {
                  ...
       
                  logger.info("Hellow World~");

                  ...

              } catch (Exception e) {
                  logger.error("Error at TestServlet", e);
              }
         }
    }



    VII. LOG4J 설정


    log4j 설정은 프로그램 내에서 할 수 있지만 설정파일을 사용함으로서 좀더 유연하게 log4j환경을 만들 수 있습니다.


    프로그램에서 설정

    <%@ page contentType="text/html;charset=MS949"
     import="org.apache.log4j.*,java.io.* "
    %>

    <%!
     static Logger logger = Logger.getLogger("log4j.jsp");
    %>

    <%
    String layout = "%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n";
    String logfilename = "DailyLog.log";
    String datePattern = ".yyyy-MM-dd ";

    PatternLayout patternlayout = new PatternLayout(layout);
    DailyRollingFileAppender appender = new DailyRollingFileAppender(patternlayout, logfilename, datePattern);
    logger.addAppender(appender);
    logger.setLevel(Level.INFO);
    logger.fatal("fatal!!");
    %>


    property 파일에 설정
    log4j.properties를 만들어 /WEB-INF/classes 밑에 놓으세요



    log4j.rootLogger=INFO, stdout, rolling

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender

    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

    log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n

    log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

    log4j.appender.rolling.File=output.log

    log4j.appender.rolling.Append=true

    log4j.appender.rolling.MaxFileSize=500KB

    log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

    log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

    log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


    #최상위 카테고리에 INFO로 레벨 설정 및 appender로 stdout, rolling을 정의

    log4j.rootLogger=INFO, stdout, rolling

    #stdout 어펜더는 콘솔에 뿌리겠다는 정의

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender

    #stdout 어펜더는 patternlayout을 사용하겠다는 정의

    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

    #페턴은 다음과 같이 포맷팅 하겠다는 것을 정의

    log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


    #역시나 rolling 어펜더는 파일로 처리한다라고 정의

    log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

    #로그 파일 이름은 output.log

    log4j.appender.rolling.File=output.log

    #true면 톰캣을 내렸다 올려도 파일이 리셋되지 않습니다.

    log4j.appender.rolling.Append=true

    #파일 최대 사이즈는 500KB로 설정

    log4j.appender.rolling.MaxFileSize=500KB

    #파일 포맷은 output.log.2005-03-10 으로 관리하겠다고 정의

    log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

    #역시나 rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의

    log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

    #rolling 어펜더는 패턴 레이아웃 포맷

    log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n



    VIII. 설정 포맷


    로그파일명 포맷 (DatePattern)
    로그파일명 포맷입니다. 날짜, 시간 및 분단위로까지 로그 파일을 분리할 수 있습니다.

    형식 설명
    '.'yyyy-MM 매달 첫번째날에 로그파일을 변경합니다
    '.'yyyy-ww 매주의 시작시 로그파일을 변경합니다.
    '.'yyyy-MM-dd 매일 자정에 로그파일을 변경합니다.
    '.'yyyy-MM-dd-a 자정과 정오에 로그파일을 변경합니다.
    '.'yyyy-MM-dd-HH 매 시간의 시작마다 로그파일을 변경합니다.
    '.'yyyy-MM-dd-HH-mm 매분마다 로그파일을 변경합니다.



    PatternLayout 포맷
    로그자체를 어떤 포맷으로 남길지 결정합니다.
    layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout등이 있으며 PatternLayout이 일반적으로 가장 많이 쓰입니다.


    형식 설명
    %p debug, info, warn, error, fatal 등의 priority 가 출력된다.
    %m 로그내용이 출력됩니다
    %d 로깅 이벤트가 발생한 시간을 기록합니다.
    포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
    %t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
    %% % 표시를 출력하기 위해 사용한다.
    %n 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
    %c 카테고리를 표시합니다
    예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
    %C 클래스명을 포시합니다.
    예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
    %F 로깅이 발생한 프로그램 파일명을 나타냅니다.
    %l 로깅이 발생한 caller의 정보를 나타냅니다
    %L 로깅이 발생한 caller의 라인수를 나타냅니다
    %M 로깅이 발생한 method 이름을 나타냅니다.
    %r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
    %x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
    %X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.

    예시) (같은 색끼리 보시면 됩니다)

    위의 test.jsp를 다음 포맷으로 출력해본다면

    [%c] [%C] [%d] [%F] [%l] [%L] [%m] [%M] [%n] [%p] [%r] [%t] [%x] [%X]는 다음과 같다

    [test.jsp] [org.apache.jsp.test_jsp] [2005-03-10 12:37:23,561] [test_jsp.java] [org.apache.jsp.test_jsp._jspService(test_jsp.java:64)] [64] [fatal!!] [_jspService] [개행] [FATAL] [765567] [http-8080-Processor25] [] []


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

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

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

    Posted by 1010
    반응형

    Digester를 이용한 Naver Open API Java Client 모듈

     

    네이버 Open API Cafe에서 검색 API의 Java Client 모듈을 보게 되었습니다.

    http://cafe.naver.com/ArticleRead.nhn?clubid=11906219&amp;amp;page=1&amp;amp;menuid=3&amp;amp;boardtype=L&amp;amp;articleid=1372

    http://insford.tistory.com/116

    위의 모듈을 참고해서 같은 역할을 하는 모듈을 다르게 구현해봤습니다.

     

    특징은 아래와 같습니다.

    •  RSS를 파싱하는 부분을 XML parsing API로 널리 알려진 Digester(http://commons.apache.org/digester/)를 사용했습니다. Digester의 예제 코드로 제공되는 RSS파싱모듈을 그대로 써서 짧은 코드로 파싱이 가능했습니다. 사용한 모듈의 API문서는 다음의 링크에서 볼 수 있습니다.

          (RSSDigester , Channel , Item)

    • 요청 파라미터를 담는 클래스를 따로 뺐습니다.  (RequestParameter.java) 이중 Target 값은 (blog, news 등 검색할 컨텐츠 유형을 선택하는 파라미터입니다.) enum으로 해서 정해진 값이 아닐 경우 compile이 안 되게 했습니다.
    • open API key값은 필수값이므로 OpenApiClient클래스의 생성자의 파라미터로 받았습니다. 대신 키 값이 없이 이 객체가 생성될 수 없도록 default 생성자는 private으로 돌려놨습니다.

     

    첨부한 파일은 이클립스에서 Dynamic Web Project로 생성한 폴더를 압축한 것입니다. 테스트 실행 서버는 Tomcat 5.5를 사용했습니다. Eclipse WTP가 설치되어 있는 환경이면 실행이 가능합니다. 그리고 enum을 썼기에 Java5이상이어야 합니다.

     

    Open API에 대한 자세한 사용법은 http://openapi.naver.com/index.nhn 를 참조하시면 됩니다.

     

    소스코드

    전체 소스와 라이브러리 다운받기 : openApiTest.zip 

     

    package openapiclient;

    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLEncoder;

    import org.apache.commons.digester.rss.Channel;
    import org.apache.commons.digester.rss.RSSDigester;

    public class NaverSearchClient {
     
     private static final String OPEN_API_URL = "http://openapi.naver.com/search";
     private String key;
     
     @SuppressWarnings("unused")
     private NaverSearchClient(){};
     
     public NaverSearchClient(String key){
      this.key = key;
     }
     public Channel search(RequestParameter param) throws Exception{
      RSSDigester digester = new RSSDigester();
      URL requestUrl = getRequestUrl(param);
      InputStream is = requestUrl.openConnection().getInputStream();
      return (Channel) digester.parse(is);
     }

     private URL getRequestUrl(RequestParameter param) throws UnsupportedEncodingException, MalformedURLException {
      StringBuffer serverUrl = new StringBuffer(OPEN_API_URL);
      serverUrl.append("?target=" + param.getTarget());
      serverUrl.append("&key=" + key);
      serverUrl.append("&start=" + param.getStart());
      serverUrl.append("&display=" + param.getDisplay());
      serverUrl.append("&query=" + URLEncoder.encode(param.getQuery(), "UTF-8"));
      if(param.getSort()!=null) serverUrl.append("&sort=" + param.getSort());
      return new URL(serverUrl.toString());
     }
    }

     

     

     package openapiclient;

    public class RequestParameter {
     
     public enum Category{
      KIN,BLOG,CAFE,DOC,WEBKR,BOOK, SHOP, ENCYC,
      KRDIC, JPDIC, ENDIC, NEWS, LOCAL, VIDEO,IMAGE;  
      public String toString(){
       return super.toString().toLowerCase();
      }
     }

     private Category target;
     private String sort;
     private int start;
     private int display;
     private String query;

     // getter and setters 생략
     }

     

    JSP에서 사용한 예제

    JSTL을 함께 사용해서 찍어본 예제입니다. http://openapi.naver.com/index.nhn 에 가셔서 API key를 발급 받으시고 소스 중간에 밑줄로 표시된 부분에 그 값을 넣으시고 돌려주시면 됩니다.

    <%@ page language="java" contentType="text/html; charset=EUC-KR"   pageEncoding="EUC-KR"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page import="openapiclient.RequestParameter" %>
    <%@ page import="openapiclient.NaverSearchClient" %>
    <%@ page import="org.apache.commons.digester.rss.Channel" %>
    <%
     String KEY = "????"; // Open API key값을 넣으세요
     NaverSearchClient client = new NaverSearchClient(KEY);
     RequestParameter param = new RequestParameter();
     param.setDisplay(10);
     param.setStart(1);
     param.setQuery("미역국");
     param.setTarget(RequestParameter.Category.NEWS);
     Channel result = client.search(param);
     result.render(System.out); // 콘솔에 받아온 내용을 확인삼아 찍어봄
     request.setAttribute("result", result);
    %>
    <!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=EUC-KR">
    <title>Naver Open API를 이용한 검색</title>
    </head>
    <body>
    <c:forEach var='item' items='${result.items}'>
      <p>
      <a href="${item.link}"> ${item.title} </a> <br/>
      ${item.description}
      </p>
    </c:forEach>
    </body>
    </html>

     

    실행결과화면

      openApiClient.JPG

     

    Posted by 1010
    반응형
    아파치 로그분석툴 awstats 설치/활용 가이드 itrend
    웹로그 분석이 필요하신 분은 사용해 보세요.

    awstats 리눅스용 tar파일도 같이 올립니다.

    리눅스포털에서 만든 가이드입니다.
    첨부파일 :  
    awstats-6.8.tar.gz   [1097 KB]   다운로드 횟수 35 회
    20060804_TD_AWSTATS의설치와활용1편.pdf   [696 KB]   다운로드 횟수 85 회
    20060901_TD_AWSTATS의설치와활용2편.pdf   [619 KB]   다운로드 횟수 86 회
    Posted by 1010
    반응형

    DBUtils에서 Clob 사용하기


    최근 오라클버젼들은 일반 String처럼 clob을 처리할 수 있지만 그렇지 않은 버젼들은 DBUtils를 조금 수정해 주서야 합니다

    resultset의 메타 정보를 이용해서 컬럼 타입이 clob인 넘들만 따로 처리하는 로직입니다


    org.apache.commons.dbutils.BasicRowProcessor.java 를 다음과 같이 수정한 후 다시 컴팔 하세요



        private Object createBean(
            ResultSet rs,
            Class type,
            PropertyDescriptor[] props,
            int[] columnToProperty,
            int cols)
            throws SQLException {


            Object bean = this.newInstance(type);       
            Object value = null;
            ResultSetMetaData meta = rs.getMetaData();
            for (int i = 1; i <= cols; i++) {

                if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
                    continue;
                }
               
                PropertyDescriptor prop = props[columnToProperty[i]];
                Class propType = prop.getPropertyType();
               
                if ("CLOB".equals(meta.getColumnTypeName(i))) {
                    value = readClob(rs, i);
                }
                else {
                    value = rs.getObject(i);
                }

                if (propType != null && value == null && propType.isPrimitive()) {
                    value = primitiveDefaults.get(propType);
                }
               
                this.callSetter(bean, prop, value);
            }

            return bean;
        }
       
        protected Object readClob(ResultSet rs, int idx) {
            StringBuffer stringbuffer = new StringBuffer();
            char[] charbuffer = new char[1024];
            int read = 0;
           
            Reader reader = null;
            String result = null;
            try {
                reader = rs.getCharacterStream(idx);
                while ((read = reader.read(charbuffer, 0, 1024)) != -1)
                    stringbuffer.append(charbuffer, 0, read);

                result = stringbuffer.toString();
            } catch (Exception exception) {
                System.out.println(exception);
            } finally {
                if (reader != null) try { reader.close(); } catch (Exception e){}
            }

            return result;
        }




    Posted by 1010
    반응형

    POI HSLF

    Java API To Access Microsoft Powerpoint Format Files



    1. POI API Document

    http://jakarta.apache.org/poi/apidocs/index.html


    2. POI Download

    http://www.apache.org/dyn/closer.cgi/jakarta/poi/


    3. 간단한 텍스트 읽기

    <%@ page import="java.io.*"%>
    <%@ page import="org.apache.poi.hslf.model.*"%>
    <%@ page import="org.apache.poi.hslf.usermodel.*"%>
    <%@ page import="org.apache.poi.hslf.*"%>

    <%

        SlideShow src =

              new SlideShow(

              new HSLFSlideShow("C:\\Web\\Tomcat 5.5\\webapps\\ROOT\\test.ppt"));


        Slide[] sl = src.getSlides();


        for (int i = 0; i < sl.length; i++) {
            Slide s = sl[i];
            TextRun[] trs = s.getTextRuns();
            for (int k = 0; k < trs.length; k++) {
                TextRun tr = trs[k];
                System.out.println(tr.getText());
               }
        }

    %>


    4. 테스트버젼

    PPT 2000


    Posted by 1010
    반응형

    POI HWPF

    Java API to Handle Microsoft Word File



    1. POI API Document

    http://jakarta.apache.org/poi/apidocs/index.html


    2. POI Download

    http://www.apache.org/dyn/closer.cgi/jakarta/poi/


    3. 간단한 텍스트 읽기

    <%@ page import="java.io.*"%>
    <%@ page import="org.apache.poi.hwpf.usermodel.*"%>
    <%@ page import="org.apache.poi.hwpf.*"%>

    <%

       HWPFDocument doc =

               new HWPFDocument(

               new FileInputStream("C:\\Web\\Tomcat 5.5\\webapps\\ROOT\\2007.doc"));


        Range r = doc.getRange();
       
        for (int x = 0; x < r.numSections(); x++) {
            Section s = r.getSection(x);
            for (int y = 0; y < s.numParagraphs(); y++) {
                Paragraph p = s.getParagraph(y);
                for (int z = 0; z < p.numCharacterRuns(); z++) {
                    CharacterRun run = p.getCharacterRun(z);
                    String text = run.text();
                    System.out.print(text);
                }
                System.out.println();
            }
        }

    %>


    4. MS Word 테스트 버젼

    MS워드 2000, MS워드 2003


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

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

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

    Posted by 1010