struts 2008. 7. 30. 10:35

struts2] struts2 + ibatis 활용한 게시판1-1

           struts2 + ibatis 를 활용한 게시판


사용자 삽입 이미지

- struts2 framework 와 ibatis를 연동한 게시판 구축
- struts2 action클래스와 paging처리하는부분은 2페이지에 걸쳐 작성



1. 환경설정
    - struts2,ibatis,oracle library 등록(web-inf/lib)

2. struts.xml
    - aciton 등록

3. web.xml
   - struts환경에서는 사용자가 action요청시 가장 먼저 FilterDispatcher가 호출되고 위에 그림처럼
      배치관리자로 인해 struts.xml 파일과 struts.properties 파일이 자동으로 호출되게 된다

    <filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
   </filter>
   
   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

4. DaoService.java
    - 데이타베이스 접속하기위한 DaoManager 를 반환하는 DaoService클래스 작성
    - 싱글턴패턴으로 만들어서 하나의 DaoManager를 공유



* struts.xml
     - 반드시 src 기본경로에 존재하여야 struts에서 인식할수있다.
     - web.xml에서 등록한 필터가 읽혀진후 해당 xml문서가 읽혀진다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC '-//Apache Software Foundation//DTD Struts Configuration 2.0//EN'
'http://struts.apache.org/dtds/struts-2.0.dtd'>
<struts>
    <package name="defalut" extends="struts-default" namespace="">
      <action name="list" class="action.ListAction">
       <result>/list.jsp</result>
      </action>
    </package>
</struts>


* DaoService.java
          - DaoService는 한번만 읽혀져서 하나만 생성되어 DaoManager는 한개만 생성된다.


package dao;

import java.io.Reader;

import com.ibatis.common.resources.Resources;
import com.ibatis.dao.client.Dao;
import com.ibatis.dao.client.DaoManager;
import com.ibatis.dao.client.DaoManagerBuilder;

public class DaoService {
  private static DaoManager daoManager;
 
  private static DaoManager getDaoManager(){
   String daoXmlResouce  = "dao.xml";  //src폴더에 dao.xml
   Reader reader = null;
   
   if(daoManager ==null)  //static으로 선언되어있기때문에 null 체크를 해서 null일경우만 생성
    try {
      reader = Resources.getResourceAsReader(daoXmlResouce);     //dao.xml파일로부터 읽음
      daoManager = DaoManagerBuilder.buildDaoManager(reader);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return daoManager;
  } 
 
  //실제 서버에서 dao객체가 필요할 경우 호출하는 메서드
  public static Dao getDao(Class inter){
   return getDaoManager().getDao(inter);
  }
}



* dao.xml
     - 사용하고자하는 dao interface 및  dao를 구현하고 있는 dao 를 명시

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE daoConfig PUBLIC "-//ibatis.apache.org//DTD DAO Configuration 2.0//EN" "http://ibatis.apache.org/dtd/dao-2.dtd">

<daoConfig>
 <context id="sqlMap">
  <transactionManager type="SQLMAP">
   <property name="SqlMapConfigResource" value="config/SqlMapConfig.xml"/> 
  </transactionManager>
  <dao interface="dao.Bbs" implementation="dao.BbsImpl"/>
 </context>
</daoConfig>



* SqlMapConfig.xml
     - 사용하는 database 환경설정이 필요한 파일이다(여기서는 oracle을 사용)

<!DOCTYPE sqlMapConfig PUBLIC '-//ibatis.apache.org/DTD SQL Map Config 2.0//EN'
'http://ibatis.apache.org/dtd/sql-map-config-2.dtd'>
<sqlMapConfig>
    <properties resource="config/sqlMapConfig.properties" />
    <transactionManager type="JDBC" commitRequired="false">
        <dataSource type="SIMPLE">
            <property name="JDBC.Driver" value="${driver}" />  //라이브러리(odbc14.jar)파일이 필요
            <property name="JDBC.ConnectionURL" value="${url}" />
            <property name="JDBC.Username" value="${user}" />
            <property name="JDBC.Password" value="${pwd}" />
        </dataSource>
    </transactionManager>
 <sqlMap resource="config/bbs.xml" />
</sqlMapConfig>



* SqlMapConfig.properties
     - 보안을 위해 db접속정보를 따로 파일로 만들어둠

driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:ORCL
user=SCOTT
pwd=TIGER


* Bbs.java(Interface)
     - 사용자가 호출할수 있는 기능적요소들을 명시

package dao;

import java.util.ArrayList;

import vo.BbsVO;

public interface Bbs {
  public ArrayList<BbsVO> getAllBbs();
}


* BbsImpl.java
     - 위의 인터페이스를 구현하고 SqlMapDaoTemplate 를 상속받는 클래스
     - 반드시 디폴트 생성자만 있으면 안되고 반드시 SqlMapDaoTemplate를 상속받고
        DaoManager를 인자로 받는 생성자를  만들어야 한다.
     - struts환경에서 여기에 정의된 메소드를 호출하게되면 결과를 받을수 있게 된다.
         ex> getAllBbs()
    
* BbsVO.java
     - DataBase에 있는 컬럼명과 동일한이름으로 멤버 변수로 정의
     - 정의된 멤버변수의 setter/getter 메서드를 정의


* bbs.xml
     - SqlMapConifg.xml 안에 <sqlMap>에 정의된 xml문서
     - 이 안에 정의된 <select> 요소의 속성 id는 값은 BbsImple.java클래스의 queryForList("") 이안에
       들어가는 인자값(String)과 동일해야 한다.  ex> select id="selectAllBbs"

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC '-//ibatis.apache.org//DTD SQL Map 2.0//EN' 'http://ibatis.apache.org/dtd/sql-map-2.dtd'>

<sqlMap namespace="memo">
     <typeAlias alias="bbsvo" type="vo.BbsVO"/>
    
