잊지 않겠습니다.

<mvc:annotation-driven>
이 설정될 경우, 여기에 따른 여러가지 설정이 많이 이루어지게 된다. 
먼저 Spring 문서에 따르면, 

 1. Support for Spring 3′s Type ConversionService in addition to JavaBeans PropertyEditors during Data Binding. A ConversionService instance produced by the org.springframework.format.support.FormattingConversionServiceFactoryBean is used by default. This can be overriden by setting the conversion-service attribute. 
2. Support for formatting Number fields using the @NumberFormat annotation 
3. Support for formatting Date, Calendar, Long, and Joda Time fields using the @DateTimeFormat annotation, if Joda Time is present on the classpath. 
4. Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. The validation system can be explicitly configured by setting the validator attribute. 
5. Support for reading and writing XML, if JAXB is present on the classpath. 
6. Support for reading and writing JSON, if Jackson is present on the classpath. 

와 같은 일들이 벌어지게 된다. 

여기서 주의할 점은 1번 항목. ConversionService가 default로 설정이 된다는것이다. ConversionService가 이미 설정이 되어 있기 때문에 Spring source에서 보면 다음과 같은 항목에 의하여 에러가 발생하게 된다.
        public void setConversionService( ConversionService conversionService ) {
               Assert .state ( this. conversionService == null, "DataBinder is already initialized with ConversionService");
               this .conversionService = conversionService ;
               if ( this .bindingResult != null && conversionService != null ) {
                      this .bindingResult . initConversion( conversionService );
               }
        }
따라서, Converter 를 이용한 @InitBinder는 사용할 수 없게 되고 에러를 발생시키게 된다. 이 경우에, @InitBinder를 이용하기 위해서는 PropertyEditorSupport를 상속받아 등록시키는 방법을 사용할 수 밖에 없게 된다. 을 통해서 설정되는 것을 xml로 표현하면 다음과 같다.
    
        
    
     
    
    

    
        
            
                
                
            
        
        
            
                
                
                    
                
                
                
                





            
        
    

    
        
        
            
                
            
        
    
위에서 주석처리 되어 있는 항목들은 해당 외부 라이브러리들이 존재하면 사용되게 된다.



Posted by Y2K
,

긁어온 내용. 출처는 > http://www.gurubee.net/pages/viewpage.action?pageId=26739591&

1. Apache HTTP 서버의 이해

1.1 개요

  • Apache HTTP 서버는 아파치 소프트웨어 재단(ASF:Apache Software Foundation)에서 개발하여 배포하고 있는 무료/오픈소스 웹 서버이다.
  • 아파치 HTTP 서버는 전세계 웹 서버 시장 점유율의 50% 이상을 차지하고 있으며 , 리눅스, 유닉스, BSD, 윈도우즈 등 다양한 플랫폼에서 사용이 가능하다.
  • 아파치 HTTP 서버는 빠르고 효율적이며, 이식성이 좋고 안정적이며, 기능이 다양하고 확장성이 좋다.

1.2 Apache HTTP 서버 왜 필요한가?

  • 보안기능 (SSL, Proxy, ACL, Directory 접근제한등..)
  • 성능적인 측면(리소스 분산처리, Cache(Expires), HTTP 표준설정(ETag등), MPM(Multi-Processing Module), KeepAlive등..)
  • 가상호스트 기능(하나의 서버에 여러 도메인 운영, 서버호스팅등..)
  • 운영적인 측면 (ErrorDocument, Access Log등..)
  • 부가적인 기능 (여러가지 유용한 Apache Module등..)

1.3 설치

2. Apache HTTP 서버의 주요 설정

2.1 가상호스트 (VirtualHost)

  • VirtualHost(가상호스트)란 하나의 웹 서버에서 여러 개의 도메인 주소를 운영 및 관리 할 수 있는 기능이다.
  • Apache HTTP Server에서는 이름기반 가상호스트와 IP 기반 가상호스트 기능을 제공한다.
이름기반 가상호스트(Name-based VirtualHost) 설정
  • 하나의 IP 주소에 여러 개의 호스 도메인 주소 사용이 가능하다.
  • httpd-vhost.conf의 VirtualHost를 설정하여 이름기반의 가상 호스트를 사용 할 수 있다.
  • httpd.conf 파일에서 httpd-vhosts.conf 파일의 주석을 해제해야 한다.
Name-based VirtualHost 예제
Listen 80
NameVirtualHost *:80

<VirtualHost *:80>
  ServerName www.oracleclub.com
  ServerAlias oracleclub.com
  DocumentRoot C:\workspace\project\oracleclub
</VirtualHost>

<VirtualHost *:80>
  ServerName wiki.oracleclub.com
  DocumentRoot C:\workspace\project\wiki
</VirtualHost>
가상호스트 설정 실습
  • $APACHE_HOME/conf/extra/httpd-vhost.conf 파일을 열어 NameVirtualHost와 VirtualHost를 아래와 같이 설정한다.
  • ServerName은 test.apache.org로 입력하고, DocumentRoot는 아파치 $APACHE_HOME/htdocs로 설정한다.
httpd-vhost.conf
NameVirtualHost *:80

<VirtualHost *:80>
    ServerName test.apache.org
    DocumentRoot C:\dev\Apache2.2\htdocs    # Apache htdocs 디렉토리 
</VirtualHost>
  • 아래와 같이 C:\Windows\System32\drivers\etc\hosts 파일에 호스트 설정을 추가 한다.
  • hosts.zip 파일을 받아 바로가기를 만들어 사용하면 편하다.
C:WindowsSystem32driversetchosts
# 127.0.0.1  localhost
127.0.0.1  test.apache.org

2.2 Files, Directory, Location 섹션

Files (파일시스템 관점)
  • 특정 파일에 대한 접근제한을 설정한다.
  • 아래는 위치에 상관없이 private.html 파일에 대한 접근을 제한하는 예이다.
    <Files private.html>
    Order allow,deny
    Deny from all
    </Files>
  • 아래는 "/home/user/webapps" 경로아래에 있는 private.html 파일에 대한 접근을 제한하는 예이다.
    <Directory /home/user/webapps>
    <Files private.html>
    Order allow,deny
    Deny from all
    </Files>
    </Directory>
