jsp 부분에서 어떤 값을 입력 받을 경우 그 데이터에 대한 유효성 검사등을 위한 form이 있어야하는데 form이 없을 경우에 이러한 에러가 발생한다.
struts-config.xml 파일에서 

<action path="/bbs/bbs2" parameter="method"
    type="bbs.bbs_Action">
   <forward name="search" path=".bbs_search" />
   <forward name="false" path="/bbs/bbs1?method=list" redirect="true" />
  </action>

type 뒤에 name 속성과 input 속성이 있어야만 에러가 발생하지 않는다.


<html:form action="writedo.do" method="post" >

=> jsp에서 입력받는 값이 있을 경우 반드시  ActionForm을 만들어 주어야 한다.

=> 반드시 struts-config.xml에 등록해 주어야 한다!!!


"Cannot retrieve definition for form bean [ActionForm 명칭]" 에러 메세지가 나올 경우에는

특정 ActionForm이 struts-config.xml 파일 내부의  "<form-bean>" 태그에 등록되어 있는지를 확인해 봐야 함


<form-bean name = "service"    type = "org.apache.struts.action.DynaActionForm"/>

출처 : http://www.ibm.com/developerworks/java/library/j-sr4.html

Dynamic radio buttons with Struts

Create radio buttons with dynamically selected elements

Danilo Gurovich (dan@gurovich.com), Principal Engineer, Earthlink Inc.

Summary:  Follow along as Struts Recipes co-author Danilo Gurovich walks you through five easy steps for creating radio buttons with dynamically selected elements.

This recipe follows on the heels of what you learned in my last article, "Dynamic checkboxes with Struts." Like dynamic checkboxes, radio buttons with dynamically selected elements can add a lot of sophistication to your Web pages, and with Struts, they're also easy to create.

In this article, I show you how to create a radio button group by nesting the Struts tags <logic:iterate/> and <html:radio/>. I then reference a specified form bean to the tags and iterate through a String[] array of radio button values, assigning an identical name attribute to each assigned value attribute.

See the Download section for the complete example source code; see Resources if you need to download the Struts framework.

A five-step recipe

For the sake of simplicity, I use the same working example for radio buttons as I used last time for dynamic checkboxes. My simple UI uses radio button elements to display a String[] array of mountain peaks in the Himalayas, and a second selectedMountain String to represent the selected radio button. The radio button is preselected by a JavaScript function invoked after the buttons have been created.

Based on the Himalayas example, the recipe for creating dynamically selected radio buttons in Struts consists of five pieces:

  1. A fake data class to hold the mountains and selectedMountain data.
  2. A form bean containing a String[] array for the radio buttons and a String for the specifically selected button.
  3. A JSP with a form that shows the radio buttons in the required state.
  4. A JSP to display elements selected in the form.
  5. A simple Action class to go from the form page to the display page.

The major difference between the radio button recipe and the dynamic checkboxes recipe you learned last time is that Struts provides no automatic facility for creating a selected value, which is usually needed to create dynamic radio buttons. While it's fairly simple to preselect checkbox and select input types using functionality built into the Struts JSP tags, the radio button input type requires a different solution. Fortunately, this is readily accomplished using information from a form bean and a couple of lines of JavaScript, as I show in Step 3.


Step 1. Create the data layer

I've assembled a fake data class to represent data coming from a business layer to the application's view layer, which is my concern. The appropriately named FakeData class contains two static final methods, as shown in Listing 1:


Listing 1. FakeData.java
/**
 * class FakeData -- represents the business logic
 */

public class FakeData {

    /**
     * data for mountains
     */
    public static final String[] MOUNTAINS = 
      {"Everest", "K2", "Kangchenjunga",
      "Lhotse", "Makalu", "Cho Oyu"};

    /**
     * data for selected mountain
     */
    public static final String SELECTED_MOUNTAIN = "Kangchenjunga";

}

Creating a fake data layer is a useful UI development practice because the persistence layer to be used by the final application is often unavailable to front-end developers. Instead of waiting around for the backend team to do its stuff, you can easily develop a fake data layer that mimics the API and functionality that will eventually be sent to you. With the fake data layer, you can proceed with your UI development and lowers your dependence on other teams. Because of the fake data layer, you also can define your API connections to other parts of the project and ensure you have less problems when it's time to integrate all the pieces.


Step 2. Create the form bean

The values that will ultimately populate your application are likely to come from more sophisticated framework than the one shown in Listing 1. The nice thing about my more bohemian example is that the form bean in Listing 2 doesn't have to do any heavy lifting, so it's just a simple Java™ object with getters and setters. The actual values are injected when the constructor is called.


Listing 2. RadioTestForm.java
package com.strutsrecipes;

import org.apache.struts.action.ActionForm;


/**
 * Radio Button Test Form to show an array of radio buttons and
 */
public class RadioTestForm extends ActionForm {
// ------------------------------ FIELDS ------------------------------

    /**
     * The selected Mountain
     */
    private String selectedMountain;


    /**
     * The list of mountains for the radio button
     */
    private String[] mountains;

// --------------------------- CONSTRUCTORS ---------------------------

    /**
     * Constructor -- using FakeData...
     */
    public RadioTestForm() {
        this.selectedMountain = FakeData.SELECTED_MOUNTAIN;
        this.mountains = FakeData.MOUNTAINS;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Getter for the mountains
     *
     * @return the mountains array
     */
    public String[] getMountains() {
        return this.mountains;
    }

    /**
     * Setter for the mountains
     *
     * @param m the Mountains array
     */
    public void setMountains(String[] m) {
        this.mountains = m;
    }

    /**
     * Getter for selectedMountain
     *
     * @return the selected mountain
     */
    public String getSelectedMountain() {
        return this.selectedMountain;
    }

    /**
     * Setter for selectedMountain
     *
     * @param sm the selectedMountain
     */
    public void setSelectedMountain(String sm) {
        this.selectedMountain = sm;
    }
}

For clarity, I've included all of the Java code for the form bean. Notice that Kangchenjunga is listed in both the selectedMountain and mountains fields, instantiated in the constructor, and populated by my FakeData class. At this point, I already have enough information to propagate Kangchenjunga to the JSP as my preselected initial value.


Step 3. Create the radio button JSP

Listing 3 contains the JSP code for the form page containing radio buttons and my preselected value. Note the correlation between the Java files and the logic, HTML, and bean tags, and the JavaScript function located at the bottom of the form. I iterate through the mountains collection to create the radio buttons. Once this is done, I add the JavaScript and populate the value of selectedMountain into it for comparison against the array of radio buttons to select the proper one.


Listing 3. The JSP containing radio buttons and a preselected value
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<html:html locale="true">
    <head>
        <title><bean:message key="testForm.title"/></title>
        <html:base/>
    </head>

    <body>
    <h3><bean:message key="testForm.heading"/></h3>
    <html:form action="/FormAction" name="testForm"
            type="com.strutsrecipes.RadioTestForm">

        <h4><bean:message key="testForm.instruction"/></h4>
        
        <!-- gets the selected radio button -->
        <bean:define id="selectedRadio" property="selectedMountain" name="testForm"/>

        <!-- creates the radio button list -->
        <logic:iterate id="mountain" property="mountains" name="testForm">
            <%-- you need this hack to get the value of the mountains to the page --%>
            <bean:define id="mountainValue">
                           <bean:write name="mountain"/>
            </bean:define>
            <html:radio property="selectedMountain" 
                       value="<%=mountainValue%>" 
                       styleId="<%=mountainValue%>"/>
            <bean:write name="mountain"/><br/>
        </logic:iterate><br/>
        
        <html:submit/>      <html:reset/>
        <script type="text/javascript">
            <!--
          //Either of the following works.  
         //Uncomment the one you wish to try and comment the other out.
        //var selectedRadio = 
          document.getElementById("<bean:write //name="selectedRadio"/>");
	      var selectedRadio = 
          document.forms["testForm"].elements["<bean:write name="selectedRadio"/>"];
	           selectedRadio.checked=true; -->
        </script>
    </html:form>
    </body>
</html:html>


Step 4. Create the display page

The display page is nearly identical to the one used for the checkbox recipe, except no iteration is needed because only one value is selected. All I need to do is list the mountain selected in the bean, as shown on Listing 4:


Listing 4. The JSP for the display page.
<%@taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
<%@taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%>
<%@taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic"%>

<%-- html code, etc... --%>
<bean:write name="mountain"/><br/>
<hr size=5 color="black"/>
<%-- some more html code, etc... --%>


Step 5. Write the Action class

As I mentioned in my previous article, the Action class doesn't do much compared to the other components of this recipe. All I'm doing here is getting my String array and selectedMountain value and making them available to my display page.


Listing 5. The Action class
ipackage com.strutsrecipes;

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 javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * The Action for the Radio Button test
 */
public final class RadioTestAction extends Action {
// -------------------------- OTHER METHODS --------------------------

