반응형
import org.apache.commons.dbcp.BasicDataSource;

import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.ResultSetDynaClass;

import java.util.Iterator;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class DynaBeansExampleV2 {
  public static void main(String args[]) throws Exception {

    Connection conn = getConnection();
    PreparedStatement ps =  conn.prepareStatement("SELECT * from movie, person " +
                                      "WHERE movie.director = person.Id");
    ResultSet rs = ps.executeQuery();

    ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);

    Iterator itr = rsdc.iterator();
    while(itr.hasNext()) {
      DynaBean bean = (DynaBean)itr.next();
      System.err.println(bean.get("title"));
    }

    conn.close();
  }

  private static Connection getConnection() throws Exception {
    BasicDataSource bds = new BasicDataSource();
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql://localhost/commons");
    bds.setUsername("root");
    bds.setPassword("");

    //bds.setInitialSize(5);

    return bds.getConnection();
  }
}

Posted by 1010
반응형
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.dbcp.*;

import java.sql.*;


public class DBCPDemo{

  public static void main(String args[]) throws Exception{

    // create a generic pool
    GenericObjectPool pool = new GenericObjectPool(null);

    // use the connection factory which will wraped by
    // the PoolableConnectionFactory
    DriverManagerConnectionFactory cf =  new DriverManagerConnectionFactory(
                                            "jdbc:jtds:sqlserver://myserver:1433/tandem",
                                            "user",
                                            "pass");

    PoolableConnectionFactory pcf =  new PoolableConnectionFactory(cf, pool, null, "SELECT * FROM mysql.db", false, true);

    // register our pool and give it a name
    new PoolingDriver().registerPool("myPool", pool);

    // get a connection and test it
        Connection conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:myPool");

        // now we can use this pool the way we want.
        System.err.println("Are we connected? " + !conn.isClosed());

        System.err.println("Idle Connections: " + pool.getNumIdle() + ", out of " + pool.getNumActive());

  }
}

Posted by 1010
반응형
import java.sql.Connection;

import org.apache.commons.dbcp.BasicDataSource;

public class BasicDataSourceExample {

  public static void main(String args[]) throws Exception {

    BasicDataSource bds = new BasicDataSource();
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql://localhost/commons");
    bds.setUsername("root");
    bds.setPassword("");

//    bds.setInitialSize(5);

    Connection connection = bds.getConnection();

    System.err.println(connection);
    connection.close();
  }
}
Posted by 1010
반응형


package com.googelcode.jpractices.common;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * Copyright 2009 @ jPractices v 1.0
 * @SVN URL : http://jpractices.googlecode.com
 * @author Ganesh Gowtham
 * @Homepage : http://ganesh.gowtham.googlepages.com
 */

public class Person {
  private String firstName;

  private String lastName;

  private int salary;

  public Person(String firstName, String lastName, int salary) {
    super();
    this.firstName = firstName;
    this.lastName = lastName;
    this.salary = salary;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public int getSalary() {
    return salary;
  }

  public void setSalary(int salary) {
    this.salary = salary;
  }

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this);
  }
}
-------------
package com.googelcode.jpractices;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.beanutils.BeanComparator;

import com.googelcode.jpractices.common.Person;
/**
 * Copyright 2009 @ jPractices v 1.0
 * @SVN URL : http://jpractices.googlecode.com
 * @author Ganesh Gowtham
 * @Homepage : http://ganesh.gowtham.googlepages.com
 */
public class BeanComparatorExample {
  List<Person> personList = new ArrayList<Person>();

  /**
   * Basic method which creates the list of person object's
   *
   */
  void setUpData() {
    personList.add(new Person("jennefer", "gowtham", 35000));
    personList.add(new Person("britney", "spears", 45000));
    personList.add(new Person("tom", "gowtham", 36000));
    personList.add(new Person("joe", "dummy", 45000));
  }
  void sortPersons(String propertyName)
  {
    Comparator<Person> comp = new BeanComparator(propertyName);
    Collections.sort(personList, comp);
    for (Person person : personList) {
      System.out.println(person);
    }
  }
  public static void main(String[] args) {
    BeanComparatorExample beanComparatorExample = new BeanComparatorExample();
    beanComparatorExample.setUpData();
    beanComparatorExample.sortPersons("firstName");
  }
}

   
   

Posted by 1010
반응형
import org.apache.commons.collections.set.MapBackedSet;

import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.Iterator;

public class SetExampleV1 {
  public static void main(String args[]) {
    // create a Map
    Map map = new HashMap();
    map.put("Key1", "Value1");

    // create the decoration
    Set set = MapBackedSet.decorate(map);

    map.put("Key2", "Any dummy value");
    set.add("Key3");

    Iterator itr = set.iterator();

    while(itr.hasNext()) {
      System.err.println(itr.next());
    }

  }
}
Posted by 1010
반응형
import org.apache.commons.collections.list.TreeList;
import org.apache.commons.collections.list.SetUniqueList;
import org.apache.commons.collections.list.CursorableLinkedList;

import java.util.List;
import java.util.ListIterator;

public class ListExampleV1 {
  public static void main(String args[]) {
    ListExampleV1 listExample = new ListExampleV1();
    listExample.createLists();

    uniqueList.add("Value1");
    uniqueList.add("Value1");
    System.err.println(uniqueList); // should contain only one element

    cursorList.add("Element1");
    cursorList.add("Element2");
    cursorList.add("Element3");

    ListIterator iterator = cursorList.listIterator();
    iterator.next(); // cursor now between 0th and 1st element
    iterator.add("Element2.5"); // adds this between 0th and 1st element

    System.err.println(cursorList); // modification done to the iterator are visible in the list
  }

  private void createLists() {
    uniqueList = SetUniqueList.decorate(new TreeList());
    cursorList = new CursorableLinkedList();
  }

  private static List uniqueList;
  private static List cursorList;
}
Posted by 1010
반응형
import java.util.HashMap;

public class MultiKeyExampleV1 {
  public static void main(String args[]) {
    HashMap codeToText_en = new HashMap();
    codeToText_en.put("GM", "Good Morning");
    codeToText_en.put("GN", "Good Night");
    codeToText_en.put("GE", "Good Evening");

    HashMap codeToText_de = new HashMap();
    codeToText_de.put("GM", "Guten Morgen");
    codeToText_de.put("GE", "Guten Abend");
    codeToText_de.put("GN", "Guten Nacht");

    HashMap langToMap = new HashMap();
    langToMap.put("en", codeToText_en);
    langToMap.put("de", codeToText_de);

    System.err.println("Good Evening in English: " +
      ((HashMap)langToMap.get("en")).get("GE"));
    System.err.println("Good Night in German: " +
      ((HashMap)langToMap.get("de")).get("GN"));
  }
}
Posted by 1010
반응형
import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;

public class HashMapExampleV1 {
  public static void main(String args[]) {
    BidiMap agentToCode = new DualHashBidiMap();
    agentToCode.put("007", "Bond");
    agentToCode.put("006", "Trevelyan");
    agentToCode.put("002", "Fairbanks");

    System.err.println("Agent name from code: " + agentToCode.get("007"));
    System.err.println("Code from Agent name: " + agentToCode.getKey("Bond"));
  }
}
Posted by 1010
반응형
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.FactoryUtils;

public class FactoryExampleV1 {
  public static void main(String args[]) {
    Factory bufferFactory = FactoryUtils.instantiateFactory(StringBuffer.class,
                          new Class[] {String.class},
                          new Object[] {"a string"});
    System.err.println(bufferFactory.create());
  }
}
Posted by 1010
반응형
import org.apache.commons.collections.ComparatorUtils;
import org.apache.commons.collections.comparators.BooleanComparator;
import org.apache.commons.collections.comparators.FixedOrderComparator;

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorExampleForBuildInDataType {

  private static Comparator boolComp;
  private static Comparator fixedComp;

  private static Boolean boolParams[] = {new Boolean(true), new Boolean(true),
                                         new Boolean(false), new Boolean(false)};
  private static String  stringParams[] = {"Russia", "Canada", "USA", "Australia", "India"};
 
  public static void main(String args[]) {
    ComparatorExampleForBuildInDataType example = new ComparatorExampleForBuildInDataType();
    example.createComparators();

    Arrays.sort(boolParams, boolComp);

    example.printArray(boolParams);

    Arrays.sort(stringParams);

    example.printArray(stringParams);

    Arrays.sort(stringParams, fixedComp);

    example.printArray(stringParams);
  }

  private void createComparators() {
    boolComp = ComparatorUtils.booleanComparator(true);
    fixedComp = new FixedOrderComparator(stringParams);
  }

  private void printArray(Object[] array) {
    for(int i = 0; i < array.length; i++)
      System.err.println(array[i]);
  }

}
           

Posted by 1010
반응형
import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;
import org.apache.commons.collections.bidimap.UnmodifiableBidiMap;

public class BidiMapExample {
  public static void main(String args[]) {

    BidiMap agentToCode = new DualHashBidiMap();
    agentToCode.put("007", "Bond");
    agentToCode.put("006", "Joe");

    agentToCode = UnmodifiableBidiMap.decorate(agentToCode);
    agentToCode.put("002", "Fairbanks"); // throws Exception
    agentToCode.remove("007"); // throws Exception
    agentToCode.removeValue("Bond"); // throws Exception
  }
}

Posted by 1010
반응형
import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.buffer.BlockingBuffer;
import org.apache.commons.collections.buffer.PriorityBuffer;

public class BufferExample {
  public static void main(String args[]) {
    Buffer buffer = new PriorityBuffer();

    buffer.add("2");
    buffer.add("1");

    buffer = BlockingBuffer.decorate(buffer);

    buffer.remove();

    System.err.println(buffer);
    buffer.clear();

    AddElementThread runner = new AddElementThread(buffer);
    runner.start();

    buffer.remove();
    System.err.println(buffer);
  }
}

class AddElementThread extends Thread {
  private Buffer buffer;

  public AddElementThread(Buffer buffer) {

    this.buffer = buffer;
  }

  public void run() {
    try {
      sleep(2000);
    } catch (InterruptedException ie) {}

    buffer.add("3");
  }
}

Posted by 1010
반응형
import org.apache.commons.collections.Bag;
import org.apache.commons.collections.bag.HashBag;
import org.apache.commons.collections.bag.TreeBag;

import java.util.Arrays;

public class CookieBagV1 {

  private Bag cookieBag;
  private Bag sortedCookieBag;

  public static void main(String args[]) {
    CookieBagV1 app = new CookieBagV1();
    app.prepareBags();
    app.printBagContents();
    app.addRandomCookies();
    app.printBagContents();
  }

  private void printBagContents() {
    System.err.println("Cookie Bag Contents: " + cookieBag);
    System.err.println("Sorted Cookie Bag Contents: " + sortedCookieBag);
  }

  private void addRandomCookies() {
    int count = (int)(Math.random() * 10);
    int pick  = (int)(Math.random() * 10);
    pick = pick > 6 ? 6 : pick;
    if (count > 5) cookieBag.add(cookieJar[pick], count);
    else sortedCookieBag.add(cookieJar[pick], count);
  }

  private void prepareBags() {
    prepareCookieBag();
    prepareSortedCookieBag();
  }

  private void prepareCookieBag() {
    cookieBag = new HashBag(Arrays.asList(cookieJar));
  }

  private void prepareSortedCookieBag() {
    sortedCookieBag = new TreeBag(Arrays.asList(cookieJar));
  }

  private String[] cookieJar =
    {"Bar", "Drop", "Brownies", "Cut Out", "Molded", "Sliced", "No Bake"};

}
Posted by 1010
반응형
import org.apache.commons.beanutils.BeanUtils;

import java.util.Map;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.GregorianCalendar;

public class BeanUtilsExampleV1 {
  public static void main(String args[]) throws Exception {
    BeanUtilsExampleV1 diff = new BeanUtilsExampleV1();
    Movie movieBean = diff.prepareData();

    System.err.println("Movie Title: " +
      BeanUtils.getProperty(movieBean, "title"));
    System.err.println("Movie Year: " +
      BeanUtils.getProperty(movieBean, "dateOfRelease"));
    System.err.println("Movie Director: " +
      BeanUtils.getProperty(movieBean, "director.name"));
    System.err.println("Movie Director Home Contact: " +
      BeanUtils.getProperty(movieBean, "director.contactNumber(Home)"));
    System.err.println("Movie Genre (Thriller): " +
      BeanUtils.getProperty(movieBean, "genre(THR)"));
    System.err.println("Movie Actor 1 name: " +
      BeanUtils.getProperty(movieBean, "actors[0].name"));
    System.err.println("Movie Actor 1 worth: " +
      BeanUtils.getProperty(movieBean, "actors[0].worth"));
    System.err.println("Movie Actor 1 other movie 1: " +
      BeanUtils.getProperty(movieBean, "actors[0].movieCredits[0].title"));

  }

  private Movie prepareData() {
    Movie movie = new Movie();
    movie.setTitle("The Italian Job");
    movie.setDateOfRelease(new GregorianCalendar(1969, 0, 1).getTime());

    // sets the genre
    Map genre_map = new HashMap();
    genre_map.put("THR", "Thriller");
    genre_map.put("ACT", "Action");

    movie.setGenre(genre_map);

    // creates the Director
    Person director = new Person();
    director.setName("Peter Collinson");
    director.setGender(1);
    Map director_contacts = new HashMap();
    director_contacts.put("Home", "99922233");
    director_contacts.put("Mobile", "0343343433");
    director.setContactNumber(director_contacts);

    movie.setDirector(director);

    // create the actors
    Actor actor1 = new Actor();
    actor1.setName("Michael Caine");
    actor1.setGender(1);
    actor1.setWorth(10000000);
    List actor1_movies = new ArrayList();

    Movie movie2 = new Movie();
    movie2.setTitle("The Fourth Protocol");

    Movie movie3 = new Movie();
    movie3.setTitle("Shiner");

    actor1_movies.add(movie2);
    actor1_movies.add(movie3);

    actor1.setMovieCredits(actor1_movies);

    Actor actor2 = new Actor();
    actor2.setName("Margaret Blye");
    actor2.setGender(2);
    actor2.setWorth(20000000);

    List actors = new ArrayList();
    actors.add(actor1);
    actors.add(actor2);

    movie.setActors(actors);

    return movie;
  }
}

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



import java.util.List;

public class Actor extends Person {
  public Actor() {
  }

  public List getMovieCredits() { return this.movieCredits; }
  public void setMovieCredits(List movieCredits) {
    this.movieCredits = movieCredits;
  }

  public long getWorth() { return this.worth; }
  public void setWorth(long worth) { this.worth = worth; }

  private List movieCredits;
  private long worth;
}

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



import java.util.Map;
import java.util.List;
import java.util.Date;

public class Movie {
  public Movie() {
  }

  public Date getDateOfRelease() { return this.dateOfRelease; }
  public void setDateOfRelease(Date dateOfRelease) {
    this.dateOfRelease = dateOfRelease;
  }

  public String getTitle() { return this.title; }
  public void setTitle(String title) {this.title = title; }

  public Person getDirector() { return this.director; }
  public void setDirector(Person director) { this.director = director; }

  public List getActors() { return this.actors; }
  public void setActors(List actors) { this.actors= actors; }

  public String[] getKeywords() { return this.keywords; }
  public void setKeyWords(String[] keywords) { this.keywords = keywords; }

  public Map getGenre() { return this.genre; }
  public void setGenre(Map genre) { this.genre = genre; }

  private Date dateOfRelease;
  private String title;
  private Person director;

  private List actors;
  private String[] keywords;

  private Map genre;
}

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



import java.util.Map;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

public class Person {
  public Person() {
  }

  public String getName() {
    return this.name == null ? "NoName" : this.name; }
  public void setName(String name) { this.name = name; }

  public int getGender() { return this.gender; }
  public void setGender(int gender) {  // 0 - Indeterminate, 1 - Male, 2 - Female
    this.gender = (gender > 2 || gender < 0) ? 0 : gender; }

  public Map getContactNumber() { return this.contactNumber; }
  public void setContactNumber(Map contactNumber) {
    this.contactNumber = contactNumber;
  }

  /**public boolean equals(Object o) {
    if(o == this) return true;
    if(!(o instanceof Person)) return false;
    Person otherPerson = (Person)o;
    if(otherPerson.getName().equals(this.name) &&
       otherPerson.getGender() == this.gender) return true;

    return false;
  }*/

  public boolean equals(Object o) {
    if(!(o instanceof Person)) return false;

    Person otherPerson = (Person)o;
    return new EqualsBuilder()
               .append(name, otherPerson.getName())
               .append(gender, otherPerson.getGender())
               .isEquals();
  }

  public int hashCode() {
    return new HashCodeBuilder(7, 51)
               .append(name)
               .append(gender)
               .append(contactNumber)
               .toHashCode();
  }

  public String toString() {
    return new ToStringBuilder(this)
               .append("Name", name)
               .append("Gender", gender)
               .append("Contact Details", contactNumber)
               .toString();
  }

  private String name;
  private int gender;
  private Map contactNumber;
}


Posted by 1010
반응형
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.LazyDynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.BasicDynaClass;

import java.util.Map;
import java.util.List;
import java.util.Date;
import java.util.HashMap;
import java.util.GregorianCalendar;

public class DynaBeansExampleV1 {
  public static void main(String args[]) throws Exception {
    Object movie = createMovieBean();
    System.err.println(BeanUtils.getProperty(movie, "title"));
    System.err.println(BeanUtils.getProperty(movie, "director.name"));
  }

  private static Object createMovieBean() throws Exception {

    // first create the properties
    DynaProperty properties[] = new DynaProperty[] {
      new DynaProperty("title", String.class),
      new DynaProperty("dateOfRelease", Date.class),
      new DynaProperty("keywords", String[].class),
      new DynaProperty("genre", Map.class),
      new DynaProperty("actors", List.class),
      new DynaProperty("director", DynaBean.class)
    };

    // next using the properties define the class
    DynaClass movieClass = new BasicDynaClass("movie", null, properties);

    // now, with the class, create a new instance
    DynaBean movieBean = movieClass.newInstance();

    // set its properties
    movieBean.set("title", "The Italian Job");
    movieBean.set("dateOfRelease",
      new GregorianCalendar(1969, 0, 1).getTime());
    movieBean.set("keywords", new String[] {"Italy", "Bank Robbery"});

    Map genre = new HashMap();
    genre.put("THR", "Thriller");

    movieBean.set("genre", genre);
    movieBean.set("genre", "ACT", "Action");

    DynaBean director = createPersonBean();
    director.set("name", "Peter Collinson");
    director.set("gender", new Integer(1));

    movieBean.set("director", director);

    return movieBean;
  }

  private static DynaBean createPersonBean() {
    DynaBean person = new LazyDynaBean();
    return person;
  }
}

Posted by 1010
반응형
import org.apache.commons.lang.time.DateUtils;

import java.util.Date;
import java.util.Calendar;
import java.util.Iterator;
import java.util.GregorianCalendar;

public class DateUtilsV1 {
  public static void main(String args[]) {
    GregorianCalendar calendar = new GregorianCalendar(1974, 5, 25, 6, 30, 30);
    Date date = calendar.getTime();

    System.err.println("Original Date: " + date);
    System.err.println("Rounded Date: " + DateUtils.round(date, Calendar.HOUR));
    System.err.println("Truncated Date: " +  DateUtils.truncate(date, Calendar.MONTH));

    Iterator itr = DateUtils.iterator(date, DateUtils.RANGE_WEEK_MONDAY);

    while(itr.hasNext()) {
      System.err.println(((Calendar)itr.next()).getTime());
    }
  }

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


import java.util.Map;
import java.util.List;
import java.util.Date;

public class Movie {
  public Movie() {
  }

  public Date getDateOfRelease() { return this.dateOfRelease; }
  public void setDateOfRelease(Date dateOfRelease) {
    this.dateOfRelease = dateOfRelease;
  }

  public String getTitle() { return this.title; }
  public void setTitle(String title) {this.title = title; }

  public Person getDirector() { return this.director; }
  public void setDirector(Person director) { this.director = director; }

  public List getActors() { return this.actors; }
  public void setActors(List actors) { this.actors= actors; }

  public String[] getKeywords() { return this.keywords; }
  public void setKeyWords(String[] keywords) { this.keywords = keywords; }

  public Map getGenre() { return this.genre; }
  public void setGenre(Map genre) { this.genre = genre; }

  private Date dateOfRelease;
  private String title;
  private Person director;

  private List actors;
  private String[] keywords;

  private Map genre;
}

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


import java.util.Map;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

public class Person {
  public Person() {
  }

  public String getName() {
    return this.name == null ? "NoName" : this.name; }
  public void setName(String name) { this.name = name; }

  public int getGender() { return this.gender; }
  public void setGender(int gender) {  // 0 - Indeterminate, 1 - Male, 2 - Female
    this.gender = (gender > 2 || gender < 0) ? 0 : gender; }

  public Map getContactNumber() { return this.contactNumber; }
  public void setContactNumber(Map contactNumber) {
    this.contactNumber = contactNumber;
  }

  /**public boolean equals(Object o) {
    if(o == this) return true;
    if(!(o instanceof Person)) return false;
    Person otherPerson = (Person)o;
    if(otherPerson.getName().equals(this.name) &&
       otherPerson.getGender() == this.gender) return true;

    return false;
  }*/

  public boolean equals(Object o) {
    if(!(o instanceof Person)) return false;

    Person otherPerson = (Person)o;
    return new EqualsBuilder()
               .append(name, otherPerson.getName())
               .append(gender, otherPerson.getGender())
               .isEquals();
  }

  public int hashCode() {
    return new HashCodeBuilder(7, 51)
               .append(name)
               .append(gender)
               .append(contactNumber)
               .toHashCode();
  }

  public String toString() {
    return new ToStringBuilder(this)
               .append("Name", name)
               .append("Gender", gender)
               .append("Contact Details", contactNumber)
               .toString();
  }

  private String name;
  private int gender;
  private Map contactNumber;
}
----------------------------------------------------------------------------


import java.util.List;

public class Actor extends Person {
  public Actor() {
  }

