<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML><HEAD><TITLE>ONJava.com: Extending Struts</TITLE> <META http-equiv=Content-Type content="text/html; charset=euc-kr"> <STYLE TYPE="text/css"> <!-- BODY { font-size:10pt; } --> </STYLE>

<META content=NOARCHIVE name=GOOGLEBOT> <META content="NOINDEX, NOFOLLOW" name=ROBOTS> <META content="MSHTML 6.00.2800.1476" name=GENERATOR></HEAD> <BODY text=#000000 bgColor=#ffffff><H2>Extending Struts</H2>by <A href="http://www.onjava.com/pub/au/1995">Sunil Patil</A><BR>11/10/2004<BR clear=all><br>

<!-- sidebar begins --><!-- don't move sidebars --><!-- sidebar ends --> <H3>Introduction</H3> <P>나는 개발자가 자신만의 MVC프레임워크를 구현한 개발자의 프로젝트를 많이 보았다. 하지만 그들은 근본적으로 Struts와 다른 어떤것을 원하지는 않기 때문에 그들은 Struts를 확장하는 방법에 대해서 알지 못한다. 당신은 당신 자신만의 MVC프레임워크를 개발함으로써 총괄적인 제어를 할수 있다. 하지만 이것은 많은 자원에 커밋을 해야하는것을 뜻하기 때문에 촉박한 시간속에 진행되는 프로젝트에서는 때때로 불가능하다. </P> <P><A href="http://struts.apache.org/">Struts</A>는 매우 강력할뿐 아니라 매우 쉽게 확장가능하다. 당신은 다음의 세가지 방법으로 Struts를 확장할수 있다.</P> <OL> <LI><CODE>PlugIn</CODE>: 애플리케이션이 시작하거나 종료될때 일부 비지니스 로직이 수행되길 원한다면 당신만의 <CODE>PlugIn</CODE>클래스를 생성하라. <LI><CODE>RequestProcessor</CODE>: 요청처리단계(request-processing phase)동안 특수한 시점에 몇몇 비지니스 로직이 수행되길 바란다면 당신만의 <CODE>RequestProcessor</CODE>를 생성하라. 예를 들면 유저가 로그인하고 모든 요청이 수행되기 전에 특수한 action이 수행되기 위한 권한를 가지는지 체크하기 위한 <CODE>RequestProcessor</CODE>를 확장할수 있을것이다. <LI><CODE>ActionServlet</CODE>: 애플리케이션이 시작되거나 종료될때 또는 요청이 처리되는 동안 당신의 비지니스 로직이 수행되길 원한다면 <CODE>ActionServlet</CODE>클래스를 확장할수 있다. 하지만 <CODE>PlugIn</CODE>이나 <CODE>RequestProcessor</CODE>이 어느 상황에서도 수행되지 않을때 이것을 사용해야 한다.</LI></OL><!-- sidebar begins -->