Directory (파일시스템 관점)
  • 운영체제 입장에서 디스크를 보는 관점이다. 운영체제 디렉토리에 대한 접근제한을 설정한다.
  • 아래 예제를 보면서 Allow,Deny에 대해 이해를 해보자
    • Order 절 순서대로 Allow와 Deny를 실행한다
    • 아래 예는 Allow를 먼저 수행하고, Deny를 수행한다.
    • 즉 모두 허용하고, 127.0.0, 192.168.123 두 개의 아이피 대역에 대해서 접근 제한을 설정한다 .
      # 특정 IP 대역의 IP 차단
      <Directory /usr/local/apache/htdocs>
          Order Allow,Deny
          Deny from 127.0.0 192.168.123
          Allow from all
      </Directory>
  • 위 예제를 이해하였다면, 아래 예제는 쉽게 이해할 수 있을 것이다.
    • Deny를 먼저 수행하고 Allow를 수행한다.
    • 127.0.0, 192.168.123 두 개의 아이피 대역만 접근이 가능할 것이다.
      # 특정 IP 대역의 IP 허용
      <Directory /usr/local/apache/htdocs>
          Order Deny,Allow
          Allow from 127.0.0 192.168.123
          Deny from all
      </Directory>
  • 아래는 Allow, Deny 설정을 잘못한 예제이다.
    • 어떻게 되겠는가?
    • 먼저 Allow를 하고, Deny from all을 하기 때문에 모두 차단하겠다라는 의미를 가진다.
      <Directory /usr/local/apache/htdocs>
         Order Allow,Deny
         Deny from all
         Allow from 192.168.123.1
      </Directory>
Location (웹 경로 관점)
  • 웹서버가 제공하는 경로를 클라이언트가 보는 사이트의 관점이다
  • 아래는 /private 경로에 대해서 접근을 제한하는 예이다.
    • www.oracleclub.com/private 문자열로 시작하는 요청이 해당된다.
      <Location /private>
      Order Allow,Deny
      Deny from all
      </Location>
FilesMatch, DirectoryMatch, LocationMatch
  • 정규표현식을 사용할 수 있다.
  • 아래는 이미지 파일에 대한 접근을 제한하는 예이다.
    <FilesMatch \.(?i:gif|jpe?g|png)$>
    Order allow,deny
    Deny from all
    </FilesMatch>
  • 아래는 WEB-INF, META-INF 디렉토리에 접근을 금지하는 예이다.
    <DirectoryMatch "(^|/)META-INF($|/)">
      Order deny,allow
      deny from all
    </DirectoryMatch>
    
    <DirectoryMatch "(^|/)WEB-INF($|/)">
      Order deny,allow
      deny from all
    </DirectoryMatch>
Directory 설정 실습
  • 위의 Directory Allow, Deny 설정 예제를 직접 실습해 보자
  • 아래와 같이 특정 IP의 접근을 막는 Directory 옵션을 추가해 보자 (아래는 로컬 IP 주소의 접근을 막는 예제이다.)
  • Apache restart 후 http://test.apache.org 접속시 "Forbidden" 에러 메시지가 나오는지 확인 해 보자
    httpd-vhost.conf
    <VirtualHost *:80>
        ServerName test.apache.org
        DocumentRoot C:\dev\Apache2.2\htdocs    
        
        <Directory C:\dev\Apache2.2\htdocs>
            Order Allow,Deny
            Deny from 192.168  127.0.0
            Allow from all
        </Directory>    
    </VirtualHost>

2.3 ErrorDocument

ErrorDocument란
  • Apache HTTP Server에서는 ErrorDocument 지시자를 사용해 특정 에러발생시 특정 페이지로 redirect 할 수 있다.
  • ErrorDocumen를 활용하여 다양한 HTTP Status Code에 대해서 설정을 해놓으면 사용자에게 좀 더 편리하고 친절하게 메시지를 보여 줄 수 있다.
  • httpd.conf 파일에서 ErrorDocument 지시자를 설정하면 된다.
  • 아래와 같이 세가지 방법으로 설정 할 수 있다.
    ErrorDocument 설정 예제
    # 1. plain text 설정 방법
    # 500 메세지를 "The server made a boo boo." 로 변경해 준다.
    ErrorDocument 500 "The server made a boo boo."
    
    # 2. local redirects 방법
    # 404 발생시 missing.html의 내용을 보여준다.
    ErrorDocument 404 /missing.html
    
    # 3. external redirects 설정 방법
    # 404 발생시 외부 페이지로 redirect 한다.
    ErrorDocument 404 http://www.example.com/subscription_info.html
ErrorDocument 설정 실습
  • Directory 에서 실습한 접근권한 설정예제에서 403 접근 권한 오류가 발생 했을 때 Apache HTTP Server의 기본 메시지인 "Forbidden" 에러 메시지 대신 특정 HTML 이 나오게 실습 해보자
  • AccessDeny.html 파일을 "$APACHE_HOME/htdocs" 디렉토리에 저장한다.
  • 아래와 같이 ErrorDocument를 설정 한다.
    httpd-vhost.conf
    <VirtualHost *:80>
        ServerName test.apache.org
        DocumentRoot C:\dev\Apache2.2\htdocs    
    
        ErrorDocument 403 /AccessDeny.html   
        
        <Directory C:\dev\Apache2.2\htdocs>
            Order Allow,Deny
            Deny from 192.168.123 127.0.0
            Allow from all
        </Directory>    
    </VirtualHost>
IE ErrorDocument 설정 기준
  • error page 의 사이즈가 아래 기준보다 작으면 IE 메세지를 보여준다.(브라우저가 알아서 에러 페이지가 성의 없다고 판단함)