    /**
     * The Action for the Radio Button test
     */
    public ActionForward execute(ActionMapping mapping, 
      ActionForm form, HttpServletRequest request, HttpServletResponse response
                                )
        throws ServletException, Exception {
        // Extract attributes needed
        String selectedMountains = ((RadioTestForm) form).getSelectedMountain();
        System.out.println("htmlString RETURNED*********\n" + selectedMountains);

        //Save your htmlString in the session
        HttpSession session = request.getSession();
        session.setAttribute(Constants.MOUNTAINS, selectedMountains);
        return (mapping.findForward("success"));
    }
}


Inside the recipe

The real muscle behind this recipe is JavaScript. First, I defined a JSP scripting variable for the selectedMountain field in Listing 5 inside the form on the JSP, as shown here:

<bean:define id="selectedRadio" property="selectedMountains" name="testForm"/>

Below the form, I created a JavaScript function consisting of the following two lines:

var selRadio = document.getElementById("<bean:write name="selectedRadio"/>");
selRadio.checked=true;
	   

Inside the client-side script, I created a selRadio JavaScript variable and then did a find for all the elements in the document whose id (or styleId in the precompiled code) matched the selectedRadio variable. I accomplished this by setting the <html:radio/> tag's styleId attribute to match its name/value. While the JavaScript function quickly iterates through the IDs on the page, it simply sets a singular radio button as selected.

An alternative approach

You could produce the same results using the JavaScript method shown here:

		
var selectedRadio =
document.forms["testForm"].elements["<bean:writename="selectedRadio"/>"];
selectedRadio.checked=true;

This script discriminates to only the form element's name attribute instead of the id. Either implementation works, so the one you choose will depend on your requirements and preferences. The actual output from the JSP form page looks like Listing 6:


Listing 6. The output from the JSP form page
<input type="radio" name="selectedMountain" value="Everest" id="Everest"/>
Everest<br/>
        
<input type="radio" name="selectedMountain" value="K2" id="K2"/>
K2<br/>

<input type="radio" name="selectedMountain" 
  value="Kangchenjunga" checked="checked" id="Kangchenjunga"/>
Kangchenjunga<br/>
        
<input type="radio" name="selectedMountain" value="Lhotse" id="Lhotse"/>
Lhotse<br/>
            
<input type="radio" name="selectedMountain" value="Makalu" id="Makalu"/>
Makalu<br/>
           
<input type="radio" name="selectedMountain" value="Cho Oyu" id="Cho Oyu"/>
Cho Oyu<br/>


In conclusion

As I mentioned in the conclusion to my last article, the lack of information about creating dynamic elements in Struts was a strong motivation for co-writing Struts Recipes with George Franciscus. Much of the silence about creating dynamic radio buttons is because of the design dilemma inherent in too tightly coupling server-side and client-side information and functionality. Using JavaScript and server-side variables, as I've demonstrated here, seems to be the only way to harness this functionality, although you still run the risk of the client and server knowing too much about each other.

The decision of whether to take the risk of "coupling" is yours to make. A good process for making the decision is to explain the pros and cons out loud to yourself. If you're still willing to take the risk after that -- and if the person in the next cube isn't laughing at you -- you're probably ready to take the plunge.

오늘의 실수.. =ㅂ=

javax.servlet.ServletException: Path .layout-main does not start with a "/" character

org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:286)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

--- 해결 --
struts-config.xml에서 타일즈를 사용하겟다는 내용을 추가해줘야 한다;

<plug-in className="org.apache.struts.tiles.TilesPlugin">
  
         <set-property property="definitions-config"    value="/WEB-INF/config/tiles-defs.xml"/>
         <set-property property="definitions-debug"     value="2"/>
         <set-property property="definitions-parser-details"   value="2"/> 
         <set-property property="definitions-parser-validate" value="true"/>
        
  </plug-in>
출처 : http://cyhome.cyworld.com/?home_id=a0940196&postSeq=2641022

DB를 사용하여 로그인 하기

이번 강좌에서는 이전 예제인 “Struts 더 더 간단한 로그인 예제”의 기능을 확장 하여 예제를 만들어 보도록 하겠습니다.

먼저 이클립스에서 Login 이라는 톰캣 프로젝트를 하나 만드세요~

이전 예제와 달라진 기능은 다음과 같습니다.

1.        개발자가 로깅을 할 수 있도록 Log.java라는 클래스를 이용하여 디버깅을 용이 하도록 했습니다.