     <select id="selectAllBbs" resultClass="bbsvo">
           select * from bbs_t order by idx desc
     </select>
</sqlMap>


* ListAction.java
     - struts 액션 클래스로서 ActionSupport를 상속
     - pojo기반으로 사용하게되면 session처리하는데 어려움이 있기때문에 ActionSupport를 상속
     - ListAction에서 ibatis DaoService에 정의된 메소드를 호출하게되면 해당 객체를 얻어낼수있다.
          ex>  Bbs bbs = (Bbs)DaoService.getDao(Bbs.class);


           


java 2008. 7. 29. 20:07

Java] java.lang.reflect.* 활용


*JSP 페이지로부터 form에 대한 값을 넘겨받아 useBean을 이용해서 값 저장
    -  jsp action 페이지에서 값을 저장후 reflect를 사용한 클래스 호출

     <jsp:useBean id="vo" class="com.bbs.BbsVO">
         <jsp:setProperty name="vo" property="*"/>
     </jsp:useBean>

     com.util.BeanExamer.survey(vo);   // 자바클래스의 reflect를 이용한 클래스


* BeanExamer.java

package com.util;
import java.lang.ref.*;
import java.lang.reflect.*;    // reflect를 사용하기 위한 import
import java.util.*;

public class BeanExamer {
 
public static void survey(Object obj){
 
  Class clz = obj.getClass();   // 인자로 전달받은 vo객체를 통해 클래스를 얻어냄
     
  Method[] methods = clz.getDeclaredMethods();  
    // 위에서 얻어낸 클래스를 통해  해당 클래스에 선언된 메소드들을 배열에 저장

  ArrayList<Method> getMethods = new ArrayList<Method>(); // ArrayList에 메소드 타입으로 제너릭 설정
 
 
  for(int i = 0; i < methods.length;i++){  
/* 클래스에 선언된 메서드수만큼 loof를 시키고 get으로 시작하는 메서드만 ArrayList에 저장 */
   if(methods[i].getName().startsWith("get") == true){
    getMethods.add(methods[i]);                           
   }
  }
 
  if(getMethods.size() <= 0){
   return;
  }
 
  for(int i = 0; i < getMethods.size() ; i++){
   Method m = getMethods.get(i);  
   try {
    System.out.println(m.invoke(obj, null));   //invoke 메소드에 관한 내용은 아래내용을 참고
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 }
}

1.원형

  Object invoke( Object obj, Object[] args )


2.사용

  Class myclass = Class.forName( "package.name.MyClass" ); //특정 클래스의 클래스를 가져옴

  Object myclassInstance = myclass.newInstance(); // 특정 클래스의 인스턴스 생성

  Method[] methods = myclass.getMethods();  // 클래스의 메소드를 가져옴


  // 메소드의 매개변수를 설정. 타입에 따라 Object 배열 초기화 코드의 내용이 틀려짐.

  Object[] params = new Object[] { new Integer( value ) };

  methods[0].invoke( myclassInstance, params );  //invoke함수는 클래스안에 메소드를 호출하게된다.

Method 클래스의 invoke 함수의 API 입니다. 보다시피 매개변수로 첫번째 메소드를 실행시킬 인스턴스나 클래스
를 넣어줘야 하고, 그 메소드의 매개변수를 Object 배열로 넣게 된다.
따라서 인트는 new Integer()로 감싸주거나 new BigDecimal()로 감싸주어 Object형으로 캐스팅 되게끔하고
String은  그 자체로서 Obejct형으로 캐스팅 되면서 Object[]배열로서 매겨변수로 들어가데 된다.
그러면 그 함수의 매개변수에 배열의 순서대로 넣어서 실행을 시킬수 있는것이다.

struts 2008. 7. 29. 15:53

struts2] Download