  public List getMovieCredits() { return this.movieCredits; }
  public void setMovieCredits(List movieCredits) {
    this.movieCredits = movieCredits;
  }

  public long getWorth() { return this.worth; }
  public void setWorth(long worth) { this.worth = worth; }

  private List movieCredits;
  private long worth;
}

Posted by 1010
반응형
import org.apache.commons.beanutils.BeanPredicate;
import org.apache.commons.collections.PredicateUtils;

public class BeanUtilsCollectionsV2 {
  public static void main(String args[]) {
    BeanPredicate predicate =
      new BeanPredicate("title", PredicateUtils.uniquePredicate());

    Movie movie = new Movie();
    movie.setTitle("The Italian Job");

    Movie movie1 = new Movie();
    movie1.setTitle("The Italian Job");

    System.err.println(predicate.evaluate(movie)); // evaluates true
    System.err.println(predicate.evaluate(movie1)); // evaluates false

  }
}

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


import java.util.Map;
import java.util.List;
import java.util.Date;

public class Movie {
  public Movie() {
  }

  public Date getDateOfRelease() { return this.dateOfRelease; }
  public void setDateOfRelease(Date dateOfRelease) {
    this.dateOfRelease = dateOfRelease;
  }

  public String getTitle() { return this.title; }
  public void setTitle(String title) {this.title = title; }

  public Person getDirector() { return this.director; }
  public void setDirector(Person director) { this.director = director; }

  public List getActors() { return this.actors; }
  public void setActors(List actors) { this.actors= actors; }

  public String[] getKeywords() { return this.keywords; }
  public void setKeyWords(String[] keywords) { this.keywords = keywords; }

  public Map getGenre() { return this.genre; }
  public void setGenre(Map genre) { this.genre = genre; }

  private Date dateOfRelease;
  private String title;
  private Person director;

  private List actors;
  private String[] keywords;

  private Map genre;
}

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


import java.util.Map;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

public class Person {
  public Person() {
  }

  public String getName() {
    return this.name == null ? "NoName" : this.name; }
  public void setName(String name) { this.name = name; }

  public int getGender() { return this.gender; }
  public void setGender(int gender) {  // 0 - Indeterminate, 1 - Male, 2 - Female
    this.gender = (gender > 2 || gender < 0) ? 0 : gender; }

  public Map getContactNumber() { return this.contactNumber; }
  public void setContactNumber(Map contactNumber) {
    this.contactNumber = contactNumber;
  }

  /**public boolean equals(Object o) {
    if(o == this) return true;
    if(!(o instanceof Person)) return false;
    Person otherPerson = (Person)o;
    if(otherPerson.getName().equals(this.name) &&
       otherPerson.getGender() == this.gender) return true;

    return false;
  }*/

  public boolean equals(Object o) {
    if(!(o instanceof Person)) return false;

    Person otherPerson = (Person)o;
    return new EqualsBuilder()
               .append(name, otherPerson.getName())
               .append(gender, otherPerson.getGender())
               .isEquals();
  }

  public int hashCode() {
    return new HashCodeBuilder(7, 51)
               .append(name)
               .append(gender)
               .append(contactNumber)
               .toHashCode();
  }

  public String toString() {
    return new ToStringBuilder(this)
               .append("Name", name)
               .append("Gender", gender)
               .append("Contact Details", contactNumber)
               .toString();
  }

  private String name;
  private int gender;
  private Map contactNumber;
}

Posted by 1010
반응형
import org.apache.commons.lang.ArrayUtils;

public class ArrayUtilsExampleV1 {

  public static void main(String args[]) {

    long[] longArray = new long[] {10000, 30, 99};
    String[] stringArray = new String[] {"abc", "def", "fgh"};

    long[] clonedArray = ArrayUtils.clone(longArray);
    System.err.println(
      ArrayUtils.toString((ArrayUtils.toObject(clonedArray))));

    System.err.println(ArrayUtils.indexOf(stringArray, "def"));

    ArrayUtils.reverse(stringArray);
    System.err.println(ArrayUtils.toString(stringArray));
  }
}

Posted by 1010
반응형
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.lang.reflect.Method;

import org.apache.commons.collections.Bag;
import org.apache.commons.collections.bag.HashBag;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

import org.apache.commons.lang.StringUtils;

public class TasteOfThingsV1 {

  private static Map testMap;
  private static TestBean testBean;

  public static void main(String args[]) throws Exception {
    prepareData();

    HashBag myBag = new HashBag(testMap.values());

    System.err.println("How many Boxes? " + myBag.getCount("Boxes"));
    myBag.add("Boxes", 5);
    System.err.println("How many Boxes now? " + myBag.getCount("Boxes"));

    Method method =
      testBean.getClass().getDeclaredMethod("getTestMap", new Class[0]);
    HashMap reflectionMap =
      (HashMap)method.invoke(testBean, new Object[0]);
    System.err.println("The value of the 'squ' key using reflection: " +
      reflectionMap.get("squ"));

    String squ = BeanUtils.getMappedProperty(testBean, "testMap", "squ");
    squ = StringUtils.capitalize(squ);

    PropertyUtils.setMappedProperty(testBean, "testMap", "squ", squ);

    System.err.println("The value of the 'squ' key is: " +
      BeanUtils.getMappedProperty(testBean, "testMap", "squ"));

    String box = (String)testMap.get("box");
    String caps =
      Character.toTitleCase(box.charAt(0)) +
      box.substring(1, box.length());
    System.err.println("Capitalizing boxes by Java: " + caps);
  }

  private static void prepareData() {
    testMap = new HashMap();
    testMap.put("box", "boxes");
    testMap.put("squ", "squares");
    testMap.put("rect", "rectangles");
    testMap.put("cir", "circles");

    testBean = new TestBean();
    testBean.setTestMap(testMap);
  }
}

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

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

public class TestBean {
  private Map testMap;

  public Map getTestMap() {
    return this.testMap;
  }
  public void setTestMap(Map testMap) {
    this.testMap = testMap;
  }
}
       
Posted by 1010
반응형

데이터 Validator 체크를 위해서 공통클래스를 만들곤 하는데 이미 만들어진걸

씀으로서 수고를 덜 수 있다.  그게 apache 에서 제공하는 common-validator jar 이다.

이것을 잘 활용한다면 많은 수고를 덜수 있다. 무식하게 만든다고 장땡이는 아니다.

있는걸 활용해서 시간을 업무에 집중할수 있는것도 중요하다.

http://commons.apache.org/validator/index.html

현재 1.3.1 까지 나와있는 상태이다. 그대로 활용해도 되지만

jar 내부에 제공하는 클래스들을 상속받아 좀더 구체적인 로직을 구현해서 사용하는것도

확장성 면에서 고려해볼만하다. 1.3. 에선 xml 검증 엔진도 추가되어있다.


routines 패키지 내에 보면 구체적으로 Validator 체크를 할수 있도록 클래스가 제공된다.

클래스 이름만 봐도 어떤 종류의 데이터를 체크할수 있을지 감이 올것이다.

그리고 제일 위 3개는 Abstract 라는 이름이 붙어있다. 이것은 추상클래스를 나타내며

3개를 제외한 클래스들에서 상속받아 구체적으로 구현한것이다. 그러므로 3개를 제외한

클래스들을 쓰면된다.

 

common-validator API 를 보면 http://commons.apache.org/validator/api-1.3.1/

설명에도 나와있지만 어떤 특정데이타를 다룰때 그 데이타에 해당하는 validator

체크할수 있도록 클래스가 분류 되어있는 것을 볼수있다. 


데이타에 대한 validator 가 아닌 일반적인 체크는 아래 클래스들을 사용하면되는데

보통 Genericvalidator 를 많이 사용한다.


함수예제를 몇가지 추려보면 다음과 같다.

public static boolean isBlankOrNull(String value) : blank or null 체크

public static boolean isByte(String value) : Byte 변환가능한지 여부

public static boolean isShort(String value) : Short 변환가능한지 여부

public static boolean isInRange(byte value, byte min, byte max)

범위지정한 곳에 속하는지에 대한 여부 등등이 있다.

 

함수들을 사용해서 구현한 예제이다.

private static final int ID_CHECK_ID = 2;

   public void validateId(String id){

   if (!GenericValidator.minLength(id, ID_CHECK_ID)){

       throw new IllegalArgumentException("ID 길이가 너무 작습니다");

   }

}


출처 : http://mainia.tistory.com/336?srchid=BR1http%3A%2F%2Fmainia.tistory.com%2F336

Posted by 1010
반응형

Validator Plugin 사용! ( 톰캣 서버를 재시작 할 필요가 없다 )


-> 간단 버젼 : 공백 유무만 체크하기

-> 필수 수정파일 : validator.xml,  ActionForm( 폼빈 ), struts-config.xml, jsp파일

-> 참고 파일 : validator-rules.xml


<<  로그인시 아이디의 공백 유무 검사하기  >>

첫 페이지 이름 : login.jsp
폼 빈 이름 : LoginForm.java
액션빈 이름 : LoginAction.java


일단 struts로 동작하도록 모든 링크 설정을 한다..
그리고 나서 공백유무의 validation 시작~~


1. Form 자바파일의 상속을 ActionForm에서 ValidatorForm 으로 변경

 (예)   public class LoginForm extends ActionForm  을

          public class LoginForm extends ValidatorForm  으로 변경


2. validator.xml 작성

 <formset>

  <form name="loginForm">               struts-config.xml에서 등록한 FormBean의 name을 연결

   <field property="id" depends="required">        property는 검사할 곳(login.jsp 파일의 아이디 입력부분의 name값)
                                                                    required는 공백유무 검사하는 명령어

    <arg key="loginForm.id" />    key는 보여질 에러 메세지이름, 이 이름대로 MessageResources.properties에 등록
   
   </field>
 
  </form>
 
 </formset>


3. jsp 파일에 html:errors 처리 위해 한줄 추가하기

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

 <html:errors />  입력하면 에러메세지 보여줌


4. 에러 메세지가 보이지 않을 때는 struts-config.xml 의 <action-mappings>안의 <action>의 validate를 true로 바꾸고 input="/돌아갈곳.jsp" 추가

(예) <action path="/login" name="loginForm" type="app.web.LoginAction" validate="true" input="/login.jsp">

 

[참고] 에러 메세지가 보여지는 방법


1. validation의 종류에 따라 validator-rules.xml 파일안의  해당 validator name의 msg 를 찾는다.


(예) validation의 종류가 required(공백체크) 라면 


------- validator-rules.xml 파일안에서 아래 msg="errors.required" 를 찾는다..

<validator name="required"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateRequired"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionMessages,
                       javax.servlet.http.HttpServletRequest"
                  msg="errors.required"/>

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


2. MessageResources.properties 파일에서 errors.required를 찾으면    errors.required={0} is required. 라고 되어있지~

요것이 보이는 메세지 이고,,, {0} 부분은 우리가 직접 셋팅 가능


3. {0} 부분은 validator.xml 내의 <arg key="loginForm.id" />  를 보면 loginForm.id라는 이름으로 등록되어있으므로

   이 이름대로 MessageResources.properties에 등록 되어 있는 것을 출력한다.

 loginForm.id 라는 이름은 사용자가 맘대로 바꿔줘도 상관없는 그냥 이름 임.. ㅋㅋㅋ


MessageResources.properties에

                                                   loginForm.id=ㅋㅋㅋㅋ 테스트다~                 

 라고 추가하면


최종 에러메세지는 


ㅋㅋㅋㅋ 테스트다~ is required.        이렇게 된다!!

출처 : http://www.underroom.com/?sort_index=regdate&order_type=asc&mid=java&page=2&document_srl=4485

Posted by 1010
반응형
출처 : http://www.apache-korea.org/ant/external.html
외부의 도구과 태스크

이 페이지는, 아파치 앤트용 외부 자료의 목록입니다: 태스크, IDE 통합 도구, 로거 입니다. 앤트에 포함되었으면 하는 것을 작성하였다면, 메일 목록 중 하나에다가 관련 정보를 보내주세요.

여기 나오는 것은, 모두 앤트 개발자들이 직접 지원하고 있는 것이 아닙니다. 무엇인가 문제가 발생할 경우에는, 연락처의 정보를 이용해 주세요.

태스크
AJC

AspectJ 는 자바의 형태를 지향하는 확장모델입니다. 이 태스크는 AspectJ 컴파일러 -- AJC 를 사용하여 소스 트리를 컴파일합니다.

적합한 버전: 앤트 1.3
URL: http://aspectj.org/dl
연락처: support@aspectj.org
라이센스: Apache Software 라이센스
Anakia

확실히, Anakia 는, JDOM, Velocity 와 앤트에 기반을 둔 XML 변환도구로, 앤트 태스크보다 더 좋습니다.

적합한 버전: 앤트 1.2 이상
URL: http://jakarta.apache.org/velocity/anakia.html
연락처: Velocity mailing lists
라이센스: Apache Software 라이센스
Anteater

Anteater 는 웹사이트와 웹서비스를 기능면에서 테스트(기능면에서 테스트하는 것이란; URL에 접속하여 올바른 반응이 돌아오는지 점검한다)하는 앤트 태스크의 집합입니다. HTTP params, response codes, XPath, regexp 과 Relax NG 표현들을 테스트할 수 있습니다. HTML 보고서(junitreport에 기반을 둔)와, 대량의 테스트 스크립트를 재빨리 처리하기 위한 계층적인 그룹 체계를 제공합니다.

적합한 버전: 앤트 1.5 이상
URL: http://aft.sourceforge.net
연락처: developer mailing list
라이센스: Apache Software 라이센스
Checkstyle

Checkstyle 은 일반적인 코딩을 덧붙여, 자바 코드를 작성하는 프로그래머를 도와주는 개발 도구입니다. 이것은, 자바 코드를 점검하는 과정을 자동화하고, 따분한(하지만 중요한) 태스크를 사람이 편하게 다룰 수 있도록 하는 것이 목적입니다.

Checkstyle 은 앤트 태스크나 커맨드 라인 유틸리티를 통해 사용할수 있습니다.

적합한 버전: 앤트 1.2부터 1.4.1까지
URL: http://checkstyle.sourceforge.net/
연락처: Oliver Burn
라이센스: 2.0 배포시에는 GNU Lesser General Public 라이센스. 이전의 배포판은 GNU General Public 라이센스입니다.
CleanImports

필요없는 import들을 제거합니다. import 부분의 체계를 다시 잡습니다. 분명치 않은 import 들은 표시를 해둡니다.

적합한 버전: 앤트 1.3
URL: http://www.euronet.nl/users/tomb/cleanImports/index.html
연락처: Tom Brus
라이센스: Apache Software 라이센스
Clover

Clover 는 앤트-기반의 코드 보호 도구입니다. 이것은, 앤트-기반의 프로젝트로 잘 사용되고 있습니다. 이것은, 메소드, 문장, 그리고 브렌치(branch)보호 분석기 를 제공하고, XML, HTML 이나 Swing GUI 를 통한 보고서 기능를 가지고 있습니다.

적합한 버전: 앤트 1.4.1 이상
URL: http://www.thecortex.net/clover
연락처: clover-support@cortexebusiness.com.au
라이센스: 상업용, 오픈 소스 프로젝트로는 무료(free) 라이센스가 가능합니다.
Configure

소스와 출력을 분리시켜, 반복되는 빌드기능(모든 패키지 등급에 대해서 앤트를 호출하고, 그 패키지에 있는 파일만 빌드하거나, 그 패키지 하위의 모든 것을 빌드합니다)을 제공합니다.

이 태스크는 임의의 하위디렉토리(CVS-디렉토리를 제외하고)에 빌드 파일을 생성합니다. 최상위에 build.xml 파일 하나만 위치시키고, 'setup' 이나 'rescan' 타겟을 호출합니다.

적합한 버전: 앤트 1.2 과 1.3
URL: http://www.dsdelft.nl/~lemval/ant/
연락처: M.J.P. van Leeuwen
라이센스: Apache Software 라이센스에서 유래된 라이센스
CVSGrab

방화벽 아래의 지역에서 cvs 로 의사소통을 할 때, 유용하게 사용할 수 있는 작은 CVS client 입니다. 이것은, 표준 http 을 통해 CVS 저장소에 접근하는 ViewCVS 웹 인터페이스를 사용하고, 그 곳에 존재하는 모든 파일을 내려받기합니다.

커맨트 라인이나 앤트 태스크로 작동합니다.

적합한 버전: 앤트 1.3 이상
URL: http://cvsgrab.sourceforge.net/
연락처: CVSGrab user mailing list
라이센스: LGPL
Doxygen task

Doxygen 문서 체계를 작동하기 위한 2가지의 앤트 태스크가 있습니다.

적합한 버전: 앤트 1.1 이상
URL: http://www.bgw.org/projects/java/ant/
연락처: Kyle R. Burton
라이센스: Apache Software Foundation 라이센스

그리고

적합한 버전: 앤트 1.5 이상
URL: http://ant-doxygen.sourceforge.net
연락처: Karthik A Kumar
라이센스: Apache Software Foundation 라이센스
GenJar

단순한 디렉토리의 내용보다 좀 더 의존관계가 있는 클래스에 기반을 둔, JAR파일을 빌드합니다.

적합한 버전: 앤트 1.4 알파 (2001/08/04 후에 빌드된) 이상
URL: http://genjar.sourceforge.net/
연락처: Jesse Stockall
라이센스: Apache Software Foundation 라이센스
Importscrubber

자바 소스 코드 파일에서 불필요한 import 문장을 제거합니다.

적합한 버전: 앤트 1.3
URL: http://importscrubber.sourceforge.net/
연락처: Tom Copeland
라이센스: LGPL
IsDirValidator

주어진 디렉토리 구조가, 태스크의 내부 요소를 거쳐 정의된 특정한 법칙에 따르는지 검사합니다.

적합한 버전: 앤트 1.4
URL: http://isvalidator.sourceforge.net/en/isDirValidator.htm
연락처: I?igo Serrano
라이센스: GNU General Public 라이센스
Jalopy

낡은 자바 소스 코드 형식을 위한 앤트 플러그-인 입니다.

적합한 버전: 앤트 1.4 (또는 이상)
URL: http://jalopy.sourceforge.net
연락처: http://jalopy.sf.net/contact.html
라이센스: 1.0 베타 6 배포시에는 BSD 라이센스. 이전의 배포판은 GNU General Public 라이센스입니다.
Javamake

자바 소스를 컴파일하고 클래스 파일의 의존관계를 관리하는 태스크입니다. 기능적으로는, 평범한 Javac 와 의존하는 태스크를 합쳐 놓은 기능과, 향상된 의존관계 체크기능을 가지고 있습니다.

적합한 버전: 앤트 1.4.1
URL: http://www.experimentalstuff.com/Technologies/JavaMake/index.html
연락처: Mikhail Dmitriev
라이센스: BSD와 유사한 라이센스
J2ME Ant Tasks

Java 2 Platform, Micro Edition (J2ME) 애플리케이션의 빌드를 도와주는 2가지의 다른 태스크 집합입니다.

이 집합은 CLDC 와 K Virtual Machine (KVM)을 지원합니다:

적합한 버전: 앤트 1.3
URL: http://www.dribin.org/dave/j2me_ant/
연락처: Dave Dribin
라이센스: Apache Software 라이센스

그리고 이 집합은 J2ME Wireless Toolkit 과 PalmOS의 MIDP에서 작동합니다:

적합한 버전: 앤트 1.3
URL: http://www.stampysoft.com/ant/
연락처: Josh Eckels
라이센스: MIT 라이센스
javarec

코볼(cobol) 복사 도서의 자바를 위한 VisualAge에게, 기록(record) 클래스들을 생성하는 앤트 태스크입니다.

적합한 버전: 앤트 1.4
URL: http://glezen.org/javarec/
연락처: Paul Glezen
라이센스: Apache Software 라이센스
Jing Task

XML 파일을 RELAX NG 와 XML Schema로 둘로 나누어 구분합니다. 앤트의 Jing task 는, 여러 파일에서 여러 RELAX NG 패턴을 효과적으로 구분하는 것을 허락하고, 다른 XML 처리법을 가지고 RELAX NG 구분자를 통합시킵니다.

적합한 버전: 앤트 1.4 이상
URL: http://www.thaiopensource.com/relaxng/jing-ant.html
라이센스: BSD와 유사한
jMetra

jMetra 는, 프로젝트의 생명주기를 측정한 코드를 수집하고, 초과시간을 측정하는 프로젝트를 분석한 JavaDoc-형식의 문서 결과를 컴파일하는 도구입니다. jMetra 는 프로젝트의 빌드 처리를 계획을 통합하는 최고의 유틸리티입니다.

이것은, 커맨트라인이나 제공된 여러 앤트 태스크를 사용하여 작동합니다.

적합한 버전: 앤트 1.4 이상
URL: http://www.jmetra.com/
연락처: R Todd Newton
라이센스: 상업용, 오픈 소스 프로젝트나 개발용으로는 무료(free) 라이센스가 가능합니다
JNI

JNI 는, Java Native Interface (JNI)를 통해 자바와 C 를 통합하는 일을 쉽게 만드는, 무료 도구모음입니다. 이것은, 자바 "proxy" 클래스로 C "peer" 클래스에 접근하고, C "proxy" 클래스로 자바 "peer" 클래스나 인터페이스에 접근하도록 양쪽 모두를 생성하는 코드생성기를 포함하고 있습니다. 이것은 또한, JNI 데이터 형을 쉽게 작동할 수 있게 하는, "helper" 클래스와 같은 단순화된 JVM 인터페이스의 코어(core) 라이브러리도 포함하고 있습니다. 코드 생성기는, GUI 프로젝트 매니저의 보조로 생성할 수 있는 XML 프로젝트 파일로 운영됩니다. 코드 생성기는 앤트나 GUI 로 작동할 수 있습니다. 인쇄가능한 많은 PDF 사용자 가이드와 많은 수의 예제를 포함하고 있습니다.

적합한 버전: 앤트 1.4
URL: http://jnipp.sf.net/
연락처: Phillip E. Trewhella
라이센스: GNU LGPL
Macker

빌드-시간 을 체계적으로 테스트하는 도구로서, 깔끔한 layering / tiering / modularity 으로 설계되었습니다. Macker 는 컴파일된 클래스 파일들에 대하여, XML 규칙(rules) 파일에서 당신의 프로젝트를 지정한 패턴-기반의 접근 규칙의 집합에 관한, 클래스들의 의존관계를 검사합니다.

적합한 버전: 앤트 1.5 이상 (1.4 는 테스트되지 않았지만 작동할 것입니다.)
URL: http://innig.net/macker/
연락처: Paul Cantrell
라이센스: GNU GPL 2.0
PMD

PMD 는 사용되지 않는 변수, 필요없는 오브젝트 생성, 등을 자바 소스에서 검사합니다.

적합한 버전: 앤트 1.4 이상
URL: http://pmd.sf.net/
연락처: Tom Copeland
라이센스: Apache Software Foundation 라이센스
Styler

styler 태스크는, 지정한 앤트 빌드 파일을, 간단하게 유용한 XSLT 변형물(transformation)의 결합으로 만듭니다. 내장 앤트 태스크 스타일 같은 것들을, styler 는 하나의 XML 파일 집합의 변형물로 만들수 있습니다. 또한 다음과 같은 것들을 할수 있습니다:

  • parallel 과 pipeline 에서, 복수의 변형물을 취급합니다.
  • 분할 / 결합 파일들을 변형물로 만들 수 있습니다.
  • XML 파일이 아닌 것들을 처리합니다, 특히 HTML (JTidy 에 기반함)
  • XSLT 변형물이 아닌 것을 적용합니다, 특히 "평범한 fragmentations"
  • 새로운 파일 포맷과 변형물 기술을 취급하는, 사용자정의 XMLReader 나 XMLFilter 를 사용합니다.
적합한 버전: 앤트 1.4
URL: http://www.langdale.com.au/styler/
연락처: Arnold deVos
라이센스: LGPL
Tidy Imports (Tim)

Tim 은 import 선언문을 자동적으로 구성해 주는, 커맨드 라인이나 앤트를 거쳐 실행될 수 있는 유틸리티입니다. Tim 은, 사용되지 않는 import를 제거하고, import 를 늘리거나 합치고, 그것들을 미리-결정된(pre-determined) 그룹에 조직화 시킬 수 있습니다.

적합한 버전: 앤트 1.3 이상
URL: http://www.chive.com/tim.htm
연락처: support@chive.com
라이센스: 상업용
TiniAnt

TiniAnt 는, TINI를 위한 애플리케이션을 빌드하는 방법을 지원하는 앤트 태스크입니다.

적합한 버전: 앤트 1.2부터 1.4.1까지
URL: http://tiniant.sourceforge.net/
연락처: Sean Kelly
라이센스: BSD와 유사한 라이센스
Venus Application Publisher's (Vamp) Ant Task Suite

웹 서버를 한 번의 클릭으로 Java Web Start 를 가동시키고, 하나의 Java Archive installer로, 내장, 다중-쓰레드, 가벼운 웹서버에 대한 내용으로 서버를 업데이트 시키는 기능의, Web Archive 들을 당신의 애플리케이션에 표시하고 패키지할 수 있도록 지원합니다.

적합한 버전: 앤트 1.2 과 1.3
URL: http://www.geocities.com/vamp201/ant.html
연락처: Gerald Bauer
라이센스: GNU General Public 라이센스
WOProject

WOProject 는, 플랫폼과 IDE에 독립적인 WebObjects 5.1 과 함께 작동하는 도구 모음을 제공합니다. 이것의 주요 기능은, 개발자의 생산성을 증가시키고, 전통적인 Makefile-기반의 접근법(approach)와 비교하여 복잡한 프로젝트 구조를 좀 더 유연하게 만드는 것입니다.

적합한 버전: 앤트 1.4
URL: http://objectstyle.org/woproject/
연락처: Andrus Adamchik
라이센스: Apache 라이센스
XDoclet

XDoclet 은 Javadoc Doclet 엔진을 앤트에서 사용하기 위해서, Javadoc Doclet 엔진을 확장시킵니다. 이것은, 사용자정의 Javadoc @tags 를 작성하고, 그 tags에 기반하여 소스 코드나 다른 파일들을 생성합니다(xml틱한 배치(deployment ) 설명자같은 것들). 템플릿과 적합한 태스크들은, EJB 과 웹 애플리케이션 배치 설명자를 생성하는 것을 제공합니다.

적합한 버전: 앤트 1.4
URL: http://sourceforge.net/projects/xdoclet/
라이센스: BSD 라이센스
XmlTask

XmlTask 는, XSLT를 배울 필요없이 XML 문서를 수정하는 단순한 방법을 제공합니다. 경로는 단순하게 변경하길 원하는 노드(node)를 지정한 XML 노드를 참조하고, XML 삽입이나 제거, 또는 속성 변경을 허락하는 방법을 당신에게 알려줍니다. 강조합니다만, 이것은 일반적인 XML 변경을 수행하는 지극히 단순한 방법을 제공합니다.

적합한 버전: 앤트 1.4 이상
URL: http://www.oopsconsultancy.com/software/xmltask.html
연락처: xmltask@oopsconsultancy.com
라이센스: GNU General Public 라이센스
컴파일러 계승체(Implementations)
miniRMI <rmic> implementation

miniRMI 는, java.rmi 원본 패키지를 경량으로 대체하는, 특히 애플릿에 적합한 무료 공개소스 라이브러리입니다. 앤트 1.4+ <rmic> 어댑터에 포함되어 있습니다.

적합한 버전: 앤트 1.4 이상
URL: http://dione.zcu.cz/~toman40/miniRMI/
연락처: Petr Toman
라이센스: Gnu Lesser Public 라이센스
IDE 나 에디터와의 통합
AntFarm

jEdit 에디터에 앤트를 통합시키는 플러그인입니다.

적합한 버전: 앤트 1.3 때 나옴
URL: http://plugins.jedit.org/plugins/AntFarm
연락처: jEdit developers mailinglist
라이센스: Apache Software 라이센스
AntMan

JDeveloper IDE와 앤트를 통합시키는 AddIn 입니다.

적합한 버전: 앤트 1.4.1
URL: http://www.erudra.com/antman/index.html
연락처: Ashok Sridhar
라이센스: GNU General Public 라이센스
AntRunner

JBuilder IDE에 앤트를 통합시키는 공개도구입니다.

적합한 버전: 앤트 1.2 과 1.3
URL: http://www.dieter-bogdoll.de/java/AntRunner/
연락처: Dieter Bogdoll
라이센스: GNU General Public 라이센스
AntWork

Jext 에디터에 앤트를 통합시키는 플러그인입니다.

적합한 버전: 앤트 1.2 과 1.3
URL: ftp://jext.sourceforge.net/pub/jext/plugins/AntWork.zip
연락처: Klaus Hartlage
라이센스: GNU General Public 라이센스
Eclipse

Eclipse 는 앤트와 통합한 세계적인 도구 플랫폼입니다.

적합한 버전: 앤트 1.3 - 1.4.1
URL: http://www.eclipse.org
연락처: news://news.eclipse.org/eclipse.tools
라이센스: Common Public 라이센스 버전 1.0
IntelliJ IDEA 2.0

Java IDE 는 리팩토링(refactoring) 지원과 앤트 통합기능을 가지고 있습니다.

적합한 버전: Ant 1.3 때 나옴
URL: http://www.intellij.com/idea/
연락처: support@intellij.com
라이센스: 상업용
JDE(E) 2.2.8

Emac 을 위한 자바 개발 환경(JDEE)은 애플리케이션을 빌드하기 위한 세가지 내장 방법 중 하나로 아파치 앤트를 지원합니다.

적합한 버전: 앤트 1.2 이상
URL: http://jde.sunsite.dk/
연락처: JDEE Mailing list.
라이센스: GNU General Public 라이센스
NetBeans / Sun ONE Studio

NetBeans 나 Sun ONE Studio (이전에는 Forte for Java) IDE 에 앤트를 통합하는 모듈입니다.

적합한 버전: Ant 1.4.1 때 나옴
URL: http://ant.netbeans.org/
연락처: nbdev@netbeans.org
라이센스: Sun Public 라이센스
HP RadPak

RadPak 는 (무료) HP Bluestone HPAS J2EE 서버에서, 자바 웹과 EJB애플리케이션을 WAR나 EAR파일들처럼 패키지화하고 배치시키는 것을 주목적으로하는 GUI 도구입니다. 이 도구는 앤트 기반으로, 보통 앤트의 개발, 태스크 속성의 입력(entry)을 형식에 맞추기, 태스크가 실행되는 모습을 애니메이션화하여 보여주기 를, GUI 처럼 사용할 수 있습니다.

적합한 버전: 앤트 1.3 과 docs 때 나옴; 앤트가 설치된 ANT_HOME 아래에 부가적인 태스크들의 jar를 추가하세요.
URL: http://www.bluestone.com/products/hp-as/
라이센스: 특허가 있음, 그러나 내려받기는 무료
WebSphere Studio Application Developer

WSAD 는, Eclipse 도구 플랫폼상에서 빌드하는 능력으로 앤트와 통합하는 기능입니다.

적합한 버전: 앤트 1.4.1 때 나옴
기사: Ant Integration Part1
라이센스:
Posted by 1010
반응형

Maven 소개

메이븐은 아파치 소프트웨어 재단의 최상급 오픈소스 프로젝트이며 원래는 자카르타 터빈(Jakarta Turbine) 프로젝트의 복잡한 빌드 과정을 관리하기 위해 만들어졌다.  현재 버전 2까지 나오면서 메이븐은 단일 팀 프로젝트에 최적화된 빌드 도구에서 대부분의 소프트웨어 개발 시나리오에 꼭 필요한 범용 빌드 관리 시스템까지 다양하게 사용하고 있다.


사용자 삽입 이미지http://whitebear.tistory.com/60

Maven 설치와 사용

javacan 블로그의 휼륭한 글

Maven 기초 사용법

maven 기반 archetype 생성 예제

maven local repository 변경

 

Maven Life-cycle

Maven은 빌드과정을 Phases와 Goal로 life-cyle을 구성한다.

Phases

compile -> test -> package -> install -> deploy

Phases & goal

maven은 플러그인 기반으로 동작하는데 phase에 해당하는 연결된 플러그인의 goal이 실행된다.


process-resources / resources:resources

compile    compiler:compile

process-classes

process-test-resources / resources:testResources

test-compile / compiler:testCompile

test / surefire:test

prepare-package

package / jar:jar



Maven의 의존관계

Maven 의존 관계 라이브러리 이용

 

Eclipse 에서 Maven 활용

 

Maven 주요 goals

apache maven eclipse plugin

Maven Jetty Plugin

 

 

Maven profiling

개발 환경마다 설정 정보를 다르게 가져가야 할 경우에, 각각의 개발환경에 적합한 설정 정보를 관리하는 기능으로 profile을 이용한다.

실행 프로파일 설정
  1.           <settings>
    ...
    <activeProfiles>
    <activeProfile>profile-1</activeProfile>
    </activeProfiles>
    ...
    </settings>



 <profiles>
  <profile>
   <id>jboss4.2.2.GA</id>
   <activation>
    <activeByDefault>true</activeByDefault>
   </activation>
   <properties>
    <deployPath>
     c:/dev/java/jboss-4.2.2.GA/server/default/deploy/${artifactId}.war/
    </deployPath>
   </properties>
  </profile>
  <profile>
   <id>tomcat5x</id>
   <properties>
    <deployPath>c:/dev/java/apache-tomcat-5.5.25/webapps/${artifactId}</deployPath>
   </properties>
  </profile>
 </profiles>

          <project>
...
<profiles>
<profile>
<id>appserverConfig-dev</id>
<activation>
<property>
<name>env</name>
<value>dev</value>
</property>
</activation>
<properties>
<appserver.home>/path/to/dev/appserver</appserver.home>
</properties>
</profile>

<profile>
<id>appserverConfig-dev-2</id>
<activation>
<property>
<name>env</name>
<value>dev-2</value>
</property>
</activation>
<properties>
<appserver.home>/path/to/another/dev/appserver2</appserver.home>
</properties>
</profile>
</profiles>
..
</project>
프로파일 실행
          mvn groupId:artifactId:goal -P profile-1,profile-2
          
프로파일 중지
          mvn groupId:artifactId:goal -P !profile-1,!profile-2
          
실행환경 저장
          실행환경별로 <resouce/>에서 디렉토리별로 클래스, 설정화일을 관리
          
           
          
  1.           mvn -Denv=dev integration-test
    <resource><directory>src/main/resources-${environment}</directory></resource>

http://maven.apache.org/guides/introduction/introduction-to-profiles.html

참고자료
Posted by 1010
반응형
이클립스에서 Log4E 플러그인 사용하기
Posted by 1010
반응형

Commons Lang


 

I. Commons Lang란?


Commons-Lang은 java.lang에 있는 클래스처럼 기능적으로 필요한 유틸리티들을 모아놓은 클래스들의 집합입니다. 아마도 여러분들 역시 직접 유틸리티 클래스들을 만들어 사용하고 있을겁니다. 즉 코딩을 하다보면 이렇거 있었으면 좋겠다 싶은것들이 Commons Lang에 다 있다고 생각하시면 됩니다 ^^



II. 다운로드 및 설치


http://jakarta.apache.org/site/downloads/downloads_commons-lang.cgi 에서 다운받자!

설치는 각 어플리케이션의 클래스 패스에 복사합니다.



III. org.apache.commons.lang.SystemUtils


SystemUtils는 java.lang.System 클래스 처럼 시스템에 관련된 기능을 쉽게 이용 할 수 있도록 하는 시스템 관련 유틸리티입니다.

시스템 변수들을 가져올 때 매번 System.getProperty("JAVA_HOME")를 하기보다 SystemUtils.JAVA_HOME하면 간단하기 때문에 SystemUtils가 쓸만합니다.

SystemUtils의 변수들은 모두 static 변수이기 때문에 SystemUtils 객체생성 없이 SystemUtils.XXX로 바로 사용할 수 있습니다.


예) System.out.println("SystemUtils.FILE_ENCODING : "+SystemUtils.FILE_ENCODING);


반환값 변수명 설명
String SystemUtils.FILE_ENCODING System.getProperty("file.encoding")
String SystemUtils.FILE_SEPARATOR System.getProperty("file.separator")
String SystemUtils.JAVA_CLASS_PATH System.getProperty("java.class.path")
String SystemUtils.JAVA_CLASS_VERSION System.getProperty("java.class.version")
String SystemUtils.JAVA_COMPILER System.getProperty("java.compiler")
String SystemUtils.JAVA_EXT_DIRS System.getProperty("java.ext.dirs")
String SystemUtils.JAVA_HOME System.getProperty("java.home")
String SystemUtils.JAVA_IO_TMPDIR System.getProperty("java.io.tmpdir")
String SystemUtils.JAVA_LIBRARY_PATH System.getProperty("java.library.path")
String SystemUtils.JAVA_RUNTIME_NAME System.getProperty("java.runtime.name")
String SystemUtils.JAVA_RUNTIME_VERSION System.getProperty("java.runtime.version")
String SystemUtils.JAVA_SPECIFICATION_NAME System.getProperty("java.specification.name")
String SystemUtils.JAVA_SPECIFICATION_VENDOR System.getProperty("java.specification.vendor")
String SystemUtils.JAVA_SPECIFICATION_VERSION System.getProperty("java.specification.version")
String SystemUtils.JAVA_VENDOR System.getProperty("java.vendor")
String SystemUtils.JAVA_VENDOR_URL System.getProperty("java.vendor.url")
String SystemUtils.JAVA_VERSION System.getProperty("java.version")
String SystemUtils.JAVA_VM_INFO System.getProperty("java.vm.info")
String SystemUtils.JAVA_VM_NAME System.getProperty("java.vm.name")
String SystemUtils.JAVA_VM_SPECIFICATION_NAME System.getProperty("java.vm.specification.name")
String SystemUtils.JAVA_VM_SPECIFICATION_VENDOR System.getProperty("java.vm.specification.vendor")
String SystemUtils.JAVA_VM_SPECIFICATION_VERSION System.getProperty("java.vm.specification.version")
String SystemUtils.JAVA_VM_VENDOR System.getProperty("java.vm.vendor")
String SystemUtils.JAVA_VM_VERSION System.getProperty("java.vm.version")
String SystemUtils.LINE_SEPARATOR System.getProperty("line.separator")
String SystemUtils.OS_ARCH System.getProperty("os.arch")
String SystemUtils.OS_NAME System.getProperty("os.name")
String SystemUtils.OS_VERSION System.getProperty("os.version")
String SystemUtils.PATH_SEPARATOR System.getProperty("path.separator")
String SystemUtils.USER_COUNTRY System.getProperty("user.country") != null ? System.getProperty("user.country") : System.getProperty("user.region")
String SystemUtils.USER_DIR System.getProperty("user.dir")
String SystemUtils.USER_HOME System.getProperty("user.home")
String SystemUtils.USER_LANGUAGE System.getProperty("user.language")
String SystemUtils.USER_NAME System.getProperty("user.name")
float JAVA_VERSION_FLOAT 자바 버젼을 float형으로
int JAVA_VERSION_INT 자바 버젼을 int형으로
boolean IS_JAVA_1_1 자바버젼이 1.1인가?
boolean IS_JAVA_1_2 자바버젼이 1.2인가?
boolean IS_JAVA_1_3 자바버젼이 1.3인가?
boolean IS_JAVA_1_4 자바버젼이 1.4인가?
boolean IS_JAVA_1_5 자바버젼이 1.5인가?
boolean IS_OS_AIX 운영체제가 AIX인가?
boolean IS_OS_HP_UX 운영체제가 HP Unix인가?
boolean IS_OS_IRIX 운영체제가 IRIX인가?
boolean IS_OS_LINUX 운영체제가 LINUX인가?
boolean IS_OS_MAC 운영체제가 MAC인가?
boolean IS_OS_MAC_OSX 운영체제가 MAC OSX인가?
boolean IS_OS_OS2 운영체제가 OS2인가?
boolean IS_OS_SOLARIS 운영체제가 SOLARIS인가?
boolean IS_OS_SUN_OS 운영체제가 SUN인가?
boolean IS_OS_WINDOWS 운영체제가 WINDOW인가?
boolean IS_OS_WINDOWS_2000 운영체제가 WINDOW 2k인가?
boolean IS_OS_WINDOWS_95 운영체제가 WINDOW 95인가?
boolean IS_OS_WINDOWS_98 운영체제가 WINDOW 98인가?
boolean IS_OS_WINDOWS_ME 운영체제가 WINDOW ME인가?
boolean IS_OS_WINDOWS_NT 운영체제가 WINDOW NT인가?
boolean IS_OS_WINDOWS_XP 운영체제가 WINDOW XP인가?

이를 바탕으로 제 컴(win2k)에서 출력한 결과입니다.


변수명 출력결과
SystemUtils.FILE_ENCODING MS949
SystemUtils.FILE_SEPARATOR \
SystemUtils.JAVA_CLASS_PATH C:\Tomcat 4.1\bin\bootstrap.jar
SystemUtils.JAVA_CLASS_VERSION 48.0
SystemUtils.JAVA_COMPILER null
SystemUtils.JAVA_EXT_DIRS C:\j2sdk1.4.2_06\jre\lib\ext
SystemUtils.JAVA_HOME C:\j2sdk1.4.2_06\jre
SystemUtils.JAVA_IO_TMPDIR C:\DOCUME~1\kitty1\LOCALS~1\Temp\
SystemUtils.JAVA_LIBRARY_PATH C:\j2sdk1.4.2_06\bin;.;C:\WINNT\system32;C:\WINNT;
SystemUtils.JAVA_RUNTIME_NAME Java(TM) 2 Runtime Environment, Standard Edition
SystemUtils.JAVA_RUNTIME_VERSION 1.4.2_06-b03
SystemUtils.JAVA_SPECIFICATION_NAME Java Platform API Specification
SystemUtils.JAVA_SPECIFICATION_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_SPECIFICATION_VERSION 1.4
SystemUtils.JAVA_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VENDOR_URL http://java.sun.com/
SystemUtils.JAVA_VERSION 1.4.2_06
SystemUtils.JAVA_VM_INFO mixed mode
SystemUtils.JAVA_VM_NAME Java HotSpot(TM) Client VM
SystemUtils.JAVA_VM_SPECIFICATION_NAME Java Virtual Machine Specification
SystemUtils.JAVA_VM_SPECIFICATION_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VM_SPECIFICATION_VERSION 1.0
SystemUtils.JAVA_VM_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VM_VERSION 1.4.2_06-b03
SystemUtils.LINE_SEPARATOR  
SystemUtils.OS_ARCH x86
SystemUtils.OS_NAME Windows 2000
SystemUtils.OS_VERSION 5.0
SystemUtils.PATH_SEPARATOR ;
SystemUtils.USER_COUNTRY KR
SystemUtils.USER_DIR C:\Tomcat 4.1
SystemUtils.USER_HOME C:\Documents and Settings\kitty1
SystemUtils.USER_LANGUAGE ko
SystemUtils.USER_NAME kitty1
JAVA_VERSION_FLOAT 1.42
JAVA_VERSION_INT 142
IS_JAVA_1_1 false
IS_JAVA_1_2 false
IS_JAVA_1_3 false
IS_JAVA_1_4 true
IS_JAVA_1_5 false
IS_OS_AIX false
IS_OS_HP_UX false
IS_OS_IRIX false
IS_OS_LINUX false
IS_OS_MAC false
IS_OS_MAC_OSX false
IS_OS_OS2 false
IS_OS_SOLARIS false
IS_OS_SUN_OS false
IS_OS_WINDOWS true
IS_OS_WINDOWS_2000 true
IS_OS_WINDOWS_95 false
IS_OS_WINDOWS_98 false
IS_OS_WINDOWS_ME false
IS_OS_WINDOWS_NT false
IS_OS_WINDOWS_XP false

위의 결과값을 보니 너무 유용하게 사용되겠죠? ^^

<PRE></PRE>

Commons-Lang



IV. org.apache.commons.lang.StringUtils