<P>이 글에서 우리는 세가지 방법을 각각 사용함으로써 Struts를 확장하기 위한 방법을 설명하는 예제 Struts애플리케이션을 사용할 것이다. 각각의 방식에 유효한 샘플코드는 이 글 끝에 있는<A href="http://www.onjava.com/lpt/a/5411#resources">Resources</A>부분에서 다운로드 받을수 있다. Struts확장의 가장 성공적인 예제 두가지는 <A href="http://struts.apache.org/userGuide/dev_validator.html">Struts Validation</A>프레임워크와 <A href="http://struts.apache.org/userGuide/dev_tiles.html">Tiles</A> framework이다.</P> <P>나는 당신이 이미 Struts프레임워크에 매우 친숙하고 이것을 사용해서 간단한 애플리케이션을 생성하는 방법을 알고 있다고 가정한다. 만약 당신이 Struts에 대해 좀더 많은 것을 알기를 원한다면 <A href="http://www.onjava.com/lpt/a/5411#resources">Resources</A>부분을 보길 바란다.</P> <H3><CODE>PlugIn</CODE></H3> <P>Struts문서에 따르면 "플러그인은 애플리케이션이 시작되거나 종료되는 이벤트에 대해 통지됨으로써 필요한 모듈형태의 자원또는 서비스를 위한 설정포장(configuration wrapper)이다." 이것은 애플리케이션이 시작되거나 종료될때 수행되는 어떤것을 위한 <CODE>PlugIn</CODE>인터페이스를 구현하는 클래스를 생성할수 있음을 의미한다. </P> <P>영속적인 기법(persistence mechanism)처럼 Hibernate를 사용하는 웹 애플리케이션을 생성하고 있다고 하자. 나는 애플리케이션이 시작되자 마자 Hibernate를 초기화하길 원한다. 나의 웹 애플리케이션이 첫번째 요청을 받았을때 Hibernate는 벌써 설정이 되었고 사용될 준비가 되어 있다. 우리는 애플리케이션이 종료될때 Hibernate가 닫히길 바란다. 우리는 다음의 두단계로 Hibernate <CODE>PlugIn</CODE>의 요구사항을 구현할수 있다.</P> <OL> <LI> <P>다음처럼 <CODE>PlugIn</CODE>인터페이스를 구현하는 클래스를 생성하자: </P><PRE><CODE> public class HibernatePlugIn implements PlugIn{ private String configFile; // This method will be called at application shutdown time public void destroy() { System.out.println("Entering HibernatePlugIn.destroy()"); //Put hibernate cleanup code here System.out.println("Exiting HibernatePlugIn.destroy()"); } //This method will be called at application startup time public void init(ActionServlet actionServlet, ModuleConfig config) throws ServletException { System.out.println("Entering HibernatePlugIn.init()"); System.out.println("Value of init parameter " + getConfigFile()); System.out.println("Exiting HibernatePlugIn.init()"); } public String getConfigFile() { return name; } public void setConfigFile(String string) { configFile = string; } } </CODE></PRE> <P>이 <CODE>PlugIn</CODE> 인터페이스를 구현하는 클래스는 <CODE>init()</CODE>와 <CODE>destroy()</CODE>의 두가지 메소드를 반드시 구현해야만 한다: <CODE>init()</CODE>는 애플리케이션이 시작될때 호출된다. 그리고 <CODE>destroy()</CODE>는 종료될때 호출된다. Struts는 <CODE>init</CODE>파라미터들을 <CODE>PlugIn</CODE>클래스에 전달한다. 전달된 파라미터를 위해 당신은 모든 파라미터를 위해 당신의 <CODE>PlugIn</CODE>클래스내에 JavaBean타입의 setter메소드를 생성해야만 한다. 우리의 <CODE>HibernatePlugIn</CODE>클래스에서 나는 이것을 애플리케이션에 하드코딩하는것 대신에 <CODE>configFile</CODE>의 이름을 전달하기를 원한다. </P> <LI> <P><I>struts-config.xml</I>에 다음라인을 추가함으로써 새로운 <CODE>PlugIn</CODE>에 대해 Struts에 알린다: </P><PRE><CODE> <struts-config> ... <!-- Message Resources --> <message-resources parameter= "sample1.resources.ApplicationResources"/>

<!-- Declare your plugins --> <plug-in className="com.sample.util.HibernatePlugIn"> <set-property property="configFile" value="/hibernate.cfg.xml"/> </plug-in> </struts-config> </CODE></PRE></LI></OL> <P><CODE>className</CODE>속성은 <CODE>PlugIn</CODE>인터페이스를 구현하는 클래스의 패키지명까지 포함하는 full name이다. <CODE>PlugIn</CODE>클래스에 전달하길 원하는 모든 초기화 파라미터를 위해 <CODE><set-property></CODE>를 추가한다. 우리의 예제에서 나는 설정파일의 이름을 넘기길 원한다. 그래서 나는 설정파일경로값을 <CODE><set-property></CODE>와 함께 추가했다.</P> <P>Tiles와 Validator프레임워크는 둘다 설정파일을 읽어서 초기화하기 위해 <CODE>PlugIn</CODE>를 사용한다. <CODE>PlugIn</CODE>클래스에서 할수 있는것은 두가지 이상이다: </P> <UL> <LI>만약 당신의 애플리케이션이 몇몇 설정 파일에 의존적이라면 <CODE>PlugIn</CODE>클래스에서 이를 체크하고 설정파일이 유효하지 않을때 <CODE>ServletException</CODE>을 던질수 있다. 이것은 <CODE>ActionServlet</CODE>이 유효하지 않게 되는 결과이다. <LI>Struts기반의 모듈을 서술하는 정적인 설정정보의 집합인 <CODE>ModuleConfig</CODE>내의 몇몇 변동을 원한다면 <CODE>PlugIn</CODE>인터페이스의 <CODE>init()</CODE>메소드가 당신의 마지막 방법이다. Struts는 <CODE>PlugIn</CODE>가 처리되었을때 <CODE>ModuleConfig</CODE>를 정지(freeze)할것이다. </LI></UL> <H3>요청이 처리되는 방법</H3> <P><CODE>ActionServlet</CODE>는 Struts프레임워크내에서 요청의 모든것을 다루기 때문에 servlet이다. 요청을 받을때마다 최근 요청을 위한 하위 애플리케이션을 찾기 시작한다. 하위 애플리케이션이 찾아졌다면 하위 애플리케이션을 위한 <CODE>RequestProcessor</CODE>객체를 생성하고 <CODE>HttpServletRequest</CODE>와 <CODE>HttpServletResponse</CODE>객체로 이것을 전달함으로써 <CODE>process()</CODE>메소드가 호출된다.</P> <P><CODE>RequestProcessor.process()</CODE>는 대부분의 요청이 처리되는 곳이다. <CODE>process()</CODE>메소드는 임시 메소드 디자인 패턴을 사용함으로써 구현된 메소드이다. <CODE>process()</CODE>메소드로 부터 순차적으로 호출되는 요청처리의 각단계에 수행되는 별개의 메소드가 있다. 예를 들면 최근요청을 가지는 <CODE>ActionForm</CODE>클래스를 찾기 위한 각각의 메소드들이 있고 만약 최근사용자가 action맵핑을 수행하기 위한 요구되는 권한을 가지는지 체크한다. 이것은 우리에게 굉장한 유연성을 제공한다. Struts내의 <CODE>RequestProcessor</CODE>클래스는 각각의 요청처리단계를 위한 기본적인 구현을 제공한다. 이것은 당신이 관심있어하는 메소드만 오버라이딩 하고 나머지 메소드는 그대로 사용하면 된다는것을 의미한다. 예를 들면 기본Struts는 <CODE>request.isUserInRole()</CODE>호출함으로써 <CODE>ActionMapping</CODE>을 수행하기 위해 필요한 권한을 가지는지 체크한다. 그러나 만약 DataBase로부터 쿼리해온 결과로 권한 체크를 하기 바란다면 <CODE>processRoles()</CODE>메소드를 모두 오버라이딩 할 필요가 있고 요청되는 권한이 있는지 없는지에 대해 boolean값을 반환해야 한다. </P> <P>처음으로 우리는 <CODE>process()</CODE>메소드가 기본적으로 구현된 방법을 볼것이고 <CODE>RequestProcessor</CODE>클래스내의 각각의 메소드에 대해서 설명할것이다. 그러면 당신은 요청 처리의 부분에 대해서 바꾸고자 하는 부분을 결정할수 있다.</P><PRE><CODE> public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Wrap multipart requests with a special wrapper request = processMultipart(request); // Identify the path component we will // use to select a mapping String path = processPath(request, response); if (path == null) { return; } if (log.isDebugEnabled()) { log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); } // Select a Locale for the current user if requested processLocale(request, response); // Set the content type and no-caching headers // if requested processContent(request, response); processNoCache(request, response); // General purpose preprocessing hook if (!processPreprocess(request, response)) { return; } // Identify the mapping for this request ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; } // Check for any role required to perform this action if (!processRoles(request, response, mapping)) { return; } // Process any ActionForm bean related to this request ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); if (!processValidate(request, response, form, mapping)) { return; } // Process a forward or include specified by this mapping if (!processForward(request, response, mapping)) { return; } if (!processInclude(request, response, mapping)) { return; } // Create or acquire the Action instance to // process this request Action action = processActionCreate(request, response, mapping); if (action == null) { return; } // Call the Action instance itself ActionForward forward = processActionPerform(request, response, action, form, mapping); // Process the returned ActionForward instance processForwardConfig(request, response, forward); } </CODE></PRE> <OL> <LI><CODE>processMultipart()</CODE>: 이 메소드에서 Struts는 <CODE>contentType</CODE>이 <CODE>multipart/form-data</CODE>인지 찾을수 있는 요청을 읽는다. 그렇다면 이것은 <CODE>contentType</CODE>을 파싱하고 <CODE>HttpServletRequest</CODE>를 구현하는것을 감춘다(wrap). 당신이 전송할 데이터를 위해서 HTML<CODE>FORM</CODE>를 생성할때 요청의 <CODE>contentType</CODE>은 기본적으로는 <CODE>application/x-www-form-urlencoded</CODE>이다. 하지만 만약에 당신이 파일을 업로드 하기 위해 <CODE>FILE</CODE>타입을 사용한다면 당신은 <CODE>contentType</CODE>을 <CODE>multipart/form-data</CODE>로 바꾸어야 한다. 그러나 이렇게 함으로써 <CODE>HttpServletRequest</CODE>의 <CODE>getParameter()</CODE>메소드를 통해 유저에 의해 서브밑된 값을 더이상 읽을수 없다. 당신은 <CODE>InputStream</CODE>처럼 요청된것을 읽어야만 하고 값을 파싱해야 한다. <LI><CODE>processPath()</CODE>: 이 메소드에서 Struts는 <CODE>ActionMapping</CODE>을 얻기 위해 사용되어야 하는 path를 결정하기 위한 요청 URI를 읽을것이다. <LI><CODE>processLocale()</CODE>: 이 메소드에서 Struts <CODE>org.apache.struts.action.LOCALE</CODE>속성값처럼 <CODE>HttpSession</CODE>에 저장되는 <CODE>Locale</CODE>을 가질것이다. <CODE>HttpSession</CODE>은 이 메소드의 측면효과처럼 생성될것이다. 만약에 당신이 이런것이 발생하지 않길 바란다면 당신은 <I>struts-config.xml</I>파일 내에 <PRE><CODE><controller> <set-property property="locale" value="false"/> </controller> </CODE></PRE>를 추가함으로써 <CODE>ControllerConfig</CODE>내의 <CODE>locale</CODE> 속성을 false값으로 셋팅할수 있다. <LI><CODE>processContent()</CODE>: <CODE>response.setContentType()</CODE>을 호출함으로써 의존적으로 <CODE>contentType</CODE>을 셋팅한다. 이 메소드는 처음에 <I>struts-config.xml</I>파일로 부터 <CODE>contentType</CODE>의 값을 가져오도록 시도한다. 이것은 기본적으로는 <CODE>text/html</CODE>를 사용한다. 이것을 오버라이딩하기 위해서는 다음처럼 하면 된다. <PRE><CODE><controller> <set-property property="contentType" value="text/plain"/> </controller> </CODE></PRE> <LI><CODE>processNoCache()</CODE>: Struts는 모든 응답을 위해 다음의 세가지 headers를 설정할 것이다. 만약에 <CODE>no-cache</CODE>로 설정한다면: <PRE><CODE> requested in struts config.xml response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 1); </CODE></PRE>만약에 당신이 <CODE>no-cache</CODE> header를 설정한다면, <I>struts-config.xml</I>파일에 다음 라인을 추가해라: <PRE><CODE> <controller> <set-property property="noCache" value="true"/> </controller> </CODE></PRE> <LI><CODE>processPreprocess()</CODE>: 이것은 총괄적인 용도에서 사용된다. 하위 클래스에 의해 오버라이딩될수 있는 선처리된 중계자(pre-processing hook)이다. <CODE>RequestProcessor</CODE>내의 구현은 어떤것도 하지 않고, 단지 항상 true값을 반환한다. 요청처리가 취소된다면 false값을 반환할것이다. <LI><CODE>processMapping()</CODE>: 이것은 <CODE>ActionMapping</CODE>객체를 얻기위한 path정보를 사용할것이다. <CODE>ActionMapping</CODE>객체는 <I>struts-config.xml</I>파일내의 <CODE><action></CODE>를 표현한다. <PRE><CODE> <action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"> <forward name="sucess" path="/sucessPage.do"/> <forward name="failure" path="/failurePage.do"/> </action></CODE> </CODE></PRE><CODE>ActionMapping</CODE><CODE>Action</CODE>클래스와 요청을 처리할때 사용되는 <CODE>ActionForm</CODE>의 이름같은 정보를 포함한다. 이것은 <CODE>ActionMapping</CODE>를 위해 설정된 <CODE>ActionForwards</CODE>정보또한 가진다. <BR> <LI><CODE>processRoles()</CODE>: Struts웹 애플리케이션 보안은 권한 스키마를 제공한다. 이것은 컨테이너로 로그인한 유저를 의미한다. Struts의 <CODE>processRoles()</CODE>메소드는 만약에 <CODE>request.isUserInRole()</CODE>호출함으로써 주어진 <CODE>ActionMapping</CODE>을 수행하기 위해 요구되는 권한을 가지는지 체크할수 있다. <PRE><CODE> <action path="/addUser" roles="administrator"/></CODE> </CODE></PRE>당신이 <CODE>AddUserAction</CODE>를 가지고 당신은 단지 administrator많이 새로운 유저를 추가할수 있기를 원한다면 당신은 <CODE>AddUserAction</CODE> action에 <CODE>administrator</CODE>값을 가지는 속성을 추가할수 있다. <CODE>AddUserAction</CODE>를 실행하기 전에 이것은 administrator권한을 가지는 유저인지 언제나 체크할것이다. <LI><CODE>processActionForm()</CODE>: 모든 <CODE>ActionMapping</CODE>은 이것은 포함하는<CODE>ActionForm</CODE>가지고 있다. Struts가 <CODE>ActionMapping</CODE>을 처리할때 이것은 포함하는 <CODE><action></CODE>의 name속성의 값으로 부터 <CODE>ActionForm</CODE>의 이름을 찾는다. <PRE><CODE><form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="firstName" type="java.lang.String"/> <form-property name="lastName" type="java.lang.String"/> </form-bean></CODE> </PRE>예제에서 <CODE>org.apache.struts.action.DynaActionForm</CODE>의 객체가 요청 범위에 존재하는지 먼저 체크할것이다. 만약에 그렇다면 이것을 사용할것이고 아니라면 요청 범위내에서 새로운 객체를 생성할것이다. <LI><CODE>processPopulate()</CODE>: 이 메소드에서는 Struts는 <CODE>ActionForm</CODE>클래스가 매치되는 요청파라미터의 값과 함께 가도록 한다. <LI><CODE>processValidate()</CODE>: Struts는 <CODE>ActionForm</CODE>클래스의 <CODE>validate()</CODE>메소드를 호출할것이다. 만약에 <CODE>validate()</CODE>메소드로 부터 <CODE>ActionErrors</CODE>를 반환한다면 이것은 <CODE><action></CODE>의 <CODE>input</CODE> 속성에 의해 표시되는 페이지로 유저를 redirect시킬것이다. <BR> <LI><CODE>processForward()</CODE> 와 <CODE>processInclude()</CODE>: Struts가 <CODE><action></CODE>의 <CODE>forward</CODE>와 <CODE>include</CODE>속성의 값을 체크할것이다. 만약에 값이 존재한다면 설정된 페이지에 <CODE>forward</CODE>또는 <CODE>include</CODE>요청을 두게 된다. <PRE><CODE> <action forward="/Login.jsp" path="/loginInput"/> <action include="/Login.jsp" path="/loginInput"/></CODE> </PRE>당신은 그것들의 이름으로 부터 그것들의 기능의 차이점을 추측할수 있다. <CODE>processForward()</CODE>는 마지막에 <CODE>RequestDispatcher.forward()</CODE>를 호출하고 <CODE>processInclude()</CODE>는 <CODE>RequestDispatcher.include()</CODE>를 호출한다. 만약에 <CODE>forward</CODE>와 <CODE>include</CODE>속성을 둘다 설정한다면 언제나 forward가 먼저 처리된다. <LI><CODE>processActionCreate()</CODE>: 이 메소드는 <CODE><action></CODE>의 <CODE>type</CODE>속성으로 부터 <CODE>Action</CODE>클래스의 이름을 가져오고 그것의 인스턴스를 생성하고 반환한다. 우리의 경우에는 <CODE>com.sample.NewContactAction</CODE>클래스의 인스턴스를 생성할것이다. <LI><CODE>processActionPerform()</CODE>: 이 메소드는 <CODE>Action</CODE>클래스로 부터 비지니스 로직을 작성해야 하는 <CODE>execute()</CODE>메소드를 호출한다. <LI><CODE>processForwardConfig()</CODE>: <CODE>Action</CODE>클래스의 <CODE>execute()</CODE>메소드는 유저에서 표시되어야 하는 페이지를 가리키는 <CODE>ActionForward</CODE>타입의 객체를 반환할것이다. 그리고 Struts는 그 페이지를 위해 <CODE>RequestDispatcher</CODE>를 생성하고 <CODE>RequestDispatcher.forward()</CODE>메소드를 호출한다. </LI></OL> <P>위의 리스트는 <CODE>RequestProcessor</CODE>의 기본적인 구현이 요청 처리의 모든 단계에서 수행되고 각각의 단계는 순차적으로 수행된다는것을 설명한다. 당신이 보는것처럼 <CODE>RequestProcessor</CODE>는 매우 유연성이 높고 당신이 <CODE><controller></CODE>의 속성을 설정함으로써 그것을 설정하도록 허락한다. 예를 들면 만약 당신의 애플리케이션이 HTML대신에 XML데이터를 다루도록 한다면 controller의 속성을 셋팅함으로써 Struts에 알릴수 있다.</P> <H3>자신의 <CODE>RequestProcessor</CODE>생성하기</H3> <P>위에서 우리는 <CODE>RequestProcessor</CODE>의 기본적인 구현방법을 보았다. 지금 우리는 자신만의 변경된 <CODE>RequestProcessor</CODE>를 생성함으로서 커스터마이징하는 방법의 예제를 볼 것이다. 변경된 <CODE>RequestProcessor</CODE>를 생성하기 위해 우리는 두가지 비지니스 요구사항을 구현하는 샘플 애플리케이션을 바꿀것이다:</P> <UL> <LI>우리는 규칙적인 HTML페이지 대신에 이미지를 가져오는 <CODE>ContactImageAction</CODE>클래스를 생성하기를 원한다. </LI> <LI>모든 요청을 처리하기 전에 우리는 세션의 <CODE>userName</CODE>속성을 체크해서 유저의 로그인을 처리하길 원한다. 만약에 그런 속성이 찾아지지 않는다면 우리는 login페이지로 유저를 redirect시킬것이다.</LI></UL> 우리는 그런 비지니스 요구사항을 구현하기 위해 두가지 단계로 샘플 애플리케이션을 바꿀것이다. <OL> <LI>자신의 <CODE>RequestProcessor</CODE>클래스를 확장하는 <CODE>CustomRequestProcessor</CODE>클래스를 생성하기: <PRE><CODE>public class CustomRequestProcessor extends RequestProcessor { protected boolean processPreprocess ( HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(false); //If user is trying to access login page // then don't check if( request.getServletPath().equals("/loginInput.do") || request.getServletPath().equals("/login.do") ) return true; //Check if userName attribute is there is session. //If so, it means user has allready logged in if( session != null && session.getAttribute("userName") != null) return true; else{ try{ //If no redirect user to login Page request.getRequestDispatcher ("/Login.jsp").forward(request,response); }catch(Exception ex){ } } return false; }

protected void processContent(HttpServletRequest request, HttpServletResponse response) { //Check if user is requesting ContactImageAction // if yes then set image/gif as content type if( request.getServletPath().equals("/contactimage.do")){ response.setContentType("image/gif"); return; } super.processContent(request, response); } } </CODE></PRE><CODE>CustomRequestProcessor</CODE>클래스의 <CODE>processPreprocess</CODE>메소드내에서 우리는 세션의 <CODE>userName</CODE>속성을 체크하고 있고 만약에 값이 없다면 login페이지로 유저를 redirect시킨다.<P></P> <P><CODE>ContactImageAction</CODE>클래스로부터 출력물같은 이미지를 가져오는 우리의 요구사항을 위해 우리는 <CODE>processContent</CODE>메소드를 오버라이딩 하고 먼저 <CODE>/contactimage</CODE>경로로 요청이 오는지 체크한다. 만약에 그렇다면 <CODE>contentType</CODE>을 <CODE>image/gif</CODE>로 설정하고 아니라면 <CODE>text/html</CODE>으로 설정한다. </P> <LI><CODE>CustomRequestProcessor</CODE>가 <CODE>RequestProcessor</CODE>클래스처럼 사용되도록 Struts에 알리고 <I>struts-config.xml</I>파일에 <CODE><action-mapping></CODE>뒤에 다음과 같은 라인을 추가한다: <PRE><CODE><controller> <set-property property="processorClass" value="com.sample.util.CustomRequestProcessor"/> </controller> </CODE></PRE> 오버라이딩 된 <CODE>processContent()</CODE>이 OK인것을 확인해라. 만약에 <CODE>text/html</CODE>과 다른 <CODE>contentType</CODE>의 산출물을 가져오기를 원하는 곳에는 몇개의<CODE>Action</CODE>을 가진다. 이런 상황이 아니라면 당신은 <CODE>contentType</CODE>처럼 이미지를 가져오는 <CODE>Action</CODE>과 <CODE>image/gif</CODE>를 위한 요청을 다루기 위한 하위 애플리케이션을 생성할것이다. </LI></OL> Tiles프레임워크는 Struts에 의해 생성되는 산출물을 꾸미기 위해서 자신만의 <CODE>RequestProcessor</CODE>를 사용한다. <H3><CODE>ActionServlet</CODE></H3> <P>만약에 당신이 Struts웹 애플리케이션의 <I>web.xml</I>파일을 조사한다면 그것은 다음과 비슷할것이다: </P> <PRE><CODE> <web-app > <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!-- All your init-params go here--> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app > </CODE></PRE> <P><CODE>ActionServlet</CODE>은 Struts로의 모든 요청을 다루는 책임을 지고 있다. 만약 애플리케이션이 시작되거나 종료될때 또는 모든 요청시 수행하고 싶은것이 있다면 <CODE>ActionServlet</CODE>의 하위클래스를 생성할수 있다. 하지만 <CODE>ActionServlet</CODE>클래스를 확장하기 전에 <CODE>PlugIn</CODE>또는 <CODE>RequestProcessor</CODE>를 생성해야만 한다. Servlet1.1전에는 Tiles프레임워크는 생성된 응답을 꾸미기 위해서 <CODE>ActionServlet</CODE>를 확장하는데 기본을 두고 있다. 하지만 1.1에서는 <CODE>TilesRequestProcessor</CODE>를 사용한다. </P> <H3><A name=resources>Resources</A></H3> <UL> <LI>이 글을 위한<A href="http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip">샘플코드 다운로드</A> <LI><A href="http://struts.apache.org/">Struts홈페이지</A> <LI>"<A href="http://www.onjava.com/pub/a/onjava/2001/09/11/jsp_servlets.html">Jakarta Struts Framework의 소개</A>" <LI>"<A href="http://www.onjava.com/pub/a/onjava/pub/a/2002/11/06/struts1.html">Jakarta Struts 1.1배우기</A>" </LI></UL> <P><I><A href="http://www.onjava.com/pub/au/1995">Sunil Patil</A>는 4년동안 J2EE기술을 사용해서 일을 했으며 지금은 IBM Software 연구소에 근무중이다.</I></P> <HR noShade SIZE=1>

<P><FONT face=verdana,arial,helvetica size=1>Copyright 2004 O'Reilly Media, Inc.</FONT></P></BODY></HTML>

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-1) was last changed on 06-Apr-2006 09:45 by UnknownAuthor