'JAVA'에 해당되는 글 76건

  1. 2009.05.18 :: 스트럿츠2 - 1일차
  2. 2009.05.18 :: iBatis에서 객체 매핑시 인스턴스 변수명 규칙?
  3. 2009.05.18 :: iBatis에서 insert후 자동 sequence Key 값을 가져오는 설정 방법
  4. 2009.05.18 :: HTTP 인증을 요구하는 페이지의 컨텐츠에 접근하기
  5. 2009.05.17 :: log4j.properties 사용 방법
  6. 2009.05.17 :: 자바 파일 압축하기
  7. 2009.05.17 :: 자바 기본 환경 설정.....
  8. 2009.05.17 :: 옵저버 패턴
  9. 2009.05.17 :: [Design Patterns] Strategy Pattern 4
  10. 2009.05.17 :: [Design Patterns] Strategy Pattern 3
  11. 2009.05.17 :: [Design Patterns] Strategy Pattern 2
  12. 2009.05.17 :: [Design Patterns] Strategy Pattern 1
  13. 2009.05.17 :: java.util.regex.PatternSyntaxException: Unclosed group near index 1
  14. 2009.05.15 :: JAVA - TCP 소켓 프로그램 예제의 흐름
  15. 2009.05.15 :: JAVA - TCP 소켓프로그램 예제
  16. 2009.05.15 :: Protocol 설계를 이용한 로그인 인증예제 (소켓, 프로토콜, 바이트스트림)
  17. 2009.05.14 :: 숫자인지 문자인지 판별하기 (JAVA)
  18. 2009.05.14 :: iBATIS기초 - JNDI설정
  19. 2009.05.07 :: 자바 Annotation 사용하기
  20. 2009.05.07 :: Log4J 레이아웃
  21. 2009.05.07 :: Log4J 사용하기
  22. 2009.05.07 :: Spring XML설정파일을 위한 12가지 가장 좋은 선택
  23. 2009.05.07 :: JXL로 Excel파일 만들고 PC에 저장하기
  24. 2009.05.07 :: 자바 JXL API 를 이용한 Excel 파일 만들기
  25. 2009.05.06 :: JUNIT
  26. 2009.05.06 :: 스프링 샘플
  27. 2009.05.06 :: 스프링 > 스트럿츠2
  28. 2009.05.06 :: [스프링]Spring Mapping Patterns
  29. 2009.05.06 :: 스프링 handlerMapping
  30. 2009.05.06 :: 스프링2.5 - 커스텀 태그
JAVA/STRUTS 2009. 5. 18. 12:39
http://localhost:8080/프로젝트명/네임스페이명/액션명.action
posted by 나는너의힘
:
JAVA/iBatis 2009. 5. 18. 00:21
sqlMap에서 SELECT 문을 다음과 같이 하고
<sqlMap>
  <select id="getContent" parameterClass="int" resultClass="content">
  <![CDATA[
    SELECT
        SEQ_ID
        , CREATED_DATE
        , TITLE
        , CONTENT
        , CONTENT_TYPE
    FROM CONTENT
    WHERE SEQ_ID=#id#
  ]]>
  </select>
</sqlMap>

객체의 인스턴스 변수를 다음과 같이 했습니다.
    private int seqId;
    private String createdDate;
    private String title;
    private String content;
    private String contentType;

TABLE 'CONTENT'에는 다음과 같이 DATA가 존재합니다.
사용자 삽입 이미지


Query를 수행 한 결과 log가 다음과 같았습니다.
[COTAMOT]  INFO,[2008-02-13 10:29:57,546],[main],(IBatisDaoTest.java:39),#### SEQ_ID : 0
[COTAMOT]  INFO,[2008-02-13 10:29:57,546],[main],(IBatisDaoTest.java:40),#### CREATE_DATE : null
[COTAMOT]  INFO,[2008-02-13 10:29:57,636],[main],(IBatisDaoTest.java:41),#### TITLE : 첫번째 글
[COTAMOT]  INFO,[2008-02-13 10:29:57,636],[main],(IBatisDaoTest.java:42),#### CONTENT : 첫번째 글 내용입니다.
[COTAMOT]  INFO,[2008-02-13 10:29:57,636],[main],(IBatisDaoTest.java:43),#### TYPE : null

네이밍 규칙에 맞지 않은듯 합니다. '_'를 쓰지 않은 'TITLE', 'CONTENT'는 올바르게 담아오지만 그 외의 것은 값을 가져오지 못했습니다. Query문을 다음과 같이 변경하였습니다.
<sqlMap>
  <select id="getContent" parameterClass="int" resultClass="content">
  <![CDATA[
    SELECT
        SEQ_ID as seqId
        , CREATED_DATE as createdDate
        , TITLE as title
        , CONTENT as content
        , CONTENT_TYPE as contentType
    FROM CONTENT
    WHERE SEQ_ID=#id#
  ]]>
  </select>
</sqlMap>

결과는 다음과 같습니다.
[COTAMOT]  INFO,[2008-02-13 10:51:39,999],[main],(IBatisDaoTest.java:39),#### SEQ_ID : 1
[COTAMOT]  INFO,[2008-02-13 10:51:39,999],[main],(IBatisDaoTest.java:40),#### CREATE_DATE : 2008-02-12
[COTAMOT]  INFO,[2008-02-13 10:51:39,999],[main],(IBatisDaoTest.java:41),#### TITLE : 첫번째 글
[COTAMOT]  INFO,[2008-02-13 10:51:39,999],[main],(IBatisDaoTest.java:42),#### CONTENT : 첫번째 글 내용입니다.
[COTAMOT]  INFO,[2008-02-13 10:51:40,009],[main],(IBatisDaoTest.java:43),#### TYPE : NN

Query문에서 각 column을 alias로 해서 인스턴스 변수와 일치시켰더니 데이터를 올바르게 가져옵니다. 자바의 기본 네이밍 규칙이 먹히지 않는 것일까요? 찾아봐야 겠습니다. 또한 resultClass를 사용하면 자동을 mapping 해서 좋은데 그 만큼 부하가 걸리기 때문에 resultMap을 사용하는 것이 더 좋다는 얘기도 있습니다. 올바를 사용법을 알아야 겠습니다.
posted by 나는너의힘
:
JAVA/iBatis 2009. 5. 18. 00:20
DB의 Table의 관계가 child의 FK가 parent의 auto_increament 속성의 PK와 연결되어 있다고 하겠습니다.
이 경우 Insert 행위시에 parent에 대해서 child도 함께 Insert 된다고 가정하겠습니다. 이럴 경우 parent를 Insert처리하고 Select를 던져서 해당 key정보를 얻어야 child를 Insert할 수 있습니다. 이럴 경우 iBatis에서 설정하는 방법을 알아보겠습니다.
  sqlMap xml설정을 다음과 같합니다.
<insert id="insertContent" parameterClass="content">
    <![CDATA[
    INSERT INTO CONTENT (
        CREATED_DATE, TITLE, CONTENT, CONTENT_TYPE
    ) VALUES (
        now(), #title#, #content#, #contentType#
    )
    ]]>
    <selectKey keyProperty="seqId" resultClass="int">
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

위와 같이 설정하고 소스부분에서 다음과 같이 작성합니다.
Content content = new Content();

content.setTitle("selectKey 테스트");
content.setContent("<selectKey.../>에 대하여 테스트 합니다.");
content.setContentType("NN");

Integer seqId = contentService.insertContent(content);

이 결과는 Integer Type의 객체로 Insert된 sequence Key 값을 가져옵니다.
  위 설정에서 <selectKey keyProperty="seqId" resultClass="int"> 부분을 살펴 보면 seqId는 Content 객체의 Integer Type의 인스턴스 변수입니다. native Type (int)으로 해도 문제가 발생하지 않는 것으로 알고 있습니다. 소스 부분에서

  Integer seqId = contentService.insertContent(content);

이 부분에서 반드시 Integer Type으로 받아야 합니다. 그렇지 않으면 CastingException이 발생하게 됩니다. (native Type int로 받아도 문제 없습니다^^)

  수행된 log를 출력하면 다음과 같습니다.
[COTAMOT] DEBUG,[2008-02-14 16:44:52,006],[main],(JakartaCommonsLoggingImpl.java:27),{conn-100000} Connection
[COTAMOT] DEBUG,[2008-02-14 16:44:52,136],[main],(JakartaCommonsLoggingImpl.java:27),{pstm-100001} Executing Statement:
INSERT INTO CONTENT (
    CREATED_DATE, TITLE, CONTENT, CONTENT_TYPE
) VALUES (
    now(), 'selectKey 테스트', '<selectKey.../>에 대하여 테스트 합니다.', 'NN'
)
[COTAMOT] DEBUG,[2008-02-14 16:44:52,346],[main],(JakartaCommonsLoggingImpl.java:27),{pstm-100002} Executing Statement:         
        SELECT LAST_INSERT_ID()

  Insert가 수행되고 다음에 Select 가 수행됨을 알수 있습니다.
  참고로 sequence 값을 가져오는 방법을 DBMS별로 나열하면 다음과 같습니다.
oracle - nextval(#sequence#)
mssql - SCOPE_IDENTITY()
mysql - LAST_INSERT_ID()

  이렇게 되면 child를 Insert 처리 할 때 FK 값을 얻는데 간편합니다.
posted by 나는너의힘
:
JAVA 2009. 5. 18. 00:14
인터넷에 존재하는 웹 페이지 중에서 접근을 위해서는 HTTP 인증을 해야 하는 페이지가 간혹 있다. 이러한 페이지에는 단순히 인증 정보를 POST와 같은 HTTP 프로토콜의 메소드로 넘겨 인증을 할 수가 없는데, 이럴 땐 Java에서 제공하는 HttpURLConnection 객체의 setRequestProperty 메소드를 이용해서 요청 헤더에 필요한 인증 정보를 첨부해서 보내면 인증을 할 수 있어 원하는 웹 컨텐츠에 접근할 수 있다.

필요한 코드는 다음과 같다.

HttpURLConnection connection = (HttpURLConnection)url.openConnection();

connection.setRequestProperty("Authorization", "Basic " +

        new sun.misc.BASE64Encoder().encode("[아이디]:[비밀번호]".getBytes()));


이 렇게 작성한 다음 인증 정보를 요청 property에 지정하였으므로 connection 객체를 통해서 InputStream 타입으로 읽어오면 원하는 컨텐츠에 접근하여 데이터를 가져올 수 있다. setRequestProperty에는 여러가지 요청정보를 추가할 수 있는데, 일단 가장 필요한 내용은 인증 정보이므로 다른건 나도 잘 모르겠고, 간단한 작업을 하는덴 그리 필요하진 않은 것 같다...

그런데 "Basic"이라는 건 인증 수준을 나타내는 건가? 검색해 보니... RFC 2617에 Basic은 클라이언트가 아이디와 패스워드를 각각의 영역에 접근하기 위해 제공해야 하는 모델에 기반한 인증 스키마라고 한다. 아래는 RFC 2617의 원문중....

"The "basic" authentication scheme is based on the model that the client must authenticate itself with a user-ID and a password for each realm."

"opaque string"이란 단어가 나오는데... BASE64Encoder로 인코딩해서 리턴되는 문자열을 가리키는 것 같다.
posted by 나는너의힘
:
JAVA/LOG4J 2009. 5. 17. 23:43
log Level 정의

 FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.
 ERROR : 일반 에러가 일어 났을 때 사용합니다.
 WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.
 INFO : 일반 정보를 나타낼 때 사용합니다.
 DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.

ConversionPattern 사용 방법

 %p  debug, info, warn, error, fatal 등의 priority 가 출력된다.
 %m  로그내용이 출력됩니다
 %d  로깅 이벤트가 발생한 시간을 기록합니다.
   포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
 %t  로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
 %%  % 표시를 출력하기 위해 사용한다.
 %n  플랫폼 종속적인 개행문자가 출력된다. rn 또는 n 일것이다.
 %c  카테고리를 표시합니다
   예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
 %C  클래스명을 포시합니다.
   예)클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
 %F  로깅이 발생한 프로그램 파일명을 나타냅니다.
 %l  로깅이 발생한 caller의 정보를 나타냅니다
 %L  로깅이 발생한 caller의 라인수를 나타냅니다
 %M  로깅이 발생한 method 이름을 나타냅니다.
 %r  어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
 %x  로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
 %X  로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.

log4j.properties 파일 사용 예

log4j.rootLogger=INFO, stdout, rolling

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
log4j.appender.stdout.target=System.err

#rolling 어펜더는 파일로 처리한다라고 정의
 log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
#로그 파일 이름은 output.log
 log4j.appender.rolling.File=output.log