CodeDescriptionFile Size
400Bad Request512 bytes
403Forbidden256 bytes
404Not Found512 bytes
500Internal Server Error512 bytes

참고자료


Apache HTTP 서버와 Tomcat서버의 연동

1. Apache와 Tomcat을 연동하는 이유

  • Tomcat 서버는 본연의 임무인 서블릿 컨테이너의 역할만 하고, Apache HTTP Server는 웹서버의 역할을 하도록 각각의 기능을 분리하기 위해 연동을 할 수 있다.
  • Apache HTTP Server에서 제공하는 편리한 기능을 사용하기 위해서 연동을 할수 있다.
  • 대규모 사용자가 사용하는 시스템을 구축할 때 웹 서버인 아파치와 연동을 하면 부하 분산의 효과를 가질 수 있다. mod_jk의 Load Balancing과 FailOver 기능을 사용하여 안정적으로 운영 할 수 있다.

2. Apache와 Tomcat 연동하기

  • 아래 내용은 윈도우 OS를 기준으로 설명하였다.
2.1 mod_jk 다운로드 및 설정
httpd.conf
# jk_module 추가
LoadModule jk_module modules/mod_jk.so
2.2 workers.properties 파일 설정
  • apache와 tomcat를 연동하기위해서는 workers.properties 파일을 설정해야 한다.
  • $APACHE_HOME/conf/workers.properties 파일을 아래 예제와 같이 생성한다.
  • workers.properties 파일은 일반적으로 httpd.conf 파일과 같은 디렉토리에 위치하게 설정한다.
workers.properties
worker.list=sample

# 톰캣 server.xml 파일 AJP/1.3 Connector의 Port를 입력한다.
worker.sample.port=8009

# 톰탯 server 호스트
worker.sample.host=localhost

# 아파치 + 톰캣 통신 프로토콜
worker.sample.type=ajp13
  • ※참고 Tomcat Worker
    • 톰캣 워커는(Tomcat worker) 웹서버로부터의 서블릿 요청을 톰캣 프로세스(Worker)에게 전달하여 요청을 처리하는 톰캣 인스턴스이다.
    • 대부분 하나의 worker를 사용하나, load 밸런싱이나 site 파티셔닝을 위해 여러개의 worker를 사용 할 수 있다.
    • 워커 타입에는 ajp12, ajp13, jni, lb 등이 있다.
2.3 workers.properties 경로 지정
  • httpd.conf 파일에 workers.properties 파일 경로를 지정한다.
httpd.conf
# workers.properties 파일 추가
JkWorkersFile conf/workers.properties
2.4 VirtualHost 설정 변경
  • $APACHE_HOME/conf/vhosts/extra/httpd-vhost.conf 파일의 VirtualHost의 DocumentRoot를 Tomcat 디렉토리로 변경하자
  • JkMount 설정을 추가하자
httpd-vhost.conf 파일 설정
NameVirtualHost *:80

<VirtualHost *:80>
    ServerName test.apache.org
    DocumentRoot C:\dev\apache-tomcat-6.0.32webapps\ROOT 
 
 # URL중 jsp로 오는 Request만 Tomcat에서 처리 함
 # sample은 workers.properties에서 등록한 worker이름