java api의 String을 보면 문자열 처리하기에는 제공하는 기능들이 너무 빈약합니다.

그래서 프로그래머들은 자기 나름데로의 독특한 Utils를 만들어 사용하지요.

하지만 Commons-lang에서 제공하는 StringUtils를 사용한다면 더이상 자신만의 Utils를 사용할 필요가 없습니다.

여기 모두 ㄷ ㅏ ~ 있기 때문이지요 ^^

한가지 더 장점이 있다면 String의 null 값때문에 더이상 NullPointerException을 생각하지 않아도 된다는 것입니다.

StringUtils에서 ㄷ ㅏ ~ 알아서 해주기 때문입니다.


그럼 어떠한 기능들의 함수가 있는지 알아봅시다


반환값 함수명 파라미터
String abbreviate String str, int maxWidth
"…"를 포함한 지정한 사이즈로 문자열을 축소한다 maxWidth은 4보다 커야한다
String abbreviate String str, int offset, int maxWidth
"…"를 포함한 지정한 사이즈로 문자열을 앞/뒤로 축소한다 offset은 7보다 커야한다
String capitalise String str
deprecated
String capitaliseAllWords String str
deprecated
String capitalize String str
문자열중 첫번째 문자를 대문자로 만든다
String center String str, int size
str의 길이가 size가 되도록 문자열 좌우에 공백문자를 하나씩 추가한다 (우측부터 시작한다)
String center String str, int size, char padChar
str의 길이가 size가 되도록 문자열 좌우에 padChar를 하나씩 추가한다 (우측부터 시작한다)
String center String str, int size, String padStr
str의 길이가 size가 되도록 문자열 좌우에 padStr을 하나씩 추가한다 (우측부터 시작한다)
String chomp String str
문자열 맨 끝에있는 '\n' , '\r', '\r\n'을 제거한다
String chomp String str, String separator
문자열 맨 끝에 separator가 있으면 이를 제거한다
String chompLast String str
deprecated
String chompLast String str, String sep
deprecated
String chop String str
문자열 맨 끝에있는 문자 하나를 제거한다
String chopNewline String str
deprecated
String clean String str
deprecated
String concatenate Object array[]
deprecated
boolean contains String str, char searchChar
str이 searchChar를 포함하고 있으면 true
boolean contains String str, String searchStr
str이 searchStr을 포함하고 있으면 true
boolean containsNone String str, char invalidChars[]
str이 invalidChars의 각 문자를 모두 포함하고 있지 않으면 true
boolean containsNone String str, String invalidChars
str이 invalidChars를 모두 포함하고 있지 않으면 true
boolean containsOnly String str, char valid[]
str이 valid의 각 문자들만 포함하고 있어야 true
boolean containsOnly String str, String validChars
str이 validChars 들만을 포함하고 있어야 true
int countMatches String str, String sub
str중에 sub가 포함되어있는 갯수를 반환한다
String defaultString String str
str이 null이면 ""를 반환하고 아니면 str을 반환한다
String defaultString String str, String defaultStr
str이 null이면 defaultStr을 반환하고 아니면 str을 반환한다
String deleteSpaces String str
deprecated
String deleteWhitespace String str
문자열중 공백문자가 있으면 모두 제거한다
String difference String str1, String str2
str1과 str2를 비교하여 다른부분을 반환한다 (str2의 부분을 반환)
boolean equals String str1, String str2
str1이 null이면 str2가 null인지 유무를 반환하고 str1이 null이 아니면 str1과 str2의 equals를 반환
boolean equalsIgnoreCase String str1, String str2
equals와 동일하며 대소문자를 무시하고 비교한다
String escape String str
deprecated
String getChomp String str, String sep
deprecated
String getNestedString String str, String tag
deprecated
String getNestedString String str, String open, String close
deprecated
String getPrechomp String str, String sep
deprecated
int indexOf String str, char searchChar
str에서 첫번째 searchChar의 인덱스를 반환한다
int indexOf String str, char searchChar, int startPos
str의 startPos 인덱스부터 첫번째 searchChar의 인덱스를 반환한다
int indexOf String str, String searchStr
str에서 첫번째 searchStr의 인덱스를 반환한다
int indexOf String str, String searchStr, int startPos
str의 startPos 인덱스로부터 첫번째 searchStr의 인덱스를 반환한다
int indexOfAny String str, char searchChars[]
str에서 searchChars중 포함하고 있는 문자의 첫번째 인덱스를 반환한다
int indexOfAny String str String searchChars
str에서 searchChars중 포함하고 있는 문자열의 첫번째 인덱스를 반환한다
int indexOfAny String str, String searchStrs[]
str에서 searchStr중 포함하고 잇는 문자열의 첫번째 인덱스를 반환한다
int indexOfAnyBut String str char searchChars[]
str에서 searchChars중 포함되지 않은 문자 첫번째 인덱스를 반환한다
int indexOfAnyBut String str, String searchChars
str에서 searchChars중 포함되지 않은 문자 첫번째 인덱스를 반환한다
int indexOfDifference String str1, String str2
str1과 str2를 비교하여 문자열이 틀려지기 시작하는 인덱스를 반환한다
boolean isAlpha String str
문자열이 모두 Character.isLetter 이면 true (모두 문자이면 true)
boolean isAlphanumeric String str
문자열이 모두 Character.isLetterOrDigit 이면 true (문자거나 숫자이면 true)
boolean isAlphanumericSpace String str
문자열이 모두 Character.isLetterOrDigit 이거나 공백이면 true
boolean isAlphaSpace String str
문자열이 모두 Character.isLetter 이거나 공백문자이면 true
boolean isBlank String str
문자열이 공백문자이거나 길이가 0이거나 null인경우 true
boolean isEmpty String str
문자열이 길이가 0이거나 null인경우 true
boolean isNotBlank String str
문자열이 공백문자도 아니고 길이가 0도 아니고 null도 아니면 true
boolean isNotEmpty String str
문자열이 길이가 0이 아니고 null도 아니면 true
boolean isNumeric String str
문자열이 모두 Character.isDigit 이면 true (모두 숫자이면 true)
boolean isNumericSpace String str
문자열이 모두 Character.isDigit 이거나 공백문자이면 true
boolean isWhitespace String str
문자열이 모두 Character.isWhitespace 이면 true (모두 공백이면 true)
String join Object array[]
array에서 문자열을 읽어와 모두 연결시킨다
String join Object array[], char separator
array에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Object array[], String separator
array에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Iterator iterator, char separator
iterator에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Iterator iterator, String separator
iterator에서 문자열을 읽어와 separator를 구분자로 연결시킨다
int lastIndexOf String str, char searchChar
str에서 마지막 searchChar의 인덱스를 반환한다
int lastIndexOf String str, char searchChar, int startPos
str의 startPos 인덱스부터 마지막 searchChar의 인덱스를 반환한다
int lastIndexOf String str, String searchStr
str에서 마지막 searchStr의 인덱스를 반환한다
int lastIndexOf String str, String searchStr, int startPos
str의 startPos 인덱스부터 마지막 searchStr의 인덱스를 반환한다
int lastIndexOfAny String str, String searchStrs[]
str에서 searchStr의 문자열들중 포함하고 있는 문자열의 마지막 인덱스를 반환한다
String left String str, int len
str의 좌측에서 len 길이만큼 문자열을 구한다
String leftPad String str, int size
str의 길이가 size가 되도록 문자열 왼쪽에 ' '을 추가한다
String leftPad String str, int size, char padChar
str의 길이가 size가 되도록 문자열 왼쪽에 padChar를 추가한다
String leftPad String str, int size, String padStr
str의 길이가 size가 되도록 문자열 왼쪽에 padStr을 추가한다
String lowerCase String str
str을 소문자로 변환한다
String mid String str, int pos, int len
str의 pos 인덱스부터 len 길이만큼의 문자열을 구한다
String overlay String str, String overlay, int start, int end
str의 start부터 end까지overlay로 변환한다
String overlayString String text, String overlay, int start, int end
deprecated
String prechomp String str, String sep
deprecated
String repeat String str, int repeat
str을 repeat만큼 반복하여 반환한다
String replace String text, String repl, String width
text에서 모든 repl을 width로 변환한다
String replace String text, String repl, String width, int max
text에서 모든 repl을 width로 변환하는데 최대 max개만큼 변환한다
String replaceChars String str, char searchChar, char replaceChar
str에서 searchChar를 replaceChar로 모두 변환한다
String replaceChars String str, String searchChars, String replaceChars
str에서 searchChars를 replaceChars로 모두 변환한다
String replaceOne String text, String repl, String width
text에서 repl를 width로 변환하는데 첫번째 하나만 변환한다
String reverse String str
문자열을 앞뒤 순서를 바꾼다
String reverseDelimited String str, char separatorChar
separatorChar를 구분으로 문자열을 나눈 후 나눠진 단어들을 역순으로 바꾼다
String reverseDelimitedString String str, String separatorChars
deprecated
String right String str, int len
str의 len길이만큼 우측에서 문자열을 구한다
String rightPad String str, int size
str의 길이가 size가 되도록 문자열 오른쪽에 ' '을 추가한다
String rightPad String str, int size, char padChar
str의 길이가 size가 되도록 문자열 오른쪽에 padChar를 추가한다
String rightPad String str, int size, String padStr
str의 길이가 size가 되도록 문자열 오른쪽에 padStr을 추가한다
String[] split String str
공백문자를 구분자로 사용하여 분리한다
String[] split String str, char separatorChar
separatorChar를 구분자로 사용하여 분리한다
String[] split String str, String separatorChars
separatorChars를 구분자로 사용하여 분리한다
String[] split String str, String seperatorChars, int max
separatorChars를 구분자로 사용하여 분리한며 최대 max개 만큼한다. 배열의 크기는 max가 된다
String strip String str
문자열 좌우에 있는 공백문자를 제거한다 trim과 동일하다
String strip String str, String stripChars
문자열 좌우에 stripChars에 존재하는 문자를 제거한다
String[] stripAll String strs[]
배열에 있는 모든 문자열 좌우에 있는 공백문자를 제거한다
String[] stripAll String strs[], String stripChars
배열에 있는 모든 문자열 좌우에 stripChars에 존재하는 문자를 제거한다
String stripEnd String str, String stripChars
문자열 우측에만 strip 한다
String stripStart String str, String stripChars
문자열 좌측에만 strip 한다
String stripToEmpty String str
str이 null이면 ""를 반환하고 아니면 strip 한다
String stripToNull String str
str이 null이면 null을 반환하고 아니면 strip 한다
String substring String str, int start
str의 start 인덱스부터 문자열을 구한다
String substring String str, int start, int end
str의 start 부터 end 인덱스 까지 문자열을 구한다
String substringAfter String str, String separator
str의 처음 separator 부분부터 문자열을 구한다
String substringAfterLast String str, String separator
str의 마지막 separator부분부터 문자열을 구한다
String substringBefore String str, String separator
str의 처음 separator 부분까지 문자열을 구한다
String substringBeforeLast String str, String separator
str의 마지막 separator부분까지 문자열을 구한다
String substringBetween String str, String tag
str에서 tag 사이에 있는 문자열을 구한다
String substringBetween String str, String open, String close
str에서 open부터 close 까지 사이에 있는 문자열을 구한다
String swapCase String str
대문자는 소문자로 변환하고 소문자는 대문자로 변환한다
String trimToNull String str
str이 null이거나 length가 0이면 null을 반환하고 그렇지 않으면 좌우 공백문자를 제거한다
String trim String str
문자열 좌우 공백문자를 제거한다
String trimToEmpty String str
str이 null이면 ""를 반환하고 그렇지 않으면 좌우 공백문자를 제거한다
String uncapitalise String str
deprecated
String uncapitalize String str
문자열의 첫문자를 소문자로 변환한다
String upperCase String str
str을 대문자로 변환한다

사용법

모두 static 함수이기때문에 다음과 같이 사용하면 됩니다.

String text = StringUtils.replace(str, repl, width);


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

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

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

저자 : GoodBug (unicorn@jakartaproject.com)

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

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



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

Lang Downloads

We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be available from the mirrors.

You are currently using http://mirror.khlug.org/apache. If you encounter a problem with this mirror, please select another mirror. If all mirrors are failing, there are backup mirrors (at the end of the mirrors list) that should be available.

Other mirrors:

The KEYS link links to the code signing keys used to sign the product. The PGP link downloads the OpenPGP compatible signature from our main site. The MD5 link downloads the checksum from the main site.

For more information concerning Lang, see the Lang site.




Posted by 1010
반응형
Struts의 Configuration File인 struts-config.xml에는
다음과 같은 내용이 기술되어 있다.

<struts-config>

1. Data Sources
데이터베이스를 위한 DataSource의 설정.

2. Form Beans
request에 의하여 전달되는 정보를 저장하는 FormBean의 설정.

3. Global Exceptions
Application에서 발생하는 Exception의 설정.

4. Global Forwards
Application에서 요청에 따른 작업을 끝낸후 Forward할 path에 대한 설정.

4. Action Mappings
Model과 통신하는 BusinessLogic을 담고 있는 Action클래스의 설정.

5. Controller
스트러츠 Controller의 설정.

6. Message Resources
Message Resource의 설정.

7. Plugins
다소 생소하겠지만 스트러츠에서는 플러그인이 가능하도록 구현되어 있다. 따라서 플러그인에 대한 설정이 가능하다

</struts-config>

닫기


struts-config.xml 설정에 관해

org.apache.struts.config package는 struts 1.1에서 추가 되었으며 자바 빈을 이용하여 실행 시 XML 설정 파일의 설정 내용을 자바 빈의 인스턴스를 이용하여 담아두는 역할을 합니다.

아래는 org.apache.struts.config에 대한 클래스 다이어그램 입니다.




설정 파일(struts-config.xml)의 검증과 파싱 작업이 마무리 되면 메모리에 올라와 있는 설정 정보를 빈즈 인스턴스를 이용하여 담아 둡니다.

위 클래스 다이어그램에서 ConfigResultSet 클래스는 다른 클래스와 다른 작업을 하는데 주로 struts 설정 파일을 해석 하는데 필요한 규칙을 포함하고 있는데 그 역할은 값을 설정하기 위해 필요한 빈의 인스턴스를 생성 하는 것 입니다.


-------------------------------
Struts-Config.xml 설정
-------------------------------

설정을 위한 DTD

<!ELEMENT struts-config (data-source?, form-beans?, global-exceptions?, global-forwards?, action-mappings?, controller?, message-resources*, plug-in*)

Struts-config.xml 파일에 설정시 위에서 정의한 순서는 지켜져야 합니다.

1.        data-source 요소

데이터 소스에 대한 설정을 하는 곳이며 대부분의 벤더들이 데이터 소스 객체의 구현을 제공 하는데 자바에서는 javax.sql.DataSource 인터페이스를 제공 하며 벤더들이 제공하는 구현 부에서 이 인터페이스를 구현해 주어야 합니다.

DTD정의

<!ELEMENT data-sources (data-source*)>
<!ELEMENT data-source (set-property*)>

예)
<data-sources>
        <!-- configuration for commons BasicDataSource -->
        <data-source key="mysql" type="org.apache.commons.dbcp.BasicDataSource">
                                <set-property property="driverClassName" value="com.mysql.jdbc.Driver" />
                                <set-property property="url" value="jdbc:mysql://localhost/test" />
                <set-property property="username" value="root" />
                <set-property property="password" value="pw" />
                <set-property property="maxActive" value="10" />
                <set-property property="maxWait" value="5000" />
                <set-property property="defaultAutoCommit" value="true" />
                                <set-property property="defaultReadOnly" value="false" />
                <set-property property="maxIdle" value="10000" />
        </data-source>
        <data-source key="oracle" type="org.apache.commons.dbcp.BasicDataSource">
                <set-property property="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
                <set-property property="username" value="scott" />
                <set-property property="password" value="tiger" />
                <set-property property="url" value="jdbc:oracle:thin:@localhost:1521:WINK" />
                <set-property property="maxActive" value="50" />
                <set-property property="maxIdle" value="10000" />
                <set-property property="maxWait" value="100" />
                <set-property property="defaultAutoCommit" value="true" />
        </data-source>
</data-sources>

속성으로 다음과 같은 것들이 있습니다.

key : 데이터 소스가 저장 될 서블릿 컨텍스트 속성, 선택적 이며 기본값은 Action.DATA_SOURCE_KEY 입니다.

type : 데이터 소스 구현체 클래스의 전체 클래스 이름, 반드시 javax.sql.DataSource 클래스를 구현해야 합니다.

2.        form-bean 요소

form-bean 요소는 여러 개의 ActionForm 클래스를 설정 할 수 있게 합니다. Form-beans 요소에서 form-bean 요소를 사용할 수도 있고 안 할 수도 있습니다. 각각의 form-bean 요소 역시 자식 요소를 포함 합니다.

DTD 정의

<!ELEMENT form-bean (icon?, display_name?, description?, set-property*, form-property*)>

가질 수 있는 속성은 다음과 같습니다…

className : 기본값은 FormBeanConfig이며 이 클래스를 사용하지 않는다면 지정한 클래스는 반드시 FormBeanConfig를 상속 해야 합니다.

dynamic : type속성 값이 org.apache.struts.DynaActionForm 클래스나 그 하위클래스라면 이 속성값을 true라고 설정하며 그렇지 않은 경우엔 false이다. 기본값은 false

name : 이 빈의 유일한 식별자

type : ActionForm 클래스를 상속하는 자바 클래스의 전체 이름, 실제 폼빈 역할을 하는 클래스의 이름을 패키지 명까지 포함해서 기술 합니다. 만약 이 값을 org.apache.struts.action.DynaActionForm으로 지정하면 스트럿츠가 DynaActionForm 클래스의 인스턴스를 동적으로 생성 합니다. 속성값은 반드시 지정해야 합니다.

<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>


3.        global-exceptions 요소

예외처리에 관한 설정을 하며 global-exceptions에는 exception 요소가 0개 이상 나타날 수 있습니다.

<!ELEMENT global-exceptions (exception*)>

exception 요소는 action 요소에도 정의가 가능한데 exception 요소가 global-exceptons와 action 요소에 동시에 정의되어 있다면 action에 선언된 것이 우선권을 가집니다.

exception 요소는 클라이언트의 요청을 처리하는 동안 발생하는 자바의 예외상황과 해당 예외를 처리하는 org.apache.struts.action.ExceptionHandler 클래스의 인스턴스를 매핑하는 역할을 합니다.

exception 요소의 DTD는 다음과 같습니다.

<!ELEMENT exception (icon?, display-name?, description?, set-property*)

Exception 요소의 속성은 다음과 같습니다.

className : 예외정보를 가지게 되는 설정 빈의 구현 클래스, 지정한 클래스는 ora.apache.struts.config.ExceptionConfig의 하위 클래스여야 하며 기본값은 ExceptionConfig 클래스 입니다.

handler : 예외 처리를 담당할 예외 처리 클래스의 전체 이름. 값을 지정하지 않으면 org.apache.struts.action.ExceptionHandler 클래스 입니다. 만약 다른 클래스를 지정한다면 ExceptionHandler의 하위클래스 이여야 합니다.

key : 하위 애플리케이션의 리소스 번들에 지정된 키 값이며 ActionError 클래스의 인스턴스에서 이 속성의 값을 사용 합니다.

path : 예외가 발생 시 전송해야 할 리소스의 상대 경로, 만약 값을 지정하지 않으면 action mapping에서 지정하는 input 속성의 값을 사용 합니다.

scope : ActionError 클래스의 인스턴스를 저장 할 스코프, “request” 또는 “session” 이여야 합니다. 기본값은 “request” 입니다.

type : 예외 처리 클래스의 전체 이름

<global-exceptions>
        <exception
                key = “global.error.invalidlogin”
                path = “/login/login.jsp”
                scope = “request”
                type = “login2.exception.InvalidLoginException”
        />
</global-exceptions>


4.        global-forward 요소

모든 Action은 View화면으로 포워드 되거나 리다이렉트 되는 방법으로 실행 됩니다. 뷰의 역할은 JSP가 할 수도 있고 HTML이 할 수도 있습니다. Struts 에서는 뷰 역할을 하는 자원을 직접 참조하는 대신 주로 어떤 이름을 사용 합니다.

global-forwards에서는 0개 이상의 forward요소를 가집니다.

<!ELEMENT global-forwards (forward*)>

forward 요소 역시 몇 개의 자식 요소를 가집니다. forward 요소는 논리적인 이름을 Web Application의 URI,의 상대 경로에 mapping 합니다. 이를 통해 애플리케이션은 논리적인 이름을 통해 포워드 또는 리다이렉트를 수행 합니다.  이러한 방법을 통해 뷰로부터 컨트롤러와 모델을 분리 할 수 있습니다.

<!ELEMENT forward(icon?, display-name?, description, set-property*)>

다음은 forward요소의 속성 입니다.

className : 포워드 설정 값을 포함하게 되는 자바 빈을 구현한 클래스, 기본값은 ForwardConfig 입니다. Optional 값

contextRelative : true로 지정 할 경우 path 속성에 지정된 리소스를 컨텍스트 상대 경로로 인식 합니다, 기본값은 false

name : 애플리케이션에서 이 포워드를 참조하기 위한 유일한 값, 반드시 값을 지정 해야 합니다.

path : 포워드 되거나 redirect 되어야 하는 애플리케이션의 상대 경로(contextRalative가 false인 경우) 또는 컨텍스트의 상대 경로의 URI를 지정,  반드시 “/”로 시작해야 합니다.

<global-forwards>
        <forward name="success" path="/main.jsp"  redirect=”true”/>
        <forward name="logoff" path="/logoff.do" />
        <forward name="login" path="/login.jsp" />
    </global-forwards>

5.        action-mapping 요소

action-mappings 요소는 action 요소를 0개 이상 포함할 수 있습니다.

<!ELEMENT action-mappings(action*)>

Action 요소는 사용자의 요청을 특정한 경로에 해당 하는 Action 클래스와 매핑을 하는 역할을 담당 합니다. Action 요소의 path 속성과 클라이언트로부터 들어온 요청 URI 경로를 매치시켜 특정 매핑을 선택 합니다.