                            Download

1. web.xml
      - 필터등록(위에서 설정한 부분과 동일)

2.  struts.properties
      - struts환경설정 파일 (위에서 설정한 부분과 동일)


3. struts.xml
      - result type에   결과를 stream으로 받기 때문에 stream으로 설정
      - param 요소에 mimeType,원 자원의 길이, 컨텐츠헤더값, inputStream이름, 버퍼크기 설정

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC '-//Apache Software Foundation//DTD Struts Configuration 2.0//EN'
'http://struts.apache.org/dtds/struts-2.0.dtd'>
<struts>
    <package name="defalut" extends="struts-default" namespace="">
    
     <action name="fileList" class="ex1.FileListAction">
   <result>/ex1/fileList.jsp</result>
  </action>
    
     <action name="fileDownload" class="ex1.FileDownLoadAction">
   <result type="stream">
         <param name="contentType">bynary/octet-stream</param>  // mimeType
         <param name="contentLength">${contentLength}</param> // 원자원의 길이
         <param name="contentDisposition">${contentDisposition}</param>
       <!-- 파일의 이름을 설정하기 위한 컨텐츠헤더값 지정-->
         <param name="inputName">input</param> // inputStream 이름
         <param name="bufferSize">4096</param> // 버퍼 크기
   </result>
  </action>
    </package>
</struts>


4. fileList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
  <h2>파일 목록</h2>
  <s:property value="basePath"/>
  <s:iterator value="fileList" status="stat">
     /*
          FileListAction 클래스로부터 받은 basePath를  param에 등록하고, fileName에 파일명을 등록
          url에 해당 param을 등록하고 href요소에 url id를 맵핑시켜 사용자가 파일클릭시
          fileDownload Action클래스를 수행하게하여 파일 다운로드가 수행되게 된다.
       */
    <s:url id="download" action="fileDownload">
       <s:param name="basePath" value="basePath"/>    
       <s:param name="fileName" >
           <s:property value="fileList[#stat.index].name"/>
       </s:param>
    </s:url>
    <li><s:a href="%{download}">
       <s:property value="fileList[#stat.index].name"/>
      </s:a>
    </li>
  </s:iterator>


5. FileListAction.java
         - ActionSupport를 상속받는 action 클래스 등록
         - properties파일에서 읽어올시 액션 파일명과 동일하게 작성하여 동일위치에 생성

package ex1;

import java.io.File;
import java.util.ArrayList;

import com.opensymphony.xwork2.ActionSupport;

public class FileListAction extends ActionSupport {
 
  private ArrayList<File> fileList = new ArrayList<File>();
  private String basePath;
 
//  필드에 선언된 setter/getter 메서드 정의
 