2.        데이터베이스 커넥션 풀(Connection Pool) 기능을 이용하여 작성 했습니다. Connection Pool은 ActionServlet을 확장 한 NewActionServlet이 초기에 사용자의 요청을 받는 시점에 여러 개의 커넥션을 생성 하도록 구성 하였으며 Action 부분에서 그 커넥션을 가져다 쓰도록 했습니다. 참고로 본 예제에서는 bitmechanic의 JDBC Pool(http://www.javaservice.net/~java/bbs/read.cgi?m=resource&b=jdbc&c=r_p&n=1073555435&k=Bitmechanic&d=tb#1073555435) (http://www.bitmechanic.com/projects/jdbcpool/)  을 이용 했으며 여러분들의 개발 목적에 따라 Poolman(http://sourceforge.net/projects/poolman/) , Expresso(http://www.jcorporate.com) 또는 Jakarta의 DBCP(http://jakarta.apache.org/commons/index.html) 등을 이용하실 수 있습니다.

“이벤트 리스너를 이용한 로깅” 이라는 강좌에 보시면 Struts의 ActionServlet을 이용하지 않고 ServletContext의 리스너를 이용하여 컴텍스트가 초기화 되는 시점 즉 톰캣이 시작되는 시점에 DB Connection Pool을 초기화 한 예제도 있으니 꼭 참고해 보시기 바랍니다.

3.        사용자의 인증을 처리함에 있어 ILoginDAO.java라는 인터페이스를 만들어 여기에서 authUser라는 메소드를 추상 메소드로 정의 했습니다. 이 인터페이스를 구현한 클래스가 LoginDAO.java 이며 이곳에서 실제 인증이 이루어 집니다.


아래는 전체 소스 입니다.

-----------------
/WEB-INF/app.tld
------------------

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE taglib  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
  "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>

  <tlibversion>1.0</tlibversion>
  <jspversion>1.1</jspversion>
  <shortname>Application Tag Library</shortname>
  <uri>http://jakarta.apache.org/taglibs/struts-example-1.0</uri>
  <info>
    Example Application.
  </info>
  <tag>

    <name>IsLogin</name>

    <tagclass>login2.IsLogin</tagclass>

    <bodycontent>empty</bodycontent>

    <info>

      Validate that there is a currently logged on user, by checking for

      the existence of a session-scope bean under the specified name.

      If there is no such bean, forward control to the specified page,

      which will typically be a logon form.

      name - Name of the session-scope bean to check for [user]
      page - Context-relative path to the logon page [/logon.jsp]

    </info>

    <attribute>

      <name>name</name>

      <required>false</required>

      <rtexprvalue>true</rtexprvalue>

    </attribute>

    <attribute>

      <name>page</name>

      <required>false</required>

      <rtexprvalue>true</rtexprvalue>

    </attribute>

  </tag>
</taglib>



----------------------
IsLogin.java
----------------------

package login2;


import! javax.servlet.http.HttpSession;
import! javax.servlet.jsp.JspException;
import! javax.servlet.jsp.tagext.TagSupport;

import! login2.Constants;


public final class IsLogin extends TagSupport {
        private String name = login2.Constants.USER_KEY;
        private String page = "/login.jsp";

        public String getName() {

                return (this.name);

        }

        public void setName(String name) {

                this.name = name;

        }

        public String getPage() {
                return (this.page);
        }

        public void setPage(String page) {
                this.page = page;
        }

        public int doStartTag() throws JspException {

                return (SKIP_BODY);

        }

        public int doEndTag() throws JspException {

                // Is there a valid user logged on?
                boolean valid = false;
                HttpSession session = pageContext.getSession();
                if ((session != null) && (session.getAttribute(name) != null))
                        valid = true;

                // Forward control based on the results
                if (valid)
                        return (EVAL!_PAGE);  //JSP 페이지의 다음을 수행
                else {
                        try {
                                pageContext.forward(page);
                        } catch (Exception e) {
                                throw new JspException(e.toString());
                        }
                        return (SKIP_PAGE);  //JSP 페이지의 다음을 스킵
                }

        }

        /**
         * Release any acquired resources.
         */
        public void release() {

                super.release();
                this.name = Constants.USER_KEY;
                this.page = "/login.jsp";

        }
}



----------------------
login.jsp
----------------------

<%@ page pageEncoding="euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
    <title><bean:message key="login.title" /></title>
    <html:base/>
</head>

<body>
<!-- 웹서버가 보내는 응답에 오류가 있다면 출력 -->
<html:errors/>

<!-- LoginSummit에 대해서는 struts-config.xml에 Mapping을 하게 된다 -->
<!-- focus="id" 라는 문법에 의해 자동으로  자바스크립트를 생성 합니다. -->
<html:form action="/LoginSubmit" focus="id">
        <table>
             <!-- 아래에서 텍스트 출력은 application.properties에서 가져 옵니다... -->
                <tr>                        
                        <th align="right"><bean:message key="prompt.id"/></th>                        
                        <!-- 아래는 input type=text 와 동일한 기능을 합니다 -->
                        <td><html:text property="id" value=""/></td>
                </tr>
                <tr>
                        <th align="right"><bean:message key="prompt.password"/></th>
                        
                        <!-- 만약 로그인을 실패하여 다시 돌아오는 경우 pwd 항목의 값을 비울때는 아래처럼 redisplay="false" 라고 하면 됩니다. HTML의 password 항목과 유사함  -->
                        <td><html:password property="pwd" redisplay="false"/></td>
                </tr>
                <tr>
                        <!-- Submit 버튼과 Reset 버튼을 생성 -->
                        <!-- 버튼의 라벨은 application.properties에서 내용을 가져 옵니다 -->
                        <th></th>
                        <td>
                            <html:submit>
                                <bean:message key="login.login" />
                                   </html:submit>
                        
                            <html:reset>
                                <bean:message key="login.reset" />
                                   </html:reset>
                        </td>
                </tr>                                
        </table>
</html:form>
</body>
</html>



-----------------
main.jsp
------------------

<%@ page language="java" %>
<%@ page contentType="text/html;charset=euc-kr" %>
<!-- 최초 사용자가 접속하게 되는 페이지 입니다. -->
<!-- 아래는 import!와 동일한 기능을 하는 태그로 태그 확장을 사용 할 수 있도록 합니다 -->
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/app.tld" prefix="myTag" %>

<!-- 인증이 안된 사용자는 login.jsp로 보내 버립니다. -->
<myTag:IsLogin/>

<html>
<head>
    <title><bean:message key="main.title" /></title>
    <html:base/>
</head>        
        <body>                
            <h3>반갑습니다.<bean:write name="user" property="id"/>님!</h3>                                        
                <ul>                        
                        <li>                            
                                <html:link forward="logoff">로그아웃</html:link>
                        </li>                        
                </ul>
        <body>
</html>



-------------------
MyFilter.java
-------------------


package filter;

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

import! org.apache.struts.action.RequestProcessor;

public class MyFilter extends RequestProcessor {

        protected boolean processPreprocess(
                HttpServletRequest request,
                HttpServletResponse response) {
                
                try {
                        request.setCharacterEncoding("euc-kr");
                                                
                        return true;
                } catch (Exception e) {                
                        return false;                        
                }
        }
}



-----------------
Constants.java
------------------

package login2;

public class Constants {
        /**
         * 로그인한 사용자의 LoginForm이 저장될 세션 범위의 Attriute 이름
         */
        public static final String USER_KEY = "user";
        
        /**
         * ActionForward에서 사용되는 값        
         */
        public static final String SUCCESS = "success";
        
        /**
         * ActionForward 로그인 동작을 나타내는 Tocken        
         */
        public static final String LOGIN = "login";
        
        /**
         * ActionForward welcome 동작을 나타내는 Tocken        
         */
        public static final String WELCOME = "welcome";
                        
}


------------------
NewActionServlet.java
------------------

Connection Pool을 사용자가 요청을 하는 시점에 생성하기 위해 ActionServlet을 상속한 NewActionServlet을 만들었습니다. 이 파일을 만든 후 사용자의 모든 요청을 이 서블릿이 받기 위해서는 web.xml 파일을 수정 해야 합니다. 본 강좌의 마지막 부분에 있는 web.xml을 참고 하시기 바랍니다.

package login2;

import! javax.servlet.ServletException;
import! javax.servlet.ServletConfig;
import! org.apache.struts.action.ActionServlet;



//Connection Pool
import! com.bitmechanic.sql.*;


public class NewActionServlet extends ActionServlet {
        private ConnectionPoolManager cpm;
        
        /*
         * poolAias는 커넥션 풀을 별칭(Alias)를 줘서 여러 개의 풀을 이용할 수 있게 해줍니다.
         * 이 값을 잘 기억하였다가 LoginDAO.java에서 실제 커넥션을 수립할때 인자로 줘야 합니다.
         */                
        private String POOL_ALIAS = "wink";
        
    private String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver";  // driver
    private String DB_URL = "jdbc:oracle:thin:@localhost:1521:WINK"; // url
    private String DB_USER = "SCOTT"; // id
    private String DB_PASSWORD = "TIGER"; // pw
    
    /* reapConnInterval은 다른 커넥션 풀들과는 틀린 부분이며 중요한
     * 옵션입니다.
     * reapConnInterval은 두가지 경우에 커넥션 풀의 connVector에서 커넥션을 제거하게 됩니다.
     * 1. 풀에서 conn이 나갔는데(checkout) checkoutTimeout 시간안에 돌아오지 않을 경우
     * 2. 풀의 connVector에서 전혀사용되지 않는 상태(idle)로 idleTimeout 시간을 초과할 경우
     * bitmechanic의  커넥션 풀은 최대 Connection 수를 설정해도  30이면 30개 커넥션이 담겨져
     * 있는 것이 아니라 상황에 따라 적절히 조정 하며 전혀 사용이 되지 않으면(Idle)수를 줄입니다.
     * 즉, maxConn이 30이어도 사용특성에 따라 몇 개만이 풀에서 운용될 수도 있다는 이야기 입니다.  
    */
    private int REAP_CONN_INTERVAL = 300; // 여기서부터는 bitmechanic 을 위한 환경 설정 값
    
    /* 커넥션 풀에 담을 수 있는 커넥션의 최대 수 */
    private int MAX_CONNECTION = 20;
    
    /* idleTimeout은  ConnectionPoolManager가 그 간격으로 수행을
     * 하게 되므로 적당한 시간을 설정하면 됩니다.
     * (데이터베이스의 불필요한 Open Session 수를 줄일 수 있슴)
     */
    private int IDLE_TIMEOUT = 60;
    
    /* checkoutTimeout은 너무 짧게 설정해도 안되는게 정상적인 query 수행시간이 길다면
     * 정상적으로 작동하는 query 수행 중에 Connection을 Reaping(제거)해 버리므로
     * 최대의 쿼리 수행 시간보다 크게 설정해야 합니다.
     * 즉 풀에서 나갔다가 돌아오는 시간의 Timeout 설정
     */
    private int CHECKOUT_TIMEOUT = 60;
    
    //Checkout의 최대 수를 지정 합니다          
    private int MAX_CHECKOUT = 15;
    
    
    public void init(ServletConfig config) throws ServletException
    {
            try
            {
                    Class.forName(JDBC_DRIVER);
                    
                    cpm = new ConnectionPoolManager(REAP_CONN_INTERVAL);
                    
                    cpm.addAlias(POOL_ALIAS,JDBC_DRIVER,DB_URL,DB_USER,DB_PASSWORD,MAX_CONNECTION,IDLE_TIMEOUT,CHECKOUT_TIMEOUT, MAX_CHECKOUT);
                    
                    // connection pool 생성 후에 부모 class의 init method 연속 수행
                    super.init(config);
            }
            catch ( Exception e1 )
            {
                    e1.printStackTrace();
            }
    }

    
    
    

}


---------------------
LoginForm.java
---------------------


package login2;

import! javax.servlet.http.HttpServletRequest;

import! org.apache.struts.action.Acti[안내]태그제한으로등록되지않습니다-xxonErrors;
import! org.apache.struts.action.Acti[안내]태그제한으로등록되지않습니다-xxonError;
import! org.apache.struts.action.ActionForm;
import! org.apache.struts.action.ActionMapping;

public class LoginForm extends ActionForm {
        private String id=null;
        private String pwd=null;
                
        
        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;
        }
        
        public void reset(ActionMapping mapping, HttpServletRequest request) {
                pwd = "";
                id = "";
        }

        
        /* struts-config.xml 에서 메핑시 validate="true" 라고 설정 되면 아래의
        validate 함수가 호출 되어 입력되는 값에 대한 validation check를 하게 됩니다.
        HTTP 요청으로 부터 해당 폼의 값을 set 한 후 아래 메소드가 호출 됩니다.
        참고로 validate에 위해 반환되는 Acti[안내]태그제한으로등록되지않습니다-xxonErrors 객체 역시 Framework에 관련된 클래스 입니다.
        만약 validate에서 null이나 empty가 아닌 Acti[안내]태그제한으로등록되지않습니다-xxonError 객체를 리턴하게 되면
        login.jsp에 있는 <html:errors/> 태그는 오류를 출력하고 아니면 넘어가게 됩니다...
        error.id.required, error.pwd.required는 일종의 키값으로  각 Locale마다 고유한 리소스를 가질 수
        있으므로 메시지에 대해 손쉬운 관리를 제공 합니다.
        예를들면 error.id.required = 사용자  ID는 필수 입력 항목 입니다. , 이러한 식으로 설정 합니다.
        */

        public Acti[안내]태그제한으로등록되지않습니다-xxonErrors validate(ActionMapping mapping, HttpServletRequest request) {
                Acti[안내]태그제한으로등록되지않습니다-xxonErrors errors = new Acti[안내]태그제한으로등록되지않습니다-xxonErrors();
                
                if((id == null) || (id.length()<1)) {
                        errors.add("error.id.required", new Acti[안내]태그제한으로등록되지않습니다-xxonError("error.id.required"));
                }
                
                if((pwd == null) || (pwd.length()<1)) {
                        errors.add("error.pwd.required", new Acti[안내]태그제한으로등록되지않습니다-xxonError("error.pwd.required"));
                }
                
                return errors;                        
        }
        

                
        
}


--------------------
LoginAction.java
--------------------


package login2;

import! org.apache.struts.action.Action;
import! javax.servlet.http.HttpServletRequest;
import! javax.servlet.http.HttpSession;
import! javax.servlet.http.HttpServletResponse;
import! login2.LoginForm;
import! login2.Constants;
import! login2.Log;
import! org.apache.struts.action.Acti[안내]태그제한으로등록되지않습니다-xxonErrors;
import! org.apache.struts.action.Acti[안내]태그제한으로등록되지않습니다-xxonError;
import! org.apache.struts.action.ActionForm;
import! org.apache.struts.action.ActionMapping;
import! org.apache.struts.action.ActionForward;

/**
* @author 이종철
*/
public class LoginAction extends Action {
                
        public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) {
        
                String id = ((LoginForm)form).getId();
                String pwd = ((LoginForm)form).getPwd();
                
                
                //Model을 이용하기 이용하여 인증부분의 처리를 합니다.
                LoginDAO loginDAO = new LoginDAO();
                UserInfoVO userinfo = loginDAO.authUser(id, pwd);
                
                //에러 내용을 담을 Acti[안내]태그제한으로등록되지않습니다-xxonErrors 객체를 생성
                Acti[안내]태그제한으로등록되지않습니다-xxonErrors errors = new Acti[안내]태그제한으로등록되지않습니다-xxonErrors();
                
                if (userinfo==null) {
                        //인증 실패                        
                        errors.add(Acti[안내]태그제한으로등록되지않습니다-xxonErrors.GLOBAL_ERROR, new Acti[안내]태그제한으로등록되지않습니다-xxonError("error.login.invalid"));                        
                }
                
                
                //로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors 메소드를 이용해  에러를 저장하고
                //이전의 로그인 페이지로 돌려 보내면 login.jsp의 <html:errors/> 태그에 의해 오류가 출력 돔
                //mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
                //input 속성에  정의된 페이지로 이동하게 됩니다.
                if (!errors.isEmpty()) {
                    saveErrors(request, errors);
                   return (mapping.getInputForward());
                }        
Log.println("LoginAction", "인증이 성공 함");                                
                //인증 성공한 경우 세션에 LoginInfoVO(로그인한 사용자 정보를 담은 객체)저장
                HttpSession session = request.getSession();
                session.setAttribute(Constants.USER_KEY, userinfo);

Log.println("LoginAction", "세션에 userinfo빈을 set");        
                
                //로그를 남기자.
                StringBuffer buf = new StringBuffer("LoginAction : User --> ");
                buf.append(id + "logged in session");
                servlet.log(buf.toString());
                                
                return (mapping.findForward(Constants.SUCCESS));                        
                
        }

        
}