JkMount  /*.jsp  sample   

# servlet 예제 실행을 위해서 추가
JkMount  /examples/* sample
</VirtualHost>
2.5 Tomcat의 server.xml 수정
  • <Context> 태그의 docBase 디렉토리를 Apache HTTP Server 설정과 동일하게 Tomcat 서버의 webapps/ROOT 디렉토리를 절대경로로 지정하자.
원하는 디렉토리를 Document Root로 사용
<Host name="localhost"  appBase="webapps"
           unpackWARs="true" autoDeploy="true"
           xmlValidation="false" xmlNamespaceAware="false">
 <Context path="" docBase="C:\dev\apache-tomcat-6.0.32\webapps\ROOT" reloadable="true"/>
 ..
</Host>

3. Apache HTTP Server와 Tomcat의 연동 테스트

Posted by Y2K
,

Filter가 적용이 되어 있는 경우, Filter에 적용되는 bean의 설정을 위해서 XmlWebApplicaionContext를 이용해서 Context객체들을 따로 로딩해줘야지 된다. 


순서는 다음과 같다. 


  1. ServletContext의 생성
  2. XmlWebApplicationContext의 생성 : 이때, Filter에 사용되는 Bean의 설정들은 모두 /WEB-INF/web.xml에 위치하고 있어야지 된다.
  3. XmlWebApplicationContext에 SevletContext를 주입하고, refresh를 시켜준다.
  4. 마지막으로, ServletContext에 속성을 추가한 후, MockFilterConfig, MockFilterChain을 이용해서 각각 Filter가 적용되는 Controller들을 테스트 한다.


    @Test

    public void get() throws Exception {

    ServletContext sc = new MockServletContext();

    XmlWebApplicationContext wctx = new XmlWebApplicationContext();    

    wctx.setServletContext(sc);            

        wctx.refresh();

        sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wctx);

        assertTrue(wctx.containsBean("sessionFactory"));

        

        

    MockHttpServletRequest request = new MockHttpServletRequest("GET","/rest/person/get/" + person.getId().toString());

    MockHttpServletResponse response = new MockHttpServletResponse();

        MockFilterConfig filterConfig = new MockFilterConfig(sc);

        MockFilterChain filterChain = new MockFilterChain();

        

        OpenSessionInViewFilter filter = new OpenSessionInViewFilter();

        filter.init(filterConfig);

        filter.doFilter(request, response, filterChain);

        

        

    Object handler = handlerMapping.getHandler(request).getHandler();

    ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);    

    System.out.println(response.getContentAsString());    

    assertNull(modelAndView);

    }



Posted by Y2K
,

Eclipse 단축키 모음

Java 2012. 11. 24. 14:26

ctrl + s: 저장 및 컴파일
ctrl + i: 소스 깔끔 정리(인덴트 중심의 자동구문정리)
ctrl + space : 어휘의 자동완성(Content Assistance)

ctrl + E : 열린파일 옮겨다니기

ctrl + shift + E : 열린파일 띄우기

ctrl + M : 에디터화면 넓게
ctrl + 1 : Quick Fix(Rename에 주로 사용)
ctrl + shift + M : 캐럿이 위치한 대상에 필요한 특정클래스 import
ctrl + shift + O : 소스에 필요한 패키지의 자동 임포트
ctrl + /: 한줄 또는 선택영역 주석처리/제거
ctrl + Q : 마지막 편집위치로 가기
ctrl + L : 특정줄번호로 가기
ctrl + D : 한줄삭제
ctrl + H : Find 및 Replace
ctrl + K : 다음찾기(또는, 찾고자 하는 문자열을 블럭으로 설정한 후 키를 누른다.)
ctrl + shift + K : 이전찾기(또는, 찾고자 하는 문자열을 블럭으로 설정한 후 역으로 찾고자 하는 문자열을 찾아감.)
alt + shift + j : 설정해 둔 기본주석 달기
Ctrl + 객체클릭(혹은 F3) : 클래스나 메소드 혹은 멤버를 정의한 곳으로 이동(Open Declaration)


ctrl + shift + f : 소스 깔끔 정리
ctrl + 2 + R : Rename(리팩토링)
ctrl + shift + / : 선택영역 block comment 설정
ctrl + shift + \ : 선택영역 block comment 제거
alt + shift + up: Enclosing Element 선택(괄호의 열고 닫기 쌍 확인에 유용함)
ctrl + O : Outline창열기

ctrl + T : 상속구조 보기, 한번더 누르면 수퍼타입/서브타입 구조가 토글된다

Alt + ->, Alt + <-: 이후, 이전
해당프로젝트에서 alt + enter : Project 속성
sysout > Ctrl + Space: System.out.println();
try > Ctrl + Space : 기본 try-catch문 완성
for > Ctrl + Space : 기본 for문 완성
템플릿을 수정,추가: Preferences > java > editor > Templates

블럭 씌운상태에서 alt + shift + z : try/catch, do/while, if, for, runnable.... 등 블럭씌우기


ctrl + N : 새로운 파일 및 프로젝트 생성
ctrl + shift + s : 열려진 모든파일 저장 및 컴파일
alt + / : Word Completion
alt + shift + R : Rename
ctrl + shift + G : 특정 메써드나 필드를 참조하고 있는 곳을 찾는다.
ctrl + shift + B : 현재커서위치에 Break point설정/해제
ctrl + alt + R
ctrl + f11 : 실행
f11 : 디버깅 시작

f4 : 상속구조 클래스 보기(메소드, 멤버)
f5 : step into
f6 : step over
f8 : 디버깅 계속
ctrl + . : 다음오류부분으로 가기
ctrl + , : 이전오류부분으로 가기
f12 : 에디터로 커서이동
ALT + UP,DOWN : 현재 줄 위치 이동
Ctrl + j : 검색할 단어를 입력하면서 실시간으로 검색
Ctrl + Shift + j : 검색할 단어를 입력하면서 실시간으로 거꾸로 검색
F4 : 클래스명을 선택하고 누르면 해당 클래스의 Hierarchy 를 볼 수 있다.
ctrl + alt + up/down : 한줄 duplicate
alt + shift + 방향 : 선택
ctrl + shift + g : 객체(변수)가 참조 되는 곳을 찾아 준다

alt + shift + m : 코드 중복 해결(중복부분을 블록선택한 다음 단축키를 누르면 이부분을 별도의 메서드로 뽑아내줌)

ctrl + alt + h : 메서드 호출구조 보기

Posted by Y2K
,

Spring Security는 다음과 같은 순서로 동작하게 된다. 


  1. <security:http> 영역안의 인증정보를 얻게 된다. 
  2. 인증이되지 않은 경우, form-login login-page 항목의 url로 이동하게 된다. 
  3. 인증은 기본적으로 {wepapp}/j_spring_security_check에서 처리되게 되며, 페이지의 in/out값은 j_username, j_password가 된다. 
  4. j_spring_security_check는 등록된 authentication-manager의 authentication-provider에 username만을 넘긴다. 
  5. username을 이용, authentication-provider는 username, password, authentication-role을 설정해서 사용자 정보를 넘겨준다.
  6. Spring security에서 j_spring_security_check 에서 반환된 사용자 정보를 이용. password가 일치하는지 확인
  7. password가 일치되는 경우, authentication-success-handler-ref 에 정의된 Handler를 이용해서, page의 이동이나 json 응답을 준다.
  8. password가 일치되지 않는 경우, authentication-failure-handler-ref 에 정의된 Handler를 이용해서, page의 이동이나 json 응답을 준다.





하나 중요한 상황이. web.xml에 반드시 securityFilter와 ContextLoaderListener가 선언이 되어야지 된다. 

spring web mvc에서 각 controller들이 url을 등록하지만, 각 servlet의 applicationContext를 모두 포함하는 parent의 개념에서 spring security가 접근이 된다고 생각해야지 된다. 


구성한 web.xml 파일의 내용은 다음과 같다

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

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 

                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

id="WebApp_ID" version="3.0">


<display-name>board</display-name>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>


<servlet>

<servlet-name>spring</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

<servlet-name>spring</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


<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>UTF-8</param-value>

</init-param>

</filter>


<filter-mapping>

<filter-name>encodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>


<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>



applicationContext.xml 파일의 내용은 다음과 같다.

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:beans="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-3.0.xsd

                    http://www.springframework.org/schema/security 

                    http://www.springframework.org/schema/security/spring-security-3.1.xsd">



<beans:bean id="userService" class="com.xyzlast.services.UserServiceImpl" />

<beans:bean id="loginProcessHandler" class="com.xyzlast.handlers.LogInProcessHandler" />

<beans:bean id="encoder"

class="org.springframework.security.crypto.password.StandardPasswordEncoder" />

<http use-expressions="true">

<intercept-url pattern="/account/index" access="permitAll" />

<intercept-url pattern="/**" access="isAuthenticated()" />

<intercept-url pattern="/welcome" access="isAuthenticated()" />

<form-login login-page="/account/index"

authentication-success-handler-ref="loginProcessHandler"

authentication-failure-handler-ref="loginProcessHandler" />

</http>


<authentication-manager>

<authentication-provider user-service-ref="userService">

<password-encoder ref="encoder" />     

</authentication-provider>

</authentication-manager>

</beans:beans>   



Posted by Y2K
,

이제 구성된 서비스를 중심으로 REST API 서버를 구축해보도록 한다. 


먼저, 구축할 REST API는 다음과 같다. 


  • rest/person/{id} : (GET) , Person 데이터를 얻어온다.
  • rest/person/list : (GET) 전체 사용자 데이터를 얻어온다.
  • rest/person/edit : (POST) 사용자 데이터를 수정한다.
  • rest/person/delete/{id} : (DELETE) 사용자를 삭제한다.

1. pom.xml 설정


REST API를 구성하기위해서, 객체의 JSON serialization을 담당하는 jar가 필요하다. 주로 사용되는 jackson을 추가하도록한다.


<dependency>

<groupId>org.codehaus.jackson</groupId>

<artifactId>jackson-mapper-asl</artifactId>

<version>1.9.9</version>

</dependency>


주의사항 : jackson-core-asl은 jackson의 core만을 가지고 있다. jackson-mapper-asl을 추가해야지, spring과 연결되는 REST view가 구현된다. 


2. web.xml에 다음과 같은 설정을 추가한다.


<servlet>

<servlet-name>spring</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring-servlet.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>spring</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


servlet이름을 spring으로 하고, spring bean 설정 file이 위치한 path를 설정한다. 이때, servlet-class는 spring web framework의 Front Controller Class인 DispatcherServlet이 된다.



3. spring-servlet.xml을 작성한다.


controller들을 등록하기 위해서, 다음 설정을 추가한다.  아래 설정은 Spring MVC의 @Controller annotation을 구성한 모든 class들을 Spring bean에 등록하고, URL에 mapping하는 작업을 한다.


<context:component-scan base-package="com.xyzlast.controllers"></context:component-scan>

<mvc:annotation-driven />



4. PersonAPIController를 작성한다. 


@Controller annotation을 이용해서 Controller로 등록을 하고, @RequestMapping을 이용해서 URL을 구성한다.


@Controller

public class PersonAPIController {

@Resource(name="personService")

private IPersonService personService;

@RequestMapping(value = "rest/person/{id}")

@ResponseBody 

public Person get(@PathVariable Integer id) {

Person person = personService.get(id);

return person;

}

@RequestMapping(value="rest/person/list")

@ResponseBody

public List<Person> list() {

return personService.getAll();

}

@RequestMapping(value="rest/person/add", method=RequestMethod.PUT)

@ResponseBody

public Person add(@RequestParam(value="firstName", required=true) String firstName,

@RequestParam(value="lastName", required=true) String lastName, 

@RequestParam(value="money", required=true) Double money) {

return personService.add(firstName, lastName, money);

}

@RequestMapping(value="rest/person/edit", method=RequestMethod.POST)

@ResponseBody

public Person edit(@RequestParam(value="id", required=true) Integer id, 

          @RequestParam(value="firstName", required=true) String firstName,

          @RequestParam(value="lastName", required=true) String lastName,

          @RequestParam(value="money", required=true) Double money) {

return personService.edit(id, firstName, lastName, money);

}

@RequestMapping(value="rest/person/delete/{id}", method = RequestMethod.DELETE)

@ResponseBody

public Person delete(@PathVariable Integer id) {

Person person = personService.get(id);

if(person != null) {

personService.delete(id);

}

return person;

}

}




5. Controller Test code를 작성한다.

Test code는 MockHttpServletRequest와 MockHttpServletResponse를 이용해서 구성하면 된다. 각 URL에서의 request/response 모두를 측정가능하다. 


먼저, servlet-api를 pom.xml에 추가한다. tomcat과 같은 servlet container가 항시 로드하는 jar지만, 지금은 JUnit test runner에서 돌리기 위한 것임으로 maven에 추가를 해준다. 테스트에서만 사용할 것이기 때문에 scope를 test로 주는것이 매우 중요하다.


<dependency>

<groupId>org.apache.tomcat</groupId>

<artifactId>servlet-api</artifactId>

<version>6.0.35</version>

</dependency>


그리고 아래와 같이 테스트 코드를 작성한다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-context.xml")
public class PersonAPIControllerTest {
@Resource
private IPersonService personService;
    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;
    
    private Person person;
    
    @Before
    public void setup() {
    person = personService.add("ykyoon", "lastName", 100.00);
    }
    
    @Test
    public void get() throws Exception {
    MockHttpServletRequest request = new MockHttpServletRequest("GET","/rest/person/" + person.getId().toString());
    MockHttpServletResponse response = new MockHttpServletResponse();

    Object handler = handlerMapping.getHandler(request).getHandler();
    ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);    
    System.out.println(response.getContentAsString());    
    assertNull(modelAndView);
    }
    
    @Test
    public void edit() throws Exception {
    MockHttpServletRequest request = new MockHttpServletRequest("POST", "/rest/person/edit");
    request.addParameter("id", person.getId().toString());
    String editedFirstName = "EDITED_FIRSTNAME";
    request.addParameter("firstName", editedFirstName);
    request.addParameter("lastName", person.getLastName());
    request.addParameter("money", person.getMoney().toString());
   
    MockHttpServletResponse response = new MockHttpServletResponse();
   
    Object handler = handlerMapping.getHandler(request).getHandler();
    ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);    
    System.out.println(response.getContentAsString());    
    assertNull(modelAndView);
   
    Person editedPerson = personService.get(person.getId());
    assertThat(editedPerson.getFirstName(), is(editedFirstName));
    }
    
    @Test
    public void delete() throws Exception {
    MockHttpServletRequest request = new MockHttpServletRequest("DELETE", "/rest/person/delete/" + person.getId().toString());
    MockHttpServletResponse response = new MockHttpServletResponse();

    Object handler = handlerMapping.getHandler(request).getHandler();
    ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);    
    System.out.println(response.getContentAsString());    
    assertThat(response.getStatus(), is(200));
    assertNull(modelAndView);

    Person deletedPerson = personService.get(person.getId());
    assertNull(deletedPerson);
    }
}






Posted by Y2K
,

구성된 project는 아주 기본적인 Hibernate를 이용한 ORM 구성을 가지게 된다. 

추가로, Transaction과 Connection Polling을 구성하도록 한다. 


1. Transaction의 추가


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>


spring trasaction을 위한 jar와 aop를 위한 jar를 추가한다.

생성된 PersonService의 interface를 선언한다. 

public interface IPersonService {
Person add(String firstName, String lastName, Double money);
List<Person> getAll();
Person get(Integer id);
Person edit(Integer id, String firstName, String lastName, Double money);
void delete(Integer id);
}

PersonService에 interface구현을 추가하고, @Transaction annotation을 붙인다. 
구현된 PersonService는 다음과 같다.

@Service("personService")
@Transactional
public class PersonService implements IPersonService {
@Resource
private SessionFactory sessionFactory;
@SuppressWarnings("unchecked")
public List<Person> getAll() {
Session session = sessionFactory.getCurrentSession();
List<Person> persons = (List<Person>) session.createCriteria(Person.class).list();
return persons;
}
public Person get(Integer id) {
Session session = sessionFactory.getCurrentSession();
return (Person) session.get(Person.class, id);
}
public Person add(String firstName, String lastName, Double money) {
Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
Session session = sessionFactory.getCurrentSession();
session.save(person);
return person;
}
public Person edit(Integer id, String firstName, String lastName, Double money) {
Person person = get(id);
Session session = sessionFactory.getCurrentSession();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
session.update(person);
return person;
}
public void delete(Integer id) {
Person person = get(id);
Session session = sessionFactory.getCurrentSession();
session.delete(person);
}
}

위 코드와 기존코드의 가장 큰 차이는 sessionFactory.getCurrentSession()이다. 기존 코드는 sessionFactory.openSession()을 사용하지만, transaction을 사용하는 Service는 sessionFactory.getCurrentSession()이 된다. 그 이유는 다음과 같다. 

Transaction annotation을 사용하는 경우에 annotation에 의해서 다음과 같은 코드로 변경이 되어서 실행이 되는것과 동일해진다. 


public Person edit(Integer id, String firstName, String lastName, Double money) {

Session s = sessionFactory.openSession();
Transaction transaction = s.beginTransaction();

Person person = get(id);
Session session = sessionFactory.getCurrentSession();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
session.update(person);

transaction.commit();
return person;
}

Transaction AOP에 의해서 항시 openSession, beginTransaction 이 실행되는것과 동일하게 구성이 되게 된다. 그리고, method의 종료시에 transaction의 commit가 자동으로 이루어지기 때문에 Transaction이 구현되는 서비스에서는 sessionFactory에서 getCurrentSession을 통해서 transaction이 적용된 session을 얻어내야지된다.

마지막으로 spring bean annotation설정을 위해서 spring-context.xml에 다음과 같은 내용을 추가하도록 한다.
<!-- sessionFactory에 transaction 구성 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" 
                         p:sessionFactory-ref="sessionFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- sessionFactory 구성 -->

테스트를 구동하고, 정상적으로 동작하는지 확인한다. 



2. Connection Pool의 추가


hibernate에서 주로 사용하는 c3p0를 connection pool로 사용하도록 한다. c3p0는 사용하기 쉽고, 가벼워서 WAS에서 제공하는 connection pool을 사용하지 않는 경우에 주로 추천되는 connection pool이다. 


c3p0 dependency를 pom.xml에 다음과 같이 추가한다.


<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-c3p0</artifactId>

<version>4.1.7.Final</version>

</dependency>



spring-context에서 기존 dataSource를 제거하고 c3p0에서 제공하는 Pooling DataSource로 대체한다. 

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="10"
p:maxStatements="50"
p:minPoolSize="5"/>


테스트를 구동하고, 정상적으로 테스트를 통과하면 완료된다.


일단, 기본 WEB Application을 개발할 수 있는 환경을 만들었지만, 하나 빼먹은 것이 계속해서보인다.; 

그것은 바로... Log!!! 


3. Log의 추가


요즘 java의 추세인 logback와 slf4j를 사용하도록 한다.; 일단 log4j보다 속도가 좋고, 거기에 log4j와 동일한 방법으로 사용이 가능하다는 장점때문에 logback를 slf4j를 이용해서 facade pattern으로 사용하게 된다. 


pom.xml에 다음을 추가한다. 

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>${logback.version}</version>

</dependency>


<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-core</artifactId>

<version>${logback.version}</version>

</dependency>


그리고,  resource에 logback.xml 파일을 추가하도록 한다. logback.xml은 기본적으로 logback이 사용될때, 기본적으로 load 되는 설정 xml이다. 주로 사용하는 RollingFileAppender를 이용해서 구성하도록 한다. 


logback의 Appender의 종류와 사용법들은 다음 site에서 참고하도록 한다.

http://logback.qos.ch/manual/appenders.html



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

<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n

</pattern>

</encoder>

</appender>

<appender name="FILEOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>c:\\Logs\\BackLog\\logFile.log</file>

<maxHistory>30</maxHistory>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>c:\\Logs\\BackLog\\logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

<maxHistory>30</maxHistory>

</rollingPolicy>

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n

</pattern>

</encoder>

</appender>

<root level="INFO">

<appender-ref ref="STDOUT" />

<appender-ref ref="FILEOUT" />

</root>

</configuration>



이제 test를 실행하면 Console과 LogFile이 정상적으로 만들어지는 것이 확인된다. 이로서 개발전 기본 설정은 모두 마쳐지게 된다. 



Posted by Y2K
,

wikibook에 spring과 hibernate를 연결하는 작업을 진행한다.


1. maven dependency 설정

최종적으로는 spring web mvc를 사용할 예정이기 때문에, wikibook project에 다음과 같은 dependency를 추가한다. 


pom.xml

<!-- spring test jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>3.1.2.RELEASE</version>

<scope>test</scope>

</dependency>

<!-- spring web mvc jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<!-- spring orm jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<!-- mysql connector -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.21</version>

</dependency>

<!-- hibernate jar -->

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-core</artifactId>

<version>4.1.7.Final</version>

</dependency>

추가 후, maven eclipse:eclipse goal을 실행해서, eclipse project로 재등록시키고, eclipse에서 refresh를 하면 모든 jar 파일이 올라오는 것을 알 수 있다. 


2. entity class 생성


테스트로 Person이라는 객체를 생성한다. 객체 코드는 다음과 같다.

@Entity

@Table(name="PERSON")

public class Person {

@Id

@GeneratedValue

@Column(name="ID")

private Integer id;

@Column(name="FIRST_NAME")

private String firstName;

@Column(name="LAST_NAME")

private String lastName;

@Column(name="MONEY")

private Double money;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;  

}

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 Double getMoney() {

return money;

}

public void setMoney(Double money) {

this.money = money;

}

}



3. PersonService를 구성한다. 

기본적인 CRUD만을 가진 Service로 구성한다.


@Service("personService")

public class PersonService implements IPersonService {

@Resource

private SessionFactory sessionFactory;

@SuppressWarnings("unchecked")

public List<Person> getAll() {

Session session = sessionFactory.openSession();

List<Person> persons = (List<Person>) session.createCriteria(Person.class).list();

return persons;

}

public Person get(Integer id) {

Session session = sessionFactory.openSession();

return (Person) session.get(Person.class, id);

}

public Person add(String firstName, String lastName, Double money) {

Person person = new Person();

person.setFirstName(firstName);

person.setLastName(lastName);

person.setMoney(money);

Session session = sessionFactory.openSession();

session.save(person);

return person;

}

public Person edit(Integer id, String firstName, String lastName, Double money) {

Person person = get(id);

Session session = sessionFactory.openSession();

person.setFirstName(firstName);

person.setLastName(lastName);

person.setMoney(money);

session.update(person);

session.flush();

return person;

}

public void delete(Integer id) {

Person person = get(id);

Session session = sessionFactory.openSession();

session.delete(person);

session.flush();

}

}


4. test code를 작성한다.

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="/spring-context.xml")

public class PersonServiceTest {


@Autowired

private IPersonService personService;

@Test

public void addPersons() {

for(Integer i = 0 ; i < 10 ; i++) {

String firstName = "FIRST_NAME " + i.toString();

String lastName = "LAST_NAME " + i.toString();

Double money = i.doubleValue();

Person addedPerson = personService.add(firstName, lastName, money);

assertThat(addedPerson.getId(), not(0));

}

}

@Test

public void getPerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

Person newPerson = personService.get(id);

assertThat(person.getId(), is(newPerson.getId()));

}

@Test

public void editPerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

String editedFirstName = person.getFirstName() + "__edited";

personService.edit(id, editedFirstName, person.getLastName(), person.getMoney());

Person editedPerson = personService.get(id);

assertThat(editedPerson.getFirstName(), is(editedFirstName));

}

@Test

public void deletePerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

personService.delete(id);

assertNull(personService.get(id));

}

}


5. test code를 작성하면 maven 설정에 따라, src/test/resources 에 다음 파일들을 위치시킨다. 


spring-context.xml : spring bean 설정

spring.properties : spring bean에 설정된 properties 파일

hibernate.cfg.xml : hibernate dialect 등 option 정보


spring-context.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"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd

http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<!-- person Service bean 정의 -->

<bean id="personService" class="com.xyzlast.services.PersonService"/>

<!-- property file load -->

<context:property-placeholder location="spring.properties" />

<!-- sessionFactory 구성 -->

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"

p:dataSource-ref="dataSource"

p:configLocation="${hibernate.config}"

p:packagesToScan="com.xyzlast.entities"/>

<!-- MySql dataSource 구성 -->

<bean id="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 

<property name="driverClassName" value="${app.jdbc.driverClassName}" />

<property name="url" value="${app.jdbc.url}" />

<property name="username" value="${app.jdbc.username}" />

<property name="password" value="${app.jdbc.password}" />

</bean>

</beans>


spring.properties

# database properties

app.jdbc.driverClassName=com.mysql.jdbc.Driver

app.jdbc.url=jdbc:mysql://localhost:3306/test

app.jdbc.username=root

app.jdbc.password=qwer12#$

 

#hibernate properties

hibernate.config=hibernate.cfg.xml



hibernate.cfg.xml

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

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create</property>

<property name="hibernate.connection.release_mode">after_transaction</property>

</session-factory>

</hibernate-configuration>


6. 테스트를 실행한다. 

eclipse JUnit plugin을 이용하건, maven command를 이용하던, 모든 방법에서 test 가 ok가 나와야지 된다.






다음은 Controller 구성과 REST 서비스를 구현해보도록 한다. 

Posted by Y2K
,

maven에서 제공하는 maven-archetype-webapp 의 경우, 최신 eclipse인 JUNO와 tomcat7에서 동작하지 않는 문제가 있다. 

기본적으로, java 버젼을 1.4로, jst.web 버젼을 2.4로 잡아버리기 때문에 발생하는 문제인데, 이를 해결하기 위해서는 m2eclipse와 같은 eclipse plug-in을 이용하는 방법이 주로 추천되고 있다. 


maven을 오래 사용한 것은 아니지만, 일단 eclipse plugin을 사용하는 것보다는 console을 이용하는 것이 좀 더 나아보인다. test, deploy 등의 작업을 좀 더 편하게 할 수 있고, m2eclipse를 사용하더라도 손으로 하나하나 다시 설정을 잡아줘야지 되는 불편함이 존재하기 때문에, pom 자체를 이용해서 이를 수정해주는 것이 더 나아보이기 때문이다. 


단계는 다음과 같다. (자바세상의 빌드를 이끄는 메이븐 을 보면서 공부를 해서... 예시는 책에 나온 wikibook을 사용한다.)


1. web app의 skeleton code를 다음 maven command를 이용해서 작성한다.


mvn archetype:generate -DgroupId=com.xyzlast -DartifactId=wikibook -DarchetypeArtifactId=maven-archetype-webapp


작성된 project의 pom 파일은 다음과 같다.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xyzlast</groupId>

  <artifactId>wikibook</artifactId>

  <packaging>war</packaging>

  <version>1.0-SNAPSHOT</version>

  <name>wikibook Maven Webapp</name>

  <url>http://maven.apache.org</url>

  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>3.8.1</version>

      <scope>test</scope>

    </dependency>

  </dependencies>

  <build>

    <finalName>wikibook</finalName>

  </build>

</project>


2. 먼저, 사용하는 java compiler의 버젼은 1.7을 사용하고 있기 때문에 compiler의 버젼을 넣어준다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>

이 상태에서 mvn eclipse:eclipse를 이용해서 eclipse용 설정을 생성하게 되면, 다음과 같은 문제가 발생하게 된다.
* M2_REPO/junit/junit/4.11/junit-4.11.jar will not be exported or published. Runtime ClassNotFoundExceptions may result. 
* Project Facets가 정상적으로 맞지 않는 문제(최신 자바 1.7만 설정이 된 경우)

3. 사용되는 jar 파일들을 WEB-INF/libs 에 위치하게 하기 위해서 다음과 같은 설정을 추가한다. 또한, javadoc과 source를 보기 위한 설정 역시 추가한다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
</configuration>
</plugin>


4. 이 상태에서 mvn eclipse:eclipse를 실행하면, .setting/org.eclipse.wst.common.project.facet.core.xml 파일의 내용은 다음과 같다.

<faceted-project>
  <fixed facet="jst.java"/>
  <fixed facet="jst.web"/>
  <installed facet="jst.web" version="2.4"/>
  <installed facet="jst.java" version="1.4"/>
</faceted-project>

여기서 eclipse로 import하기 전에 text 파일을 고쳐주면 문제가 해결되긴 하지만, 자동화를 위해서 다음과 같은 파일을 작성한다.

java_1.7_web_3.0.xml
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
     <fixed facet="jst.web"/>
     <fixed facet="java"/>
     <fixed facet="wst.jsdt.web"/>
     <installed facet="java" version="1.7"/>
     <installed facet="jst.web" version="3.0"/>
     <installed facet="jst.jsf" version="2.0"/>
     <installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

또한, 생성된 skeleton project의 web.xml은 web app version 2.5의 것을 사용하고 있기 때문에 이것을 3.0으로 넣어줄 기본 web.xml 파일을 작성한다.

web_3.0_default.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
         id="WebApp_ID" version="3.0">

<display-name>wikibook</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

작성된 2개의 파일을 MAVEN_HOME의 app-conf Directory에 카피한다.

5. maven-eclipse-plugin 정보에 다음과 같은 추가 설정을 한다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
<additionalConfig>
<file>
<name>.settings/org.eclipse.wst.common.project.facet.core.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/java_1.7_web_3.0.xml</location>
</file>
<file>
<name>src/main/webapp/WEB-INF/web.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/web_3.0_default.xml</location>
</file>
</additionalConfig>
</configuration>
</plugin>

추가 설정의 내용은 단순하다. MAVEN_HOME에 위치한 facet 파일과 web.xml을 이용해서 eclipse plugin에 의해 생성되는 파일을 대치해버리는 것이다. 

최종적인 pom.xml파일 내용은 다음과 같다. (JUnit 4를 사용하기 위해서, JUnit버젼 역시 3.8.1에서 4.11로 변경하였다.)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xyzlast</groupId>
<artifactId>wikibook</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>wikibook Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
<version>6.0.35</version>
</dependency>
</dependencies>
<build>
<finalName>wikibook</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
<additionalConfig>
<file>
<name>.settings/org.eclipse.wst.common.project.facet.core.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/java_1.7_web_3.0.xml</location>
</file>
<file>
<name>src/main/webapp/WEB-INF/web.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/web_3.0_default.xml</location>
</file>
</additionalConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>


6. 이제 console에서 mvn eclipse:eclipse를 실행한다. 
실행후, eclipse에서 Import를 이용해서 project를 load 한다. 결과는 다음과 같다.



test에 사용되는 junit부분 warning이고, 이 것은 무시 가능하다. delete로 지워버려도 된다.


이제 Tomcat을 구성하고, Tomcat에서 project를 추가하면 다음과 같은 페이지를 보는 것이 가능하다. 



Posted by Y2K
,

InvocationHandler

Java 2012. 9. 12. 00:00

JAVA와 .NET의 차이점을 볼때, 가장 특이한 점이라고 보이는 Interface.


Dynamic Proxy의 핵심 Interface이고, Proxy.newProxyInstance method를 통해서 기본적인 Dynamic Proxy 객체를 생성해주는 것이 가능하다.

코드는 다음과 같다.


Hello proxyHello = (Hello) Proxy.newProxyInstance(

                             getClass().getClassLoader(),

                             new Class[] { Hello.class },

                             new InvocationHandler(new HelloTarget()));


재미있어보이는 코드이고, 일단 Target의 Interface를 모두 상속받은 Proxy객체를 만들어준다는 것이 엄청나다는 느낌이 든다. 


물론. 이 코드를 바로 사용하는 일은 거의 존재하지를 않는다. 

Spring에서 제공하는 ProxyFactory를 이용해서, Dynamic Proxy를 구성하는 것이 일반적이기 때문이다. 

Posted by Y2K
,