  @Override
  public String execute() throws Exception {
    basePath = getText("path"); //프로퍼티파일에서 읽혀짐
    //basePath = "D:/jspwork/struts2_FileUpload/WebContent/fileUpload";
    /*
       파일들이 저장되어 있는 폴더를 File객체로 생성한다.
       이유는 그렇게 해야 그안에 있는 파일들을 가져와서 ArrayList에 저장할 수 있다.
       내부의 모든 요소들을 반복하여 파일인지 아닌지를 비교하고 비교후 파일일경우 list에 추가
     */
    File dir = new File(basePath);
    File[] files = dir.listFiles();
    if(files!=null && files.length>0)
     for(File f : files)
        if(f.isFile())  
           fileList.add(f);
 
    return SUCCESS;
  }
}


6. FileDownLoadAction.java
         - 사용자가 파일명클릭시 수행되는 ActionClass(다운로드가 수행되는 ActionClass)
         - struts.xml에 등록한것처럼 결과를 steam으로 전달하여 파일다운로드가 이루어짐

package ex1;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLEncoder;

import com.opensymphony.xwork2.ActionSupport;

public class FileDownLoadAction extends ActionSupport{
 
  private String basePath,fileName,contentType,contentDisposition;
  private InputStream input;
  private long contentLength;
 
     //  필드에 선언된 setter/getter 메서드 정의
   
  @Override
  public String execute() throws Exception {
     /* jsp페이지에서 taglib에 설정된 param name(value값)이 전달됨 */
    String path  = basePath + System.getProperty("file.separator") + fileName;
    File f = new File(path);
    setContentLength(f.length());
    setContentDisposition("attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
    setInput(new FileInputStream(path));
   
   return SUCCESS;
  }
 
 
}


 

struts 2008. 7. 29. 14:41

struts2] multi upload

                      Multi Upload


1. web.xml 필터 등록
       - singleupload에서 설정한것과 동일  

2.struts.xml / struts.properties 설정

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC '-//Apache Software Foundation//DTD Struts Configuration 2.0//EN'
'http://struts.apache.org/dtds/struts-2.0.dtd'>
<struts>
    <package name="defalut" extends="struts-default" namespace="">
        <action name="multiUpload"> 
            <result>/ex2/multiUpload.jsp</result>
         </action>
      <action name="multiUpload_ok" class="ex2.MultiUploadAction">
          <result name="input">/ex2/multiUpload.jsp</result>
          <result>/ex2/multiUploadResult.jsp</result>
      </action>

  </package>
</struts>


3. FileService.java
    - 파일에 관한 처리가 이루어지는 클래스(저장폴더 생성 및 파일저장작업이 이루어지는 메서드정의)


package ex1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileService {
 
/*
    파일들이 저장될 폴더를 생성하는 메서드
    폴더가 존재하지않을경우 폴더생성
*/

 public void makeBasePath(String n){  
   File dir = new File(n);
   if(!dir.exists())  
    dir.mkdirs();    
  }
 
/*
      File, 저장경로(basePath),파일명을 인자로 받는 메서드
*/

  public String saveFile(File file, String basePath, String fileName) throws Exception{
   
   if(file == null || file.getName().equals("")||file.length()<1)
      return null;
   
   makeBasePath(basePath);  // 저장될 폴더 생성! 폴더존재시 수행하지않음
   
   String serverFullPath = basePath + System.getProperty("file.separator") + fileName; //파일명이 포함된 절대경로
 
   FileInputStream fis = new FileInputStream(file);  // 저장할 파일에 스트림 생성
   
   /*
      이제 위에 있는 스트림을 통해 읽은 자원을 저장할 스트림 생성
      FileOutputStream은 경로만 존재한다면 그 위치에 파일이 있든 없든 무조건 파일을 만든다.
    */
   FileOutputStream fos = new FileOutputStream(serverFullPath);  
   
   /*
     위에서 생성한 빈파일이 서버측에 생성되었으므로 준비된 스트림을 통하여 채워넣는다.
    */
   int readSize = 0 ;
   byte[] buf = new byte[1024];
   while((readSize = fis.read(buf))!=-1){
    fos.write(buf, 0, readSize); //준비된 스트림을 통해 쓴다.    
   }
   
    fos.close();
    fis.close();    
   return serverFullPath;
  }
}


4. multiUpload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"   pageEncoding="UTF-8"%>

<%@ taglib  prefix="s" uri="/struts-tags"%>
  <s:actionerror/>
  <font color="red"><s:fielderror/></font><!-- 유효성 검사 오류시 표현 -->
  <s:form method="post" action="multiUpload_ok" enctype="multipart/form-data" theme="simple">
   <b>멀티 파일 업로드 </b><hr>
    File 1 : <s:file name="upload"/><br>  //파일명을 동일하게 설정
    File 2 : <s:file name="upload"/><br>
    File 3 : <s:file name="upload"/><br>
    <s:submit/>  
  </s:form>



5. MultiUploadAction.java
      - ActionSupport 클래스를 상속받은 action클래스
      - action클래스 (일반적인 setter/getter, execute()메소드로 구성됨)
      - jsp페이지에서 action클래스가 불려질때 폼에서 전달된 값과 함께 자동적으로 execute()메서드가 호출됨


package ex2;

import java.io.File;

import com.opensymphony.xwork2.ActionSupport;

import ex1.FileService;

public class MultiUploadAction extends ActionSupport {
 