--------------------
LogoffAction.java
--------------------

package login2;

import! org.apache.struts.action.Action;
import! javax.servlet.http.HttpServletRequest;
import! javax.servlet.http.HttpSession;
import! javax.servlet.http.HttpServletResponse;
import! org.apache.struts.action.ActionForm;
import! org.apache.struts.action.ActionForward;
import! org.apache.struts.action.ActionMapping;

import! login2.Log;
import! login2.Constants;

public class LogoffAction extends Action {        
        
        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
                
                //필요한 어트리뷰트를 뽑아 냅니다.
                //LoginAction에서 사용자의 로그온개체를 Consants.USER_KEY라는 이름으로 세션에 저장했음
                HttpSession session = request.getSession();
                UserInfoVO userinfo = (UserInfoVO)session.getAttribute(Constants.USER_KEY);
                
                if (userinfo != null) {
                        //로그를 남기자
Log.println("LogoffAction","userinfo 빈이 세션에 있다고 함");                
                        StringBuffer buf = new StringBuffer("User Logout : " + userinfo.getId());
                        servlet.log(buf.toString());                                        
                }                
                
                //사용자의 로그인을 삭제, session.invaidate() 의 경우 션의 모든 것을 무효화 시킴.
                session.removeAttribute(Constants.USER_KEY);
                //session.invalidate();
                
                //성공적으로 처리 되었음을 알림
                return (mapping.findForward(Constants.SUCCESS));
                
        }

        
}



--------------------
ILoginDAO.java
--------------------

DAO 클래스가 구현해야 하는 메소드를 정의 했습니다.

package login2;

import! login2.UserInfoVO;

/**
* 데이터를 Access 하는 것들에 대한 인터페이스를 정의
*/
public interface ILoginDAO {
        public UserInfoVO authUser(String id, String pwd);  //abstract method
}


----------------------
LoginDAO.java
----------------------

package login2;

/* 아래의 예문중에 Log.println 이라고 하는 것은 로깅을 위한 것입니다.
* 빈들 사이에서 제어가 왔다 갔다 할때 디버깅을 하는 경우 어려움이 많습니다.
* 전 하드디스크의 특정 폴더에 파일에 로그를 기록 하도록 Log.java를 만들어
* 사용 합니다. Log.java의 내용은 간단 합니다.
* println 메소드는 로그파일의 이름과 로그의 내용을 인자로 줍니다.
*/

import! java.sql.SQLException;
import! java.sql.Connection;
import! java.sql.PreparedStatement;
import! java.sql.ResultSet;
import! java.sql.DriverManager;
import! login2.Log;

import! com.bitmechanic.sql.*;

public class LoginDAO implements ILoginDAO {
        
        
        public UserInfoVO authUser(String id, String pwd) {
                /**
                * 이부분은 실제 DB에서 ID와 PASWORD로 인증을 해야 하는 부분 입니다.
                */
                                
                Connection con=null;
                
                try {                
//Connection Pool에서 Connection을 가지고 옵니다. with bitmechanic 커넥션 풀
//아래에서 wink는 NewActionServlet에서 정의한 커넥션 풀에 대한 Alias 입니다.
                  con = DriverManager.getConnection(ConnectionPoolManager.URL_PREFIX+"wink");
                  con.setAutoCommit(false);
                  
                  String sql = "select pwd from userinfo where id = ?";
                        
                  PreparedStatement pstmt = con.prepareStatement(sql);
                  pstmt.setString(1, id);

                  ResultSet rs = pstmt.executeQuery();

                  if (rs.next()) {
Log.println("LoginDAO","ID같은 데이터가 있다고 함");                          
                         if (rs.getString("pwd").toString().equals(pwd)) {
                                 return new UserInfoVO(id,(String)rs.getString("pwd"));
                         }        
                         else {
                                 return null;
                         }                         
                   }
                  else {
                          return null;
                  }                                        
                }
                catch (SQLException sqle) {
                         Log.println("LoginDAO", sqle.toString());
                         return null;
                }
                catch (Exception e) {
                         Log.println("LoginDAO", e.toString());
                         return null;
                }
                finally {
                            try{
                               if (con != null) {
                                       con.close();
                               }
                            }
                            catch (SQLException e) {      
                            }
                 }                                         
        }
}




--------------------
UserInfoVO.java
--------------------

package login2;
/**
* 로그인이 정상적으로 이루어진 경우 사용자의 정보 등을 저장 하는 빈
* 본 예제에서는 ID와 PASSWORD를 저장 합니다.
*/
public class UserInfoVO {
        private String id = null;
        private String pwd = null;
        
        //LoginDAO에서 인증이 성공인 경우 아래의 생성자를 불러 값을 setting 합니다.
        public UserInfoVO(String id, String pwd){
                this.id = id;
                this.pwd = pwd;
        }
        
        public  String getId() {
                return id;
        }

        public  String getPwd() {
                return pwd;
        }
}




---------------------
Log.java
---------------------

필요에 따라 로그를 남기기 위한 클래스 입니다. 아래에서 로그가 남는 경로는 본인의 PC에 맞게 설정 하시기 바랍니다

package login2;

import! java.io.*;

public class Log{
    
    public static final String log_path = "c:/tomcat5/logs/";
    
    public static void println(String filename, String log){
        try{
            FileWriter fw = new FileWriter(log_path+filename+".log", true);
            
            fw.write(log+"\n");
            fw.flush();
            fw.close();
        }catch(Exception e){                 
        }
    }        
}




-------------------
Application.properties
-------------------

이 파일은 각종 메시지를 보관하는 파일 입니다. 이클립스 작업시 /WEB-INF/src/ 아래에 package(resources)를 만드시고 그곳에 이 파일을 만드세요~


login.title = LogIn
login.login = Login
login.reset = Cancel

prompt.id= User ID :
prompt.password=Password :

main.title=Main


###############################################3
error.id.required=<li>ID REQUIRED.</li>
error.pwd.required=<li>PASSWORD REQUIRED</li>
error.login.invalid=<li>ID/PASSWORD NOT CORRECT</li>

errors.header=<h3><font color="red">ERROR!</font></h3>
                  You must correct the following error(s) before proceeding:<UL>
errors.footer=</ul><hr>



-----------------------
struts-config.xml
-----------------------

<?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="login2.LoginForm">                   
            <form-property name="pwd" type="java.lang.String" />
            <form-property name="id" type="java.lang.String" />            
        </form-bean>            
    </form-beans>
    
  
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards>
        <forward name="success" path="/main.jsp" />
        <forward name="logoff" path="/logoff.do" />
        <forward name="login" path="/login.jsp" />
    </global-forwards>
    
    <!-- ========== Action Mapping Definitions =============================== -->
    <!-- valiedate를 true라고 함으로써 LoginForm의 validate가 호출 됩니다.             -->
    <action-mappings>
            <!-- loginAction에 대한 정의 -->
        <action          
                path="/LoginSubmit"
                type="login2.LoginAction"
            name="loginForm"                        
            validate="true"
            input="/login.jsp"
        />
        
        <!-- logoffAction에 대한 정의 -->
        <action          
                path="/logoff"
                type="login2.LogoffAction"                          
            validate="false"          
        />                      
    </action-mappings>  
        
    
    <!-- 한글 문제 처리를 위해 -->
    <controller
            contentType="text/html;charset=euc-kr"
            debug="3"            
            locale="true"
            nocache="true"
            processorClass="filter.MyFilter"/>
            
        <!-- ========== 아래는 스트러츠 Application에서 사용할 Message Resource들을 설정 ====== -->
    <!-- 확장자가 properties인 application.properties를 만들어 넣으면 된다 ============ -->
    <message-resources parameter="resources.application"/>
            
        
</struts-config>



-------------------
web.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>    
        
        <servlet>
                <servlet-name>action</servlet-name>
                <servlet-class>login2.NewActionServlet</servlet-class>
                <init-param>
                        <param-name>config</param-name>
                        <param-value>/WEB-INF/struts-config.xml</param-value>
                </init-param>


JSP 에서의 파일 업로드

작성자 : (tornado@dolpung.pe.kr)

 

파일을 업로드 한다는 것은 원격의 사용자가 서버 측으로 네트워크를 통해 파일을 전송하는 일이다

보통 WEB 환경에서 사용자의 입력 값을 다른 페이지로 이동시키기 위해서는 Form 통한 데이터 전송을 하게 된다.

간단하게 사용자의 이름을 입력 받고 내용을 JSP 페이지나 기타 파일로 전송을 해야 때는 아래와 같은 HTML 폼이 작성이 되어야 것이다.

 

<Form action=receive.jsp Method=POST>

이름 : <Input type=TEXT name=userName> <BR>

<input type=SUBMIT value= >

</Form>


위의 파일을 실행하면 Action 태그에 정의된 페이지에서 사용자의 입력 값을 전송 받을 있게 된다. 하지만 File 경우는 그리 만만치 않다. 사용자가 입력하는 값은 Text 형태의 값이 대부분이고, 전송되는 File 형태는 Binary 타입이기 때문이다.

때문에 파일이 전송될 때에는 파일의 형태를 읽어 들일 있는 Servlet 또는 Java Bean 제작해야 한다.

 

아래는 파일전송을 위한 HTML 형태이다.

 

<!-- FileName : uploadForm.html  파일위치 c:\tomcat4/webapps/ROOT/uploadTest -->

<Form action=receive.jsp Method=POST enctype=multipart/form-data>

이름 : <Input type=TEXT name=userName> <BR>

파일 : <Input type=FILE name=userFile><BR>

<input type=SUBMIT value= >

</Form>

 

위의 파일을 보면 enctype 이라는 태그가 데이터를 전송하게 타입을 지정하는 부분이다.

아래쪽의 Input type=FILE 이라는 부분은 파일을 선택할 있는 창이 뜨게 되는 부분이다.

 