#true면 WAS를 내렸다 올려도 파일이 리셋되지 않습니다.
 log4j.appender.rolling.Append=true
#파일 최대 사이즈는 500KB로 설정
 log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의
 log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#rolling 어펜더는 패턴 레이아웃 포맷
 log4j.appender.rolling.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:33

자바 파일 압축하기 소스를 만들어 보았다.

기본적으로 주석들이 영어인 이유는…… 그냥 api에서 해당 함수들에 대한 설명을 긁어왔기 때문이니….

알아서 해석하시길…ㅎㅎ

package com.eunicon.common.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class MakeZip {
    public void compress(String[] source, String target){
        byte[] buf = new byte[1024];

        try {           
            // for writing files in the ZIP file format.
            ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(target));

            // source라는 문자 배열에 정의된 파일 수 만큼 파일들을 압축한다.
            for (int i = 0; i < source.length; i++) {
                FileInputStream in = new FileInputStream(source[i]);

                /* Begins writing a new ZIP file entry and positions the stream to the start of the entry data.
                 * new ZipEntry(source[i]) : the ZIP entry to be written.
                 */
                zipOut.putNextEntry(new ZipEntry(source[i]));

                int len;               
                while ((len = in.read(buf)) > 0) {
                    /* Writes an array of bytes to the current ZIP entry data.
                     *
                     * buf    : the data to be written.
                     * 0    : the start offset in the data.
                     * len    : the number of bytes that are written.
                     */
                    zipOut.write(buf, 0, len);
                }

                // Closes the current ZIP entry and positions the stream for writing the next entry.
                zipOut.closeEntry();
                in.close();
            }
            // Closes the ZIP output stream as well as the stream being filtered.
            zipOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        /* 압축할 원본 파일들
         * WINDOWS 파일 시스템을 기준으로 한 절대경로로 지정함.
         */
        String[] source = new String[] {
                "D:\\DownLoads\\smsp2.gif",
                "D:\\DownLoads\\2008_03_IMG_6449.jpg",
                "D:\\DownLoads\\2008_03_IMG_6244.jpg" };

        // 압축 파일이 저장될 target을 지정한다.
        String target = "D:\\DownLoads\\20090109.zip";
        MakeZip makeZip =  new MakeZip();
        makeZip.compress(source, target);
    }
}

posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:31
한동한 포스팅이 너무 뜸했다...

초심이 점점 변하면서 날이 갈수록 게을러 지는거 같다...

무엇인가를 포스팅하고 싶은데 마땅히 할게 없어서 자바 기본 세팅에 대해 포스팅 해보고자 한다.

환경설정을 위해 우선 내컴퓨터 - 마우스 오른쪽 클릭 - 속성으로 이동한다.
사용자 삽입 이미지

속성 - 고급 - 환경변수를 클릭한다.
사용자 삽입 이미지

아래와 같이 환경변수 창이 뜨는 것을 확인할 수 있다.
프로그램 전반에 걸쳐 사용할 것들이 있다면 Administrator에 정의 하면되고, 해당 사용자에 대해서 설정할 것이라면 시스템변수에 설정하면 된다. 필자는 시스템 변수에 설정할 것이다.
아래 그림에서 시스템 변수의 새로 만들기 버튼을 클릭한다.
사용자 삽입 이미지

먼저 JAVA_HOME을 설정한다.
JAVA_HOME은 jdk가 설치된 폴더의 위치를 의미한다.
간혹 JRE가 설치된 폴더와 착각하는 사람들이 있는데 혼동하지 말자!!!
사용자 삽입 이미지

아래 그림과 같이 JAVA_HOME일 설정된 것을 확인 할 수 있다.
사용자 삽입 이미지

JAVA_HOME이 설정되었다면 다음은 path를 설정해 보자.
시스템변수 창에서 Path항목을 더블클릭 하면 아래와 같은 창이 뜬다.
Path의 가장 앞에 %JAVA_HOME%\bin; 이라고 타이핑하자.
가장 앞에 놓는 이유는 JDK가 오라클이나 기타 다른 프로그램에서 다른 버전으로 사용할 수도 있기 때문이다.
사용자 삽입 이미지

설정을 마쳤다면 확인해 봐야 한다.
아래 그림과 같이 실행창을 띄운다.
실행창은 윈도우키 + r 을 누르면 뜬다.
사용자 삽입 이미지

java -version 이라고 typing해보자.
이 명령은 jre를 통해 해당 자바 실행 환경의 버전을 확인할 수 있다.
사용자 삽입 이미지

자바의 버전을 확인 했다면 이번에는 JDK가 환경세팅에서 정상적으로 세팅되었는지 확인하기 위해 javac 라고 typing해보자.
아래 그림과 같이 여러가지 옵션들이 출력된다면 정상적으로 JDK가 환경설정에 등록된 것이다.
사용자 삽입 이미지

이상으로 자바 환경 설정에 대해 간단히 알아 보았다.
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:31

오늘 보려고 하는 내용은 옵저버 패턴이다.

옵저버 패턴의 정의는 다음과 같다.

옵저버 패턴(Observer Pattern)에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고 자동으로 내용이 갱신되는 일대다(one-to-many) 의존성을 정의합니다.

처음 보는 사람들은 저게 무슨말인지 도통 모를것 같다. 이 글만 읽고도 안다면 능숙한 개발자이거나 개발을 하면 크게 될 사람이라고 생각한다..ㅋㅋㅋ (ㅡㅡ;;)

간단히 풀어서 설명하면, 옵저버 패턴에서는 상태를 저장하고 있는 주제객체와 주제객체가 가지고 있는 상태값에 의존하는 옵저버들이 있다.

1. 주제 객체가 생성되어 서비스를 시작한다.

2. 옵저버 객체가 되기 위해 주제 객체에 옵저버 객체로 등록한다.

3. 옵저버 객체에서 탈퇴하고자 주제 객체에서 해지한다.

위의 순서를 그림으로 표현하면 아래와 같다.

사용자 삽입 이미지
 위의 이미지가 옵저버 패턴을 이해하는데 많은 도움이 될거라 생각한다.

다음 포스팅에서는 옵저버 패턴을 이용한 소스코드를 작성해 보자!!!
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:31

이전 포스팅을 동적으로 나는 행위와 우는 행위를 바꿀수 있도록 수정해 보자.

클래스 다이어 그램은 다음과 같다.

사용자 삽입 이미지

수정해야 할 내용은 다음과 같다.

1. Duck클래스에 2개의 세터메소드를 추가해준다.
   setFlyBehavior(), setQuackBehavior()

2. 시뮬레이션 프로그램에서 원하는 메소드를 호출한다.

소스가 필요한 사람을위해 소스 추가해 놓는다.

src.zip


http://eunicon.tistory.com/11
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:30

이전 포스팅에서 만든 프로그램의 문제점에 대해 다시 정리해 보자.

자바 인터페이스에는 구현된 코드가 전혀 들어가지 않기때문에 코드 재사용을 할 수가 없다. 즉, 한 행동을 바꿀때마다 그 행동이 정의 되어 있는 모든 서브클래스들을 일일이 수정해줘야 하는데, 이는 새로운 버그를 발생시킬 확률이 높다.

이러한 상황에 어울리는 디자인 원칙은 다음과 같다.

"바뀌는 부분을 바뀌지 않는 부분과 분리해서 캡슐화 시킨다. 그렇게 하면 나중에 바뀌지 않는 부분에는 영향을 미치지 않은채로 그 부분만 고치거나 확장할 수 있다."

무슨 뜻인지 이해가 가는지...?? ㅋㅋㅋ

클래스 다이어 그램을 먼저 보면서 이해해 보자.
사용자 삽입 이미지

위에서 말한 디자인 원칙에 따르면, 자주 변하는 부분은 fly()와 quack() 이다.
이 부분들을 Duck 클래스와 분리하려면, 나는것과 관련된 집합과 우는것과 관련된 집합. 즉, 두 개의 클래스 집합(set)을 만들어야 한다.

그리고 각 클래스 집합에는 각각의 행동을 구현한 것을 모두 집어 넣으면 된다.
(ex 꽥꽥거리는 클래스, 삑삑거리는 클래스, 소리는 내지 않는 클래스 ... etc)

두개의 클래스 집합을 만들어야 한다는 것을 알았다면 다음으로 알아야 할 것이 무었일까??

바로 최대한 유연하게 코드를 작성해야 한다는 것이다. 처음부터 이런 문제가 발생한 원인은 오리의 행동인 fly()와 quack()가 유연하지 않았기 때문이었다.

추가로 Duck의 인스턴스에 행동을 할당할 수 있어야 한다. (이해가 힘들다면 소스를 확인해 보길...설명하기 귀찮아 지기 시작함..ㅡㅡ;;)

위와 같이 유연하게 코드를 작상하고자 한다면, 구현이 아닌 인터페이스에 맞춰서 프로그래밍해야 한다.

인터페이스에 맞춰 프로그래밍한다는 말은 자바의 인터페이스를 사용하라는 의미일 수도 있지만, 조금더 깊게 의미를 생각하면, 상위 형식에 맞춰서 프로그래밍함으로써 다형성을 활용해야 한다는 것을 의미한다.

그래서 위 다이어 그램과 같이 FlyBehavior인터페이스를 구현한 클래스 집합과 QuackBehavior인터페이스를 구현한 클래스 집합을 만든 것이다.

이런식으로 디자인 한다면, 다른 형식의 객체에서도 나는 행동과 꽥꽥 거리는 행동을 재사용 할 수가 있다.

음....추가로 설명해야 할 것들이 상당히 있지만, 역시나 공부한 내용을 다시 설명하는 것은 정말이지 어려운 것 같다. 새삼 선생님들을 존경하게 되었다는...ㅡㅡ;;;

마지막으로 소스하나를 올리고 이번 포스팅을 마치고자 한다.
src.zip



http://eunicon.tistory.com/10
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:30

이전 포스팅에서 구현한 프로그램의 문제점에 대해서 생각해 보자

간단한 예를 들어서 문제를 집어보겠다.

오리 시뮬레이션에서 오리가 하늘을 날 수 있도록 하고 싶을때 어떻게 구현해야 할까??

첫번째로 생각해 볼 수 있는것은 super class인 Duck 클래스에 fly()를 추가해주는 것이다.

누구나 가장 먼저 이 생각을 하지 않을까 싶다.

하지만 이렇게 할 경우, 하늘을 날지 않는 나무 오리라든가 고무 오리마저 하늘을 나는 경우가 발생한다.

나무 오리나 고무 오리가 하늘을 날지 못하게 하려면 별 수 없이 overriding(재정의)를 해야 한다.

거기에다가 나무 오리의 경우는 꽥꽥거리지 못하게 quack()도 overriding해줘야 한다.

이렇게 super class에 의존도가 높아지는 코딩을 한다면 다음과 같은 단점이 생긴다.

  1. 서브 클래스에서 코드가 중복된다.
  2. 실행시에 특징을 바꾸기 힘들다.
  3. 모든 오리들의 행동을 알기 힘들다.
  4. 코드를 변경했을 때 다른 오리들한테 원치 않은 영향을 끼칠수 있다.

만약 프로그램을 자주 갱신해야 한다면, 정말이지 위와 같은 방법으로 코딩하는 것은 개발자를 잡는 일일 것이다.

그렇다면 다음으로 생각할 수 있는 것은 무엇일까??

상속을 통해서 되지 않는다면, 다음으로 생각할 수 있는 것은 인터페이스가 아닐까 싶다.

인터페이스를 이용한 클래스 다이어 그램을 보자.

사용자 삽입 이미지
위와 같이 인터페이스를 이용한다면 어떨까??

새로운 행동을 추가할때 인터페이스를 따른다면 새로운 클래스를 추가할 때마다 fly()와 quack()메소드를 일일이 살펴보고 상황에 따라 override할 필요는 없을 거 같다.

하 지만 이렇게 구현할 경우 코드가 중복될 수밖에 없다. 또한 수정해야 하는 경우 중복되는 모든 코드들을 수정해야 한다.(ex fly()를 수정해야 할 경우) 즉, 코드 재사용은 생각할 수가 없다는 것이다. 게다가 코드 관리를 하기도 정말이지 힘들 것이다.(오리마다 나는 방식이 모두 다르다면, 모든 오리 클래스에 fly()가 다 다를테니 당연히 관리가 힘들다.)

정말이지 문제점이 많은 것 같다.
그래서 필요한 것이 디자인패턴이 아닌가 싶다.
다음 포스팅에서 이런 문제들을 해결할 Strategy Pattern 에 대해 포스팅 해보겠다.

소스들
SimpleDuckSimul.java