Action 요소의 자식요소는 다음과 같습니다.

<!ELEMENT action (icon?, display-name?, description, set-property*, exception*, forward*)>

앞에서 설명한 exception 요소의 경우 global, action 양쪽에서 선언이 가능하다고 했습니다. 물론 action에서 정의한 exception이 global로 선언 한 것 보다는 우선권이 있습니다.


또한 forward인 경우에도 global, action 양쪽에서 선언이 가능하며 exception의 경우와 비슷합니다.

다음은 action 요소의 속성 입니다.

attribute : 이 action이 접근 가능한 요청 또는 세션 scope의 폼빈 이름, 이속성은 name 속성에 폼빈이 지정되어 있는 경우에만 사용 가능 합니다.

className : 액션과 관련된 정보를 포함하게 되는 빈을 구현한 클래스, 값을 지정하지 않으면 org.apache.struts.action.ActionMapping이 기본 클래스 입니다. 이속성은 선택적 입니다.

forward : 포워드될 서블릿이나 JSP의 상대경로, 이 속성은 선택적이며 org.apache.struts.action.ForwardAction 클래스가 동일한 역할을 하는데 서용 됩니다.

include : 클라이언트의 요청에 대해 포함되어야 할 서블릿이나 JSP의 상대경로

input : 폼빈에서 검증오류가 생기는 경우 돌아갈 곳

name : 폼빈의 이름

path : 액션의 이름, 요청을 처리할 액션의 상대 경로

parameter : action 인스턴스에 어떤 정보를 넘기기 위해 사용, 여기에 값을 지정하면 getParameter  메소드를 이용하여 값을 알아낼 수 있습니다.

prefix : 예를 들어 폼빈의 모든 속성이 “pre_”로 시작한다면 prefix 속성을 세팅해서 요청 인자가 ActionForm 속성과 일치  하도록 할 수 있습니다. Name 속성이 지정된 경우에만 사용 가능 합니다.

scope : 폼빈의 스코프, name 속성을 지정한 경우에만 사용 가능 하며 “request” or “session” 이 들어 올 수 있으며 기본값은 “session” 입니다.

suffix : 예를 들어 폼빈의 모든 속성이 “_foo” 로 끝난다면 suffix 속성을 지정하여 요청인자가 ActionForm 빈의 속성과 일치하게 합니다.

type : Action 클래스를 상속하는 즉 액션을 처리하는 클래스의 전체경로 및 이름

unknown : 이 액션을 애플리케이션에서 기본으로 설정 해야 하는지 여부를 표시 한다. true로 설정하면 이 액션은 처리할 액션이 지정되지 않은 모든 요청을 처리

validate : 폼빈의 validate() 메소드가 execute() 메소드 전에 실행 되어야 하는 지의 여부를 표시, 기본값은 true

<action-mappings>
        <!-- loginAction에 대한 정의 -->
        <action          
                path="/LoginSubmit"
                type="login2.LoginAction"
                    scope=”rewuest”
                name="loginForm"                        
                validate="true"
                input="/login.jsp"
        >        
                   <forward name=”SUCCESS” path=”/success.jsp” redirect=”true”/>
            </action>
    </action-mappings>


6.        controller 요소

struts 1.1이상에서 새로 나온 기능으로 이전 버전에서는 ActionServlet 클래스가 컨트롤러 기능을 하며 이를 상속해서 컨트롤러의 기능을 구현 했습니다. 그러나 1.1이상에서는 대부분의 컨트롤러의 기능은 RequestProcessor 클래스로 이전 했습니다.

1.1이전의 예

<servlet>
<servlet-name>action</servlet-name>
<servlet-class>myPackage.myActionServlet</servlet-class>
</servlet>


ActionServlet은 여전히 사용자의 요청을 받아 들이지만 그 요청에 대한 처리는 RequestProcessor 클래스의 인스턴스에 위임 합니다. 이렇게 함으로서 요청을 처리하는 클래스를 할당하고 기능을 수정 하는 것이 가능 해집니다.

Controller 요소는 자식요소 하나를 포함 합니다.

<!ELEMENT controller (set-property*)>

아래는 controller 요소의 속성 입니다.

bufferSize : 파일 업로드 시 사용하는 입력 버퍼의 크기, 기본값은 4096 입니다.

className : 컨트롤러 정보를 담아놓을 자바 빈을 구현한 클래스, org.apache.struts.config.ControllerConfig의 하위클래스여야 합니다. 기본값은 ControllerConfig 입니다.

contentType : 응답 경과를 보낼 때 사용하는 contentType, 이 속성은 선택적으로 사용 할 수 있으며 기본값은 “text/html” 입니다. 이 속성에 값이 있더라도 JSP 파일에 contentType을 기술 한다면 JSP 파일의 내용이 우선 합니다.

debug : 디버깅 레벨, 값이 클수록 많은 로깅 정보들이 기록되며, 기본값은 0으로 로깅 정보가 기록되지 않습니다.

locale : 사용자의 지역 정보를 세션에 저장 할지 여부, 기본값은 false

maxFileSize : 파일 업로드 시 허용되는 최대 용량

multipartClass : Multipart 요청에 대한 처리를 담당할 클래스, 기본값은 ora.apache.struts.upload 패키지의 DiskMultipartRequestHandler 입니다.

nocache : 응답에 있는 nocache 헤더에 대한 설정, true 또는 false, 기본값은 false, c가 소문자임을 주의

processClass : 사용자의 요청을 처리 할 클래스의 전체 이름

tempDir : 파일 업로드의 경우 사용할 임시 디렉토리


<controller
            contentType="text/html;charset=euc-kr"
            debug="3"            
            locale="true"
            nocache="true"
            processorClass="filter.MyFilter"/>


7.        message-resources 요소

메시지 리소스 번들과 관련된 특징을 기술 합니다.

각각의 스트럿츠 설정 파일은 메시지 리소스 번들을 하나 이상 지정 할 수 있습니다.

<!ELEMENT message-resources (set-propert*)>

필요한 속성만 살펴 보도록 합니다.

className : message-resources의 정보를 담을 자바빈을 구현한 클래스

key : 메시지 리소스 번들이 저장될 서블릿 컨텍스트 속성, 기본값은 Action.MESSAGE_KEY에 의해 지정 됩니다.

null : 정의되지 않은 메시지 키가 사용된 경우 MessageResource의 하위 클래스에서 어떻게 처리 할지를 나타내는 Boolean 값. true로 설정하면 빈 문자열을 돌려줍니다. false로 설정하면 “???global.label.missing???”와 같은 문자를 돌려 줍니다.

parameter : 예를 들어 리소스 번들의 이름이 Application.properties 라면 이 속성의 값은 Application이 됩니다. 반드시 정의해야 하는 속성 입니다. 만약 리소스 번들이 패키지화 되어 있다면 전체 경로를 지정 해야 합니다.

<message-resources parameter="resources.application"/>


8.        plug-in 요소

struts application이 구동 시에 동적인 자원을 처리 할 수 있게 해주는 기능

예를 들어 애플리케이션 구동 시 원격의 시스템에 연결 해야 된다면 직접 코딩하지 않고 플로그인 기능을 이용하여 이 기능을 구현할 수 있습니다. 플러그인을 사용하기 위해서는 org.apache.struts.PlugIn 인터페이스를 구현하는 자바 클래스를 생성 한 후 설정 파일의 plug-in 요소에 추가 하면 됩니다.

public interface PlugIn {
public void init(ActionServlet servlet, ApplicationConfig config)
throws ServletException;
public void destroy();
}


1.1이전 버전에서는 ActionServlet의 subClass를 통해 애플리케이션 구동 시 애플리케이션의 리소스를 초기화 했지만 1.1이후 버전에서는 Plug-In을 통해 이것이 가능 하게 됨

Plug-In의 경우 스트럿츠 애플리케이션이 초기화 되는 경우 ActionServlet이 Plug-In의 Init() method 를 호출 합니다.

Plug-In은 struts-config.xml 파일에 기술된 순서에 따라  호출됩니다.

<!ELEMENT plug-in (set-property*)>

<plug-in className="org.apache.struts.plugins.ModuleConfigVerifier"/>
<plug-in
className="org.apache.struts.webapp.example.memory.MemoryDatabasePlugIn">
<set-property property="pathname" value="/WEB-INF/database.xml"/>
</plug-in>

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml"/>
</plug-in>

예제

public final class MemoryDatabasePlugIn implements PlugIn {
...

        public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {
                log.info("Initializing memory database plug in from '" +        pathname + "'");
                // Remember our associated configuration and servlet
                this.config = config;
                this.servlet = servlet;

                // Construct a new database and make it available
                database = new MemoryUserDatabase();
                try {
                        String path = calculatePath();
                        if (log.isDebugEnabled()) {
                                log.debug(" Loading database from '" + path + "'");
                        }
                        database.setPathname(path);
                        database.open();
                } catch (Exception e) {
                        log.error("Opening memory database", e);
                        throw new ServletException("Cannot load database from '" +        pathname + "'", e);
                }

                // Make the initialized database available
                servlet.getServletContext().setAttribute(Constants.DATABASE_KEY,database);

                // Setup and cache other required data
                setupCache(servlet, config);
        }
}

public final class MemoryDatabasePlugIn implements PlugIn {
...

        public void destroy() {
                log.info("Finalizing memory database plug in");
                if (database != null) {
                        try {
                                database.close();
                        } catch (Exception e) {
                                log.error("Closing memory database", e);
                        }
                }

                servlet.getServletContext().removeAttribute(Constants.DATABASE_KEY);
                database = null;
                servlet = null;
                database = null;
                config = null;
        }
}
Posted by 1010
반응형

1장. 사용자 관리 프로젝트로 알아본 스트럿츠 탄생 배경

Summary : 스트럿츠 프레임워크 워크북의 1장 원고를 통하여 스트럿츠 탄생 배경에 대하여 총 5번의 강좌를 통하여 알아본다. 이번 강좌에서는 사용자 관리 프로젝트의 모델 파트를 개발한다. 이번 강좌에서 개발되는 모델은 앞으로 진행할 3번의 강좌에서 공통적으로 사용한다.

2.모델(Model) 부분의 개발

지금까지 사용자 관리를 위한 요구분석을 진행하였다. 이 절에서는 앞절의 요구분석을 바탕으로 사용자 관리 프로젝트의 모델(Model)부분을 개발하도록 하겠다. 이 절에서 개발되는 모델은 계속해서 진행될 모델1 개발방식, 모델2 개발방식, 스트럿츠 프레임워크를 이용한 개발에 공통적으로 사용될 것이다.

이 절에서 개발되는 모델이 3가지 개발방식에 모두 사용되기 때문에 모델1 개발방식에는 적합하지 않을 수도 있다. 그렇지만, 모델1 개발방식도 이 절에서 살펴보는 방식으로 모델을 개발할 경우 유지보수가 편해질 수 있으며, JSP(Java Server Pages : 이하 JSP)의 복잡도를 다소나마 줄일 수 있을 것이다.

2.1 실행환경 설정

사용자 관리 모델의 직접적인 개발에 들어가기에 앞서 기본적인 실행환경을 설정하도록 하겠다. 이 절에서는 실행환경 설정에 대한 설명은 최대한 줄이도록 하겠다. 부족한 부분은 부록과 다양한 외부 문서를 링크하는 방식을 취하도록 하겠다.

이 책에서 다룰 예제들의 기본적인 실행환경은 다음과 같다.

JDK : 1.3이상

Servlet Server : Jakarta-Tomcat 4.1.24

관계형 데이터베이스 : MySQL 4.0.x

JDBC 드라이버 : MySQL JDBC 드라이버 2.0.14

Connection Pooling :

빌드 툴 : Apache Ant 1.5.3

스트러츠 프레임워크 : Jakarta-Struts 1.1

위에서 살펴본 항목들이 이 책의 예제들을 개발하고, 테스트해보기 위한 기본적인 환경이다. 컨넥션 풀링을 사용한 이유는 최근에 진행하는 대부분의 프로젝트에 컨넥션 풀링을 사용하고 있기 때문에 실전 프로젝트에서 직접 사용이 가능하도록 하기 위해서이다. MySQL JDBC 드라이버는 최신 버전인 3.0.8을 사용하지 않고 2.0.14로 사용한 이유는 최신 버전인 3.0.8는 데이터를 읽을 때 버그가 있기 때문이다.

이 책의 예제를 실행하기 위해서는 JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT가 설치되어 있어야 한다. 나머지 패키지는 예제 소스에 포함되어 있기 때문에 신경쓰지 않아도 된다. 예제를 실행하는 환경을 위와 같이 선택한 이유는 예제 소스를 최대한 쉽게 테스트해볼 수 있도록 위함이다. 모든 툴이 설치가 간단하고 좋은 사양의 컴퓨터가 아니라도 테스트가 가능하다.

모든 예제소스는 ANT툴로 빌드하여 서버에 디플로이 시키기 때문에 예제를 실행하기 위하여 클래스패스를 추가할 필요가 없다. ANT툴에 대한 자세한 사용방법은 부록2를 참조하기 바란다.

JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT중 설치하는 방법을 모르는 것이 있다면 이 책의 부록 1을 참고하여 예제를 실행해볼 수 있는 기본 환경을 세팅하기 바란다.

2.2 사용자 관리 테이블과 생성 쿼리

사용자 관리 테이블은 1절의 요구분석에서
정의했듯이 사용자 아이디, 비밀번호, 사용자 이름, 사용자 이메일주소만을 포함하도록 생성하였다. 가능한 작은 규모의 프로젝트를 만들기 위한 의도였다.

# DROP TABLE USERINFO;

CREATE TABLE USERINFO ( 	
userId          varchar(12)		NOT NULL, 	
password		varchar(12)		NOT NULL,	
name			varchar(20)		NOT NULL,	
email			varchar(50),  		
PRIMARY KEY               (userId),  		
INDEX USERINFO_userId_idx  (userId));

INSERT INTO USERINFO VALUES('admin', 'admin', '관리자', 'admin@javajigi.net');

2.3 데이터베이스 설정 및 테이블 생성
사용자 관리 모델을 만들기 위하여 먼저 데이터베이스 정보를 설정한 다음 테이블을 생성해야 한다. 데이터베이스 설정은 컨넥션 풀링에서 사용하고 있는 /chapter1/src/dbpool.properties의 값을 자신의 데이터베이스 환경에 맞도록 설정하면 된다. dbpool.properties의 내용은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.

사용자 관리용 테이블 생성은 ANT를 이용하였다. ANT를 이용한 이유는 독자들의 테스트를 쉽고 빠르게 하기 위해서이다. 많은 책들의 경우 너무도 많은 환경 설정과 클래스패스를 추가해주어야 하기 때문에 독자들이 테스트하는데 많은 시간을 소비해왔다. 따라서 필자는 테스트하는데 가능한 적은 시간을 소비하고, 책에서 다루고있는 내용에 독자들이 많은 시간을 투자하여 공부할 수 있게하기 위함이다. 그 일환으로 테이블의 생성 또한 ANT를 이용하였다.

ANT를 이용하여 테이블을 생성하는 자세한 방법은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.

2.4 모델 개발

많은 개발자들이 모델이라는 용어를 사용한다. 하지만, 대부분의 개발자들이 모델파트에서 담당해야하는 부분이 무엇인지에 대하여 모르고 있다. 필자 또한 MVC 아키텍처(모델, 뷰, 컨트롤러 계층으로 나뉘어 개발. 이하 MVC)에서 모델파트가 담당해야하는 부분이 무엇이라고 정확하게 경계선을 그어서 말하기는 힘들다. 각 계층의 경계선을 명확히 했다고 하지만 경우에 따라 경계가 무너지는 경우도 종종 본다.

하지만 간략하게나마 모델이 담당해야할 역할에 대해서 규명할 필요는 있다고 생각한다. 따라서 필자는 모델파트에서 담당해야하는 가장 큰 역할을 다음 두가지로 규명하겠다.

첫째, 애플리케이션의 비즈니스 로직적인 부분을 담당한다.

둘째,
애플리케이션이 데이터베이스, 레거시 시스템, 파일 시스템과의 데이터 조작등의 작업을 담당한다.

이와 같은 기본적인 개념하에 이 책에서 진행하게될 모델 예제들을 위한 기본적인 클래스 설계는 다음과 같이 진행하도록 하겠다.

첫째, 객체의 정보를 포함하고 있는 도메인 클래스이다. 도메인 클래스의 역할은 사용자가 입력한 정보를 모델에 전달하는 역할과 데이터베이스의 정보를 JSP에 전달하는 매개자 역할을 한다. 이 개념은 Value Object 패턴과 Data Transfer Object 패턴이라는 이름으로 많이 알려져 있다. 각각의 기능으로 나누어 개발할 수도 있지만 대부분의 Value Object와 Data Transfer Object는 같은 속성과 setter, getter를 가지고 있기 때문에 필자는 하나의 클래스로 이 두가지 기능을 모두 포함하도록 구현하기를 좋아한다.

참고문서

Value Object 패턴 :

  • "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 486Page
  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 261Page

Data Transfer Object패턴 :

  • "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 401Page

둘째, 데이터베이스나 기존의 레거시 시스템과의 데이터작업을 전담하게될 데이터 접근 객체(Data Access Object:DAO)클래스이다. 모델 개발시 중요한 역할중의 하나는 데이터베이스나 기존의 레거시 시스템과의 데이터 교환작업이다. 비즈니스 로직과 별개로 이 같은 작업을 전담하는
클래스가 필요하게 되었다. 따라서 앞으로 진행되는 모든 프로젝트에서 데이터 접근 로직은 전담하는 이 같은 DAO클래스가 존재할 것이다. DAO클래스라고 해서 색다른 것이 아니다. 단지 독자들이 JSP와 여러 클래스에서 데이터베이스와의 데이터교환을 하던 부분을 하나의 클래스에서 전담하는 역할을 한다고 생각하면 된다.

참고문서

Data Acces Object 패턴 :

  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 390Page

셋째, 비즈니스 로직을 전담하게될 비즈니스 객체(Business Object:BO)클래스이다. 단, 비즈니스 로직이 간단할 경우에 이 클래스는 생략할 수 있다. 모델을 개발할 때 또 하나의 중요한 부분은 비즈니스 로직을 담당하는 부분이다. 프로젝트의 성격에 따라 비즈니스 로직이 복잡한 경우에는 비즈니스 로직을 전담할 비즈니스 객체가 필요한 경우가 발생할 수 있다. 하지만, 간단한 경우에는 네번째로 살펴볼 Manager클래스에서 비즈니스 로직을 담당할 수도 있다.

넷째, 모델1 개발방식이라면 JSP, 모델2 개발 방식(MVC)이라면 컨트롤러(Controller)가 직접적으로 사용하게될 API를 전담하는 Manager클래스이다. Manager클래스를 만들 경우 비즈니스 로직이 간단할 경우에는 Manager클래스에서 비즈니스 로직을 수행할 수도 있으며, 모델을 접근할 때 Manager클래스만을 통해 접근하기 때문에 API를 사용하기 편하다는 장점이 있다. 사용자가 접근해야 하는 클래스가 여러 클래스에 분산되어 있다면, API사용에 익숙하지 않은 개발자들은 모델을 사용하는데 어려움을 느낄 수 있다. Manager클래스를 두는 방식을 Session Facade 패턴이라고 일컫는다.

참고문서

Session Facade 패턴 :

  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 291Page

지금까지 살펴본 4개 클래스의 관계를 살펴보면 [그림 1-8]과 같다.

모델 API를 사용하기 위하여 Manager클래스만을 접근하는 것을 볼 수 있다. Manager클래스는 도메인 클래스에 담긴 데이터를 비지니스 객체와 데이터 접근 객체에 전달하는 역할을 하며, 작업후 결과 반환되는 도메인 클래스를 JSP, Servlet, Action에 전달하는 역할을 한다.

사용자 관리는 1절의 요구사항에서도 볼 수 있듯이 복잡한 비즈니스 로직을 포함하고 있지 않다. 따라서 비즈니스 객체를 따로 두지않고 Manager클래스에서 비즈니스 로직을 수행하도록 구현하였다. 사용자 관리 모델파트의 클래스 다이어그램은 다음과 같다.[그림1-9]

먼저 사용자의 정보를 담고 있는 도메인 클래스를 살펴보자. 사용자 관리의 도메인 클래스는 USERINFO테이블의 각 칼럼과 일치하는 속성들을 가지고 있으며, 각각의 속성에 대한 setter와 getter 메소드를 가진다.

package net.javajigi.user;

/** 
 * 사용자 관리를 위하여 필요한 도메인 클래스. 
 * USERINFO 테이블의 각 칼럼에 해당하는 setter와 getter를 가진다.  
 */
public class User {
	private String userId = null;
	private String password = null;
	private String name = null;
	private String email = null;
	public String getEmail() {
		return email;
	}
	public String getName() {
		return name;
	}
	public String getPassword() {
		return password;
	}
	public String getUserId() {
		return userId;
	}
	public void setEmail(String string) {
		email = string;
	}
	public void setName(String string) {
		name = string;
	}
	public void setPassword(String string) {
		password = string;
	}
	public void setUserId(String string) {
		userId = string;
	} 
	
	/**	 
	 * 비밀번호가 일치하는지 여부를 결정하는 메소드.	
	 */
	public boolean isMatchPassword(String inputPassword) {
		if (getPassword().equals(inputPassword)) {
			return true;
		} else {
			return false;
		}
/* This is the better.
                return getPassword().equals(inputPassword);
*/
	}
}

[예제 1-1]의 User 도메인 클래스에도 비밀번호가 일치하는지의 여부를 체크하는 간단한 메소드를 가진다.

UserDAO는 사용자 관리 테이블인 USERINFO와의 데이터 작업을 전담하는 클래스이다.

package net.javajigi.user;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import net.javajigi.db.ConnectionManager;

/**
 * 사용자 관리에서 데이터베이스와의 작업을 전담하는 클래스.
 * UserInfo 테이블에 사용자를 추가, 수정, 삭제, 검색등의 작업을 한다.  
 */
public class UserDAO {