이렇게 전송되는 데이터를 receive.jsp 에서는 request 객체를 이용해서는 절대로 값을 가져오지 못한다.  Enctype 명시가 되면 넘어오는 파라메터들은 하나의 스트림을 통해 전송이 되게 된다. 그렇기 때문에 스트림으로 넘어오는 데이터를 각각의 구분자를 이용하여 짤라내야 한다.

그럼 데이터는 어떻게 받아낼까.

ServletRequest 인터페이스 에는 getInputStream() 이라는 메소드가 있다. 메소드는 클라이언트 측에서 전송되는 스트림을 서버측에서 받아줄 통로를 열어주는 메소드 이다. 위의 메소드를 통하여 사용자가 전송하는 스트림을 받아올 것이다.

아래는 데이터를 받아오는 receive.jsp 파일이다.

 

<%-- FileName : receive.jsp  SavePath : c:\tomcat4/webapps\ROOT\uploadTest --%>

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

<%@ page import=java.io.* %>

<%

         BufferedReader bufReader =

 new BufferedReader(new InputStreamReader(request.getInputStream()));

         String read = “”;

         while((read = bufReader.readLine()) != null){

                  out.print(read + <BR>);

}

bufReader.close();

%>

위의 파일을 해당 경로에 두고 브라우져에서 HTML 파일부터 실행 시키고 Text 형태의 파일을 전송해보자. JAVA SOURCE 파일도 괜찮다.   실행이 되었다면 아래와 같은 그림이 나오게 된다.

 

 

 

 

 

 

 

 

위의 그림을 자세히 보면 일정한 양식으로 입력 값이 전송된다는 것을 있다.

-----------------------------7d33d86502e0

위와 같은 부분을 Delimeter 라고 부른다 이렇게 넘어온 딜리미터를 이용하여 각각의 내용을 수동으로 잘라내야 한다.

처음 나오는 딜리미터 아래를 보면 Content-Desposition: form-data; name=userName  이라는 부분이 나오는 것을 있는데 이것은 HTML 에서 넘어오는 Form Data 구분해서 그에 상응하는 Parameter Name Parameter Value 나눠서 보여준다는 것을 있다.  물론 tornado 라는 것은 파라미터 값이 되겠다.

 

다시 딜리미터가 시작이 되고 아랫줄에 filename= 이라는 곳이 시작이 되는데 이곳부터 실제 파일이 시작이 되는 것이다. 현재 업로드 파일은 TEXT 형식이기 때문에 위와 같이 보여지지만 Binary 형태의 자료가 업로드가 된다면 이상한 문자들을 화면 가득 만날 것이다.

 

참고로 Binary 자료를 서버측에서 받으려 한다면 절대로 Reader 계열의 IO 클래스들을 사용해서는 안된다. Reader 계열 클래스 들은 문자를 읽어 들이는 스트림 이기 때문에 Binary 자료를 읽어 들이게 되면 제대로 업로드가 되리라는 보장이 없다.

 

위와 같이 복잡한 과정을 통해 업로드가 된다는 것을 알아보았다.

다시 한번 강조하지만 HTML Form 태그에 enctype 명시가 되면 절대로 Request 객체로 값을 받지 못한다는 것을 상기하기 바란다. 자료를 업로드 때에는 Method 속성이  POST 여야 한다는 것도 잊지 말자.(그렇다고 GET 방식은 절대로 못한다는 것은 아니다)

MultipartRequest 파일 업로드 하기

위와 같은 복잡한 과정을 거쳐 업로드를 하게 된다면 자바 IO 제대로 공부하지 않은 사람은 업로드 게시판은 절대 꿈도 것이다. 하지만 이렇게 복잡한 업로드를 간단히 해결해 주는 클래스들이 인터넷 상에 제공되고 있다.

우리가 사용할 MultipartRequest 라는 업로드 컴포넌트는 Oreilly 사에서 제공하는 컴포넌트이며 컴포넌트를 사용할 때에는 저작권이 따른다는 것을 알고 있기를 바란다.

또한 컴포넌트를 사용하려면 Oreilly 책을 구입해야 한다고 명시하고 있다.

 

위의 컴포넌트를 받으려면 아래의 웹사이트를 방문하여야 한다.

http://servlets.com/cos/index.html

 

위의 주소로 접속을 하고 화면을 아래로 스크롤 하게 되면 압축파일이 있는 표를 만나게 되는데

날짜를 확인하고 제일 최신 버전을 다운로드 받으면 된다.

참고로 현재 최신 버전은 2002 11 05 판이다.

 

cos-05Nov2002.zip 같은 파일을 찾았다면 다운로드를 하고 적당한 곳에 압축을 푼다.

압축을 풀면 아래와 같은 디렉토리가 생기게 된다.

 


 

 

디렉토리의 구조는 아래와 같다.

classes 디렉토리는  MultipartRequest 클래스 들이 위치한 디렉토리 이다.

Doc 디렉토리는 MultipartRequest API Document 들어있는 디렉토리 이다

Lib 디렉토리는 MultipartRequest 클래스들을 압축하여 놓은 Library 디렉토리이다.

Src 디렉토리는 Source 디렉토리 이다.

upload.war 파일은 아카이브 라는 형태의 파일로서 이것을 webapps 놓고 톰캣을 Restart 하게 되면 해당 war 파일이 압축이 풀리며 바로 사용 가능한 상태로 되는 파일형식이다. 파일을 이용하여 테스트를 있다.

 

Lib 디렉터리에 있는 cos.jar 이라는 파일을 톰캣 설치디렉토리의 common/lib 복사 한다. (c:/tomcat4/common/lib)

common/lib 위치 시켜야 하는데 이유는 톰캣이 처음 시작될 common/lib 있는 라이브러리를 읽어 들일 있게 해당 파일을 로딩하기 때문이다. JDBC 드라이버도 마찬가지로 이곳에 위치시켜야 한다.

Tomcat restart 하게 되면 cos.jar 파일에 들어있는 클래스들을 이용할 있게 된다.

 

본격적으로 업로드를 해보자.

 

위에서 작성한 receive.jsp 파일을 아래와 같이 작성한다.

<%-- FileName receive.jsp  SavePath c:/tomcat4/webapps/ROOT/uploadTest --%>

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

<%@ page import="com.oreilly.servlet.MultipartRequest" %>

<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>

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

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

 

<%

             String savePath="c:/tomcat4/webapps/ROOT/uploadTest";   // 저장할 디렉토리 (절대경로)

             int sizeLimit = 10 * 1024 * 1024 ;      // 파일업로드 용량 제한.. 10Mb

             try{

                           MultipartRequest multi = new MultipartRequest(request, savePath,

                                                                  sizeLimit,"euc-kr",new DefaultFileRenamePolicy());

                           String userName = multi.getParameter("userName");

                           String fileName= multi.getFilesystemName("userFile");

                           String originFileName = multi.getOriginalFileName("userFile");

 

                           if(fileName == null) { 

                                        out.print("파일이 업로드 되지 않았습니다!!");

                           } else {

                                        fileName=new String(fileName.getBytes("ISO-8859-1"),"euc-kr");

                                        out.print("User Name : " + multi.getParameter("userName") + "<BR>");

                                        out.print("getFilesystemName()  : " + fileName + "<BR>");

                                        out.print("getOriginalFileName() : " + originFileName +"<BR>");

                           } // end if

             } catch(Exception e) {

                           out.print(e.getMessage());

             }

%>

 

http://localhost:8080/uploadTest/uploadForm.html 부터 실행을 한다.

업로드 파일을 선택하고 전송 버튼을 누르게 되면 아래와 같은 그림이 나오게 된다.

 

 

 

 

  

 

다시 위의 과정을 반복해 본다. 아래와 같은 그림이 나온다.

 

 

 

 

 

이제 업로드 디렉터리를 보게 되면 아래와 같은 그림이 나오게 된다.

 

 

 

 

 

 

위의 결과치를 보게 되면 MultipartRequest 안에 들어있는 가지의 메소드의 용법을 있다.

String fileName= multi.getFilesystemName("userFile");

String originFileName = multi.getOriginalFileName("userFile");

 

getFilesystemName() 메소드는 서버측으로 실제 업로드 파일의 이름을 얻어오는 메소드 라는 것을 수가 있다.

getOriginalFileName() 메소드는 사용자가 업로드 실제 파일 이름을 얻어오는 메소드 이다.

그리고 중복 파일이 있을 경우에는 자동으로 파일이름 뒤에 중복된 숫자가 붙는다는 것을 있다.

이제 이렇게 업로드 파일의 이름을 데이터베이스에 입력을 하게 되면 자료실을 쉽게 구현 수가 있게 된다.

 

다음으로 알아볼 것은 업로드 파일의 사이즈를 알아내는 부분이다.

MultipartRequest 에는 파일의 사이즈를 반납해 주는 메소드는 없다. 하지만 File 객체 자체를 반납해 주는 메소드가 존재한다.

실제 파일객체를 받게 경우 File 객체에서 사용할 있는 모든 메소드를 사용할 있다는 장점이 있다.

파일의 사이즈는 아래와 같이 얻어오면 된다.

File f = multi.getFile("userFile");

같이 받아오면 된다.  이제 이렇게 받아온 파일을 통해 파일의 사이즈 각종 정보를 얻을 있게 된다.

 

 

아래의 코드를 receive.jsp 파일에 추가한다. 위치는 userFile 이름을 받아오는 아랫쪽에 위치 시키면 된다.

File f = multi.getFile(userFile);

int fileSize = (int)file.length();

out.print(<BR> 파일 사이즈 : + fileSize + <BR>);

사이즈가 출력이 되는 것을 있을 것이다. 참고로 int 형변환 하는 것은 File 객체의 length() 메소드의 반환값이 long 이기 때문에 형변환을 해준 것이다.

 

이제 여러 개의 파일을 업로드를 해보자. 이렇게 업로드 파일을 데이터 베이스의 테이블에 저장해 보도록 하자.

테이블의 구조는 아래와 같다. MySQL test 데이터베이스에 만들도록 하자.

 