http://eunicon.tistory.com/9
posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:29

포스팅한다는 것이 다른 사람들에게 보여주기 위한 것일수도 있지만,
머 어차피 이 포스팅은 내가 내 만족을 위해서 내 멋대로 적는 것이니 절대로...경어는 안쓴다..ㅡㅡ;;

한동안 머리를 굴리지 않았더만 머리가 녹이 슨 것같다.
그래서 이전에 공부했던 디자인패턴 책을 보면서 포스팅을 시작해 보려 한다.
오늘 포스팅하려는 것은 Head First Design Pattern 의 제일 첫번째 Strategy Pattern에 대해서이다.
본격적으로 스트릿티지 패턴에 들어가기 전에 다음 클래스 다이어 그램을 보자.

사용자 삽입 이미지

위의 다이어 그램은 간단한 오리 시뮬레이션 프로그램이다.
소스 코드는 첨부 파일을 참조해 주길 바란다.

위의 다이어 그램에 대해 간단히 설명하자면 다음과 같다.
우선 오리의 특성(quack, swimming, shape)을 가진 super class를 만든다.
(오리의 모양은 오리마다 다르므로 추상메소드로 선언한다.)
그리고 물오리, 붉은머리 오리등 오리별로 Duck클래스를 상속받아 개개의 개체를 구현한다.

시뮬레이션을 원하는 오리가 있다면, 시뮬레이션 프로그램에서 구현한다.
(구현한다는 말은 new 연산자를  사용해서 객체를 생성한다는 의미로 보면 된다.)

위의 다이어 그램을 이해하고 문제점을 찾아내는 것이 이번 패턴 공부의 시작이다.
다음 포스팅때가지 각자가 한번 생각해 보길 바란다.
http://eunicon.tistory.com/8

http://eunicon.tistory.com/attachment/hk2.java
http://eunicon.tistory.com/attachment/ik3.java
http://eunicon.tistory.com/attachment/jk1.java
http://eunicon.tistory.com/attachment/hk3.java





posted by 나는너의힘
:
JAVA 2009. 5. 17. 23:26
테스트 서버 가지고 뻘짓을 하던중 다음과 같은 exception이 발생했다.

java.util.regex.PatternSyntaxException: Unclosed group near index 1
(
 ^
        at java.util.regex.Pattern.error(Pattern.java:1541)
        at java.util.regex.Pattern.accept(Pattern.java:1399)
        at java.util.regex.Pattern.group0(Pattern.java:2313)
        at java.util.regex.Pattern.sequence(Pattern.java:1586)
        at java.util.regex.Pattern.expr(Pattern.java:1558)
        at java.util.regex.Pattern.compile(Pattern.java:1291)
        at java.util.regex.Pattern.<init>(Pattern.java:1047)
        at java.util.regex.Pattern.compile(Pattern.java:808)
        at eluon.xroshot.util.StringUtil.replaceIgnoreCase(StringUtil.java:321)
        at eluon.xroshot.util.StringUtil.xssFilter(StringUtil.java:332)
        at eluon.xroshot.action.sms.SendStep1Controller.process(SendStep1Controller.java:80)
        at eluon.xroshot.action.BaseController.handleRequestInternal(BaseController.java:514)
        at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
        at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.ja
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:819)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:754)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:399)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:354)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:126)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:103)
        at com.caucho.server.http.FilterChainServlet.doFilter(FilterChainServlet.java:96)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:77)
        at com.caucho.server.http.FilterChainFilter.doFilter(FilterChainFilter.java:88)
        at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:127)
        at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:52)
        at com.caucho.server.http.FilterChainFilter.doFilter(FilterChainFilter.java:88)
        at com.caucho.server.http.Invocation.service(Invocation.java:315)
        at com.caucho.server.http.CacheInvocation.service(CacheInvocation.java:135)
        at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:253)
        at com.caucho.server.http.HttpRequest.handleConnection(HttpRequest.java:170)
        at com.caucho.server.TcpConnection.run(TcpConnection.java:139)
        at java.lang.Thread.run(Thread.java:534)

PatternSyntaxException이라... 네이놈에게 물어본 결과 이 녀석은 정규식에서 문법적 에러를 나타내는 unchecked exception이라고 한다. 먼말인지...ㅡㅡ;;;
그래서 일단 프레임워크 쪽 문제라고  보기 힘들어서 우리 소스에서 처음 문제가 발생한 소스로 이동했다.
해당 메소드를 확인해보니, Cross-Site Scripting에 대비해서 문자코드를 변경하는 코드였다.
그렇다면 문자코드를 변환하면서 문제가 발생했다고 유추할수 있었다.
그래서 일단 api에서 에러가 발생하는 exception에 대해 찿아보았다.
(확인해보니 이 패키지는 1.4버전부터 지원된 것이었다. ㅡ,.ㅡ;;)

확인하니 역시 정규식 패턴에 문법 에러를 나타낸다고 한다. (이건 네이놈이랑 같자나....)
그래서 패턴에 java.util.regex.Pattern을 확인해 보니
"(" , ")"을 처리할 경우에는 \\를 앞에 붙여 줘야 한다고 한다.

그래서 해당하는 함수 xssFilter()에 있는 replaceIgnoreCase()에서 "(" 과 ")"을 변화하는 부분에서 앞에 \\를 붙여 주었더니 정상적으로 동작햇다.
posted by 나는너의힘
:
JAVA 2009. 5. 15. 00:27

* 참고
- DatagramPacket : 애플리케이션에서 주고받을 데이터와 관련된 클래스 (생성자로 송신/수신기능 구분)
- DatagramSocket : 실제 데이터의 전송을 책임지는 클래스



UDPEchoServer.java

package socket.echo.udp;

import java.net.*;
import java.io.*;

public class UDPEchoServer {

//생성자
 public UDPEchoServer(int port){
  
  try{
//port를 소스로 해서 DatagramSocket 객체를 생성한다. 
   DatagramSocket ds = new DatagramSocket(port);
   
   while(true){
    
//UDP의 실제 데이터는 일반적으로 512바이트로 제한하는 경우가 많기 때문에 바이트 배열을 크기를 512로 했다.
    byte[] buffer = new byte[512];
//DatagramPacket의 생성자 중 데이터수신을 위한 생성자. 바이트배열 buffer의 길이만큼 저장한다.
    DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
    System.out.println("ready");
//DatagramSocket객체로 수신된 Datagram을 dp에 저장한다.
    ds.receive(dp);
//Datagram의 실제 데이터를 문자열로 변환한다.
    String str = new String(dp.getData());
    System.out.println("수신된 데이터 : "+str);
    

//송신을 위한 Datagram의 ip주소를 InetAddress 객체로 반환한다.
    InetAddress ia = dp.getAddress();
//송신을 위한 Datagram의 포트번호를 반환한다.
    port = dp.getPort();
    System.out.println("client ip : "+ia+", client port : "+port);
//송신을 위한 DatagramPacket 객체를 생성한다. 이 때 IP주소와 포트번호는 수신된 Datagram의 IP주소와 포트로 해야 한다. (이 예제에서는 송신처로 그대로 보내므로 아래와 같은 형태가 된다.)
    dp = new DatagramPacket(dp.getData(), dp.getData().length, ia, port);
    ds.send(dp);
   }
   
  }catch(IOException e){
   e.printStackTrace();
  }
 }

 public static void main(String[] args){
  new UDPEchoServer(3000);
 }
 
}





UDPEchoClient.java

package socket.echo.udp;

import java.net.*;
import java.io.*;

public class UDPEchoClient {

 private String str;
 private BufferedReader file;
 private static int SERVERPORT = 3000;
 
 public UDPEchoClient(String ip, int port){
  
  try{
//InetAddress 객체를 생성한다. 여기서는 송신할 원격지가 같기 때문에 localhost로 설정했지만, 송신할 원격지가 다르다면 원격지의 IP주소로 설정해야 한다.
   InetAddress ia = InetAddress.getByName(ip);
//port를 인자로 DatagramSocket객체를 생성한다.
   DatagramSocket ds = new DatagramSocket(port);
   System.out.println("message : ");
   file = new BufferedReader(new InputStreamReader(System.in));   //키보드로부터
   str = file.readLine();
//입력받은 문자열을 바이트배열로 바꾸어 저장한다.
   byte[] buffer = str.getBytes();   
//송신을 위한 DatagramPacket 객체를 생성한다. 이 때 IP주소와 포트번호는 송신할 원격지의 IP주소와 포트로 해야 한다.
   DatagramPacket dp = new DatagramPacket(buffer, buffer.length, ia, SERVERPORT);
//DatagramSocket 객체로 송신을 위한 Datagram을 전송한다.

   ds.send(dp);

   buffer = new byte[512];
//수신을 위한 DatagramPacket 객체를 생성한다.
   dp = new DatagramPacket(buffer, buffer.length);
//DatagramSocket 객체로 수신된 Datagram을 dp에 저장한다.
   ds.receive(dp);

   System.out.println("server ip : "+dp.getAddress()+", server port : "+dp.getPort());
   System.out.println("수신된 데이터 : "+new String(dp.getData()).trim());
   
   
  }catch(IOException ioe){
   ioe.printStackTrace();
  }
 }

 public static void main(String[] args){
  new UDPEchoClient("localhost", 2000);
 }
 
}




- 출처 : [한빛미디어] 자바 5.0 프로그래밍 -

posted by 나는너의힘
:
JAVA 2009. 5. 15. 00:26

1. TCP 소켓 프로그램 예제의 흐름

(1) 서버 소켓 생성
(2) 서버 소켓으로 청취
(3) 클라이언트 소켓 생성
(4) (서버, 클라이언트) 소켓을 이용하여 스트림 생성
(5) 클라이언트 메시지 전송
(6) 서버 메시지 읽음
(7) 서버 메시지 전송
(8) 클라이언트 메시지 읽음
(9) (서버, 클라이언트) 소켓 종료

* 본 예제에서는 클라이언트가 메시지를 한 번만 전송할 수 있는데, 만약 클라이언트가 계속 메시지를 전송하기 위해서는 서버쪽에 스레드를 생성해야 한다.




EchoServer.java

package socket.echo;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.net.ServerSocket;    //ServerSocket 클래스는 TCP서버소켓을 의미한다.
import java.net.Socket;


public class EchoServer {

 private BufferedReader bufferR;
 private BufferedWriter bufferW;
 private InputStream is;
 private OutputStream os;
 private ServerSocket serverS;

 
 public EchoServer(int port){
  
  try{
   //ServerSocket 객체를 3000번 포트를 이용하여 생성.
   serverS = new ServerSocket(port);
  }catch(IOException ioe){
   ioe.printStackTrace();
   System.exit(0);
  }
 
  while(true){
    
    try{
       System.out.println("클라이언트의 요청을 기다리는 중");
    
//accept() 메서드를 이용하여 클라이언트의 TCP커넥션(클라이언트에서 Socket객체를 생성하는 것)을 기다리고 있다.
//accept() 메서드는 클라이언트의 Socket객체가 생성될 때까지 블로킹 되고 있다가 클라이언트의 Socket객체가 생성되면 서버에서 클라이언트와 통신할 수 있는 Socket 객체를 리턴하게 된다.
//accept() 메서드의 유효시간이 지나면 java.net.SocketTimeoutException이 발생하는데, 이 예외가 발생하더라도 ServerSocket객체는 계속 유효하다.
//실험결과 accept() 메서드가 Socket 객체를 리턴하지 않는 한 while문의 루프도 돌아가지 않았다.
    Socket tcpSocket = serverS.accept();
    System.out.println("클라이언트의 IP주소 : "+tcpSocket.getInetAddress().getHostAddress());
    is = tcpSocket.getInputStream();     //'소켓으로부터' 읽고
    os = tcpSocket.getOutputStream();  //'소켓에' 쓴다.
    bufferR = new BufferedReader(new InputStreamReader(is));
    bufferW = new BufferedWriter(new OutputStreamWriter(os));
    String message = bufferR.readLine();
    System.out.println("수신메시지 : "+message);
    message += System.getProperty("line.separator");  //엔터키넣기
    bufferW.write(message);
    bufferW.flush();
    bufferR.close();
    bufferW.close();
    tcpSocket.close();
    
   }catch(IOException ie){
    ie.printStackTrace();
   }
   
  }
 
 }
 
 
 public static void main(String[] args){
    new EchoServer(3000);
 }
  
}





EchoClient.java

package socket.echo;

import java.io.*;
import java.net.*;

public class EchoClient {

