-------------------------------------------------------------
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를 작성한다.