Create table upTable(

             num int unsigned not null auto_increment primary key,

             name varchar(50) not null,

             subject varchar(200) not null,

             contents text not null,

             up1 varchar(50),

             up2 varchar(50),

             up3 varchar(50),

             size1 int,

             size2 int,

             size3 int,

);

 

먼저 파일을 선택할 있는 HTML 형식의 문서가 있어야 한다.

 

) multiUp.html 

<HTML>

<HEAD>

             <TITLE>JSP UPLOAD Test</TITLE>

<HEAD>

<BODY>

<FORM name="frm" method="POST" ACTION="multiUp.jsp" enctype="multipart/form-data">

<TABLE Border="1">

             <TR>

                           <TD>이름 <input type="text" name="userName"></td>

             </TR>

             <TR>

                           <TD>제목 <input type="text" name="subject" size=40></td>

             </TR>

             <TR>

                           <TD>내용<TextArea name="contents" rows="10" cols="60"></textArea></td>

             </TR>

             <TR>

                           <TD>파일 1  <input type="file" name="up1"> </TD>

             </TR>

             <TR>

                           <TD>파일 2  <input type="file" name="up2"> </TD>

             </TR>

             <TR>

                           <TD>파일 3  <input type="file" name="up3"> </TD>

             </TR>

             <TR>

                           <TD> <INPUT TYPE="SUBMIT" value=" "> </TD>

             </TR>

</TABLE>

</FORM>

</BODY>

</HTML>

 

위의 파일에서 주의 하여야 것은 enctype=multipart/form-data 부분이다. 다시 강조하지만 enctype 부분이 생략되면 업로드가 안된다는 것을 알고 있기 바란다.

이제 위의 HTML 폼에서 넘어가는 파일들과 파라메터들을 받아서 저장해줄 JSP 파일이 필요하다.

아래의 파일이 역할을 해주는 파일이다.

) multiUp.jsp

<%-- FileName : multiUp.jsp   savePath : c:/tomcat4/webapps/ROOT/uploadTest  --%>

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

<%@ page import="com.oreilly.servlet.MultipartRequest" %>

<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>

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

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

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

 

<%

 

             String savePath = "c:/tomcat4/webapps/ROOT/uploadTest"; // 업로드 경로

             int sizeLimit = 5 * 1024 * 1024;

 

             MultipartRequest multi = new MultipartRequest(request, savePath, sizeLimit, new DefaultFileRenamePolicy());

             /*

                오라클의 경우 아래와 같이 MultipartRequest 생성자 호출시에 한글이라는 것을 명시해야 한글이 안깨진다.

                MultipartRequest multi =

                          new MultipartRequest(request, savePath, sizeLimit, "euc-kr", new DefaultFileRenamePolicy());

             */

            

             // 넘어오는 파라메터.

             String userName = multi.getParameter("userName");

             String subject = multi.getParameter("subject");

             String contents = multi.getParameter("contents");

            

             // 넘어오는 파일 이름

             String fileName1 = multi.getFilesystemName("up1");

             String fileName2 = multi.getFilesystemName("up2");

             String fileName3 = multi.getFilesystemName("up3");

 

             // 파일  사이즈 받기

             int size1 = (int)(new File(savePath + "/" + fileName1).length());

             int size2 = (int)(new File(savePath + "/" + fileName2).length());

             int size3 = (int)(new File(savePath + "/" + fileName3).length());

            

             // 데이터베이스 접속

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

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

 

             String query = "insert into upTable values(null, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

             PreparedStatement pstmt = conn.prepareStatement(query);

             pstmt.setString(1, userName);

             pstmt.setString(2, subject);

             pstmt.setString(3, contents);

             pstmt.setString(4,  fileName1);

             pstmt.setString(5,  fileName2);

             pstmt.setString(6,  fileName3);

             pstmt.setInt(7, size1);

             pstmt.setInt(8, size2);

             pstmt.setInt(9, size3);

             int affectedRow = pstmt.executeUpdate();

 

             pstmt.close();

             conn.close();

 

             if(affectedRow != 1){

                           out.print("데이터 베이스 입력에 실패하였습니다");

                           // 입력에 실패하였다면 서버측에 업로드 파일을 삭제한다.

                           Enumeration enum = multi.getFileNames();

                           while(enum.hasMoreElements()){

                                        new File(savePath + "/" + multi.getFilesystemName((String)enum.nextElement())).delete();

                           }

             }else{

                           response.sendRedirect("upList.jsp");

             }

%>

 

위의 파일을 보게 되면 넘어오는 파라메터를 request 객체로 받지 않고 request MultipartRequest 매개변수로 넘겨준 MultipartRequest 인스턴스인 multi 받아낸다는 것을 있다.

특히 한글 파일이 업로드가 때에는 주의를 해야 한다. MultipartRequest 객체로 파일을 받아올 charset 지정되지 않으면 한글인 경우 파일이름이 깨지게 된다. 이때에는 주석 처리를 부분과 같이 한글문자셋을 지정해 줘야 한다.

하지만 MySQL 경우에는 한글인코딩을 하게 되면 한글이 다시 깨지게 되기 때문에 업로드 되는 파일의 이름만 부분적으로 한글 인코딩을 해줘야 하게 된다. 여러가지 시도를 해보고 각자의 환경에 맞게 고쳐서 쓰기 바란다.

 

파일의 사이즈를 받는 부분은 파일을 생성하고 그곳에 length() 메소드를 호출하여 int 형변환 하였다는 것을 있다.

반환값이 long 타입이기 때문에 형변환은 해줬지만 자신의 입맛에 맞게 사용하기 바란다.

 

앞서 했던 파일을 하나만 올리는 예제와 비교해 보면 이번 예제는 데이터베이스에 저장을 한다는 부분이 추가되었다.

데이터베이스에 저장할 때는 파일의 이름과 파일의 사이즈를 입력해주면 된다.

위에서 받아온 파일의 이름을 PreparedStatement IN 파라메터에 각각 대입을 해주고 executeUpdate 숫자를 받아주면 데이터베이스에 입력이 되었는가의 판단 여부를 알아 있다. 만약 데이터베이스에 입력이 되었다면 1 반환될 것이고 그렇지 않다면 0 반납이 되기 때문이다. 이렇게 나온 숫자를 이용하여 데이터 베이스에 입력이 안되었다면 기존의 업로드 파일들을 지워줘야 한다.

 

MultipartRequest 클래스의 메소드 중에는 파일입력 파라메터들의 이름을 Enumeration 객체로 반납해 주는 메소드가 있다.

바로 getFileNames() 라는 메소드 인데, 메소드가 해주는 역할은 파일이름을 반납하는 것이 아니라 파일이름이 담겨서 오는 파라메터들을 반납해 준다는 것이다. 이렇게 알아낸 파일들의 파라메터 이름들을 이용하여 getFilesystemName() 메소드에 알려주게 되면 실제 업로드 (중복파일이 있다면 변경된 이름을 반납) 파일의 이름을 알아 수가 있다.

이렇게 알게 파일의 실제 이름을 File 객체로 생성하여 delete() 메소드를 호출하게 되면 업로드 파일이 서버측에서 삭제되게 된다.

 

만약 데이터베이스에 입력이 되었다면 upList.jsp 파일로 요청이 넘어가게 되는데 upList.jsp 파일에서는 데이터베이스에 입력되어 있는 모든 레코드를 num 순서의 역방향(desc) 정렬하여 사용자 측에 보여주게 된다.

아래의 그림은 upList.jsp 파일이다. 제목을 클릭하면 업로드된 게시물의 내용과 업로드 파일을 수가 있다.

 

그림 . upList.jsp

 

 

 

 

                       

 

그럼 데이터 베이스에는 어떻게 들어가 있는지 보자.

MySQL 서버가 깔려있는 경로로 이동하여 mysql u root p test 라고 입력한 패스워드를 입력한다.

Select * from upTable; 같이 입력하면 아래와 같은 그림이 나오게 된다.

 

 

 

 

 

 

 

아래는 upList.jsp 파일의 소스이다.

예제 : upList.jsp

<%--  FileName upList.jsp    savePath=c:/tomcat4/webapps/ROOT/uploadTest  --%>

 

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

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

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

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

 

<%

             // 데이터베이스 접속

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

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

 

             String query = "select * from uptable order by num desc";

             Statement stmt = conn.createStatement();

             ResultSet rs = stmt.executeQuery(query);

 

             out.print("<Table border=1>");

             out.print(" <tr>");

             out.print("              <td>번호</td>");

             out.print("              <td width=100>제목</td>");

             out.print(" </tr>");

 

             while(rs.next()){

               out.print("<tr>");

               out.print("  <td>"+rs.getInt("num")+"</td>");

               out.print("  <td><a href=upView.jsp?num="+rs.getInt("num")+">"+rs.getString("subject")+"</a></td>");

               out.print("            </tr>");

             }

             out.print("</table>");

 

             rs.close();

             stmt.close();

             conn.close();

%>

 

위에 있는 upList.jsp 그림과 같이 보인다면 제목을 클릭해 보자. upView.jsp 페이지가 이동되는 것을 있다.

이제 upView.jsp 에서 해야 일이 머릿속에 그려질 것이다.

먼저 해야 일은 upList.jsp  에서 넘어오는 파라메터의 값을 받아내야 한다.

num 값을 이용하여 쿼리를 전송하고 그렇게 얻어지는 결과치를 브라우져에 알맞게 뿌려주면 된다.

또한 파일들에 대하여 링크를 걸어줘야 파일을 클라이언트 측에서 인식을 수가 있게 된다.

현재는 파일에 대해 파일이름을 직접 지정하지만 추후 제작할 업로드 게시판에서는 다운로드를 해주는 파일을 제작할 것이다.

아래의 예제는 upView.jsp 파일이다. 파일이름에 대소문자 구별하여 저장한다.

 

예제 upView.jsp

<%-- FileName : upView.jsp   savePath : c:/tomcat4/webapps/ROOT/uploadTest --%>

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

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