 private String ip;
 private int port;
 private String str;
 BufferedReader file;
 
//생성자
 public EchoClient(String ip, int port) throws IOException{
  
  this.ip = ip;
  this.port = port;
//Socket객체를 생성한다. Socket객체가 생성되면 서버와 TCP 커넥션이 이루어지게 된다. 동시에 서버의 accept()메서드는 클라이언트와 통신할 수 있는 Socket객체를 반환하게 된다. 따라서 클라이언트의 Socket객체와 서버의 Socket객체가 각각의 스트림을 이용하여 통신할 수 있게 된다.
  Socket tcpSocket = getSocket();   //사용자 메서드
  OutputStream os_socket = tcpSocket.getOutputStream();   //소켓에 쓰고
  InputStream is_socket = tcpSocket.getInputStream();   //소켓에서 읽는다
  
  BufferedWriter bufferW = new BufferedWriter(new OutputStreamWriter(os_socket));
  BufferedReader bufferR = new BufferedReader(new InputStreamReader(is_socket));
  System.out.print("입력 : ");
  
//소켓으로부터 읽는 것이 아니라 키보드로부터 읽는 또 하나의 BufferedReader
  file = new BufferedReader(new InputStreamReader(System.in));
  str = file.readLine();
  str += System.getProperty("line.separator");
  bufferW.write(str);
  bufferW.flush();
  str = bufferR.readLine();
  System.out.println("Echo Result : "+str);
  
  file.close();
  bufferW.close();
  bufferR.close();
  tcpSocket.close();
  
 }
 
 
 public Socket getSocket(){   //호스트의 주소와 포트를 사용, 소켓을 만들어 리턴하는 사용자 메서드
  
  Socket tcpSocket = null;
  
  try{
//원격 호스트(여기서는 서버)의 주소포트(여기서는 멤버변수)를 사용하여 Socket객체를 생성한다.
//호스트를 찾을 수 없거나, 서버의 포트가 열려 있지 않은 경우에는 UnknownHostException 이 발생하고,
//네트워크의 실패, 또는 방화벽 때문에 서버에 접근할 수 없을 때에는 IOException 이 발생할 수 있다.

   tcpSocket = new Socket(ip, port);
  }catch(IOException ioe){
   ioe.printStackTrace();
   System.exit(0);
  }
  return tcpSocket;
  
 }
 

 public static void main(String[] args)throws IOException{
    new EchoClient("localhost", 3000);
 }
 
}





- 출처 : [한빛미디어] 자바 5.0 프로그래밍 -


posted by 나는너의힘
:
JAVA 2009. 5. 15. 00:17

1. 개요

- 서버, 클라이언트가 공유하는 '요청의 종류와 내용에 관한 프로토콜'을 만들어 로그인예제에 적용한다.



2. 로그인 예제 프로그램의 흐름

(1) 서버가 클라이언트에 로그인 요청을 한다.
(2) 클라이언트는 아이디와 패스워드를 서버에게 전송한다.
(3) 아이디와 패스워드가 정확히 맞았다는 메시지를 전송한다.
(3) 패스워드가 틀린 경우의 메시지를 전송한다.
(3) 아이디가 틀린 경우의 메시지를 전송한다.
(4) 클라이언트는 서버에게 종료 메시지를 전송한다. 이 때 서버도 프로그램을 종료한다.



3. 클래스 설명

- Protocol.java : 이 클래스는 클라이언트와 서버에서 사용하는 클래스다. 주요 목적은 packet 바이트 배열을 생성하여 프로토콜 타입과 실제 데이터(ID와 PWD)를 저장하여 packet 바이트 배열을 클라이언트와 서버가 전송하게 된다.

LoginServer.java : 서버를 의미하며, 일반적인 서버는 클라이언트의 요청이 있는 경우 통신을 시작하는데, 이 클래스는 클라이언트가 프로그램을 시작하면 서버에서 로그인 요청을 하게 된다. 클라이언트에서 로그인 요청이 왔을 때 ID와 PWD를 체크한 후에 결과를 전송하고 클라이언트가 종료되면 프로그램도 같이 종료된다.

- LoginClient.java : 클라이언트를 의미하며, 서버에서 로그인 요청이 있는 경우 ID와 PWD를 입력하여 로그인 인증을 받게 된다. 로그인 인증의 결과와 상관없이 프로그램은 종료된다.



4. 실행
- java LoginServer
- java LoginClient 호스트주소 포트번호






Protocol.java

package socket.protocol;

import java.io.Serializable;

public class Protocol implements Serializable{

 //프로토콜 타입에 관한 변수
 public static final int PT_UNDEFINED = -1;   //프로토콜이 지정되어 있지 않을 경우에
 public static final int PT_EXIT = 0;
 public static final int PT_REQ_LOGIN = 1;   //로그인요청
 public static final int PT_RES_LOGIN = 2;   //인증요청
 public static final int PT_LOGIN_RESULT = 3;  //인증결과
 public static final int LEN_LOGIN_ID = 20;   //ID길이
 public static final int LEN_LOGIN_PASSWORD = 20; //PW길이
 public static final int LEN_LOGIN_RESULT = 2;  //로그인인증값 길이
 public static final int LEN_PROTOCOL_TYPE = 1;  //프로토콜타입 길이
 public static final int LEN_MAX = 1000;    //최대 데이타 길이
 
 protected int protocolType;
 
 private byte[] packet;   //프로토콜과 데이터의 저장공간이 되는 바이트배열

//생성자
 public Protocol(){
  this(PT_UNDEFINED);
 }
 
//생성자
 public Protocol(int protocolType){

  this.protocolType = protocolType;
  
//어떤 상수를 생성자에 넣어 Protocol 클래스를 생성하느냐에 따라서 바이트배열 packet 의 length 가 결정된다.
  getPacket(protocolType);
 }
 
 
 public byte[] getPacket(int protocolType){
  
  if(packet == null){
   
   switch(protocolType){
   
    case PT_REQ_LOGIN : packet = new byte[LEN_PROTOCOL_TYPE]; break;
    case PT_RES_LOGIN : packet = new byte[LEN_PROTOCOL_TYPE + LEN_LOGIN_ID + LEN_LOGIN_PASSWORD]; break;
    case PT_UNDEFINED : packet = new byte[LEN_MAX]; break;
    case PT_LOGIN_RESULT : packet = new byte[LEN_PROTOCOL_TYPE + LEN_LOGIN_RESULT]; break;
    case PT_EXIT : packet = new byte[LEN_PROTOCOL_TYPE]; break;
   }
  }

  packet[0] = (byte)protocolType;   //packet 바이트배열의 첫번째 방에 프로토콜타입 상수를 셋팅해 놓는다.
  return packet;
 }

 //로그인후 성공/실패의 결과값을 프로토콜로 부터 추출하여 문자열로 리턴
 public String getLoginResult(){
  //String의 다음 생성자를 사용 : String(byte[] bytes, int offset, int length)
  return new String(packet, LEN_PROTOCOL_TYPE, LEN_LOGIN_RESULT).trim();
 }
 
 
 //String ok를 byte[] 로 만들어서 packet의 프로토콜 타입 바로 뒤에 추가한다.
 public void setLoginResult(String ok){
  //arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
  System.arraycopy(ok.trim().getBytes(), 0, packet, LEN_PROTOCOL_TYPE, ok.trim().getBytes().length);
 }
 
 
 public void setProtocolType(int protocolType){
  this.protocolType = protocolType;
 }

 
 public int getProtocolType(){
  return protocolType;
 }
 
 
 public byte[] getPacket(){
  return packet;
 }
 
 
 //Default 생성자로 생성한 후 Protocol 클래스의 packet 데이타를 바꾸기 위한 메서드
 public void setPacket(int pt, byte[] buf){
  packet = null;
  packet = getPacket(pt);
  protocolType = pt;
  System.arraycopy(buf, 0, packet, 0, packet.length);
 }
 
 
 public String getId(){
  //String(byte[] bytes, int offset, int length)
  return new String(packet, LEN_PROTOCOL_TYPE, LEN_LOGIN_ID).trim();
 }
 
 
 //byte[] packet 에 String ID를 byte[]로 만들어 프로토콜 타입 바로 뒷부분에 추가한다.
 public void setId(String id){
  System.arraycopy(id.trim().getBytes(), 0, packet, LEN_PROTOCOL_TYPE, id.trim().getBytes().length);
 }
 
 
 public String getPassword(){
  //구성으로 보아 패스워드는 byte[] 에서 로그인 아이디 바로 뒷부분에 들어가는 듯 하다.
  return new String(packet, LEN_PROTOCOL_TYPE + LEN_LOGIN_ID, LEN_LOGIN_PASSWORD).trim();
 }
 
 
 public void setPassword(String password){
  System.arraycopy(password.trim().getBytes(), 0, packet, LEN_PROTOCOL_TYPE+LEN_LOGIN_ID, password.trim().getBytes().length);
  packet[LEN_PROTOCOL_TYPE + LEN_LOGIN_ID + password.trim().getBytes().length] = '\0';
 }
  
}







LoginServer.java

package socket.protocol;

import java.net.*;
import java.io.*;

public class LoginServer {

 public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException{
  
  ServerSocket sSocket = new ServerSocket(3000);
  System.out.println("클라이언트 접속 대기중 ...");
  Socket socket = sSocket.accept();
  System.out.println("클라이언트 접속");
  
//어차피 바이트배열로 전송할 것이기 때문에 필터스트림 없이 Input/OutputStream만 사용해도 된다.
  OutputStream os = socket.getOutputStream();
  InputStream is = socket.getInputStream();

  //로그인정보 요청용 프로토콜 객체 생성
  Protocol protocol = new Protocol(Protocol.PT_REQ_LOGIN);
  
  //로그인정보 요청 패킷을 전송 
  os.write(protocol.getPacket());
  

  while(true){
  
   //새 Protocol 객체 생성 (기본 생성자)
   protocol = new Protocol();
   
   //기본 생성자로 생성할 때에는 바이트배열의 길이가 1000으로 지정됨
   byte[] buf = protocol.getPacket();
   
   //socket으로부터 읽어서(클라이언트의 입력) buf 에 저장한다. (블로킹메서드)
   is.read(buf);
   
   //패킷 타입을 얻고 Protocol 객체의 packet 멤버변수에 buf를 복사한다.
   int packetType = buf[0];
   protocol.setPacket(packetType, buf);
   
   if(packetType == Protocol.PT_EXIT){
    protocol = new Protocol(Protocol.PT_EXIT);
    os.write(protocol.getPacket());
    System.out.println("서버종료");
    break;
   }
  
   
   switch(packetType){
   
   //클라이언트가 로그인 정보 응답 패킷인 경우 (클라이언트의 로그인 정보 전송일 경우)
   case Protocol.PT_RES_LOGIN :
    
    System.out.println("클라이언트가 로그인 정보를 보냈습니다.");
    String id = protocol.getId();
    String password = protocol.getPassword();
    System.out.println(id+"@@"+password+"@@");
    
    if(id.equals("bruce")){
     
     if(password.equals("1111")){
      
      //로그인 성공
      protocol = new Protocol(Protocol.PT_LOGIN_RESULT);
      protocol.setLoginResult("1");
      System.out.println("로그인 성공");
     
     }else{
      
      //암호 틀림
      protocol = new Protocol(Protocol.PT_LOGIN_RESULT);
      protocol.setLoginResult("2");
      System.out.println("암호 틀림");
     }
     
    }else{
     
     //아이디 존재 안함
     protocol = new Protocol(Protocol.PT_LOGIN_RESULT);
     protocol.setLoginResult("3");
     System.out.println("아이디 존재 안함");
    }
   
    System.out.println("로그인 처리 결과 전송");
    os.write(protocol.getPacket()); //socket의 OutputStream 에 기록한다.
    break;
   
   }//end switch
  
  }//end while
  
  is.close();
  os.close();
  socket.close();
 
 }
 
}







LoginClient.java


package socket.protocol;

import java.net.*;
import java.io.*;

public class LoginClient {