  private File[] upload;    // 멀티 업로드의 경우 배열로 받아 처리
  private String[] uploadFileName,uploadContentType,serverFullPath;

public String execute() throws Exception{
  serverFullPath = new String[upload.length];
  //String basePath = getText("upload_folder");  //아래에 설정한 properties파일에서 내용을 가져옴
  String basePath =  "D:/jspwork/struts2_FileUpload/WebContent/fileUpload";
  FileService fileService = new FileService();
 
  for(int i =0 ; i<upload.length; i++){
    serverFullPath[i] = fileService.saveFile(upload[i], basePath, uploadFileName[i]);
  }
   return SUCCESS;
 }
}


6. MultiUploadAction.properties
    - properties파일을 이용하여 값을 받아오게 설정할수 있다.
      (action클래스명.xml 형식으로 작성시 struts에서 자동으로 호출됨)
   
upload_folder = D:/jspwork/struts2_FileUpload/WebContent/fileUpload


7. multiUploadResult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MultiUpload</title>
</head>
<body>
   <h2> 멀티 파일 업로드 결과 </h2>
   <s:iterator value="upload" status="stat">
      /*
          <s:property>선언시 자동적으로 action클래스에 getter메서드가 호출됨
           배열로 받기 때문에 iterator를 이용하여 출력
      */
    <li>파일 <s:property value="%{#stat.index+1}"/></li>  
    <li>컨텐츠 타입<s:property value="%{uploadContentType[#stat.index]}"/></li>
    <li>로컬 파일 이름<s:property value="%{uploadFileName[#stat.index]}"/></li>
    <li>서버 전체 경로<s:property value="%{serverFullPath[#stat.index]}"/></li>
    <li>임시 파일 이름<s:property value="%{upload[#stat.index]}"/></li>
    <hr>
   </s:iterator>  
</body>
</html>

struts 2008. 7. 29. 14:25

struts2] single upload

                      Single Upload


1. web.xml 필터 등록

    <filter>
         <filter-name>struts2</filter-name>
         <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
   </filter>
   
   <filter-mapping>
         <filter-name>struts2</filter-name>
         <url-pattern>/*</url-pattern>
   </filter-mapping>
   

2.struts.xml / struts.properties 설정

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC '-//Apache Software Foundation//DTD Struts Configuration 2.0//EN'
'http://struts.apache.org/dtds/struts-2.0.dtd'>
<struts>
    <package name="defalut" extends="struts-default" namespace="">
       <action name="singleUpload">
           <result>/ex1/singleUpload.jsp</result>
       </action>      

      <action name="singleUpload_ok" class="ex1.SingleUploadAction">
           <result name="input">/ex1/singleUpload.jsp</result>
           <result>/ex1/singleUploadResult.jsp</result>
      </action>
   </package>
</struts>


3. FileService.java
    - 파일에 관한 처리가 이루어지는 클래스(저장폴더 생성 및 파일저장작업이 이루어지는 메서드정의)


package ex1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileService {
 
/*
    파일들이 저장될 폴더를 생성하는 메서드
    폴더가 존재하지않을경우 폴더생성
*/

 public void makeBasePath(String n){  
   File dir = new File(n);
   if(!dir.exists())  
    dir.mkdirs();    
  }
 