<%

             int num = Integer.parseInt(request.getParameter("num"));

             // 데이터베이스 접속

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

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

            

             String query = "select * from upTable where num="+num;

             Statement stmt = conn.createStatement();

             ResultSet rs = stmt.executeQuery(query);

            

             out.print("<table border=1>");

             if(rs.next()){

                           out.print("<tr>");

                           out.print(" <td>번호 : " + rs.getInt("num") + "</td>");

                           out.print(" <td colspan=2>제목 : " + rs.getString("subject") + "</td>");

                           out.print("</tr>");

                           out.print("<tr>");

                           out.print(" <td colspan=3>");

                           out.print("   파일 1 <a href=/uploadTest/"+rs.getString("up1")+">"+ rs.getString("up1") + "</a>");

                           out.print("  파일 2 <a href=/uploadTest/"+rs.getString("up2")+">"+ rs.getString("up2") + "</a>");

                           out.print("  파일 3 <a href=/uploadTest/"+rs.getString("up3")+">"+ rs.getString("up3") + "</a>");

                           out.print("</tr>");

                           out.print("<tr>");

                           out.print(" <td>내용</td>");

                           out.print(" <td colspan=2 height=50>" + rs.getString("contents") + "</td>");

                           out.print("</tr>");

                           out.print("<tr>");

                           out.print(" <td colspan=3>");

                           out.print("              <a href=modify.jsp?num="+rs.getInt("num")+">수정</a>");

                           out.print("              <a href=delete.jsp?num="+rs.getInt("num")+">삭제</a>");

                           out.print("</tr>");

             }

             out.print("</table>");

             rs.close();

             stmt.close();

             conn.close();

%>

 

위의 파일을 실행하면 아래와 같은 그림이 나오게 된다.

 

 

  

화면 하단에 보면 수정/삭제 부분이 보인다. 여기서 수정이란 기존의 파일을 삭제하고 새로운 파일을 올리겠다는 것이고

삭제는 기존의 파일과 데이터베이스에 들어있는 정보를 같이 삭제 하겠다는 것이다.

 

그럼 삭제를 먼저 해보자. 위의 그림에서 보이는 것과 같이 삭제 라는 곳에 마우스를 올리고 브라우져 하단에 나오는 링크정보를 보도록 하자 delete.jsp?num=1 같이 정보가 넘어가는 것을 있다.

delete.jsp  파일에서는 넘어오는 파라메터를 이용하여 데이터베이스에 쿼리를 하고 얻어진 파일이름을 이용하여 파일을 삭제하고 다시 데이터베이스의 레코드를 삭제하면 된다.  , 정보가 삭제되었다면 다시 upList.jsp 파일로 페이지를 이동시켜주면 된다.

아래의 예제를 작성한 실행 보고 윈도우탐색기에서 파일이 삭제 되었는지 보고, 데이터베이스에서는 레코드가 삭제 되었는지 확인해 보자.

 

예제. delete..jsp

<%-- FileName : delete.jsp   SavePath : c:/tomcat4/webapps/ROOT/uploadTest   --%>

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

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

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

<%

             int num = Integer.parseInt(request.getParameter("num"));

 

             String savePath = "c:/tomcat4/webapps/ROOT/uploadTest";

             String query1 = "select up1, up2, up3 from upTable where num="+num;

 

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

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

             Statement stmt = conn.createStatement();

             ResultSet rs = stmt.executeQuery(query1);

             ResultSetMetaData rsmd = rs.getMetaData();

             int columnCount = rsmd.getColumnCount();

            

             if(rs.next()){

                           for(int i = 1; i <= columnCount; i++){

                                        if(rs.getString(i) != null || rs.getString(i).length() > 0){

                                                     new File(savePath + "/" + rs.getString(i)).delete();                                        

                                        }

                           }

             }

 

             String query2 = "delete from uptable where num="+num;

             stmt.executeUpdate(query2);

 

             rs.close();

             stmt.close();

             conn.close();

          response.sendRedirect(upList.jsp);

%>

 

위의 파일에서는 ResultSet 에서 나오는 테이블 정보를 이용하여(ResultSetMetaData) 선택된 컬럼의 개수를 알아내고, 그렇게 알아낸 컬럼의 숫자만큼 순환문을 돌면서 파일객체를 생성하여 업로드 파일을 삭제 하는 것을 있다.

이후 두번째 쿼리문 에서는 해당 레코드를 삭제하는 쿼리를 데이터베이스에 전송한 다는 것을 있다.

현재의 예제 파일에는 중간에 에러발생시 전혀 복구가 불가능한 상태인데 파일을 삭제 시에 정말 삭제 하는 것이 아니라 임시디렉토리를 하나 두고 삭제할 파일을 임시 디렉토리로 이동시킨후 해당 레코드가 정상적으로 삭제가 되었으면 임시디렉토리에 있는 파일을 실제로 삭제하는 방법을 써야 한다. 물론 업로드 때에도 위와 같은 방법을 쓰는 것이 좋다.

 

마지막으로 수정 하는 부분을 작성해 보도록 하자.

기존 업로드 파일들을 새로운 파일로 대치 하고, 내용에 대한 사항을 수정해야 하는 것이기 때문에 위의 delete.jsp 보다는 약간 복잡하다.

수정을 하려면 새로운 내용과 새로운 파일을 입력을 받을 있는 HTML 폼이 필요하다. 이곳에서 넘어가는 파라메터들에 대하여 작업을 해야 JSP 파일도 필요하다.

먼저 새로운 내용을 입력받는 형식을 작성해 보도록 하자.

 

예제. modify.jsp

<%-- FileName : modify.jsp   SavePath : c:/tomcat4/webapps/ROOT/uploadTest  --%>

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

<%

             // 넘어오는 Number 값을 받아 Hidden 변수타입으로 넘겨준다.

             int num = Integer.parseInt(request.getParameter("num"));

%>

<HTML>

<HEAD>

             <TITLE>JSP UPLOAD Test</TITLE>

<HEAD>

<BODY>

<FORM name="frm" method="POST" ACTION="modifyUp.jsp" enctype="multipart/form-data">

<TABLE Border="1">

             <TR>

                           <TD>이름 <input type="text" name="userName"></td>

             </TR>

             <TR>

                           <TD>제목 <input type="text" name="subject" size=40></td>

             </TR>

             <TR>

                           <TD>내용<TextArea name="contents" rows="10" cols="60"></textArea></td>

             </TR>

             <TR>

                           <TD>파일 1  <input type="file" name="up1"> </TD>

             </TR>

             <TR>

                           <TD>파일 2  <input type="file" name="up2"> </TD>

             </TR>

             <TR>

                           <TD>파일 3  <input type="file" name="up3"> </TD>

             </TR>

             <TR>

                           <TD> <INPUT TYPE="SUBMIT" value=" "> </TD>

             </TR>

</TABLE>

<!-- 히든 변수로 modifyUp.jsp 파일에서 참조할 변수를 전달 한다 -->

<input type="hidden" name="num" value=<%= num %>>

</FORM>

</BODY>

</HTML>

 

modify.jsp 파일은 생각보다 상당히 단순하다. 그냥 HTML 입력폼 Hidden 변수타입만 추가된 형태이다.

이곳에서 hidden 변수를 modifyUp.jsp 파일에 반드시 넘겨줘야 한다. 그렇지 않으면 기존의 어느 레코드에 업데이트를 해야 수가 없기 때문이다. 이렇게 넘어간 num 변수 값과 새롭게 입력된 파라메터들을 modifyUp.jsp 파일에서 데이터 베이스 쪽으로 새롭게 갱신해 줘야 한다. 새롭게 넘어오는 파일이 있다면 해당 파라메터에 상응하는 컬럼이 가지고 있는 기존의 실제 파일을 삭제 해줘야 한다. 세곳의 파일 입력폼에서 전부 파일이 전송된다면 컬럼에 들어있는 파일정보를 이용하여 실제 파일을 삭제해야 하며 만약 한군데의 입력폼 에서만 파일이 전송된다면 해당되는 입력폼에 상응하는 컬럼이 가지고 있는 실제 파일을 삭제해 줘야 한다.

 

어렵겠지만 다음 장의 예제를 자세히 보자.

 

예제. modifyUp.jsp 

<%-- FileName : modifyUp.jsp   SavePath : c:/tomcat4/webapps/ROOT/uploadTest   --%>

 

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

<%@ page import="com.oreilly.servlet.MultipartRequest" %>

<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>

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

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

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

<%

             String savePath = "c:/tomcat4/webapps/ROOT/uploadTest"; // 업로드 경로

             int sizeLimit = 10 * 1024 * 1024;

 

             MultipartRequest multi = new MultipartRequest(request, savePath, sizeLimit, new DefaultFileRenamePolicy());

 

             int num = Integer.parseInt(multi.getParameter("num"));

             String subject = multi.getParameter("subject");

             String contents = multi.getParameter("contents");

            

             boolean selectFlag = false;

             String selectQuery = "select ";

             String updateQuery = "update uptable set ";

             Enumeration e = multi.getFileNames();

             while(e.hasMoreElements()){

                           String formName = (String)e.nextElement();

                           if(multi.getFilesystemName(formName) != null){

                                        selectQuery += formName + ",";

                                        updateQuery += formName + "='" + multi.getFilesystemName(formName) + "',";

                                        selectFlag = true;

                           }

             }

            

             if(selectQuery.trim().endsWith(",")){

                           selectQuery = selectQuery.substring(0, selectQuery.length()-1);

             }

             selectQuery += " from upTable where num="+num;

             updateQuery += " subject='"+ subject + "', contents='"+contents+"' where num="+num;

 

//           out.print(selectQuery + "<BR>" + updateQuery);

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

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

             Statement stmt = conn.createStatement();

             if(selectFlag){

                           ResultSet rs = stmt.executeQuery(selectQuery);

                           ResultSetMetaData rsmd = rs.getMetaData();

                           int columnCount = rsmd.getColumnCount();

                           if(rs.next()){

                                        for(int i = 1; i <= columnCount; i++){

                                                     new File(savePath + "/" + rs.getString(i)).delete();

                                        }

                           }

                           rs.close();

             }

             stmt.executeUpdate(updateQuery);       

             stmt.close();

             conn.close();

             response.sendRedirect("upList.jsp");