 public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException{
  
  if(args.length <2)System.out.println("사용법 : java LoginClient 호스트주소 포트번호");

  Socket socket = new Socket(args[0], Integer.parseInt(args[1]));
  
  OutputStream os = socket.getOutputStream();
  InputStream is = socket.getInputStream();
  Protocol protocol = new Protocol();
  byte[] buf = protocol.getPacket();
  
  //while문을 사용하는 이유 : InputStream 으로부터 계속 읽어들이기 위해서...?
  while(true){
   
//소켓의 InputStream 으로부터 읽어들여서 바이트배열 buf에 저장한다. (서버로부터 온 값)
   is.read(buf);

   int packetType = buf[0];
   protocol.setPacket(packetType, buf);
   
   if(packetType == Protocol.PT_EXIT){
    System.out.println("클라이언트 종료");
    break;
   }
   
   switch(packetType){
   
   case Protocol.PT_REQ_LOGIN :
    System.out.println("서버가 로그인정보 요청");
    BufferedReader userIn = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("아이디 : ");
    String id = userIn.readLine();
    System.out.print("암호 : ");
    String pwd = userIn.readLine();

    //서버로 패킷 전송 (로그인 정보 전송)
    protocol = new Protocol(Protocol.PT_RES_LOGIN);
    protocol.setId(id);
    protocol.setPassword(pwd);
    System.out.println("로그인 정보 전송");
    os.write(protocol.getPacket());
    break;
    
   case Protocol.PT_LOGIN_RESULT :
    System.out.println("서버가 로그인 결과 전송");
    String result = protocol.getLoginResult();
    if(result.equals("1")){
     System.out.println("로그인 성공");
    }else if(result.equals("2")){
     System.out.println("암호 틀림");
    }else if(result.equals("3")){
     System.out.println("존재하지 않는 아이디");
    }
    protocol = new Protocol(Protocol.PT_EXIT);
    System.out.println("종료패킷 전송");
    break;
   }
   
  }//end while

  os.close();
  is.close();
  socket.close();
 }
}







- 출처 : [한빛미디어] 자바 5.0 프로그래밍 -


posted by 나는너의힘
:
JAVA 2009. 5. 14. 22:46

//JAVA 입력받은 스트링이 숫자이면 true 문자이면 false를 리턴한다.

//JAVA To determine whether numeric or string value

//JAVA 数値か文字かどうかを判別 


 public static boolean isNumber(String str) { 
        boolean check = true;
        for(int i = 0; i < str.length(); i++) {
            if(!Character.isDigit(str.charAt(i)))

{
                check = false;
                break;
            }// end if
        } //end for
        return check;  
 } //isNumber


posted by 나는너의힘
:
JAVA/iBatis 2009. 5. 14. 22:25

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

<!DOCTYPE sqlMapConfig     
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"     
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
 
 <settings
  cacheModelsEnabled="true"
  enhancementEnabled="true"
  lazyLoadingEnabled="true"
  maxRequests="50"
  maxSessions="25"
  maxTransactions="10"
  useStatementNamespaces="false"
 />

 <!--Type aliases allow you to use a shorter name for long fully qualified class names. -->

 <typeAlias alias="order" type="testdomain.Order"/>

  <!-- Configure a built-in transaction manager.  If you're using an
       app server, you probably want to use its transaction manager
       and a managed datasource -->
  <transactionManager type="JDBC" commitRequired="false">
     <dataSource type="JNDI">
         <property name="DataSource" value="java:/comp/env/jdbc/DB"/>
     </dataSource>
  </transactionManager>
 
  <!--sqlMap resource="C:\LuckyData\Project\Java1.5\data\ibatis\Member.xml"/-->
  <sqlMap resource="board/board.xml"/>
  <sqlMap resource="login/login.xml"/> 
  <sqlMap resource="db/dbChange.xml"/>
  <sqlMap resource="statics/static.xml"/>
 
  <!-- List more here...
  <sqlMap resource="com/mydomain/data/Order.xml"/>
  <sqlMap resource="com/mydomain/data/Documents.xml"/>
  -->

</sqlMapConfig>


빨간색 부분처럼 바꿔준다. JNDI 물론 톰캣 SERVER.XML에 설정이 되있어야 겠다.

SERVER.XML
<Context docBase="" path="" reloadable="true">
        <Resource name="jdbc/IMARC" auth="Container" type="javax.sql.DataSource"                
        driverClassName="oracle.jdbc.driver.OracleDriver" 
                             url="jdbc:oracle:thin:@XXX.XXX.XXX.X:XXX:XXXX" 
                  username="XXXX" password="XXXX maxActive="20" maxIdle="10" maxWait="10000" />
</Context>

각자설정에 알맞게 바꿔야겠죠?^^

출처 : http://matz.egloos.com/1442344
posted by 나는너의힘
:
JAVA 2009. 5. 7. 16:21

Annotation 용도

컴파일러를 위한 정보
컴파일러가 에러를 탐지하거나 경고를 경감시키는 용도

컴파일 시와 설치 시 처리
소프트웨어 도구가 코드, XML 파일 등을 생성하기 위한 용도

실행 시 처리
일부는 실행 시에 검사 용도



Annotation 위치
클래스, 필드, 메소드, 다른 프로그램 요소의 선언부



문서화 Annotation

문서화 목적으로 클래스마다 다음과 같은 주석을 작성한다고 가정하자.

public class Generation3List extends Generation2List {

   // Author: John Doe
   // Date: 3/17/2002
   // Current revision: 6
   // Last modified: 4/12/2004
   // By: Jane Doe
   // Reviewers: Alice, Bill, Cindy

   // class code goes here

}

이를 Annotation을 사용하여 다음과 같이 변경할 수 있다.

우선 Annotation Type을 정의한다.

@interface ClassPreamble {
 
 String author();
 String date();
 int currentRevision() default 1;
 String lastModified() default "N/A";
 String lastModifiedBy() default "N/A";
 String[] reviewers();
 
}

다음과 같이 Annotation Type을 사용할 수 있다.

@ClassPreamble (
 author = "John Doe",
 date = "3/17/2002",
 currentRevision = 6,
 lastModified = "4/12/2004",
 lastModifiedBy = "Jane Doe",
 reviewers = { "Alice", "Bob", "Cindy" }
)
public class Generation3List extends Generation2List {

}

Javadoc에 포함시키고 싶다면,

다음과 같이 Annotation Type의 정의 앞에 @Documented라는 Annotation을 붙인다.

@Documented
@interface ClassPreamble {
 
 String author();
 String date();
 int currentRevision() default 1;
 String lastModified() default "N/A";
 String lastModifiedBy() default "N/A";
 String[] reviewers();
 
}



컴파일러 용 Annotation

@Deprecated

메소드, 클래스, 필드가 더 이상 사용하지 않을 것을 권장한다.

현재는 backward compatiblity 차원에서 지원되나, 차후 버전에는 지원되지 않을 수 있다.

컴파일러는 이 Annotation이 붙은 요소를 사용하면 컴파일 시 경고 메시지를 보여준다.

@Override

메소드를 명시적으로 오버라이드한다.

오버라이드할 때 이 Annotation이 필요하지는 않다.

다만 상위 메소드를 하위 메소드에서 명시적으로 오버라이드함을 표기함으로써

컴파일러가 컴파일 시 오버라이드가 발생하는지를 검사한다.

상위 메소드명과 다르게 작성함으로써

오버라이딩이 아닌 오버로딩의 발생을 조기에 차단할 수 있다.

@SuppressWarnings

컴파일러가 컴파일 시에 경고 메시지를 보여주지 않도록 한다.

현재 사용가능한 경고는 "deprecation"과 "unchecked"가 있다.

"unchecked"는 Generics를 사용하지 않을 경우 발생하는 경고이다.



처리 용 Annotation

JDK 5부터 Annotation Processing Tool인 apt를 제공하고,

JDK 6부터는 자바 컴파일러의 일부가 되었다.

실행 시에 Annotation 정보를 사용하기 위해서 다음과 같이 Annotation Type 정의 앞에

@Retention(RetentionPolicy.RUNTIME)라는 Annotation을 붙인다.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationForRuntime {

 // Elements that give information
 // for runtime processing

}



Reference
:
http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html

posted by 나는너의힘
:
JAVA/LOG4J 2009. 5. 7. 16:20

Log4J는 다음과 같은 레이아웃을 제공한다.

SimpleLayout
PatternLayout
HTMLLayout
XMLLayout

SimpleLayout은 다음과 같은 포맷으로 고정되어 있다.

레벨 - 메시지

PatternLayout은 C의 printf()처럼 포맷 설정에 대한 자유도가 크다.

HTMLLayout은 HTML 테이블을 포맷으로 한다.

XMLLayout은 XML 문서를 포맷으로 한다.

Reference:
http://www.allapplabs.com/log4j/log4j_layouts.htm

posted by 나는너의힘
:
JAVA/LOG4J 2009. 5. 7. 16:19

System.out.println()을 사용하면 되는데 굳이 logger를 사용할 필요가 있을까?

logger를 사용하면, 다음과 같은 장점들이 있다.

- 로그 레벨별로 logging 여부를 결정할 수 있다.
- 로그를 콘솔뿐만 아니라 파일 등에 쓸 수 있다.
- 레벨이나 appender 설정을 파일에서 할 수 있다.

가장 보편적으로 사용되는 Apache Log4J를 사용해보자.

우선 log4j.properties 파일을 다음과 같이 작성한다.

log4j.rootLogger=ALL, console, another_appender

log4j.appender.console=org.apache.log4j.ConsoleAppender

log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d [%c:%p] - %m%n

log4j.appender.another_appender=org.apache.log4j.RollingFileAppender
log4j.appender.another_appender.File=log/example.log

log4j.appender.another_appender.MaxFileSize=100KB
# Keep one backup file
log4j.appender.another_appender.MaxBackupIndex=1

log4j.appender.another_appender.layout=org.apache.log4j.PatternLayout
log4j.appender.another_appender.layout.ConversionPattern=%d [%c:%p] - %m%n

log4j.properties 파일을 클래스 패스에 포함시켜야만 Log4J가 찾을 수 있다.

이클립스를 사용한다면, properties 폴더에 log4j.properties 파일을 넣고,

Build Path -> Use as Source Folder를 선택한다.

다음으로 Log4J를 사용하는 클래스를 작성한다.

package com.izeye.log4jexample;

import org.apache.log4j.Logger;

public class Log4JExample {
 
 private static final Logger logger = Logger.getLogger(Log4JExample.class);
 private static final Logger anotherLogger = Logger.getLogger("another");
 