	/**	
	 * 사용자 관리 테이블에 새로운 사용자 생성.	 
	 */
	public int create(User user) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			StringBuffer insertQuery = new StringBuffer();
			insertQuery.append("INSERT INTO USERINFO VALUES ");
			insertQuery.append("(?, ?, ?, ?)");
			con = ConnectionManager.getConnection();
			pstmt = con.prepareStatement(insertQuery.toString());
			pstmt.setString(1, user.getUserId());
			pstmt.setString(2, user.getPassword());
			pstmt.setString(3, user.getName());
			pstmt.setString(4, user.getEmail());
			int result = pstmt.executeUpdate();
			pstmt.close();
			con.close();
			return result;
		} finally {
			if (pstmt != null) {
				pstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}
	}

	/**	 
	 * * 기존의 사용자 사용자 정보를 수정.	 */
	public int update(User user) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}

	/**	 
	 * 사용자 아이디에 해당하는 사용자를 삭제.	 
	 */
	public int remove(String userId) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}

	/**	 
	 * 사용자 아이디 정보를 데이터베이스에서 찾아 User 도메인 클래스에 	 
	 * 저장하여 반환.	 
	 */
	public User findUser(String userId) throws SQLException { 
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}
	
	/**	 
	 * 사용자 리스트를 만들기 위한 부분으로 현재 페이지와 	
	 * 페이지당 카운트수를 이용하여 해당부분의 사용자만을 List콜렉션에	 
	 * 저장하여 반환.	 
	 */
	public List findUserList(int currentPage, int countPerPage) {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	

	}
	/**	
	 * 인자로 전달되는 아이디를 가지는 사용자가 존재하는지의 
	 * 유무를 판별. 	 
	 */
	public boolean existedUser(String userId) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.		
	}
}

[예제 1-2]를 보면 데이터베이스와의 데이터 교환 작업 이외에 다른 작업은 존재하지 않음을 확인할 수 있다.

package net.javajigi.user;

import java.sql.SQLException;
import java.util.List;

/** 
 * 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스. 
 * UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, 
 * 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다. 
 * 비지니스 로직이 복잡한 경우에는 비지니스 로직만을 전담하는 클래스를  
 * 별도로 둘 수 있다. 
 */
public class UserManager {
	private UserManager() {
	}
	public static UserManager instance() {
		return (new UserManager());
	}
	public int create(User user) throws SQLException, ExistedUserException {
		if (getUserDAO().existedUser(user.getUserId())) {
			throw new ExistedUserException(user.getUserId() + "는 존재하는 아이디입니다.");
		}
		return getUserDAO().create(user);
	}
	public int update(User user) throws SQLException {
		return getUserDAO().update(user);
	}
	public int remove(String userId) throws SQLException {
		return getUserDAO().remove(userId);
	}
	public User findUser(String userId) throws SQLException, UserNotFoundException {
		User user = getUserDAO().findUser(userId);
		if (user == null) {
			throw new UserNotFoundException(userId + "는 존재하지 않는 아이디입니다.");
		}
		return user;
	}
	public List findUserList(int currentPage, int countPerPage) throws SQLException {
		return getUserDAO().findUserList(currentPage, countPerPage);
	}
	public boolean login(String userId, String password)
		throws SQLException, UserNotFoundException, PasswordMismatchException {
		User user = findUser(userId);
		if (!user.isMatchPassword(password)) {
			throw new PasswordMismatchException("비밀번호가 일치하지 않습니다.");
		}
		return true;
	}
	private UserDAO getUserDAO() {
		return new UserDAO();
	}
}

[예제 1-3]은 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스로서UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다.

이상으로 사용자 관리를 위한 3개의 클래스를 모두 살펴보았다. 사용자 관리 프로젝트의 규모가 작기 때문에 위 3개의 클래스만으로 모델을 구현하는 것이 가능했다.

강좌에 대하여

작성자 : 박재성
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

Posted by 1010
반응형

1장. 사용자 관리 프로젝트로 알아본 스트럿츠 탄생 배경

Summary : 스트럿츠 프레임워크 워크북의 1장 원고를 통하여 스트럿츠 탄생 배경에 대하여 총 5번의 강좌를 통하여 알아본다. 이번 강좌에서는 사용자 관리 프로젝트에 대한 간단한 요구분석을 진행해 보도록 하겠다. 프로젝트라는 용어를 사용해서 사용자 관리 프로젝트가 상당히 거창하게 들릴지도 모른다. 그렇지만 독자들이 생각하는 것만큼 거창한 프로젝트는 아니다. 오히려 너무 간단해서 "이게 무슨 프로젝트야?"라고 실망감이 클지도 모르겠다.

예제 실행 방법

스트럿츠의 세부 항목을 살펴보기에 앞서 사용자 관리프로젝트 예제를 먼저 실행해보는 것이 이 장을 이해하는데 많은 도움이 될 것이다. 따라서 사용자 관리프로젝트 예제를 실행하는 방법에 대하여 살펴보도록 하겠다. 1장 예제의 디렉토리 구조를 살펴보면 다음과 같다.

[그림 1-1]은 이 장에서 개발하게 될 사용자 관리프로젝트의 디렉토리 구조이다. 1장 예제를 실행하기 위한 과정을 살펴보면 다음과 같다. MySQL, Tomcat, ANT의 설치에 관한 자세한 내용은 부록 1을 참조하기 바란다. 다음 설명은 MySQL, Tomcat, ANT가 설치되어 있다는 가정하에 설명을 진행하도록 하겠다.

사용자 관리 프로젝트를 위한 테이블 생성

1) chapter1/src 디렉토리를 보면 dbpool.properties파일이 존재한다. 이 파일은 MySQL데이터베이스에 대한 정보를 담고 있다. 이 파일을 자신의 데이터베이스 환경에 맞도록 수정해준다. dbpool.properties파일의 내용은 다음과 같다.

#JDBC Driver 등록한다.
drivers=org.gjt.mm.mysql.Driver

#사용할 데이터베이스의 URL
mysql.url=jdbc:mysql://localhost:3306/struts?useUnicode=true&characterEncoding=KSC5601

#사용할 데이터베이스의 사용자 아이디
mysql.user=javajigi

#사용할 데이터베이스의 비밀번호
mysql.password=javajigi

#초기 Connection pooling 사이즈를 입력한다.
mysql.initsize=5#Connection pooling 

최대 사이즈를 입력한다.
Mysql.maxpool=10

2) MySQL데이터베이스를 시작한 후 프롬프트 상에서chapter1 디렉토리로 이동하여 "ant db"를 실행하면 사용자 관리 프로젝트 예제에 필요한 테이블이 생성된다. 자료실 게시판을 위한 SQL스크립트는 chapter1/resources 디렉토리 아래의 chapter1.sql파일을 보면 된다.

[그림 1-2]와 같은 결과화면을 볼 수 있다면 데이터베이스 설정 및 테이블 생성이 제대로 된 것이다.

3) "ant db"를 실행할 때 빌드가 실패하면 두가지 원인이 있다. 첫번째는 dbpool.properties파일의 설정정보가 맞지 않을 경우이다. dbpool.properties파일에서 잘못된 부분을 수정한 다음 다시 실행한다. 두번째는 테이블을 drop할 때 테이블이 존재하지 않거나, 테이블을 생성할 때 테이블이 이미 존재할 때 발생한다. 이때는 에러 메시지에 따라 drop SQL의 주석을 추가하거나 제거해주면 된다.

사용자 관리 프로젝트 예제를 빌드.

사용자 관리 프로젝트 예제를 빌드하는 방법은 간단하다. 이 장에서 개발한 사용자 관리 프로젝트 예제는 모델1, 모델2, 스트럿츠 3가지 개발 방식으로 개발되었다. 따라서 3가지 예제를 각각 실행해 보기 위하여 빌드 또한 3가지 방법으로 할 수 있도록 구현하였다. 따라서 빌드하는 과정을 3가지 방법으로 살펴보도록 하겠다.

모델 1

1) 모델 1 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant model1" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4)
Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/model1/login.jsp URL로 접근하여 모델1 방식으로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 3절의 본문을 참조하기 바란다.

모델 2

1) 모델 2 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant model2" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4) Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/model2/user_list.m2?command=list URL로 접근하여 모델2방식으로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 4절의 본문을 참조하기 바란다.

스트럿츠

1) 스트럿츠 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant struts" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4) Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/user_list.do URL로 접근하여 스트럿츠로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 5절의 본문을 참조하기 바란다.

기타

1) 웹 애플리케이션 기본 디렉토리 구조로 생성된 예제는 chapter1/build 디렉토리에 생성된다. 따라서 [그림 1-1]의 디렉토리 구조가 생소한 독자들은 빌드를 실행한 다음 chapter1/build 디렉토리를 참조하면 쉽게 이해할 수 있다.

2) 각각의 개발 방법으로 새로 빌드하면 이전에 빌드한 디렉토리 내용은 삭제된다. 따라서 모델 1방식으로 개발된 사용자 관리 프로젝트의 예제 소스만을 보고 싶다면 "ant model1"으로 빌드한 다음 chapter1/build 디렉토리를 보면 된다. 모델2, 스트럿츠 또한 동일한 방법으로 확인할 수 있다.

빌드시 주의할 점과 궁금한 점.

1) 처음 빌드할 때는 상관없지만 한번 빌드한 예제의 소스를 수정한 다음 다시 빌드하고자 할 때는 Tomcat서버를 멈춘 상태에서 해야한다. 이유는 Tomcat서버가 시작된 상태일 경우 이전에 디플로이했던 디렉토리가 삭제되지 않는 문제가 발생해 빌드가 실패하는 경우가 발생하기 때문이다. 따라서 빌드는 항상 Tomcat서버가 멈춘 상태에서 실시해야 한다.

2) [그림
1-1]을 보면 WEB-INF디렉토리와 컴파일된 class파일이 없는 것을 볼 수 있다. 또한 독자들이 첨부된 CD의 예제소스를 보면 web.xml, struts-config.xml 파일들이 보이지 않는다. 이 파일들은 ANT로 빌드하면 자동생성되기 때문에 빌드를 실행한 다음 chapter1/build/WEB-INF 디렉토리에서 찾을 수 있다. 따라서 예제 소스에 보면 web.xml, struts-config.xml파일들이 없다고 의아해할 필요는 없다.

3) 모델 2나 스트럿츠를 기반으로한 애플리케이션을 개발하기 위해서는 기존의 모델1에 추가적으로 설정해야하는 정보가 있다. 그러나 이 장의 예제는 ANT로 빌드시 자동적으로 설정되기 때문에 추가적인 설정이 필요없다.

4) 예제 소스를 독자들이 수정해보고 싶다면 src디렉토리의 예제 소스나 JSP파일을 수정한 다음 새로 빌드하면 자동 컴파일 및 war로 압축되기 때문에 독자들이 수정한 예제를 테스트해볼 수 있다.

예제 테스트시 고려할 사항.

1) 이 장에서 개발한 예제는 사용자 관리 프로젝트를 위한 최소한의 기능을 포함하고 있다. 사용자 추가와 같이 새로운 내용을 추가, 수정하는 부분에서 자바스크립트를 이용한 데이터 검증과 같이 세세한 부분의 구현은 빠져있다. 예제를 테스트하는 사람이 개발자로 생각했기 때문에 비정상적으로 발생하는 부분까지 고려하지 않았다.

2) 실제 사용자 관리 프로젝트라면 권한에 따라 기존의 사용자를 수정, 삭제할 수 있겠지만 권한 기능이 빠져 있기 때문에 로그인이 가능한 상태에서는 모든 사용자들의 정보를 수정, 삭제할 수 있도록 구현하였다.

서두

초기 웹은 하이퍼 텍스트로 대표되는 HTML로 작성된 문서를 통하여 급속하게 발전하였다. 초창기 웹은 정적인 문서를 다루도록 설계되었으나, 웹이 점차 발전하면서 사용자들의 요구수준 또한 높아지면서 동적인 서비스를 요구하게 되었다.

이와 같은 동적인 페이지의 생성을 위하여 CGI, PHP, ASP, JSP등 다양한 언어가 등장하게 되었다. 이 같은 기술은 웹 환경을 획기적으로 변화시켰으며, 더 나은 웹 환경을 만들어내기 위한 기틀을 마련할 수 있었다. 그러나 웹 환경이 지속적으로 발전하면서 사용자들의 요구수준 또한 한층 높아지게 되었으며, 기존에 클라이언트/서버 환경으로 존재하던 수많은 애플리케이션이 웹 기반으로 전환하는 과정까지 진행되었다.

동적인 페이지들이 등장하기 시작했을 때는 웹 애플리케이션을 얼마나 빨리 만들어내느냐가 최대의 관심사였다. 따라서 고객이 원하는 결과물을 최대한 빨리 만들어 내는 기업들이 IT업계에서 인정받을 수 있었으며, 급성장하는 결과를 가져올 수 있었다. 그러나 시간이 지나면서 많은 기업들은 배보다 배꼽이 더 크다는 것을 느낄 수 있었다. 즉, 초기 웹 애플리케이션을 구축하는 비용보다 유지보수하는 비용이 몇배 더 발생하게 되었다. 또한 급속히 변하는 웹 환경에 기존의 개발 방법으로는 신속히 대응하기 힘들다는 것을 느끼기 시작했다.

이 같은 이유로 기존의 모델1개발 방식의 한계를 느끼게 된 개발자들은 MVC(Model, View, Controller)모델에 기반한 모델2라는 개발 방법론을 웹에 적용하게 된다. 하지만 모델2방식으로 전환하려고 했던 많은 개발자들은 너무도 복잡한 개발 방법과 재사용성에 한계를 느끼고, 이전의 개발 방법인 모델 1방식으로 되돌아가게 되었다. 모델2 방식으로 개발하기 위해서는 코드의 재사용성이 가능해야했으며, 웹 애플리케이션을 쉽게 만들 수 있도록 지원하는 기반이 마련되어야 했다. 이러한 필요성으로 인해 스트럿츠
프레임워크가 탄생하게 되었다. 모델1, 모델2, 스트럿츠에 대한 자세한 내용은 앞으로 자세하게 다룰 것이기 때문에 각각의 개발 방법을 모른다고 너무 걱정하지 말기 바란다.

스트럿츠 프레임워크의 기본 골격은 모델2와 같이 MVC 모델에 기반하고 있다. 모델2에서 부족했던 많은 부분들을 프레임워크 형태로 만들어 많은 부분의 재사용성을 높일 수 있도록 하였다. 따라서 아무런 기반도 없는 환경에서 모델2방식으로 개발을 시도했던 많은 개발자들이 더 쉽게 웹 애플리케이션을 만들 수 있는 기반을 제공하고 있는 것이다. 개발자들은 웹 애플리케이션의 하부에 대한 고민은 최소화하고, 애플리케이션의 비즈니스 로직에 집중할 수 있게 된 것이다.

책을 시작하는 이 장에서는 대부분의 웹 애플리케이션에 등장하는 사용자 관리 프로젝트를 모델1, 모델2, 스트럿츠 프레임워크 3가지 방법으로 개발해본다. 먼저 지금까지 가장 많은 개발자들이 이용하고 있는 모델1 방식에 대하여 살펴보겠다. 모델 1 개발방식의 장,단점을 살펴본 다음, 모델2 개발방식으로 변화되어 가는 과정을 예제를 통하여 이해할 수 있도록 하겠다. 모델 1에서 모델2로 바뀐다는 것은 기존의 웹 개발방식에 대한 사고의 전환을 필요로한다. 모델2로의 사고전환에 가장 핵심적인 부분에 대해서도 알아볼 것이다. 마지막으로 모델2의 한계점에 대하여 알아본 다음, 스트러츠와 같은 프레임워크가 등장할 수 밖에 없었던 배경에 대하여 살펴보겠다.

이 책은 스트러츠의 실전 활용 예제들을 중심으로 다루고 있는 책이다. 하지만 스트러츠에 대하여 바로 들어가지 않는 이유는 모델1, 모델2개발방식을 거쳐 스트러츠가 만들어지게된 배경이 무엇보다도 중요하기 때문이다. 또한, 모델 1 개발 방법에서 모델2 개발 방법으로 옮겨가기 위해서는 기존의 개발 방법에 대한 생각을 완전히 깰 필요가 있다. 기존에 가지고 있던 생각을 깨기에 복잡한 구조로 구성되어 있는 스트러츠
프레임워크를 바로 다루기 보다는 모델2를 통하여 사고의 전환을 꾀하고자 했다. 마지막 이유는 간단한 모델2 예제를 통하여 MVC모델에 대하여 깊이 있게 이해하려는 의도이다. 필자가 처음 MVC개념을 이해하기 위하여 스트럿츠 프레임워크를 접했을 때, 스트럿츠의 복잡한 구조와 세팅 등으로 인해 MVC에 대한 개념을 이해하기 전에 지쳐버렸던 기억이 있기 때문이다.

1. 사용자 관리 프로젝트 요구분석

프로젝트라는 용어를 사용해서 사용자 관리 프로젝트가 상당히 거창하게 들릴지도 모른다. 그렇지만 독자들이 생각하는 것만큼 거창한 프로젝트는 아니다. 오히려 너무 간단해서 "이게 무슨 프로젝트야?"라고 실망감이 클지도 모르겠다.

이 같이 작은 프로젝트를 선택하게 된 이유는 이 장에서 독자들이 이해해야하는 부분이 새로운 프로젝트를 이해하는 것이 주가 아니기 때문이다. 이 장에서 독자들이 꼭 이해해야 하는 부분은 모델 1 개발 방식과 모델 2개발방식, 스트럿츠 프레임워크를 이용한 개발 방법의 차이점을 이해하고 점진적으로 변화해가는 과정을 이해하는 것이 더욱 중요하다. 따라서 가능한 작은 규모로 프로젝트를 진행하면서 각각의 개발방식을 이해하는 데 중점을 둘 수 있도록 했다.

실전 프로젝트에서는 회원관리 같은 경우 무수히 많은 정보들을 요구한다. 하지만 이 장에서 살펴볼 사용자 관리 프로젝트는 사용자 아이디, 비밀번호, 사용자 이름, 사용자 이메일 주소만을 관리하도록 구현하겠다.

사용자 관리 프로젝트를 위하여 필요한 기능들을 사용자 관리 화면과 더불어 살펴보도록 하겠다.

로그인 기능을 제공해야 한다. 사용자가 입력한 아이디와 비밀번호가 같을 경우 로그인이 가능하며, 세션에 사용자 정보를 저장한다. 회원가입하지 않은 상태일 경우 회원가입 후에 로그인이 가능하다. 로그인이 된 상태에서 로그아웃을 할 수 있는 기능을 제공해야한다.

회원가입을 하지 않은 상태이면 회원가입을 통해 사용자 관리 프로젝트에 로그인이 가능하도록 해야한다.

사용자가 로그인 하면 사용자 리스트를 볼 수 있는 화면으로 이동한다. 사용자 리스트 화면은 현재 사용자 관리 프로젝트에 접근할 수 있는 모든 사용자들을 볼 수 있어야한다.

사용자 관리 프로젝트는 사용자별 정보를 볼 수 있는 기능을 제공해야한다. 사용자 정보화면에서 수정, 삭제화면으로 이동하여 수정, 삭제가 가능하도록 해야한다.

지금까지 살펴본 기능들을 제공한다면 사용자 관리를 위해 필요한 모든 기능들을 제공하는 것이다. 이상의 기능들을 바탕으로 유즈케이스 다이어그램을 그려보면 다음과 같다.[그림1-7 참조]

지금까지 살펴본 사용자 관리 프로젝트의 기능은 실제 사용자 관리 시스템을 구축하기 위한 최소한의 기능이다. 실제로 현업에서 개발되는 사용자 관리 시스템은 더 많은 사용자 정보를 요구하며, 사용자변 권한 관리까지 추가되기 때문에 이 절에서 분석한 기능보다 훨씬 더 복잡하고 많은 기능들을 요구하게 된다.

계속되는 절에서는 지금까지 살펴본 사용자 관리 기능을 구현해 보도록 하겠다. 먼저 모델1, 모델2, 스트럿츠에서 공통적으로 사용할 모델(비즈니스 로직과 데이터베이스 접근 로직을 말한다.)을 먼저 개발한다. 모델 1방식으로 개발할 경우 모델을 따로 만들지 않고 모델파트에서 할 모든 작업을 JSP에서 처리하는 경우도 있다. 이 장의 예제는 모델 1방식에 필요한 모델을 따로 분리하여 개발하는 것으로 한다.

그 다음 모델1, 모델2, 스트럿츠 개발방식으로 페이지의 흐름을 처리하는 방법과 모델 데이터를 사용자에게 보여주는 과정을 살펴보도록 하겠다.

강좌에 대하여

작성자 : 박재성
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

Posted by 1010
반응형

Jakarta Struts 강좌 7 - 스트러츠와 데이터베이스 연결

Summary : 지금까지 6번의 강좌를 통하여 스트러츠의 기본적인 내용에 대하여 살펴보았다. 최근 대부분 웹 애플리케이션은 데이터베이스와의 연동을 통하여 구현이 되고 있다. 따라서 스트러츠 프레임?을 이용하여 관계형 데이터베이스와 연결하여 사용하는 방법에 대하여 살펴보는 것 또한 중요하다. 따라서 이번 강좌에서는 스트러츠 프레임?에서 관계형 데이터베이스와 연동하여 구현하는 방법에 대하여 살펴본다. 이번 강좌에서 사용할 예제는 지난 강좌까지 만들어 왔던 사용자 관리를 mySQL 데이터베이스를 이용할 수 있도록 변경해 보도록 하겠다.

사용자 관리 클래스 다이어 그램

사용자 관리의 클래스는 앞으로 계속해서 진행될 강좌(EJB, JDO)를 위하여 다양한 J2EE패턴을 적용하여 설계하였다. 사용자 관리의 클래스 다이어그램은 다음과 같다.

위 사용자 관리 클래스 다이어그램에서 사용한 패턴은 다음과 같다.

첫째, 다양한 데이터베이스의 지원이 가능하도록 하기 위하여 Factory Method 패턴과 Abstract Factory 패턴을 적용하였다. Abstract Factory 패턴을 제대로 적용하려고 했으나 부족한 부분이 있다.