/*
      File, 저장경로(basePath),파일명을 인자로 받는 메서드
*/

  public String saveFile(File file, String basePath, String fileName) throws Exception{
   
   if(file == null || file.getName().equals("")||file.length()<1)
      return null;
   
   makeBasePath(basePath);  // 저장될 폴더 생성! 폴더존재시 수행하지않음
   
   String serverFullPath = basePath + System.getProperty("file.separator") + fileName; //파일명이 포함된 절대경로
 
   FileInputStream fis = new FileInputStream(file);  // 저장할 파일에 스트림 생성
   
   /*
      이제 위에 있는 스트림을 통해 읽은 자원을 저장할 스트림 생성
      FileOutputStream은 경로만 존재한다면 그 위치에 파일이 있든 없든 무조건 파일을 만든다.
    */
   FileOutputStream fos = new FileOutputStream(serverFullPath);  
   
   /*
     위에서 생성한 빈파일이 서버측에 생성되었으므로 준비된 스트림을 통하여 채워넣는다.
    */
   int readSize = 0 ;
   byte[] buf = new byte[1024];
   while((readSize = fis.read(buf))!=-1){
    fos.write(buf, 0, readSize); //준비된 스트림을 통해 쓴다.    
   }
   
    fos.close();
    fis.close();    
   return serverFullPath;
  }
}


4. singleUpload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"   pageEncoding="UTF-8"%>

<%@ taglib  prefix="s" uri="/struts-tags"%>
  <s:actionerror/>
      <font color="red"><s:fielderror/></font><!-- 유효성 검사 오류시 표현 -->
 
  <s:form method="post" action="singleUpload_ok" enctype="multipart/form-data">
   <b>파일업로드 </b><hr>
    <s:file name="upload"/>
    <s:submit/>
  </s:form>  



5. SingleUploadAction.java
      - ActionSupport 클래스를 상속받은 action클래스
      - action클래스 (일반적인 setter/getter, execute()메소드로 구성됨)
      - jsp페이지에서 action클래스가 불려질때 폼에서 전달된 값과 함께 자동적으로 execute()메서드가 호출됨


package ex1;

import java.io.File;

import com.opensymphony.xwork2.ActionSupport;

public class SingleUploadAction extends ActionSupport {
 
  private File upload;
  private String uploadFileName,uploadContentType,serverFullPath;

    public String execute() throws Exception {
    String basePath = "D:/jspwork/struts2_FileUpload/WebContent/fileUpload";
    FileService fileService = new FileService();
    serverFullPath = fileService.saveFile(upload, basePath, uploadFileName);
    return SUCCESS;    
  } 
}


6. SingleUploadAction-validation.xml
    - validation 설정을 하는 부분(action클래스명-validation.xml 형식으로 작성시 struts에서 자동으로 호출됨)
   
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC '-//OpenSymphony Group //XWork Validator 1.0.2//EN' 'http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd'>

<validators>
    <field name="upload">
        <field-validator type="fieldexpression">
            <param name="expression">
                <![CDATA[
                upload.length() > 0
                ]]>
            </param>
            <message>
                업로드할 파일을 선택하십시오.
            </message>
        </field-validator>
    </field>
</validators>


7. SingleUploadResult.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
   <h2> 단일 파일 업로드 결과 </h2>
   <li>컨텐츠 타입<s:property value="uploadContentType"/></li>
   <li>로컬 파일 이름<s:property value="uploadFileName"/></li>
   <li>서버 전체 경로입<s:property value="serverFullPath"/></li>
   <li>임시 파일 이름<s:property value="upload"/></li>  
</body>
</html>