%>

 

위의 예제에서 보면 쿼리문장을 순환문을 통해 작성을 한다는 것을 수가 있다.

이는 사용자가 새로 갱신될 파일이나 내용을 어떻게 전송할 예측할 수가 없기 때문에 순환문을 통해 실제 업로드 파라메터에 해당하는 부분만 쿼리 문장에 포함을 시키려고 것이다.

위의 예제를 실행해 보면 실제 업로드 파일에 해당하는 부분의 컬럼 정보와 그곳에 저장되어있는 파일만 삭제 된다는 것을 있다. 어렵게 생각되는 예제 이지만 쇼핑몰의 상품 사진 업로드 부분을 작성하려면 위와 같은 방법을 응용해야 것이다.


java.lang.ClassNotFoundException: org.gjt.mm.mysql.Driver

후. mysql은 5.1이고, tomcat 5.5이고, jdk.1.5를 쓰고 있는데..

저 에러가 뜬다 =ㅂ-;

클래스 패스에 jar를 추가해줘도 안 되고,

java\jre\lib\ext에 넣어줘도 안된다.

그래서..............

찾아봤더니 이제 나온다.

tomcat5.5\common\lib에

mysql-connector-java-3.1.14-bin.jar를 추가해주면 된다.

뻘 짓 한참 하고야 해결 =ㅂ=;
사용자 삽입 이미지

window - Preferences 선택 Connectivity 선택후 mySql 5.1 선택하고 add 클릭



사용자 삽입 이미지

작은 화면으로 나온다. 창을 늘리면 더욱 잘 보인다.



사용자 삽입 이미지

창을 늘리면 이렇게 된다. mysql 선택하면 아래 5.1 이 나온다. 다시 클릭하면 mysql jdbc driver 선택 후 ok



사용자 삽입 이미지

창이 잘 안보이니 다시 늘이면 잘 보인다.



사용자 삽입 이미지

창을 늘린 화면


jdbc 설정을 위해서 mysql-connector-java-5.1.5-bin.jar 파일을 다은 받아야 한다.

http://dev.mysql.com/downloads/connector/j/5.1.html

mysql-connector-java-5.1.5-bin.jar 다운로드 위치


다운받은 파일은 압축을 풀고 jre_home\lib\ext 안에 넣으면 된다.

참고로 나는  C:\Java\jdk\jre\lib\ext 이렇게 사용중이다.

jre_home\lib\ext 안에 넣지 않으면 나중에 jdbc 연동이 안되니 참고하시길.


사용자 삽입 이미지

Driver Files 에서 기존 파일을 선택 후 Remove 한 후 add 하여 mysql-connector 파일 위치를 선택후 ok



사용자 삽입 이미지

Properties 아래에 본인의 mysql 설정을 기입힌다.



사용자 삽입 이미지

기입이 완성된 화면을 확인하고 ok 클릭



사용자 삽입 이미지

mysql jdbc driver이 새로 생긴 것을 확인할 수 있다.




사용자 삽입 이미지

아래 DataSource Explorer 을 클릭하면 Databases 가 생긴것을 확인할 수 있다.



사용자 삽입 이미지

mysql 연결을 위해 databases 에 마우스를 놓고 오른쪽 키 클릭하면 new 가 나온다.



사용자 삽입 이미지

새 창이 뜨면 Generic jdbc connection 선택하고 next 클릭



사용자 삽입 이미지

자신의 db 명을 적는다.



사용자 삽입 이미지

창 우측에 ... 아이콘을 클릭한다.



사용자 삽입 이미지

여러가지 목록이 나온다. 밑으로 내리면 mysql 이 나온다.



사용자 삽입 이미지

mysql 에 5.1 을 선택하면 아래에 방금전에 우리가 설정해 놓은 mysql jdbc driver이 보인다. 선택 후 ok 클릭



사용자 삽입 이미지

안에 내용이 전부 알아서 기입이 된다. 다만 아래 save password 의 체크 박스에 클릭 후 next 클릭



사용자 삽입 이미지

설정 상태 확인 후 finish 클릭한다.



사용자 삽입 이미지

아래에 자신의 db 명이 보인다.



사용자 삽입 이미지

db명에 마우스 놓고 오른쪽 마우스키를 누르면 Connect 클릭



사용자 삽입 이미지

정상적으로 접속이 되면 이렇게 표시된다.



이렇게 설정이 완료 ..

참 간단한데 아무것도 모르는 상태에서 이것을 설정하기 위해 참 많은 삽질을

했다는 것을 참고해주시길..

디비 연결이 안될때 보면 디비 클릭후 properties을 클릭

왼쪽 메뉴에서 JDBC Connection Properties에서

Database와 URL을 손봐야한다.

예를 들어 내 디비에 mydb라는 이름의 데이터베이스가 있다면

Database: mydb
URL : jdbc:mysql://3306/mydb

이렇게 입력하고 password까지 입력하면 문제 없을 것이다.

1.request.getParameter 이용

: 클라이언트 페이지에서 발생하는 데이터를 서버 페이지에서 받았다.(getParameter)


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


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

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

<%@ page import="com.ex1.beans.*" %>


<jsp:useBean id="data" scope="page" class="com.ex1.beans.Mem_Data"/> 


<html>

<head>

<title>자바 빈즈 활용 예제 #1</title>

</head>

<body>


<%

//전달 받은 값을 저장할 객체 생성

//Mem_Data data = new Mem_Data();


//setter 메소드에 의해서 넘겨 받은값 저장

data.setName(request.getParameter("name"));

data.setId(request.getParameter("id"));

data.setPwd(request.getParameter("pwd"));

data.setEmail(request.getParameter("email"));


//전달 받은 값 출력

out.println("<h3>전달 받은 값</h3>");

out.println("성명 : " + data.getName() + " <br>");

out.println("아이디 : " + data.getId() + " <br>");

out.println("비밀번호 : " + data.getPwd() + " <br>");

out.println("이메일 : " + data.getEmail() + " <br>");


%>


<!--3초 후  sample1.jsp 페이지로 이동 -->

<meta http-equiv="REFRESH" content="3;url=./sample1.jsp">


</body>

</html>


2.<jsp:setProperty name="ID" property="속성이름"/> 이용

: Action Tag를 통해서 ID속성이름을 설정해서 처리.


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


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

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

<%@ page import="com.ex1.beans.*" %>


<jsp:useBean id="data" scope="page" class="com.ex1.beans.Mem_Data">

        <jsp:setProperty name="data" property="name"/>

        <jsp:setProperty name="data" property="id"/>

        <jsp:setProperty name="data" property="pwd"/>

        <jsp:setProperty name="data" property="email"/>

</jsp:useBean>


<html>

<head>

<title>자바 빈즈 활용 예제 #2</title>

</head>

<body>


<h3>전달 받은 값</h3>

성명 : <jsp:getProperty name="data" property="name"/> <br>

아이디 : <jsp:getProperty name="data" property="id"/> <br>

비밀번호 : <jsp:getProperty name="data" property="pwd"/> <br>

이메일 : <jsp:getProperty name="data" property="email"/> <br>


<!--3초 후  sample1.jsp 페이지로 이동 -->

<meta http-equiv="REFRESH" content="3;url=./sample2.jsp">


</body>

</html>


3.<jsp:setProperty name="ID" property="*"/> 이용

: 파라미터 변수 이름(HTML Input 태그의 name 속성)과 데이터 타입을 가지고 있는 Mem_Data 빈의 멤버필드 이름을 일치하게 작성 하였을때 처리.

- 위의 2가지 방법보다 훨씬 효율적인 코딩 방법이다.

- 클라이언트 페이지에서 넘어오는 데아터를 자동으로 생성된 빈의 각각 멤버필드에 자동으로 설정한다.


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


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

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

<%@ page import="com.ex1.beans.*" %>


<jsp:useBean id="data" scope="page" class="com.ex1.beans.Mem_Data"/>

<jsp:setProperty name="data" property="*"/>


<html>

<head>

<title>자바 빈즈 활용 예제 #3</title>

</head>

<body>


<%

//전달 받은 값 출력

out.println("<h3>전달 받은 값</h3>");

out.println("성명 : " + data.getName() + " <br>");

out.println("아이디 : " + data.getId() + " <br>");

out.println("비밀번호 : " + data.getPwd() + " <br>");

out.println("이메일 : " + data.getEmail() + " <br>");


%>


<!--3초 후  sample1.jsp 페이지로 이동 -->

<meta http-equiv="REFRESH" content="3;url=./sample3.jsp">


</body>

</html>



[참고] ‘request.getParameter 이용’ 소스에서

아래와 같이 수정해서 한번 실행해 보기 바란다.

<jsp:useBean id="data" scope="page" class="com.ex1.beans.Mem_Data"/> --> 삭제

Mem_Data data = new Mem_Data();  -- 추가

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


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

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

<%@ page import="com.ex1.beans.*" %>


<html>

<head>

<title>자바 빈즈 활용 예제 #1</title>

</head>

<body>


<%

//전달 받은 값을 저장할 객체 생성

Mem_Data data = new Mem_Data();


//setter 메소드에 의해서 넘겨 받은값 저장

data.setName(request.getParameter("name"));

data.setId(request.getParameter("id"));

data.setPwd(request.getParameter("pwd"));

data.setEmail(request.getParameter("email"));


//전달 받은 값 출력

out.println("<h3>전달 받은 값</h3>");

out.println("성명 : " + data.getName() + " <br>");

out.println("아이디 : " + data.getId() + " <br>");

out.println("비밀번호 : " + data.getPwd() + " <br>");

out.println("이메일 : " + data.getEmail() + " <br>");


%>


<!--3초 후  sample1.jsp 페이지로 이동 -->

<meta http-equiv="REFRESH" content="3;url=./sample1.jsp">


</body>

</html> 

sample.alz

Mem_Data.java


[출처]
okjsp.pe.kr - 재키님의 글

+ Recent posts