둘째, 비지니스 로직과 데이터 베이스와의 조작을 분리하기 위하여 데이터 베이스와의 작업을 전담하는 Data Access Object클래스를 따로 두었다. J2EE패턴의 DAO패턴을 적용하였다.

셋째, J2EE패턴의 Transfer Object, Value Object를 만들어 적용해 보았다. 굳이 J2SE만을 이용하는 프로젝트에서는 필요없는 부분이지만 앞으로의 예제를 위하여 미리 구현해봤다.

강좌를 단순히 하기 위하여 패턴의 적용없이 구현하려고 했으나 앞으로 계속되는 강좌를 위해 패턴을 적용하여 확장성을 기해보았다.

강좌를 위한 초기 설정

이번 강좌에서는 데이터베이스와 연관되는 부분이 있기 때문에 초기에 설정해야 하는 작업이 있다.

첫번째로 데이터베이스는 mySQL을 사용하고 있다. http://www.mysql.com에서 3.23.X버전 이상을 다운받아 설치하기 바란다. mySQL에 대한 자세한 설명은 웹상의 문서들을 참조하기 바란다.

두번째는 http://homepages.nildram.co.uk/~slink/java/DBPool/에서 제공하는 Connection Pooling을 사용하고 있다. zigimall/WEB-INF/classes아래에 보면 dbpool.properties파일을 자신의 데이터베이스 환경에 맞도록 수정한다. dbpool.properties의 내용은 다음과 같다.

#JDBC Driver 등록
drivers=org.gjt.mm.mysql.Driver

#초기 Connection Pool 사이즈
mysql.initsize=10

#최대 Connection Pool 사이즈
mysql.maxpool=20

#Mysql의 비밀번호
mysql.password=javajigi

#MySQL URL
mysql.url=jdbc:mysql://127.0.0.1:3306/zigimall?useUnicode=true&characterEncoding=KSC5601

#MySQL의 User ID
mysql.user=javajigi

fdsdf
세번째는 사용자 관리에 사용할 테이블의 생성이다. 사용자 관리 테이블은 단순히 사용자 아이디, 비밀번호, 이름, 이메일 정보만을 가지도록 생성하였다.

CREATE TABLE userInfo ( 
	userId          varchar(12)		NOT NULL, 
	password		varchar(12)		NOT NULL,
	name			varchar(10)		NOT NULL,
	email			varchar(50),
  	
  	PRIMARY KEY               (userId),  	
  	INDEX userInfo_userId_idx  (userId)
);

이상으로 이번강좌에서 사용자 관리를 만들기 위하여 필요한 데이터베이스 설정이 완료되었다. 다음은 데이터베이스와의 작업을 전담하는 클래스 내부에 대하여 살펴보도록 하겠다.

데이터베이스 전담 클래스들

데이터베이스 전담 클래스는 섹션 1에서 살펴본 클래스 다이어그램에 해당하는 클래스들이다. 이번 섹션에서는 데이터베이스와의 작업을 전담하는 클래스 각각의 내부에 대하여 살펴보도록 하겠다.

package net.javajigi.mall.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import snaq.db.ConnectionPoolManager;

/**
 * 데이터베이스의 Connection객체를 얻는 작업과 
 * Connection, Statement, ResultSet를 닫는 역할을 하는 클래스.
 */
public class RDBDatabase {
	protected Connection getConnection() throws SQLException {
		ConnectionPoolManager conMgr = ConnectionPoolManager.getInstance();

		return conMgr.getConnection("mysql");
	}

	protected void cleanUp(Connection con, Statement stmt)
		throws SQLException {
		cleanUp(con, stmt, null);
	}


	protected void cleanUp(Connection con, Statement stmt, ResultSet rs)
		throws SQLException {
		if ( rs != null ) {
			rs.close();
		}
		
		if ( stmt != null ) {
			stmt.close();
		}
		
		if ( con != null ) {
			con.close();
		}
	}
}
package net.javajigi.mall.user;

import java.sql.SQLException;
import java.util.List;

/**
 * 사용자 관리에 필요한 일들을 정의하는 인터페이스
 */
public interface UserDAO {
	public UserVO getUser(String userId) throws SQLException;

	public int addUser(UserTO userTO) throws SQLException;
	
	public List getUsers() throws SQLException;
	
	public int updateUser(UserTO userTO) throws SQLException;
	
	public int removeUser(String userId) throws SQLException;
}
package net.javajigi.mall.user.rdb;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import net.javajigi.mall.db.RDBDatabase;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserTO;
import net.javajigi.mall.user.UserVO;

/**
 * UserDAO를 구현하는 클래스.
 * MySQL데이터베이스와의 작업을 전담하는 클래스.
 */
public class UserMysqlDAO extends RDBDatabase implements UserDAO {

	/**
	 * @see net.javajigi.mall.user.UserDAO#getUser(String)
	 */
	public UserVO getUser(String userId) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			String findUserQuery =
				"SELECT "
					+ "userId, password, "
					+ "name, email "
					+ "FROM userinfo "
					+ "WHERE userId = ?";

			pstmt = getConnection().prepareStatement(findUserQuery);
			pstmt.setString(1, userId);

			rs = pstmt.executeQuery();

			if (rs.next()) {
				return new UserVO(
					(String) rs.getString("userId"),
					(String) rs.getString("password"),
					(String) rs.getString("name"),
					(String) rs.getString("email"));
			} else {
				return null;
			}
		} finally {
			cleanUp(con, pstmt, rs);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#addUser(UserTO)
	 */
	public int addUser(UserTO userTO) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;

		try {
			String insertQuery = "INSERT INTO userinfo VALUES (?, ?, ?, ?)";

			pstmt = getConnection().prepareStatement(insertQuery);
			pstmt.setString(1, userTO.getId());
			pstmt.setString(2, userTO.getPassword());
			pstmt.setString(3, userTO.getName());
			pstmt.setString(4, userTO.getEmail());

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#getUsers()
	 */
	public List getUsers() throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			String findUsersQuery =
				"SELECT "
					+ "userId, password, "
					+ "name, email "
					+ "FROM userInfo ";

			pstmt = getConnection().prepareStatement(findUsersQuery);

			rs = pstmt.executeQuery();

			List users = new ArrayList();

			while (rs.next()) {
				UserVO userVO =
					new UserVO(
						(String) rs.getString("userId"),
						(String) rs.getString("password"),
						(String) rs.getString("name"),
						(String) rs.getString("email"));

				users.add(userVO);
			}

			return users;
		} finally {
			cleanUp(con, pstmt, rs);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#updateUser(UserTO)
	 */
	public int updateUser(UserTO userTO) throws SQLException {
		Connection con = getConnection();
		PreparedStatement pstmt = null;

		try {
			String updateQuery =
				"UPDATE userinfo SET "
					+ "password = ?, "
					+ "name = ?, "
					+ "email = ? "
					+ "WHERE userId = ?";

			pstmt = con.prepareStatement(updateQuery);
			pstmt.setString(1, userTO.getPassword());
			pstmt.setString(2, userTO.getName());
			pstmt.setString(3, userTO.getEmail());
			pstmt.setString(4, userTO.getId());

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#removeUser(String)
	 */
	public int removeUser(String userId) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;

		try {
			String removeQuery = "DELETE FROM userinfo WHERE userId = ?";

			pstmt = getConnection().prepareStatement(removeQuery);
			pstmt.setString(1, userId);

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}
}
package net.javajigi.mall.user;

import net.javajigi.mall.user.rdb.UserMysqlDAO;

public class UserDAOFactory {
	private static UserDAOFactory _instance = null;
	
	public static int DATABASE_MYSQL = 1;

	private UserDAOFactory() {
	}

	public static synchronized UserDAOFactory instance() {
		if (_instance == null) {
			_instance = new UserDAOFactory();
		}

		return _instance;
	}
	
	public UserDAO getUserDAO(int dbType) {
		if ( dbType == DATABASE_MYSQL ) {
			return (new UserMysqlDAO());
		} else {
			return null;
		}
	}
}
package net.javajigi.mall.user;

public class UserTO {
	private String id = null;
	private String password = null;
	private String name = null;
	private String email = null;
	
	public String getEmail() {
		return email;
	}

	public String getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getPassword() {
		return password;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setId(String id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}
package net.javajigi.mall.user;

public class UserVO {
	private String id = null;
	private String password = null;
	private String name = null;
	private String email = null;
	
	public UserVO(String id, String password, String name, String email){
		this.id = id;
		this.password = password;
		this.name = name;
		this.email = email;
	}
	
	public String getId() {
		return id;
	}

	public String getPassword() {
		return password;
	}

	public String getEmail() {
		return email;
	}

	public String getName() {
		return name;
	}

	public boolean equals(Object userVO) {
		return this.equals(userVO);
	}

	public String toString() {		
		return getId();
	}
}

스트러츠의 Action클래스, JSP파일

이번 섹션에서는 사용자 관리를 위한 Action클래스에 대하여 살펴본다. 모든 구조는 앞에서 살펴본 Action클래스와 같다. 단지 달라진 부분은 데이터베이스와의 작업이 가능하도록 수정해 주면 된다. 다음에 살펴볼 클래스들은 앞에서 살펴본 데이터베이스의 접근을 전담하는 클래스들을 이용하는 Action클래스들이다.

또한 각각의 Action클래스 작업이 완료한 다음 뷰를 전담하는 JSP파일에 대하여 살펴보도록 하겠다.

package net.javajigi.mall.user.action;

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;

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

public class UserListAction extends Action {

	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {

		
		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
		List users = userDAO.getUsers();
		
		//사용자의 정보를 얻어와 사용자의 정보를 담은 객체를 
		//request에 저장하여 전달하고 있다.
		//이것이 가능한 이유는 RequestDispatcher를 이용하기 때문이다.
		request.setAttribute("listuser", users);	
		
		return mapping.findForward("userlist");
	}
}
<%@ page contentType="text/html;charset=EUC-KR"%>

<%@ page import="java.util.*"%>
<%@ page import="net.javajigi.mall.user.*"%>

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

<html:html locale="true">
<head>
    <title>사용자 리스트</title>
    <html:base/>
<script language="JavaScript">
function update(userId) {
	f.userId.value = userId;
	f.action = "./userUpdateForm.do";
	f.submit();
}

function del(userId) {
	f.userId.value = userId;
	f.action = "./deleteuser.do";
	f.submit();
}
</script>    
</head>

<body>
<form name="f" method="post">
	<html:errors />
	<br/>
	<table border="1">
		<tr>
			<td><bean:message key="prompt.id" /></td>
			<td><bean:message key="prompt.name" /></td>
			<td><bean:message key="prompt.email" /></td>	
			<td></td>	
		</tr>
<%
	List users = (List)request.getAttribute("listuser");

	for (int i=0; i < users.size(); i++) {
		UserVO userVO = (UserVO)users.get(i);
%>
		<tr>
			<td><%= userVO.getId() %></td>
			<td><%= userVO.getName() %></td>
			<td><%= userVO.getEmail() %></td>
			<td>
				<a href="javascript:update('<%= userVO.getId() %>')">수정</a> 
				<a href="javascript:del('<%= userVO.getId() %>')">삭제</a>				
			</td>	
		</tr>
<%
	}
%>		
	</table>
	<input type="hidden" name="userId"/>

    <html:link page="/useraddform.do">
        <bean:message key="user.useradd" />
    </html:link>
    <html:link page="/login.jsp">
        <bean:message key="useradd.cancel" />
    </html:link>    
<form>
</body>
</html:html>
package net.javajigi.mall.user.action;

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

import net.javajigi.mall.user.form.UserForm;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;
import net.javajigi.mall.user.UserTO;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class UserAddAction extends Action {
	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		UserForm useraddForm = (UserForm) form;

		ActionErrors errors = new ActionErrors();
		
		//요청된 Request의 Token이 유효한지를 처리하는 부분이다.
		//Token이 유효하지 않을 경우 ActionErrors에 에러를 저장한다.
		//따라서 같은 요청이 재요청될 경우 에러를 발생시키기 때문에 같은 요청이 
		//두번 반복해서 처리되지는 않는다.
        if (!isTokenValid(request)) {
            errors.add(ActionErrors.GLOBAL_ERROR,
                       new ActionError("error.transaction.token"));
        }
       	resetToken(request);		
		        
		// Report any errors we have discovered back to the original form
		if (!errors.isEmpty()) {
		    saveErrors(request, errors);
 	        saveToken(request);

	        return (mapping.getInputForward());
		}

		UserTO userTO = new UserTO();
		
/*		
		userTO.setId(useraddForm.getId());
		userTO.setPassword(useraddForm.getPassword());
		userTO.setName(useraddForm.getName());
		userTO.setEmail(useraddForm.getEmail());
*/
		PropertyUtils.copyProperties(userTO, useraddForm);
		
		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
				
		userDAO.addUser(userTO);
		
		useraddForm.reset(mapping, request);
		
		return(mapping.findForward("loginForm"));
	}
}
package net.javajigi.mall.user.action;

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

import net.javajigi.mall.Constants;
import net.javajigi.mall.user.form.UserForm;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;
import net.javajigi.mall.user.UserVO;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class LoginAction extends Action {
	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		
		UserForm loginForm = (UserForm)form;
		
		String inputId = loginForm.getId().trim();
		String inputPassword = loginForm.getPassword();

		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
		UserVO userVO = userDAO.getUser(inputId);
		
		ActionErrors errors = new ActionErrors();

		if ( userVO == null ) {
			//UserVO가 null이면 존재하지 않는 아이디로 파악하여 에러 메세지를 출력.
       		errors.add(ActionErrors.GLOBAL_ERROR,
            	new ActionError("error.id.notexisted"));
		} else {
			//사용자 아이디와 비밀번호가 틀릴경우 비밀번호가 틀리다는 에러 메세지 출력
			if ( !inputPassword.equals(userVO.getPassword()) ) {
	       		errors.add(ActionErrors.GLOBAL_ERROR,
	            	new ActionError("error.password.match"));
			}			
		}
		
		//로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors에 에러를 저장하고
		//로그인 폼페이지로 반환하게된다.
		//mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
		//input attribute에 정의된 페이지로 이동하게 된다.
		if (!errors.isEmpty()) {
	    	saveErrors(request, errors);
           	return (mapping.getInputForward());
		}
		
		//로그인 과정이 정상적으로이루어 지면 userVO객체를 세션에 저장.
		HttpSession session = request.getSession();
		session.setAttribute(Constants.USER_KEY, userVO);
		
		//로그인 과정이 정상적으로이루어진 다음 메인페이지로 이동한다.
		//mapping의 findForward에서 사용하는 key값은 struts-config.xml의 forward태그의 
		//name attribute를 이용한다.
		return(mapping.findForward("mainpage"));
	}
}

지금까지 스터르츠의 Action클래스와 데이터베이스의 데이터를 가져와 JSP에서 사용하는 방법에 대하여 살펴보았다. 사용자 관리를 각각의 작업들이 모두 위와 같은 과정으로 구현되어 있기 때문에 모든 소스를 보여주지는 않았다. 이 섹션에서 살펴본 예제 이외의 소스는 첨부되는 소스를 참고하여 구현 과정을 살펴보기 바란다.

강좌에 대하여

작성자 : [Javajigi](박재성)
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

출처 : http://wiki.javajigi.net/pages/viewpage.action?pageId=79

Posted by 1010
반응형

Summary : 지난 강좌에서는 Model 1 개발 방식과 Model 2 개발 방식에 대하여 살펴보았다. Model 2 개발 방식으로 개발하게 된 배경과 Model 2에 기반한 스트러츠가 나타나게 된 배경에 대하여 살펴보았다. 이번 강좌에서는 스트러츠의 Configuration 파일들에 대하여 살펴볼 것이다. 스트러츠 Application을 만들기 위하여 필요한 대표적인 Configuration 파일인 struts-config.xml과 web.xml에 대하여 살펴보겠다.

스트러츠를 위한 web.xml 파일

스트러츠 Application파일을 만들기 위한 작업이 생각보다 간단하지 않다. 스트러츠 Configuration에 관여하는 파일이 많으며, Configuration 파일이 복잡한 것이 사실이다. 스트러츠 첫번째 강좌에서 스트러츠 Application을 만들기 위한 설치과정을 살펴보았다. 생각보다 많은 파일들이 스트러츠 Application을 만들기 위하여 관여하는 것을 알 수 있었다.

각각의 Configuration파일을 설명하면서 스트러츠에 대하여 더 깊이 있게 이해하도록 하겠다.

스트러츠 Application을 만들기 위하여 우선 설정할 파일은 Web Application에 꼭 필요한 web.xml파일의 설정이다. 많은 개발자들이 web.xml에 대하여 잘 알고 있을 것으로 생각되지만 처음 접하는 개발자들을 위하여 web.xml에 대하여 자세하게 살펴보도록 하자.

스트러츠는 MVC 모델에 기반하고 있다. Model 2에서는 각 모듈별로 Servlet을 만들어 Controller역할을 할 수 밖에 없었다. 만약 하나의 Servlet에서 모든 request를 담당한다면, 다양한 정보를 하나의 Servlet에서 모두 처리하기에는 많은 문제점을 가지고 있다. 두번째 강좌의 Model 2에 대한 내용을 보면 알 수 있을 것이다.

이 같은 문제점을 보완하기 위하여 스트러츠에서는 Servlet에서 가지고 있던 정보들을 XML파일로 빼내서 처리하였다. 그리고 모든 request를 하나의 Servlet으로 중앙집중화하였다.

Servlet에서 처리해야할 정보들을 빼낸 XML파일이 struts-config.xml파일이고, 중앙집중적으로 Controller역할을 하는 Servlet이 ActionServlet이다. 이 정보들을 web.xml에 다음과 같이 설정함으로서 스트러츠 Application에서 사용이 가능하게 된다.

<servlet>
	<servlet-name>action</servlet-name>
	<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
</servlet>

<!-- Action Servlet Mapping -->    
<servlet-mapping>
	<servlet-name>action</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

web.xml에서 Action Servlet을 mapping하는 부분이다. 이 부분이 나타내는 의미는 URL의 확장자가 .do로 끝나는 모든 request는 ActionServlet으로 mapping된다는 것이다.

따라서 다음과 같은 형태의 URL이 처음에 접근하게 되는 것이 ActionServlet이라는 의미이다.

http://www.javajigi.net/myApplication/actionName.do

스트러츠 Application을 위한 struts-config.xml을 설정하는 과정은 다음과 같이 ActionServlet에 인자로 전달함으로서 가능하게 된다. ActionServlet은 여러개의 초기 인자를 받아서 ActionServlet의 초기화를 진행하게 된다. web.xml에서 초기 인자를 전달하는 것은 init-param태그를 이용하여 가능하다.

<web-app>
	<!-- Action Servlet Configuration -->
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
       
	    <!-- Resources bundle base class -->
		<init-param>
            <param-name>application</param-name>
            <param-value>net.javajigi.tutorial.MessageResources</param-value>
	    </init-param>
        
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>3</param-value>
        </init-param>
        <init-param>
            <param-name>detail</param-name>
            <param-value>3</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- Action Servlet Mapping -->    
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</webapps>

모든 Servlet에 name-value쌍이면 어떠한 인자도 전달할 수 있다. ActionServlet에서는 init-param으로 위와 같은 정보들을 받는다. 스트러츠에서 사용할 Resource Bundle파일(net.javajigi.tutorial.MessageResources), Configuration파일(/WEB-INF/struts-config.xml)등의 정보들을 전달할 수 있다. ActionServlet을 위한 초기인자의 name은 고정되어 있지만 value들을 개발자들이 원하는 것으로 바꾸어 사용하는 것이 web.xml의 수정만으로 가능하게 된다. 소스코드에 이 정보들을 가지고 있는 것보다는 쉽게 설정의 변경이 가능함을 알 수 있다.

헉, 내가 생각해도 설명이 부족함을 느낀다. 어찌 설명해야 쉽게 이해시킬 수 있을지 난감하다. 우선 강좌를 진행하고 앞으로 계속되는 강좌나 Q&A를 통하여 부족한 부분은 보강하도록 하겠다.

Tag Libraries의 Configuration

스트러츠 프레임워크는 뷰를 쉽게 생성하도록 돕기 위하여 많은 커스텀 태그를 제공한다. 이 태그들을 사용하기 위하여 web.xml에서 다음과 같이 설정한다. 스트러츠에서 제공한 모든 tld파일에 대하여 다음과 같이 설정하면 된다.

	<!-- 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> 
	<taglib>
    	<taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
	    <taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
	</taglib>
	<taglib>
    	<taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
	    <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
	</taglib>
	<taglib>
    	<taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
	    <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
	</taglib>

커스텀 태그를 사용해본 개발자들이라면 web.xml에 커스텀 태그의 사용을 위한 tld파일을 설정하는 부분에 대하여 쉽게 이해할 수 있을 것이다. web.xml에서 위와 같이 설정한 다음 JSP에서 다음과 같이 사용할 커스텀 태그를 지정하는 것이 가능하게 된다.

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

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

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

커스텀 태그의 설정 및 사용에 대한 부분은 이 강좌에서는 다루지 않겠다.

지금까지 스트러츠 Application을 위한 web.xml의 설정에 대하여 살펴보았다. web.xml에는 이 외에도 다양한 태그들을 제공한다. 개발자들이 web.xml에서 제공하는 모든 기능을 유용하게 활용할 수 있다면 Web Application을 개발하는데 많은 도움이 될 것이다. web.xml에 대하여 더 깊이 알고 싶은 개발자들은 http://java.sun.com/dtd/web-app_2_3.dtd의 DTD를 보면 설명이 잘 되어 있기 때문에 꼭 한번 보기 바란다.

XML에 익숙하지 않거나 XML의 기초를 공부한 개발자들은 이 기회를 통해 XML에 대하여 더 깊이 있게 공부해 보기 바란다.

스트러츠 Configuration File

스트러츠 Configuration File의 중심은 뭐니뭐니 해도 struts-config.xml파일이다. web.xml에서 보아서 알 수 있듯이 꼭 struts-config.xml파일이 아니어도 된다. 다른 이름을 가지는 XML파일이어도 상관없다.

먼저 struts-config.xml파일에서 사용 가능한 태그들은 struts-config_1_1.dtd을 살펴보면 다음과 같다.

<!ELEMENT struts-config (data-sources?, form-beans?, global-exceptions?, global-forwards?, action-mappings?, controller?, message-resources*, plug-in*)>

이 내용을 바탕으로 살펴보면 struts-config.xml에서 설정하는 정보는 다음과 같음을 추측할 수 있다.

1. 데이터베이스를 위한 DataSource의 설정.

2. request에 의하여 전달되는 정보를 저장하는 FormBean의 설정.

3. Application에서 발생하는 Exception의 설정.

4. Application에서 요청에 따른 작업을 끝낸후 Forward할 path에 대한 설정.

5. Model과 통신하는 BusinessLogic을 담고 있는 Action클래스의 설정.

6. 스트러츠 Controller의 설정.

7. Message Resource의 설정.

8. 다소 생소하겠지만 스트러츠에서는 플러그인이 가능하도록 구현되어 있다. 따라서 플러그인에 대한 설정이 가능하다.

struts-config.xml의 내용을 자세하게 이해하면 스트러츠가 무엇을 하는 놈인지 많은 부분을 이해할 수 있게 된다. 따라서 struts-config.xml에 대하여 자세하게 살펴보도록 하겠다. 아직 FormBean이나 Action과 같이 처음 접하는 부분이 많기 때문에 이해하는데 어려움을 느끼는 개발자들도 많을 것으로 예상된다. 이 부분은 다음 강좌에서 다룰 부분이기 때문에 이번 강좌에서는 앞의 두 강좌에서 다룬 내용을 바탕으로 설명할 수 밖에 없을 것으로 생각된다.

먼저 struts-config.xml과 같이 Controller의 설정파일까지 생기게된 배경에 대하여 간략하게 설명하고 넘어가겠다.

앞의 Model 2 개발방식에서도 간략하게 설명했다. Model 2의 CommandFactory를 보면 알 수 있듯이 5개의 작업만을 위해서 5번의 if/else가 나타나고 있음을 알 수 있다. 그러나 실제 Web Application을 개발할 때는 수백에서 수천개의 작업이 발생한다. 이 때마다 CommandFactory를 만들 수는 없는 것이다. 또한 그에 따른 Servlet을 각각 생성해 주어야 하는 불편함이 있다. Servlet의 수도 많아질뿐만 아니라 Factory 클래스도 무수히 많이 늘어나게 될 것이다.

이 같은 단점을 보완하기 위하여 모든 request는 하나의 Servlet에서 처리하도록 했으며, 그에 대한 정보는 struts-config.xml을 두어 설정하도록 했다.

Model 2 개발방식으로 개발할 경우, Action을 상속하는 클래스들에서 요청한 작업이 완료된 후 접근할 URL을 지정하고 있다. ListAction을 보면 다음 URL은 list.jsp를 반환하고 있다. 하지만 실무 개발에서 작업 완료후 접근하게될 URL이 바뀌는 경우는 수도 없이 많다. 이럴때마다 해당 Action 클래스를 찾아서 URL을 변경한 후 다시 컴파일, 서버 재시작등의 작업이 이루어져야 한다. 상당히 많은 시간과 짜증나는 일이 아닐 수 없다. 이 같이 Model 2개발방식에서 많은 시간을 요하거나, 재사용이 힘들었던 부분들을 struts-config.xml에서 설정하도록 하여 변경사항이 발생할 경우 struts-config.xml의 정보만 수정하면 되도록 구현한 것이다.

이 같이 Model 2에서 짜증나고, 많은 시간을 요하는 부분을 Framework화하여 재사용이 가능하고, 사용하기 편하도록 만든 것이 스트러츠이다. 이 같은 방식을 강좌 2에서도 살펴보았듯이 Model 2+1이라고 하는 것이다.

그렇다면 struts-config.xml의 내부에 대하여 살펴보도록 하자.

action-mappings 태그
struts-config.xml에서 가장 중심이 되는 부분은 action-mappings태그이다. action-mappings태그는 Model 2 개발방식에서 Command와 Action클래스를 mapping시키는 부분과 해당 command에 해당하는 작업을 완료한 다음 접근하게 될 path를 지정하는 부분이 포함된다.

<action-mappings type="org.apache.struts.action.ActionMapping">
	<action
		path="/login"
		attribute="loginForm"
		input="/tutorial/login.jsp"
		name="loginForm"
		type="net.javajigi.tutorial.action.LoginAction" />
        <forward name="mainpage" path="/tutorial/mainpage.jsp" />
</action-mappings>

action-mappings태그에는 여러개의 action태그를 가질 수 있다. action태그는 Model 2에서 보았던 list명령어와 ListAction을 mapping시키는 역할을 한다. 또한 ListAction에서 작업완료후 접근하게 될 path를 struts-config.xml로 설정한 것이다.

위 login action은 강좌 1에서 다룬 부분을 조금 수정한 것이다. 각각에 대하여 설명하면 다음과 같다.

1. Command에 해당하는 부분이 path이다. URL이 login.do일 경우 위 action을 찾게 된다.

2. Command가 login일 경우 mapping되는 Action클래스는 net.javajigi.tutorial.action.LoginAction임을 type에서 명시하고 있다.

3. 에러 없이 작업을 완료했다면 forward할 path를 지정하고 있다. action태그에서 사용되는 forward정보는 자바의 지역변수와 같이 해당 action에서만 사용이 가능하다. 만약 여러개의 action에서 같이 사용되는 forward정보라면 global-forwards태그에서 정의하면 전체 action에서 사용이 가능하다. 이 부분은 계속되는 예제를 통하여 이해할 수 있을 것이다.

4. 로그인을 위해서는 id와 password를 입력값으로 받아야 한다. 사용자가 입력한 값을 저장하는 역할을 하는 것이 FormBean이다. 따라서 로그인 과정에서도 id와 password 정보를 포함하고 있는 FormBean을 지정할 수 있다. 물론 FormBean에 대한 정보는 form-beans태그에서 정의한 것을 사용하게 된다. action태그에서 FormBean을 사용하는 부분은 name attribute를 통하여 사용이 가능하다.

이 외에도 action태그에서 사용가능한 attribute는 무수히 많다. 하지만 처음부터 너무 많은 부분을
다뤄봤자 혼랍스럽기만 할 것으로 생각되어 꼭 필요한 부분만 우선 설명했다. 필자가 생각하기에는 스트러츠의 전체적인 구조 및 직접 테스트를 해보는 것이 중요할 것으로 생각되어 필요한 부분들을 추출하였다.

form-beans 태그
다음은 action태그에서 이용했던 FormBean을 정의하는 form-beans태그에 대하여 살펴보도록 하겠다.

    <form-beans type="org.apache.struts.action.ActionFormBean">
        <form-bean name="loginForm" type="net.javajigi.tutorial.form.LoginForm">
            <form-property name="password" type="java.lang.String" />
            <form-property name="id" type="java.lang.String" />            
        </form-bean>        
    </form-beans>

form-beans 태그내에도 각각의 FormBean을 설정하는 form-bean태그가 있다.

form-bean태그는 다음과 같은 정보를 담고 있다.

1. FormBean의 타입을 설정.(type attribute)

2. struts-config.xml에서 사용할 FormBean의 name을 설정.(name attribute)

3. FormBean을 이용하여 전달하 property이름과 type을 설정. 위 예제에서는 로그인 입력 폼에서 전달한 id와 password를 전달한다.

위 form-bean태그에서 loginForm으로 설정한 FormBean은 action태그에서 name attribute로 사용된다.

global-forwards 태그\
global-forwards 태그는 action태그 전체에서 사용할 forward정보를 설정한다. 위에서 살펴본 바와 같이 login action 내에서 사용한 mainpage forward는 login action에서만 사용이 가능하다. 만약 login action만이 아닌 여러개의 action에서 사용하고자 한다면 global-forwards에 다음과 같이 설정해야 한다.

<global-forwards type="org.apache.struts.action.ActionForward"> 
    <forward name="mainpage" path="/tutorial/mainpage.jsp" /> 
</global-forwards> 

message-resources 태그

message-resources태그는 스트러츠 Application에서 사용할 Message Resource들을 설정한다. 앞의 예제에서 우리가 사용한 Message Resource는 net.javajigi.tutorial.MessageResources이였다. struts-config.xml에서 다음과 같이 설정하면 된다.

 <message-resources parameter="net.javajigi.tutorial.MessageResources" /> 

지금까지 struts-config.xml에 대하여 간략하게 살펴보았다. 스트러츠를 시작하는 단계에서 struts-config.xml를 전체를 살펴보는 것이 무의미할 것으로 생각되어 이번 강좌에서는 생략했다. 단지 필요한 정보들만 우선 설정해보고 이 부분이 익숙해진 다음 다른 설정으로 넘어가는 것이 좋을 것으로 생각되어 이번 강좌에서는 간략하게 살펴보았다.

지금은 스트러츠가 구현되는 전체적인 구조를 먼저 이해하는 것이 중요하다고 생각된다.

지금까지 진행한 강좌에 대하여 더 깊이 이해하기 위하여 스트러츠 강좌 1에서 진행했던 로그인 예제를 확장하여 로그인 과정을 완성해 보도록 하겠다.

로그인 예제 Model 부분

지금까지 진행한 스트러츠의 Configuration을 더 깊이 이해하기 위하여 실제 개발에서 많이 이루어지고 있는 로그인 과정을 스트러츠로 구현해보자.

예제를 단순화하기 위하여 사용자의 추가, 수정, 삭제는 다루지 않았다. 단지 한 사용자를 고정하여 로그인 예제를 다루도록 하였다.

로그인 예제의 흐름은 로그인 페이지에서 로그인 과정이 정상적으로 이루어지면 사이트의 메인 페이지로 이동한다. 만약 아이디가 없는 사용자이거나 비밀번호가 틀리면 에러메세지를 출력하고 로그인 페이지로 되돌아온다.

사용자에 대한 정보는 데이터베이스와 같은 영구 저장소에 저장하는 것이 일반적이다. 따라서 로그인을 과정을 정상적으로 진행하기 위해서는 먼저 사용자가 입력한 아이디에 해당하는 사용자가 있는지 데이터베이스에서 확인 작업을 거친뒤 사용자가 로그인 하도록 구현하다.

여기에서 사용자의 정보를 데이터베이스에서 가져오는 부분은 담당하는 부분이 Model 부분이다. 예제를 단순화하기 위하여 데이터베이스에 대한 접속부분은 이번 예제에서 제외시켰다.

package net.javajigi.tutorial.user;

public class UserDAO {
	
	public UserVO getUser(String id) {
		/*
		데이터베이스에 접속하여 해당하는 User의 정보를 가져오는 코드가
		실제 Application에서는 있을 것이다.
		User의 정보를 가져와 User Value Object에 해당 정보를 저장하여 반환하는
		역할을 한다.
		
		이번 예제에서는 스트러츠에 집중하기 위하여 데이터베이스에 접속하여 데이터를
		가져오는 부분은 생략했다.
		
		id가 javajigi일 경우 UserVO를 생성하고, 
		그렇지 않을 경우 존재하지 않는 아이디로 간주하여 null을 반환하도록 
		구현했다.
		*/
		
		if ( id.equals("javajigi") ) {
			return new UserVO("javajigi", "password");
		} else {
			return null;
		}		
	}
}
package net.javajigi.tutorial.user;

public class UserVO {
	private String id = null;
	private String password = null;
	
	public UserVO(String id, String password){
		this.id = id;
		this.password = password;
	}
	
	public String getId() {
		return id;
	}

	public String getPassword() {
		return password;
	}
}

로그인을 위한 Model은 단지 두개의 클래스만을 이용하였다. EJB와 같은 복잡한 구조가 아니기 때문에 두개의 클래스만으로 충분히 소화할 수 있을 것으로 생각되었다.

UserDAO는 사용자가 입력한 아이디에 해당하는 사용자 정보를 데이터베이스에서 가져오는 getUser() 메써드를 포함하고 있다. getUser()메써드는 가져온 데이터를 UserVO에 저장하여 반환하고 있다. 실무에서 실질적인 데이터베이스와 접속 및 데이터 조작은 UserDAO를 통하여 이루어지게 된다.

UserDAO를 보면 사용자의 아이디가 javajigi일 경우에 아이디가 javajigi, 비밀번호 password를 가지는 UserVO를 반환하고 아이디가 javajigi가 아닌 사용자에 대해서는 null을 반환한다.

로그인 예제 Controller 부분

로그인 과정을 처리하기 위하여 뷰에서 데이터를 받아 모델과 통신의 역할을 하는 것이 Controller의 역할이다. 스트러츠에서 Control의 역할은 ActionServlet에서 struts-config.xml의 설정 정보를 이용하여 이루어진다. 모델과의 Communication을 담당하는 부분은 Action을 상속하는 클래스에서 이루어지게 된다.

이 예제에서는 LoginAction으로 정의하였다.

package net.javajigi.tutorial.action;

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

import net.javajigi.tutorial.Constants;
import net.javajigi.tutorial.form.LoginForm;
import net.javajigi.tutorial.user.UserDAO;
import net.javajigi.tutorial.user.UserVO;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class LoginAction extends Action {
	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		
		LoginForm loginForm = (LoginForm)form;
		
		String inputId = loginForm.getId().trim();
		String inputPassword = loginForm.getPassword();
		
		UserDAO userDAO = new UserDAO();
		UserVO userVO = userDAO.getUser(inputId);
		
		ActionErrors errors = new ActionErrors();

		if ( userVO == null ) {
			//UserVO가 null이면 존재하지 않는 아이디로 파악하여 에러 메세지를 출력.
       		errors.add(ActionErrors.GLOBAL_ERROR,
            	new ActionError("error.id.notexisted"));
		} else {
			//사용자 아이디와 비밀번호가 틀릴경우 비밀번호가 틀리다는 에러 메세지 출력
			if ( !inputPassword.equals(userVO.getPassword()) ) {
	       		errors.add(ActionErrors.GLOBAL_ERROR,
	            	new ActionError("error.password.match"));
			}			
		}
		
		//로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors에 에러를 저장하고
		//로그인 폼페이지로 반환하게된다.
		//mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
		//input attribute에 정의된 페이지로 이동하게 된다.
		if (!errors.isEmpty()) {
	    	saveErrors(request, errors);
           	return (mapping.getInputForward());
		}
		
		//로그인 과정이 정상적으로이루어 지면 userVO객체를 세션에 저장.
		HttpSession session = request.getSession();
		session.setAttribute(Constants.USER_KEY, userVO);
		
		//로그인 과정이 정상적으로이루어진 다음 메인페이지로 이동한다.
		//mapping의 findForward에서 사용하는 key값은 struts-config.xml의 forward태그의 
		//name attribute를 이용한다.
		return(mapping.findForward("mainpage"));
	}
}

LoginAction은 사용자가 입력한 데이터를 LoginAction의 FormBean으로 사용되고 있는 LoginForm을 이용하여 받고 있다. 사용자가 입력한 아이디를 UserDAO에 전달하여 UserVO를 얻고 있음을 볼 수 있다. 이와 같이 Action클래스에서 뷰와 모델의 실질적인 통신역할을 하고 있다.

뷰와 모델의 데이터들을 비교하여 로그인 유무를 결정하며, 로그인이 정상적으로 이루어지면 세션에 사용자 정보를 저장한다음 메인페이지로 이동함을 볼 수 있다.

LoginAction에서 FormBean으로 사용된 LoginForm은 다음과 같다.

package net.javajigi.tutorial.form;

import javax.servlet.http.HttpServletRequest;

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

public class LoginForm extends ActionForm {
	private String id = null;
	private String password = null;	

	public ActionErrors validate(
		ActionMapping mapping,
		HttpServletRequest request) {

       	ActionErrors errors = new ActionErrors();
			
		if ( (id.length() < 4) || (id.length() > 10) ) {
			errors.add("error.id.lengtherror", new ActionError("error.id.lengtherror"));
		} 
		
		if ( (password.length() < 4) || (password.length() > 10) ) {
			errors.add("error.password.lengtherror", new ActionError("error.password.lengtherror"));
		}		

		return errors;	    
	}

	public void reset(ActionMapping mapping, HttpServletRequest request) {
		password = "";
		id = "";

	}

	public String getPassword() {
		return password;
	}

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

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
}

강좌 1에서의 LoginForm과 거의 비슷하다. 달라진 부분은 validate내부이다. 이 예제에서는 아이디와 비밀번호가 4자리 이상, 10자리 이하여야 된다고 가정하고 validate를 작성하였다. FormBean에서는 LoginForm과 같이 사용자가 입력한 값에 대한 유효성을 검사를 validate 메써드를 이용하여 서버사이드에서 가능하다. 물론 대부분의 웹 개발에서 유효성 검사는 클라이언트에서 스트립트를 이용하여 이루어지는 것이 현실이다.

로그인 예제 Configuration 및 Resource Bundle

지금까지 생성한 LoginAction과 LoginForm을 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>
    
    <!-- ========== Data Source Configuration =============================== -->
    <data-sources />
    
    <!-- ========== Form Bean Definitions ================================== -->
    <form-beans type="org.apache.struts.action.ActionFormBean">
        <form-bean name="loginForm" type="net.javajigi.tutorial.form.LoginForm">
            <form-property name="password" type="java.lang.String" />
            <form-property name="id" type="java.lang.String" />            
        </form-bean>        
    </form-beans>
    
    <!-- ========== Global Exception Definitions ============================== -->
    <global-exceptions />
    
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards type="org.apache.struts.action.ActionForward">
        <forward name="mainpage" path="/tutorial/mainpage.jsp" />
        <forward name="loginForm" path="/tutorial/login.jsp" redirect="true" />
    </global-forwards>
    
    <!-- ========== Action Mapping Definitions =============================== -->
    <action-mappings type="org.apache.struts.action.ActionMapping">
        <action
            attribute="loginForm"
            input="/tutorial/login.jsp"
            name="loginForm"
            path="/login"
            type="net.javajigi.tutorial.action.LoginAction" />        
    </action-mappings>
    
    <!-- ========== Controller Configuration ================================ -->
    <controller />
    
    <!-- ========== Message Resources Definitions ============================ -->
    <message-resources parameter="net.javajigi.tutorial.MessageResources" />
    
    <!-- ========== Plug Ins Configuration ================================= -->
</struts-config>

form-bean태그를 이용하여 LoginForm을 설정하고 있으며, action에서 loginForm을 이용하고 있음을 볼 수 있다. action에서 LoginAction을 path login에 mapping시키고 있다.

다음은 LoginAction과 LoginForm에서 사용한 에러 메세지는 MessageResources에 다음과 같이 저장하면 된다.

login.title = Welcome to RegisterUser
login.login = 로그인

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

mainmenu.title=Main Page
mainmenu.presentation=이 페이지는 Main Menuh2.


#에러 메세지
error.id.lengtherror=<li>아이디는 4자리 이상, 10자리 이하여야 합니다.</li>
error.password.lengtherror=<li>비밀번호는 4자리 이상, 10자리 이하여야 합니다.</li>

error.id.required=<li>아이디는 꼭 입력해야 됩니다.</li>
error.id.notexisted=<li>존재하지 않는 아이디입니다.</li>
error.password.required=<li>비밀번호는 꼭 입력해야 됩니다.</li>
error.password.match=<li>비밀번호가 틀립니다.</li>

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

로그인 예제 View부분

로그인 예제의 View는 강좌1의 로그인과 크게 달라진 것이 없다.

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
    <title><bean:message key="login.title" /></title>
    <html:base/>
</head>

<body>
<html:form action="/login">
	<html:errors />
	<br/>
	
    <bean:message key="prompt.id" />
    <html:text property="id" />
    <br />
    <bean:message key="prompt.password" />
    <html:password property="password" />
    <br />
    <html:submit>
        <bean:message key="login.login" />
    </html:submit>
</html:form>
</body>
</html:html>
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>

<app:checkLogin/>

<html:html locale="true">
<head>
    <title><bean:message key="mainmenu.title" /></title>
    <html:base/>
</head>

<body>

<bean:message key="mainmenu.presentation" />
</body>
</html:html>

Login.jsp는 강좌 1과 같다. mainpage.jsp(강좌 1에서는 mainmenu.jsp)에서 달라진 부분은 <app:checkLogin/>가 추가되었다. checkLogin태그의 역할은 로그인하지 않은 사용자는 로그인 페이지로 이동하도록 하는 역할을 한다. 이 같이 여러페이지에서 사용되는 코드는 커스텀 태그화하여 재사용하면 유용하다.

checkLogin태그의 소스는 다음과 같다.

package net.javajigi.tutorial.tag;

import java.io.IOException;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

import net.javajigi.tutorial.Constants;
import org.apache.struts.action.Action;
import org.apache.struts.util.MessageResources;
import org.apache.struts.config.ModuleConfig;

public final class CheckLoginTag extends TagSupport {
	private String name = Constants.USER_KEY;
	private String page = "/tutorial/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);
		else {
			try {
				pageContext.forward(page);
			} catch (Exception e) {
				throw new JspException(e.toString());
			}
			return (SKIP_PAGE);
		}

	}

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

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

	}
}

CheckLoginTag는 사용자 정보가 세션에 있는지를 판단후에 세션에 사용자 정보가 없을 경우 login.jsp로 이동시키는 역할을 한다. CheckLoginTag의 tld 및 설정은 첨부되는 소스를 통하여 이해하기 바란다.

지금까지 스트러츠 프레임?을 이용하여 로그인예제를 구현하는 과정을 살펴보면서 스트러츠 Configuration과정을 살펴보았다. 스트러츠 Configuration에 대한 전체를 설명하지 않았지만 계속되는 강좌와 예제를 통하여 하나씩 추가하여 설명하도록 하겠다.

다음 강좌에서는 ActionServlet, RequestProcessor, Action, ActionForm등 스트러츠 프레임?의 근간이 되는 클래스들에 대하여 살펴보도록 하겠다.

참고 자료

Posted by 1010