 public static void main(String[] args) {
  String message = "TEST MESSAGE";
  while (true) {
   logger.debug(message);
   logger.info(message);
   logger.warn(message);
   logger.error(message);
   logger.fatal(message);
   
   anotherLogger.debug(message);
   anotherLogger.info(message);
   anotherLogger.warn(message);
   anotherLogger.error(message);
   anotherLogger.fatal(message);
   
   try {
    Thread.sleep(10000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }
 
}

위 클래스를 실행하면, 콘솔의 결과는 다음과 같다.

2009-02-04 19:26:58,375 [com.izeye.log4jexample.Log4JExample:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,375 [com.izeye.log4jexample.Log4JExample:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:FATAL] - TEST MESSAGE
......

log/example.log의 내용도 위와 동일하다.

log4j.rootLogger는 모든 logger의 부모 logger이다.

따라서 등록된 모든 logger가 이 logger의 설정에 영향을 받는다.

com.izeye.log4jexample.Log4JExample 클래스 logger는

WARN 이상의 메시지만 콘솔로 출력하고,

'another' logger는 ERROR 이상의 메시지만

log/example.log 파일로 출력하도록 하기 위해서 다음과 같이 수정한다.

#log4j.rootLogger=ALL, console, another_appender

log4j.logger.com.izeye.log4jexample.Log4JExample=WARN, console

log4j.logger.another=ERROR, another_appender

posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 7. 12:26

[퍼옴 : http://openframework.or.kr/blog/?p=40 ]

 

Spring XML설정파일을 위한 12가지 가장 좋은 선택

 

원제는 Twelve Best Practices for Spring XML Configuration Files 이다.
원문의 위치는 http://lizjason.com/blog/?p=12

2006년 1월 26일 :
ONJava.com의 Twelve Best Practices For Spring XML Configurations 글을 기반으로 예제및 설명에 대한 일부 수정

Spring은 강력한 자바 애플리케이션 프레임워크이고 자바 애플리케이션의 넓은 범위에서 사용된다. 이것은 단순함과 테스트의용이성을 달성하기 위해 의존성삽입(Dependency Injection)을 사용한다. 의존성과 bean생성은 XML설정파일에대개 명시된다. XML설정은 장황하고 큰 프로젝트에서는 관리하기가 어려울수도 있다. 설정파일의 가독성과 관리의 용이성이 고려되는만큼, 나는 다음의 사항이 매우 유용하리라고 생각한다.

1. autowire를 사용하지 말라.
내 의견에서, autowire는 시장광고용(marketing) 기능이다. 이것은 실제 프로젝트에서 결코 사용되지 말아야 한다. 이것은 몇몇 타이핑의 수고와 설정조각을 줄이지만, 명백함과 설정의 유지보수성을 희생한다.

trollswagen, naimdjon, Johannes Brodwall, 토지님께서 autowire는 오히려 xml이커지면 커질수록 xml의 구조를 쉽게 파악하게 해주는 정말 좋은 기능중에 하나라는 의견을 주셨습니다. autowire에 관련된사항은 프로젝트 도입시 장,단점을 다시 살펴 사용하길 권합니다.

2. 명명 규칙을 사용하라.
이것은 자바코드와 같은 의도이다. 예를 들면 bean id를 위해, 당신은 자바 클래스 필드 명명규칙을 따를수 있다. OrderServiceDAO의 인스턴스를 위한 bean id는 orderServiceDAO가 될것이다.

3. 단축형태(shortcut forms)를 사용하라.
단축형태는 자식요소에서 속성으로 프라퍼티값과 참조를 이동시켜 다소 덜 장황하게 만든다. 이 단축형태는 1.2버전 이후 지원된다.

단축형태는 다음과 같은 기능이다.
1.2이전버전에서는 다음과 같이 셋팅한다.

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<
property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<
property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property>
<
property name="username"><value>root</value></property>
</
bean>

1.2이후 단축형태를 사용하면 다음과 같이 셋팅이 가능하다.

<bean id="myDataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">
<
property name="driverClassName" value="com.mysql.jdbc.Driver" />
<
property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<
property name="username" value="someone" />
</
bean>

4. 인자를 맞추기 위한 인덱스보다 타입을 선호하라.
인덱스를 사용하는 것은 때때로 다소 덜 장황하게 만든다. 하지만 이것은 에러를 좀더 생성하고 읽기 어렵다.

다음의 소스는 인덱스를 사용하는 예제이다.

<bean id="billingService" class="com.lizjason.spring.BillingService">
<
constructor-arg index="0" value="lizjason"/>
<
constructor-arg index="1" value="100"/>
</
bean>

하지만 다음처럼 타입을 사용하는 것이 추천한다.

<bean id="billingService" class="com.lizjason.spring.BillingService">
<
constructor-arg type="java.lang.String" value="lizjason"/>
<
constructor-arg type="int" value="100"/>
</
bean>

5. 가능하다면 bean정의를 재사용하라.
당신은 중복을 제거하기 위한 기법처럼 상속을 사용할수 있다. 당신이 할필요가 있는 모든것은 상위 bean에 abstract=true를 명시하고 자식 bean에 parent참조를 두는 것이다. 당신이클래스나 factory메소드를 명시하지 않는다면, bean은 함축적으로 abstract상태가 된다.

예를 들면 다음과 같이 셋팅한다.

<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<
property name="transactionManager">
<
ref bean="transactionManager" />
</
property>
<
property name="transactionAttributes">
<
props>
<
prop key="save*">PROPAGATION_REQUIRED</prop>
<
prop key="remove*">PROPAGATION_REQUIRED</prop>
<
prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</
props>
</
property>
</
bean>

<
bean id="userManager" parent="txProxyTemplate">
<
property name="target">
<
bean class="org.appfuse.service.impl.UserManagerImpl">
<
property name="userDAO" ref="userDAO" />
</
bean>
</
property>
</
bean>

6. import 보다는 ApplicationContext를 통해 bean정의를 조립(assembling)하는 것을 선호하라.
Ant스크립트내 import처럼, 그것들은 모듈화된 bean정의를 조립하는데 유용하다. 어쨌든, 이것은ApplicationContext를 통해 그것들을 조립하기 위해 좀더 유연하다. 당신은 ApplicationContext의생성자를 위해 bean정의의 배열을 전달할수 있다.

다음은 import를 조합하여 xml을 설정하는 소스이다.

<beans>
<
import resource="billingServices.xml"/>
<
import resource="shippingServices.xml"/>
<
bean id="orderService" class="com.lizjason.spring.OrderService"/>
<
beans>

import를 사용하여 xml을 조합하는 방식보다는 아래와 같이 ApplicationContext를 사용하는 것이 좀더 유연한 개발을 도와준다.

String[] serviceResources = {"orderServices.xml", "billingServices.xml", "shippingServices.xml"};
ApplicationContext orderServiceContext = new ClassPathXmlApplicationContext(serviceResources);

7. 가능하다면, bean 확인자로 id를 사용하라.
id를 사용하는 것은 가독성을 증가시키지 않는다. 하지만 이것은 bean참조를 확인하기 위해 XML파서에 영향을 끼칠수 있다. 만약 id가 XML IDREF제한을 위해 사용될수 없다면, 당신은 이름(name)을 사용할수 있다.

8. 개발시에는 의존성체크(dependency-check)를 사용하라.
당신은 bean정의의 dependency-check속성을 디폴트인 none이 아닌 다른값으로 셋팅할수 있다. 그래서 컨테이너는 당신을 위해 의존성체크를 할수 있다.

<bean id="orderService" class="com.lizjason.spring.OrderService" dependency-check="objects">
<
property name="companyName" value="lizjason"/>
<
constructor-arg ref="orderDAO"/>
</
bean>

9. 각각의 XML파일을 위해 헤더(header) 주석을 추가하라.
XML파일내 내부 주석대신에 서술적인 id와 name을 사용하는것이 선호된다. 어쨌든, 각각의 파일이 정의된 bena을 요약하는 헤더를 가진다면 이해하기가 쉽다.

<beans>
<
description>
이 파일은 거래(billing) 서비스 관련 bean을 정의하고
서비스 bean템플릿을 제공하는 baseService.xml파일을 의존한다.
</description>
...
</beans>

10. 변경을 위해 팀멤버간 의사소통을 하라.
당신이 자바소스코드를 리팩토리할때, 당신은 설정파일을 그 상황에 따라 변경하고 팀멤버에게 알릴 필요가 있다.

11. 생성자 삽입(constructor injection)보다 setter 삽입(setter injection)을 선호하라.
생성자 삽입은 bean들이 비정상적인 상태에서 생성될수 없다는 것을 확인할수 없다. 하지만 setter 삽입은 좀더 유연하고 관리가 가능하다. 특히 클래스가 다중 프라퍼티를 가진다면 더욱 그러하다.

다음은 생성자 삽입을 사용하는 예이다.

<bean id="orderService" class="com.lizjason.spring.OrderService">
<
constructor-arg ref="orderDAO"/>
</
bean>

다음은 setter삽입을 사용하는 예이다.

<bean id="billingService" class="com.lizjason.spring.BillingService">
<
property name="billingDAO" ref="billingDAO">
</
bean>

12. 의존성 삽입을 남용하지 말라.
마지막에, Spring ApplicationContext는 당신을 위해자바객체를 생성할수 있다. 하지만 모든 자바객체가 의존성삽입을 통해서 생성될수는 없다. 기억하라. 강력한 IDE인Eclipse와 IntelliJ를 사용하여, 자바코드는 좀더 읽고, XML파일보다 유지및 관리가 쉽다. 즉 의존성삽입을 설정하는XML파일보다는 자바코드가 개발자의 입장에서는 가독성이 좋다.

posted by 나는너의힘
:
JAVA 2009. 5. 7. 12:24

<%@ page language="java" contentType="text/html;charset=EUC-KR" %>
<%@ page import="java.util.*, java.io.*" %>
<%@ page import="jxl.*, jxl.write.*, jxl.format.*" %>
<%@ page import="kr.co.kt.adims.form.Top10Form" %>
<%
 String filename = "excelConvert.xls";

 WritableWorkbook workbook = Workbook.createWorkbook(new File("c:/"+filename));
 WritableSheet sheet = workbook.createSheet("Sheet1", 0);

 jxl.write.WritableCellFormat  format= new WritableCellFormat();
 jxl.write.WritableCellFormat  format0= new WritableCellFormat();
        
 format.setBackground(jxl.format.Colour.GRAY_25 );
 format.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
 format.setAlignment(jxl.format.Alignment.CENTRE);
 format0.setBackground(jxl.format.Colour.WHITE );
 format0.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
 format0.setAlignment(jxl.format.Alignment.CENTRE);
 sheet.setColumnView(0,8);

 jxl.write.Label label = null;                       
 jxl.write.Blank blank = null;

 label = new jxl.write.Label(0,0,"순위",format);
 sheet.addCell(label);
 label = new jxl.write.Label(1,0,"본부",format);
 sheet.addCell(label);
 label = new jxl.write.Label(2,0,"서버팜",format);
 sheet.addCell(label);
 label = new jxl.write.Label(3,0,"DHCP서버",format);
 sheet.addCell(label);
 label = new jxl.write.Label(4,0,"지사",format);
 sheet.addCell(label);
 label = new jxl.write.Label(5,0,"지점",format);
 sheet.addCell(label);
 label = new jxl.write.Label(6,0,"L3명(DHCP)",format);
 sheet.addCell(label);
 label = new jxl.write.Label(7,0,"EquipAlias",format);
 sheet.addCell(label);
 label = new jxl.write.Label(8,0,"LocationCode",format);
 sheet.addCell(label);
 label = new jxl.write.Label(9,0,"Mac Address",format);
 sheet.addCell(label);
 label = new jxl.write.Label(10,0,"수집날짜",format);
 sheet.addCell(label);
 label = new jxl.write.Label(11,0,"패킷수",format);
 sheet.addCell(label);
 
 int cnt = 0;
 
 ArrayList staList = null;
 Top10Form staForm = new Top10Form();
 
 staList = (ArrayList)request.getAttribute("topList");
 
 cnt = staList.size();
 
 for(int i=0; i<cnt; i++)
 {
  staForm = (Top10Form)staList.get(i);
  
  label = new jxl.write.Label(0, i+1, staForm.getNo(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(1, i+1, staForm.getBonbu(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(2, i+1, staForm.getServerParm(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(3, i+1, staForm.getEquipAlias(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(4, i+1, staForm.getJisa(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(5, i+1, staForm.getJijum(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(6, i+1, staForm.getL3Name1(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(7, i+1, staForm.getL3Name2(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(8, i+1, staForm.getL3LocationCode(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(9, i+1, staForm.getName(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(10, i+1, staForm.getRegDate(), format0);
  sheet.addCell(label);
  label = new jxl.write.Label(11, i+1, String.valueOf(staForm.getCnt()), format0);
  sheet.addCell(label);
  
 }
           
 workbook.write();
 workbook.close();
%>

 

<%
 //여기부터 화일 다운로드 창이 자동으로 뜨게 하기 위한 코딩(임시화일을 스트림으로 저장)
 File file = new File ("c:/"+filename);  //해당 경로의 파일 객체를 만든다.
 byte[] bytestream = new byte[(int)file.length()];  //파일 스트림을 저장하기 위한 바이트 배열 생성.
 FileInputStream filestream = new FileInputStream(file);   //파일 객체를 스트림으로 불러온다.
 int i = 0, j = 0;   //파일 스트림을 바이트 배열에 넣는다.

 while((i = filestream.read()) != -1) {
  bytestream[j] = (byte)i;
  j++;
 }
 filestream.close();   //FileInputStream을 닫아줘야 file이 삭제된다.

 try{
  boolean success = file.delete(); //화일을 생성과 동시에 byte[]배열에 입력후 화일은 삭제
  if(!success) System.out.println("<script>alert('not success')</script>");
 } catch(IllegalArgumentException e){
  System.err.println(e.getMessage());
 }

 // response.setContentType("application/x-msdownload;charset=EUC-KR");  //응답 헤더의 Content-Type을 세팅한다. 
 response.setHeader("Content-Disposition","attachment; filename="+filename); //Content-Disposition 헤더에 파일 이름 세팅.

 OutputStream outStream = response.getOutputStream();  // 응답 스트림 객체를 생성한다.
 outStream.write(bytestream);  // 응답 스트림에 파일 바이트 배열을 쓴다.
 outStream.close();

%>

posted by 나는너의힘
:
JAVA 2009. 5. 7. 12:23

#. 자바 JXL API 를 이용한 Excel 파일 만들기

1. JXL API 다운 받기

2. JSP에서 사용할 class 만들기

3. JSP에서 적용하기

4. 요구 사항에 맞게 확장하기


1. 환경 설정

JAVA에서 Excel을 만들기 위해서는 JXL API 나 POI를 이용해야 한다.

우선 이번장에서는 JXL을 이용하여 만드는 방법에 대해서 소개하기로 한다.

Java Excel API - A Java API to read, write and modify Excel spreadsheets ( 이하 JXL )은

다음과 같은 홈페이지에서 다운 받을 수 있다


파일 다운 받기 :  

http://www.andykhan.com/jexcelapi/download.html

API 정보  :

http://www.andykhan.com/jexcelapi/index.html


우선 사이트에서 가장 최신 버젼의 JExcelApi를 다운 받는다.

압축을 해제한 후에 각 사용자 환경에 맡게 jar 파일을 옮긴다.

( 일반 JAVA User - j2sdk 폴더 밑에 jrelibext 파일로 옮긴다. )


2. JSP에서 사용할 class 만들기

- 파일 다운로드 -

1. Parameter 정보

JSP에서 class로 넘겨주는 정보는 다음과 같다.

Vector data  :: Query를 통해 나온 Recordset 집합

String[] column  ::  column 이름 <- Excel 에서 Head 역할

int[] columntype :: column 타입 <- 문자형, 정수형, 실수형 으로 구분하였다.

String FilePath :: Excel 파일을 저장하기위한 절대 경로

String SheetName :: Sheet 이름


2. Excel File 생성하기

Excel 파일음 다음과 같이 생성을 한다

WritableWorkbook workbook = Workbook.createWorkbook(new File(FilePath));
WritableSheet sheet = workbook.createSheet(SheetName, 0);

- FilePath :: Excel 파일을 저장하기위한 절대 경로
- SheetName :: 시트 이름
- 0 :: 시트 인덱스 번호


3. Cell Type 지정하기

a. Text 형
jxl.write.WritableCellFormat format_column = new WritableCellFormat();
jxl.write.WritableCellFormat format_data = new WritableCellFormat();


b. 정수형 ( 1000단위 마다 , 찍기 X )
jxl.write.WritableCellFormat format_integer1 = new WritableCellFormat(NumberFormats.INTEGER);


c. 정수형 ( 1000단위 마다 , 찍기 O )
jxl.write.NumberFormat moneytype1 = new NumberFormat("###,##0");
jxl.write.WritableCellFormat format_integer2 = new WritableCellFormat(moneytype1);


d. 실수형 ( 1000단위 마다 , 찍기 X )
jxl.write.WritableCellFormat format_float1 = new WritableCellFormat(NumberFormats.FLOAT);

e. 실수형 ( 1000단위 마다 , 찍기 0 , 소수점 2자리 허용 )
jxl.write.NumberFormat moneytype2 = new NumberFormat("###,##0.00");
jxl.write.WritableCellFormat format_float2 = new WritableCellFormat(moneytype2);


4. CELL Color & BackGround 지정하기
                      
format_column.setBackground(jxl.format.Colour.GRAY_25 );
format_column.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
  
.
.


5. DB To Excel

a. Head 부분 생성하기

   for ( cellnum = 0; cellnum < column.length; cellnum++ ) {
    label = new jxl.write.Label(cellnum,0,column[cellnum],format_column);
    sheet.addCell(label);
   }

b. Data 집어 넣기

   for ( rownum = 1; rownum <= data.size() ; rownum++) {
    dataResult = (String[])data.get(rownum-1);

    for (cellnum = 0; cellnum < dataResult.length - 1; cellnum++) {
      num = new jxl.write.Number(cellnum, rownum, Long.parseLong(dataResult[cellnum+1]),format_integer2);
      sheet.addCell(num);
    }
   }
                                  

3. JSP에서 사용하기
<%@ page import="java.util.Vector, crm.util.Util, java.sql.*" contentType="text/html; charset=euc-kr" %>
<jsp:useBean id="cExcel" class="crm.util.CreateExcel" scope="page"/>


cExcel.CreateExcelFile(queryResult,column,columntype,file_path+file_name,sheet_name,str_search);


4. 요구 사항에 맞게 확장하기

JXL API를 제공하는 홈페이지에 가면 JXL API SDK를 다운 받을 수 있다.

JXL API SDK를 다운 받은 후 사용자 요구 사항에 맞게 수정을 하면 될 것이다.
posted by 나는너의힘
:
JAVA 2009. 5. 6. 14:12

unit-test-with-junit-1218180084342700-8.ppt


junit4.0.zip

posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 6. 12:14


스프링 프레임워크의 프로세스는 대충 이해 했습니다.
스프링MVC에서 삽질 3일++ , 스프링JDBC에 도전해서 이틀동안 삽질한 결과 데이터베이스 연동을 할 수 있었네요..ㅋ
스프링.. 좋거나 혹은 나쁘거나 입니다. 제가 본 것은 빙산의 일각에 불과하지만 처음부터 너무 진을 빼놓네요.. 스트럿츠2가 그립습니다.
클래스를 많이 제공하는 것은 기호에 맞게 선택할 수 있는 폭이 넓어 좋지만, 반면 어떤 것을 적용해야 할지 어떤 것이 최선인가 하는데에 시간 투자를 많이 해야 합니다.
서블릿 설정파일에서도 시간을 많이 소비하였고 아직도 헷갈립니다. 톰캣과 궁합이 안좋은가...
JDBC연동 부분은 확실히 편하긴 하지만 커맨드,서비스,컨트롤러,DAO + servlet.xml 설정파일과 어떻게 연동이 되는가하는 부분에서도 하루를 날렸네요.

web.xml
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/dataSource.xml</param-value>
 </context-param>
<listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>
 <filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>
   org.springframework.web.filter.CharacterEncodingFilter
  </filter-class>
  <init-param>
   <param-name>encoding</param-name>
   <param-value>EUC-KR</param-value>
  </init-param>
 </filter>
<filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

<servlet>
  <servlet-name>dispatcherJdbc</servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
    /WEB-INF/boardService-servlet.xml
   </param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <servlet>
  <servlet-name>dispatcherBank</servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
    /WEB-INF/sampleBankingServlet-servlet.xml , /WEB-INF/sampleBanking-services.xml
   </param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
 </servlet>

<servlet-mapping>
  <servlet-name>dispatcherJdbc</servlet-name>
  <url-pattern>/test/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>dispatcherBank</servlet-name>
  <url-pattern>/bank/*</url-pattern>
 </servlet-mapping>

최범균님 책에 나와있는데로 다시 web.xml을 설정을 해보았다.. 잘 될 줄 알았는데... 역시나 에러... 어플 하나는 꼭 안되더군..

TestDAO.java
public class TestDAO {

 private DataSource dataSource;
 public void setDataSource(DataSource dataSource) {
  this.dataSource = dataSource;
 }

public Object insert(TestCommand test) throws Exception{
  Connection conn = null;
  PreparedStatement pstmt = null;
  
  try{
   conn = DataSourceUtils.getConnection(dataSource);
   pstmt = conn.prepareStatement("insert into member(id,name,password) values(?,?,?)");
   pstmt.setString(1, test.getId());
   pstmt.setString(2, test.getName());
   pstmt.setString(3, test.getPassword());
   pstmt.executeUpdate();
  }catch (Exception e) {
   // TODO: handle exception
   e.printStackTrace();
  }finally{
   DataSourceUtils.releaseConnection(conn, dataSource);
  }
  return dataSource.getConnection();
 }
}

DAO파일은 dataSource객체만 선언해 준 것 빼고 거의 비슷하게 했다. 스프링의 jdbc템플릿을 쓰지 않았다.

Controller
public class JoinController extends SimpleFormController{
 
 public JoinController(){}
//서비스 객체 생성
 private TestService service;
 public TestService getService() {
  return service;
 }
 public void setService(TestService service) {
  this.service = service;
 }
 public ModelAndView onSubmit(Object command) throws Exception{
  
  TestCommand test = (TestCommand) command;
  
  //서비스를 호출
  service.insertMemeber(test);
  
  //직접 DAO접근
  //to.insert(test);

  return new ModelAndView(getSuccessView(), "success" , service.insertMemeber(test));
 }
}

컨트롤러는 간단하다. 서비스는 DAO와 연결해주는 빈이고.. 이것을 컨트롤러에서 실행한다.. 이상하게도 DAO에 직접 접근하면 에러가 나더라..

boardService-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
<bean id = "testDao"

   class = "sboard.DAO.TestDAO">
   <property name="dataSource">
            <ref bean="dataSource" />
        </property>
 </bean>

 <bean id="simpleUrlMapping"
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/testjoin.htm">joinController</prop>
            </props>
        </property>
    </bean>
   
     <bean id="testService"
  class="sboard.services.TestService"
  p:tdao-ref="testDao" />

<bean id="joinController"
          class="sboard.controller.JoinController">

        <property name="sessionForm">
            <value>true</value>
        </property>

        <property name="commandName">
            <value>joinCommand</value>
        </property>

        <property name="commandClass">
            <value>sboard.commands.TestCommand</value>
        </property>        

        <property name="formView">
            <value>join</value>
        </property>

        <property name="successView">
            <value>memberlist</value>
        </property>

<property name="service">
            <ref bean="testService" />
        </property>
       
    </bean>
     <!--뷰리졸버 세팅    -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass">
            <value>org.springframework.web.servlet.view.JstlView</value>
        </property>
        <property name="prefix">
            <value>/WEB-INF/test/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
</beans>

dataSource는 dataSource.xml에 정의 되어 있다. 그것을 가져와서 testDao에 담고,,, 서비스 클래스에 선언된 tdao객체를 초기화하기 위해 testService선언.. 파라미터로 testDao객체를 넘겨준다. 컨트롤러에서 사용할 서비스객체를 초기화하기 위해서 컨트롤러 빈 내에
service프로퍼티에 testService빈을 파라미터로 넘겨준다.(복잡하다...)

삽질한 만큼 끝내 결과를 보게 되서 다행입니다.;;
posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 6. 12:11


오늘도 또 봤지요... 스프링 인 액션2 책을 구해서 봤는데 약간 이해가 되다가도 예제가 그지같군요..(예제가 단적이지 않고 통합되어 있어서 소스를 분석해야함)

그래도 오기로.. 신상철박사님의 사이트에서 예제를 다운받아 실행이라도 되게끔 세팅해 보았습니다. 중간에 오류도 많았구여.
SimpleFormController로 컨트롤러를 구현한 예제였습니다.
차분히 컨트롤러 파일부터 한줄한줄 분석해 보았습니다.
아주 조금씩 이해가 되갈라 카네여..
작년에 스트럿츠2로 만들던 웹게임소스가 있어서 봤는데... 헐.. 로직이 쉽고 설정파일의 가독성이 이렇게나 아름다울수가!!!
스트럿츠2에 새삼 감사했지만, 강력함은 스프링에 못 미칩니다. 스트럿츠2는 단조롭죠.. 물론 더 알아봐야 겠지만요./

컨트롤러
package springexample.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import springexample.commands.AccountDetail;
import springexample.commands.LoginCommand;
import springexample.services.AccountServices;
import springexample.services.AuthenticationService;
/**
 * SimpleFormController is a concrete FormController implementation that
 * provides configurable form and success views, and an onSubmit chain for
 * convenient overriding. Automatically resubmits to the form view in case
 * of validation errors, and renders the success view in case of a valid
 * submission.
 */
public class LoginBankController extends SimpleFormController {
    public LoginBankController() {
    }
    // The submit behavior can be customized by overriding one of the onSubmit methods. 오버라이딩 필수
    protected ModelAndView onSubmit(Object command) throws Exception {
        LoginCommand loginCommand = (LoginCommand) command; //오브젝트의 커맨드객체를 형변환시킨다.커맨드에는 폼에서 전달한 아뒤와 패스워드 정보가 담겨있다.
        authenticationService.authenticate(loginCommand); //로그인인증.. 아작스처리가 낫지 않을까?
        AccountDetail accountdetail = accountServices.getAccountSummary(loginCommand.getUserId());//커맨드를통해정보를담자..머이리 복잡한것인가!!
        return new ModelAndView(getSuccessView(), "accountdetail", accountdetail);
    }
   
    //어디서 많이 본듯한 장면아닌가..스트럿츠2의 액션인가?? 겟셋..
    private AuthenticationService authenticationService;
    private AccountServices accountServices;
    public AccountServices getAccountServices() {
        return accountServices;
    }
    //sampleBankingServlet-servlet.xml에서  <property name="accountServices">로 설정해 객체를 넘겼다.
    //해당객체를 주입하여 인자로 넘겨줌으로써 위에서 선언한 객체변수들을 쓸 수 있는 것이다.
    public void setAccountServices(AccountServices accountServices) {
        this.accountServices = accountServices;
    }
    public AuthenticationService getAuthenticationService() {
        return authenticationService;
    }
    public void setAuthenticationService(
            AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }
}

패키지는 controller , services , commands로 나뉜다..
controller : 말그대로 컨트롤러 클래스 위치
commands : User ,  Account , Login 등 빈즈 위치
services : 비즈니스 로직이 구현된 클래스 위치.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   
    <bean id="simpleUrlMapping"
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/login.html">loginBankController</prop>
            </props>
        </property>
    </bean>
   
    <bean id="logonValidator" class="springexample.commands.LogonValidator"/>
   
    <!-- LoginBankController is a SimpleFormController -->
    <bean id="loginBankController"
          class="springexample.controller.LoginBankController">
 <!--AbstractFormController에 sessionForm()가 정의되어있어 사용가능하다        -->
        <property name="sessionForm">
            <value>true</value>
        </property>
       
        <!-- This is Command object.  The command object can be
             accessed in a view through <spring:bind path="loginCommand"> -->
 <!--커맨드명설정 BaseCommandController클래스에 commandName()메소드가 정의되어있다            -->
        <property name="commandName">
            <value>loginCommand</value>
        </property>
 <!--커맨드클래스로 LoginCommand클래스 사용. 파라미터타입은 class타입 -->
        <property name="commandClass">
            <value>springexample.commands.LoginCommand</value>
        </property>
 <!-- BaseCommandController에 setValidator()메소드가 정의되어있다.        -->
        <property name="validator">
            <ref bean="logonValidator"/>
        </property>
       
        <!-- Indicates what view to use when the user asks for a new form
             or when validation errors have occurred on form submission. -->
 <!--SimpleFormController클래스에 setFormView()메소드 정의되어있다. 인자로넘긴 login은 login.jsp로 연결 
      결국 onSubmit()에서 에러가 나면 폼입력화면..또는 처음요청은 GET방식이니까 폼입력으로..  -->
        <property name="formView">
            <value>login</value>
        </property>
       
        <!-- Indicates what view to use when successful form submissions
             have occurred. Such a success view could e.g. display a submission
             summary. More sophisticated actions can be implemented by
             overriding one of the onSubmit() methods.-->
 <!-- onSubmit()이 성공하면 getSuccessView()를 리턴하는데 여기서 setSuccessView를 세팅해준다.
   accountdetail.jsp로 가는거다.        -->
        <property name="successView">
            <value>accountdetail</value>
        </property>
       
        <property name="authenticationService">
            <ref bean="authenticationService" />
        </property>
        <property name="accountServices">
            <ref bean="accountServices" />
        </property>
       
    </bean>
 <!--뷰리졸버 세팅    -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass">
            <value>org.springframework.web.servlet.view.JstlView</value>
        </property>
        <property name="prefix">
            <value>/WEB-INF/jsp/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
   
</beans>
오늘도 또 봤지요... 스프링 인 액션2 책을 구해서 봤는데 약간 이해가 되다가도 예제가 그지같군요..(예제가 단적이지 않고 통합되어 있어서 소스를 분석해야함)

그래도 오기로.. 신상철박사님의 사이트에서 예제를 다운받아 실행이라도 되게끔 세팅해 보았습니다. 중간에 오류도 많았구여.
SimpleFormController로 컨트롤러를 구현한 예제였습니다.
차분히 컨트롤러 파일부터 한줄한줄 분석해 보았습니다.
아주 조금씩 이해가 되갈라 카네여..
작년에 스트럿츠2로 만들던 웹게임소스가 있어서 봤는데... 헐.. 로직이 쉽고 설정파일의 가독성이 이렇게나 아름다울수가!!!
스트럿츠2에 새삼 감사했지만, 강력함은 스프링에 못 미칩니다. 스트럿츠2는 단조롭죠.. 물론 더 알아봐야 겠지만요./

컨트롤러
package springexample.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import springexample.commands.AccountDetail;
import springexample.commands.LoginCommand;
import springexample.services.AccountServices;
import springexample.services.AuthenticationService;
/**
 * SimpleFormController is a concrete FormController implementation that
 * provides configurable form and success views, and an onSubmit chain for
 * convenient overriding. Automatically resubmits to the form view in case
 * of validation errors, and renders the success view in case of a valid
 * submission.
 */
public class LoginBankController extends SimpleFormController {
    public LoginBankController() {
    }
    // The submit behavior can be customized by overriding one of the onSubmit methods. 오버라이딩 필수
    protected ModelAndView onSubmit(Object command) throws Exception {
        LoginCommand loginCommand = (LoginCommand) command; //오브젝트의 커맨드객체를 형변환시킨다.커맨드에는 폼에서 전달한 아뒤와 패스워드 정보가 담겨있다.
        authenticationService.authenticate(loginCommand); //로그인인증.. 아작스처리가 낫지 않을까?
        AccountDetail accountdetail = accountServices.getAccountSummary(loginCommand.getUserId());//커맨드를통해정보를담자..머이리 복잡한것인가!!
        return new ModelAndView(getSuccessView(), "accountdetail", accountdetail);
    }
   
    //어디서 많이 본듯한 장면아닌가..스트럿츠2의 액션인가?? 겟셋..
    private AuthenticationService authenticationService;
    private AccountServices accountServices;
    public AccountServices getAccountServices() {
        return accountServices;
    }
    //sampleBankingServlet-servlet.xml에서  <property name="accountServices">로 설정해 객체를 넘겼다.
    //해당객체를 주입하여 인자로 넘겨줌으로써 위에서 선언한 객체변수들을 쓸 수 있는 것이다.
    public void setAccountServices(AccountServices accountServices) {
        this.accountServices = accountServices;
    }
    public AuthenticationService getAuthenticationService() {
        return authenticationService;
    }
    public void setAuthenticationService(
            AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }
}

패키지는 controller , services , commands로 나뉜다..
controller : 말그대로 컨트롤러 클래스 위치
commands : User ,  Account , Login 등 빈즈 위치
services : 비즈니스 로직이 구현된 클래스 위치.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   
    <bean id="simpleUrlMapping"
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/login.html">loginBankController</prop>
            </props>
        </property>
    </bean>
   
    <bean id="logonValidator" class="springexample.commands.LogonValidator"/>
   
    <!-- LoginBankController is a SimpleFormController -->
    <bean id="loginBankController"
          class="springexample.controller.LoginBankController">
 <!--AbstractFormController에 sessionForm()가 정의되어있어 사용가능하다        -->
        <property name="sessionForm">
            <value>true</value>
        </property>
       
        <!-- This is Command object.  The command object can be
             accessed in a view through <spring:bind path="loginCommand"> -->
 <!--커맨드명설정 BaseCommandController클래스에 commandName()메소드가 정의되어있다            -->
        <property name="commandName">
            <value>loginCommand</value>
        </property>
 <!--커맨드클래스로 LoginCommand클래스 사용. 파라미터타입은 class타입 -->
        <property name="commandClass">
            <value>springexample.commands.LoginCommand</value>
        </property>
 <!-- BaseCommandController에 setValidator()메소드가 정의되어있다.        -->
        <property name="validator">
            <ref bean="logonValidator"/>
        </property>
       
        <!-- Indicates what view to use when the user asks for a new form
             or when validation errors have occurred on form submission. -->
 <!--SimpleFormController클래스에 setFormView()메소드 정의되어있다. 인자로넘긴 login은 login.jsp로 연결 
      결국 onSubmit()에서 에러가 나면 폼입력화면..또는 처음요청은 GET방식이니까 폼입력으로..  -->
        <property name="formView">
            <value>login</value>
        </property>
       
        <!-- Indicates what view to use when successful form submissions
             have occurred. Such a success view could e.g. display a submission
             summary. More sophisticated actions can be implemented by
             overriding one of the onSubmit() methods.-->
 <!-- onSubmit()이 성공하면 getSuccessView()를 리턴하는데 여기서 setSuccessView를 세팅해준다.
   accountdetail.jsp로 가는거다.        -->
        <property name="successView">
            <value>accountdetail</value>
        </property>
       
        <property name="authenticationService">
            <ref bean="authenticationService" />
        </property>
        <property name="accountServices">
            <ref bean="accountServices" />
        </property>
       
    </bean>
 <!--뷰리졸버 세팅    -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass">
            <value>org.springframework.web.servlet.view.JstlView</value>
        </property>
        <property name="prefix">
            <value>/WEB-INF/jsp/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
   
</beans>
posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 6. 12:09

(key=”<mapping-pattern>”)

● /myapp/*.foo (pattern)
– /myapp/x.foo, /myapp/yz.foo (examples that match the pattern)

● /myapp/p*ttern
– /myapp/p1ttern, /yapp/pxttern

● /**/example
– /myapp/example, youapp/yourdir/example

● /myapp/**/mydir/foo.*
– /myapp/yourapp/yourdir/mydir/foo.x

● /**/*.jsp
– /yourapp/yourdir/x.jsp, /myapp/mydir/yz.jsp
posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 6. 12:06


두개의 servlet.xml 파일을 만들어서 각각 요청에 맞게 사용하고 싶은데.. 어떻게 해야할까...
책에 ApplicationContext설정을 보니까
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/front.xml</param-value>
</init-param>

이런식으로 쓰더라구..

처음 만들었던 dispatcher-servlet.xml과 새로 만든 hello-servlet.xml 을 web.xml에 위에 맞게 각각 설정해줬다..

hello.htm , hello2.htm 요청을 하니 hello2.htm만 정상적으로 뜬다,.
어찌된 걸까... 하나를 주석처리하고 실행해보면 각각 잘 된다.
그렇다면 설정에 문제가 있다고 생각했다...
삽질에 삽질...
책을 다시 봤다..
웹요청과 컨트롤러 매핑 : HandlerMapping 챕터에서 설명이 나와있었다..
문제는 <servlet-mapping> 두 서블릿 모두 .htm 으로 url-pattern을 설정한 것...

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

<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
 <display-name>
 SpringTest</display-name>
 <welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.htm</welcome-file>
  <welcome-file>index.jsp</welcome-file>
  <welcome-file>default.html</welcome-file>
  <welcome-file>default.htm</welcome-file>
  <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
<!--한글인코딩 -->
  <filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>
   org.springframework.web.filter.CharacterEncodingFilter
  </filter-class>
  <init-param>
   <param-name>encoding</param-name>
   <param-value>EUC-KR</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 
 <servlet>
  <servlet-name>
   dispatcher
  </servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
 </servlet>
 
 <servlet>
  <servlet-name>
    hello
  </servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
 </servlet>
<!--http://localhost/spring/say/hello.htm으로 호출 -->
 <servlet-mapping>
  <servlet-name>
   dispatcher
  </servlet-name>
  <url-pattern>/say/*</url-pattern>
 </servlet-mapping>
<!--http://localhost/spring/hello2.htm으로 호출 --> 
 <servlet-mapping>
  <servlet-name>
   hello
  </servlet-name>
  <url-pattern>*.htm</url-pattern>
 </servlet-mapping>
</web-app>

hello-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans   
             http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
                http://www.springframework.org/schema/aop  
                     http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
                        http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="kame.spring" />   
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">      
            <props>        
                   <prop key="/index.htm">indexController</prop>   
                   <prop key="/hello2.htm">helloController</prop>
           </props>   
      </property>  
</bean>
<bean id="viewResolver"  class="org.springframework.web.servlet.view.InternalResourceViewResolver"
         p:prefix="/WEB-INF/test/"    
         p:suffix=".jsp" />
<bean name="indexController"  class="org.springframework.web.servlet.mvc.ParameterizableViewController"
      p:viewName="index" />
</beans>
============================================================================
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 <!--핸들러-->
 <bean id="handlerMapping"
  class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
 <bean name="/hello.htm" class="kame.spring.chap04.HelloController" />
<bean id="viewResolver"
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/view/" />
  <property name="suffix" value=".jsp" />
 </bean>
</beans>      

스프링은 설정파일 부분이 너무 복잡하다. 방식도 다양하고 상속관계도 알아둘 필요가 있다.
아무튼 성공!!
posted by 나는너의힘
:
JAVA/SPRING 2009. 5. 6. 12:02


<%@ taglib prefix = "form" uri=http://www.springframework.org/tags/form %>

<form:form>
    <form:input path = "userId" />
    ...
    <input type="submit" value="회원가입" />
</form:form>

<form:form commandName = "memberInfo">
 ....
</form:form>
커맨드객체의 이름이 기본 값인 command가 아니라면 위와 같이 commandName속성에 커맨드 객체의 이름을 명시해 주어야 한다.

<input>을 위한 커스텀태그 <form:input>,<form:password>,<form:hidden>

<form:form commandName = "memberInfo">
<p>
 <form:label path="userId">회원아뒤</form:label>
 <form:input path="userId"/>
 <form:errors path="userId"/>
</p>


path속성에서 지정한 커맨드 객체의 프로퍼티 값이 출력된다. 그러니까 memberInfo.userId 가 되겠지...(안해봤지만ㅋ)


이외에도 <form:select>, <form:options>, <form:option> referenceData()메소드 이용...
체크박스, CSS및 HTML공통속성
에러관련 커스텀 태그 등이 있다.,.. 책을 참조하자..
posted by 나는너의힘
: