메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Spring Roo 소개, 파트 3: Spring Roo 추가 기능 개발

Shekhar Gulati, 선임 연구원, Xebia
Shekhar Gulati는 Xebia India에서 근무하는 Java 컨설턴트이다. 그의 엔터프라이즈 Java 경력은 6년이며, Spring, Spring-WS, Spring Roo 등의 Spring 포트폴리오 프로젝트에 폭넓은 경험을 보유하고 있다. 그의 관심사는 Spring Roo, 클라우드 컴퓨팅(주로 Google App Engine, CloudFoundry, OpenShift같은 PaaS 서비스), Hadoop과 같은 Spring, NoSQL 데이터베이스, Hadoop, RAD 프레임워크이다. 그는 JavaLobby, Developer.com, IBM developerWorks 및 개인 블로그(http://whyjava.wordpress.com/)에 활발하게 기사를 게재하고 있다. 트위터(@http://twitter.com/#!/shekhargulati)를 통해 Gulati를 팔로우할 수도 있다

요약:  Spring Roo는 애플리케이션(주로 웹 애플리케이션)을 빠르고 쉽게 빌드할 수 있게 해주는 RAD 도구이다. 그 내부를 살펴보면, Spring Roo는 OSGI 추가 기능 아키텍처를 기반으로 하고 있어 추가 기능을 추가하여 Spring Roo를 손쉽게 확장할 수 다. Spring Roo는 Spring Roo 사용자 커뮤니티에서 매우 쉽게 사용하도록 할 수 있는 추가 기능을 작성하기 위한 명령을 제공한다. 이 기사에서는, 우선 Spring Roo 아키텍처에 대해 설명하면서 Spring Roo가 어떻게 자체적인 추가 기능 아키텍처를 활용하여 다양한 기능을 제공하는지 살펴본 다음, Roo 쉘을 사용하여 추가 기능을 작성하고 요구사항에 맞춰 이를 적절히 수정해보자.

이 연재 자세히 보기

기사 게재일:  2011 년 11 월 01 일
난이도: 중급 원문:  보기 PDF:  A4 and Letter (131KB | 29 pages)Get Adobe® Reader®
페이지뷰:  1635 회
의견:  


시작

"Spring Roo 소개" 시리즈 기사의 파트 1파트 2에서는 Spring Roo를 사용하여 완벽한 기능을 갖춘 엔터프라이즈 애플리케이션을 처음부터 빌드했다. 첫 두 기사에서는 Spring Roo의 빠른 애플리케이션 개발 환경을 사용하여 웹 애플리케이션을 빌드하는 데 초점을 맞추었다. 그러면서 JPA, Selenium 테스트, Spring Security, 이메일 통합, Spring Roo 소셜 기능, 데이터베이스 리버스 엔지니어링 등과 같은 많은 기능을 다루었다. 이번 기사에서는 Spring Roo의 추가 기능 아키텍처를 살펴볼 것이다. 그런 다음, addon create 명령을 사용하여 Spring Roo 추가 기능을 작성해본다. 본 기사의 학습 목표는 Spring Roo용 추가 기능을 직접 빠르고 쉽게 작성하는 것이다.


Spring Roo 추가 기능 아키텍처

가장 간단한 양식으로 작성 시, 추가 기능은 소프트웨어 애플리케이션에 특정한 능력을 부가하는 소프트웨어 컴포넌트라 할 수 있다. 예를 들어, 대부분의 웹 브라우저에는 비디오 지원이 추가 기능으로 제공된다. 또 다른 예는 Eclipse(많은 Java™ 개발자가 사용하거나 최소한 알고는 있는 오픈 소스 IDE)이다. 대부분의 기능은 JUnit 지원, SVN 지원 등과 같은 추가 기능에 의해 제공된다. 필자는 플러그인 및 확장을 나타내는 일반적인 용어로서 추가 기능이란 용어를 사용하고 있다.

Spring Roo 역시 추가 기능에 대해 다음과 같은 개념을 두고 있다.

  1. 써드파티 개발자가 Spring Roo의 능력을 확장하는 기능을 작성할 수 있도록 하는 것
  2. Spring Roo로 기능을 손쉽게 새로 추가할 수 있게 돕는 것
  3. Spring Roo가 마치 요술램프의 요정처럼 몸집을 줄여 작은 크기가 되도록 돕는 것

Spring Roo 논리 컴포넌트

Spring Roo는 두 개의 논리 파트로 분할된다.

Spring Roo 코어 컴포넌트: 추가 기능을 손쉽게 개발할 수 있도록, Spring Roo는 다양한 추가 기능을 위해 호스트된 실행 환경을 형성하는 코어 컴포넌트 세트를 제공한다. 이런 컴포넌트는 Classpath로서, Process Manager와 Shell을 지원한다. 또한, Process Manager는 Project와 File Undo를 지원하고 Project는 Model, Metadata 및 File Monitor를 지원한다. 마지막으로, 이들 모두가 사용하는 Support 컴포넌트가 있다. 그림 1은 이런 관계를 시각적으로 나타낸 것이다.


그림 1. Spring Roo 코어 컴포넌트
Spring Roo 컴포넌트 간의 관계를 보여주는 다이어그램

몇 가지 코어 모듈에 대해 얘기해보자.

  • Supportorg.springframework.roo.support 모듈은 모든 코어 모듈과 추가 기능에서 사용하는 공통 유틸리티 클래스를 제공한다. 일부 유틸리티 클래스에는 Assert, FileCopyUtils, XmlUtils, StringUtils, FileUtils 등이 포함된다. 예를 들어, 한 파일의 컨텐츠를 다른 파일에 복사하려는 경우 FileCopyUtils를 사용하면 이 작업을 자동으로 수행할 수 있다.
  • Metadata — org.springframework.roo.metadata 모듈은 메타데이터 서비스 제공자 인터페이스와 종속성 등록 및 캐싱을 포함하는 구현을 제공한다.
  • File monitor — org.springframework.roo.file.monitor 모듈은 발견된 파일 시스템 변경 이후 이벤트를 게시한다(기본 구현에서는 크기가 자동으로 조정되는 디스크 폴링을 사용함).
  • File undo — org.springframework.roo.file.undo 모듈은 프로세스 관리자가 사용하기 위한 파일 실행 취소 기능을 제공한다.
  • Project — org.springframework.roo.project 모듈은 Apache Maven 및 Apache Ant와 같은 전형적인 일반 사용자 프로젝트 빌드 시스템을 추상화한다.
  • Process manager — org.springframework.roo.process.manager 모듈은 디스크 롤백과 프로세스 동기화를 포함한 ACID와 같은 파일 시스템 추상화를 제공한다.
  • Classpath — org.springframework.roo.classpath 모듈은 추상 구문 트리 구문 분석과 Java 및 AspectJ 컴파일 단위의 유형 바인딩을 수행한다.

Spring Roo 코어 컴포넌트: Spring Roo는 추가 기능을 통해 모든 기능을 제공한다. Roo V1.1.3에 기본적으로 제공되는 추가 기능은 다음과 같다.

  • Add-On Creator — org.springframework.roo.addon.creator 추가 기능을 사용하면 써드파티 Roo 추가 기능을 손쉽게 작성할 수 있다.
  • Backup — org.springframework.roo.addon.backup 추가 기능을 사용하면 backup을 입력하여 ZIP 파일에 대한 백업을 만들 수 있다.
  • Cloud Foundry — org.springframework.roo.addon.cloud.foundry 추가 기능은 VMware Cloud Foundry 지원을 제공한다.
  • Configurable — org.springframework.roo.addon.configurable 추가 기능은 AspectJ ITD를 통해 Spring의 @Configurable 어노테이션 도입을 위한 지원을 제공한다.
  • Database reverse engineering — org.springframework.roo.addon.dbre 추가 기능은 기존 데이터베이스의 점진적 리버스 엔지니어링을 위한 지원을 제공한다.
  • Data on Demand — org.springframework.roo.addon.dod 추가 기능은 통합 테스트에 사용되는 샘플 데이터의 자동 작성을 위한 지원을 제공한다.
  • Email — org.springframework.roo.addon.email 추가 기능은 대상 프로젝트에서 Spring의 이메일 지원 통합 및 구성을 위한 지원을 제공한다.
  • Entity — org.springframework.roo.addon.entity 추가 기능은 Java Persistence API @Entity 클래스를 자동으로 유지보수하기 위한 광범위한 지원을 제공한다.
  • Dynamic Finder — org.springframework.roo.addon.finder는 형식이 안전한 코드 완성 호환 가능 JPA 쿼리 언어 찾기 프로그램을 작성한다.
  • Git — org.springframework.roo.addon.git 추가 기능은 프로젝트에서 GIT 통합을 위한 지원을 제공한다. 올바로 실행된 각각의 명령은 로컬 GIT 저장소에 자동으로 커미트된다.
  • GWT — org.springframework.roo.addon.gwt 추가 기능은 Google Web Toolkit을 사용하여 UI 스캐폴딩을 위한 지원을 제공한다.
  • JavaBean — org.springframework.roo.addon.javabean 추가 기능은 @RooJavaBean 어노테이션으로 클래스에 대한 JavaBean getter/setter를 자동으로 유지보수한다.
  • JDBC — org.springframework.roo.addon.jdbc 추가 기능은 (주로 다른 추가 기능에서 사용하는) 다른 번들에 포함되어 제공되는 JDBC 드라이버에 대한 OSGi 규격에 따른 액세스를 캡슐화한다.
  • JMS — org.springframework.roo.addon.jms 추가 기능은 대상 프로젝트에서 Java Messaging System 설정을 구성하기 위한 지원을 제공한다.
  • JPA — org.springframework.roo.addon.jpa 추가 기능은 지정된 JPA 제공자를 설치하고 JDBC를 적절히 설정한다.
  • JSON — org.springframework.roo.addon.json 추가 기능은 JSON 관련 serialization 및 deserialization 메소드를 POJO에 추가한다.
  • Logging — org.springframework.roo.addon.logging 추가 기능은 명령 기반 로그 레벨 구성을 포함한 Log4j를 설정한다.
  • Pluralization — org.springframework.roo.addon.plural 추가 기능은 명사를 복수화하는 기능을 제공한다(주로 다른 추가 기능에서 사용함).
  • Property Editor — org.springframework.roo.addon.property.editor 추가 기능은 Spring MVC에서 요구하는 대로 특성 편집기를 관리한다.
  • Property File — org.springframework.roo.addon.propfiles 추가 기능은 대상 프로젝트에서 특성 파일 관리를 위한 지원을 제공한다.
  • RooBot Client — org.springframework.roo.addon.roobot.client 추가 기능은 RooBot 서버를 통한 추가 기능 관리를 위한 지원을 제공한다.
  • Security — org.springframework.roo.addon.security 추가 기능은 로그인 페이지, 필터 및 종속 항목을 포함하여, Spring Security를 설정한다.
  • Serializable — org.springframework.roo.addon.serializable 추가 기능은 요청된 Java 형식에 (UID 유지보수와 같은) java.io.Serializable 지원을 추가한다.
  • Solr — org.springframework.roo.addon.solr 추가 기능은 대상 프로젝트에서 Apache Solr 기능의 구성과 통합을 위한 지원을 제공한다.
  • Integration Test — org.springframework.roo.addon.test 추가 기능은 프로젝트 엔티티에 대한 JUnit 통합 테스트를 생성한다.
  • ToString — org.springframework.roo.addon.tostring 추가 기능은 @RooToString 어노테이션을 가진 클래스에 대해 유효한 toString() 메소드를 생성한다.
  • WebFlow — org.springframework.roo.addon.web.flow 추가 기능은 대상 프로젝트에서 Spring Web Flow 기능의 구성과 통합을 위한 지원을 제공한다.
  • Web MVC Controller — org.springframework.roo.addon.web.mvc.controller 추가 기능은 대상 프로젝트에서 Spring MVC 제어기의 구성과 통합을 위한 지원을 제공한다.
  • Web MVC Embedded — org.springframework.roo.addon.web.mvc.embedded 추가 기능은 맵, 비디오 등과 같이 임베드된 기능을 웹 페이지에 추가할 수 있도록 허용하는 MVC 추가 기능에 대한 확장을 제공한다.
  • Web MVC JSP — org.springframework.roo.addon.web.mvc.jsp 추가 기능은 대상 프로젝트에서 Spring MVC JSP 기능을 구성하고 통합한다.
  • Selenium — org.springframework.roo.addon.web.selenium 추가 기능은 대상 프로젝트에서 Selenium 웹 테스트의 구성 및 통합 기능을 제공한다.

Spring Roo 코어 컴포넌트와 Spring Roo에서 제공되는 기본 추가 기능을 살펴보았으므로, 우리 스스로 추가 기능을 작성해보자.


OSGi 런타임 환경

Spring Roo는 Roo의 추가 기능 아키텍처에 이상적인 OSGi를 기반으로 한다. OSGi는 모듈식 및 임베드된 서비스 지향 애플리케이션을 개발하기에 매우 적합한 인프라를 제공한다.

Roo 쉘은 컴포넌트 관리를 위한 SCR(Service Component Runtime) 및 번들 해결을 위한 OBR(OSGi Bundle Repository)과 함께 Apache Felix를 OSGi 런타임 프레임워크로 사용한다. Roo 쉘에서 다양한 OSGi 명령을 사용할 수 있는데, 목록 1에 표시된 것처럼 help osgi를 입력하면 볼 수 있다.


목록 1. OSGi에 대한 Roo 도움말

roo> help osgi
* osgi find - Finds bundles by name
* osgi framework command - Passes a command directly
through to the Felix shell infrastructure
* osgi headers - Display headers for a specific bundle
* osgi install - Installs a bundle JAR from a given URL
* osgi log - Displays the OSGi log information
* osgi obr deploy - Deploys a specific OSGi Bundle Repository (OBR) bundle
* osgi obr info - Displays information on a specific OSGi Bundle Repository (OBR) bundle
* osgi obr list - Lists all available bundles from the
OSGi Bundle Repository (OBR) system
* osgi obr start - Starts a specific OSGi Bundle Repository (OBR) bundle
* osgi obr url add - Adds a new OSGi Bundle Repository (OBR) repository file URL
* osgi obr url list - Lists the currently-configured
OSGi Bundle Repository (OBR) repository file URLs
* osgi obr url refresh - Refreshes an existing
OSGi Bundle Repository (OBR) repository file URL
* osgi obr url remove - Removes an existing
OSGi Bundle Repository (OBR) repository file URL
* osgi ps - Displays OSGi bundle information
* osgi resolve - Resolves a specific bundle ID
* osgi scr config - Lists the current SCR configuration
* osgi scr disable - Disables a specific SCR-defined component
* osgi scr enable - Enables a specific SCR-defined component
* osgi scr info - Lists information about a specific SCR-defined component 
* osgi scr list - Lists all SCR-defined components 
* osgi start - Starts a bundle JAR from a given URL 
* osgi uninstall - Uninstalls a specific bundle 
* osgi update - Updates a specific bundle 
* osgi version - Displays OSGi framework version 


Spring Roo 추가 기능 작성 명령

Spring Roo에는 다양한 유형의 추가 기능을 작성하기 위한 추가 기능 작성 명령이 번들로 제공된다. addon create 명령을 노출하는 Add-on Creator 역시 Roo 추가 기능이다. Roo는 현재 다음 네 가지 유형의 추가 기능을 지원한다.

  1. 자국어 지원 추가 기능 — 이 추가 기능은 Roo의 스캐폴드 방식 Spring MVC 애플리케이션을 위한 언어 변환 추가를 지원한다(예: 힌디어를 위한 변환 추가).
  2. 단순 추가 기능 — 단순 추가 기능은 프로젝트 종속 항목 또는 구성이나 둘 모두에 약간의 기능 추가를 지원한다(예: 일부 JAR 또는 Maven 플러그인 추가와 같은 maven pom.xml 수정).
  3. 고급 추가 기능 — 이 추가 기능은 많은 역할을 수행하며 Java 코드를 작성해야 하는 완벽한 기능을 갖춘 Spring Roo 추가 기능을 빌드하는 데 사용된다(예: 도메인 오브젝트를 위한 equalshashcode 메소드를 작성할 수 있는 추가 기능 빌드). 이미 이런 기능을 위한 커뮤니티 추가 기능이 있다.
  4. 랩퍼 추가 기능 — 이 추가 기능은 Maven 아티팩트를 랩핑하여 OSGi 준수 번들을 작성한다. 추가 기능 고유의 기능을 완료하기 위해 종속 항목이 필요할 때 랩퍼 추가 기능이 필요하다. 예를 들어, Spring Roo 데이터베이스 리버스 엔지니어링 추가 기능은 고유의 태스크를 완료하는 데 Postgres JDBC 드라이버가 필요하므로, 이 추가 기능을 사용하여 Postgres JDBC 드라이버를 랩핑한다.

이런 추가 기능은 다음과 같은 추가 기능을 새로 작성하여 Roo 추가 기능을 쉽게 개발하기 위한 명령을 작성한다.

httppgp://

Spring Roo V1.1에서는 PGP(Pretty Good Privacy)가 도입되어, 사용자가 다운로드하여 Roo 쉘에서 활성화할 소프트웨어를 서명할 때 어떤 개발자를 신뢰하는지 지정할 수 있었다. 사실, Roo 자체의 각 릴리스는 현재 PGP 서명이 되어 있다. URL에 PGP 보호 장치가 분리된 서명도 사용 가능함을 표시하기 위해 Roo에는 httppgp://라는 새로운 프로토콜 핸들러가 도입되었다. 이 핸들러는 악성 다운로드로부터 사용자를 보호하기 위해 공개된 양식의 보안 기능을 제공한다. 이런 표준을 통해 독립형 PHP 도구를 사용하여 Roo의 조작을 독립적으로 확인할 수도 있다.

  • Google Code SVN 소스 코드 제어와 통합됨
  • Google 코드 프로젝트의 일부로 작성된 공용 Maven 저장소에 호스트됨
  • 공용 Roo OBR 파일의 중요한 컨텐츠를 인덱싱하는 VMware 호스트 서비스인 RooBot 규격에 따름. OBR 파일은 번들 메타데이터의 XML 기반 표시이다. RooBot 규격을 준수하려면 추가 기능이 다음 요건을 만족해야 한다.
    1. OSGi 규격 준수
    2. 공개 키로 PGP 서명된 아티팩트
    3. httppgp:// 프로토콜을 통해 등록됨

Roo addon create 명령을 사용하면 사용자의 추가 기능에 대해 위에 언급된 모든 기능을 자동으로 구성할 수 있다. 그 덕분에, 추가 기능을 작성하여 외부에 게시하는 시간이 확실히 단축된다.

추가 기능 작성을 시작하기 전, 올바로 작동하는 Spring Roo 환경이 설정되어 있어야 한다. Spring Roo를 설치하기 위한 지시사항은 이 시리즈의 파트 1에서 확인할 수 있다.


힌디어 지원(i18N 추가 기능 작성)

Spring Roo를 사용하여 Spring MVC 기반 웹 애플리케이션을 작성할 때 web mvc language 명령을 사용하여 다른 언어에 대한 지원을 추가할 수 있다. Spring Roo는 기본적으로 영어, 독일어, 스페인어, 이탈리아어, 네덜란드어 및 스웨덴어를 지원한다. webapp 디렉토리에 JSPX 파일이 있어야만 사용되는 웹 MVC JSP 추가 기능에서 자국어 지원을 제공한다. JSPX 파일은 간단한 JAR 기반 애플리케이션을 Spring MVX 웹 애플리케이션으로 변환하는 제어기 명령에 의해 생성된다.

필자는 인도인이므로, 내 웹 애플리케이션에 힌디어에 대한 지원을 추가하고 싶었다. Spring Roo는 힌디어와 같은 새로운 언어에 대한 지원을 추가하는 web mvc install language command에 대한 확장을 제공하는 addon create i18n 명령을 제공한다. 이 명령에 필요한 유일한 것은 원하는 언어로 messages.properties 파일을 변환하는 것이다.

Spring Roo는 Spring MVC 웹 애플리케이션을 작성할 때 application.properties 및 messages.properties라는 두 개의 특성 파일을 작성한다. application.properties 파일에는 애플리케이션 이름과 같은 애플리케이션별 특성이 포함된다. 삭제 단추를 클릭할 때의 "Are you sure want to delete this item?" 메시지나 로그인, 로그아웃 등의 메시지와 같이, messages.properties 파일에는 어떤 애플리케이션에도 특정하지 않은 특성이 포함된다. 따라서 i18n 추가 기능을 작성할 때 messages.properties 파일에 대한 번역을 제공해야 한다.

Spring Roo의 기본 자국어 지원에 대해 설명했으므로, 힌디어 지원을 추가할 수 있는 i18n 추가 기능을 작성해보자. Google 코드에 대한 프로젝트를 설정하고, 해당 명령을 사용하여 추가 기능을 작성하고, 외부 환경에 이를 게시 및 릴리스하고, 마지막으로 RooBot 서비스에 등록하는 방법을 설명하겠다. RooBot 서비스에 등록하는 것은 중요하다. 그래야 RooBot에서 사용자의 추가 기능을 인덱싱하고 다른 개발자가 addon search 명령을 사용하여 검색할 때 이 추가 기능을 표시할 수 있을 것이기 때문이다.

프로젝트 설정

Spring Roo 문서에 Google 코드에 프로젝트와 Maven 저장소를 설정하는 방법이 자세히 설명되어 있으므로, 여기서 같은 설명을 되풀이하진 않겠다. 필자는 "roo-hind-addon"을 프로젝트 이름으로 사용할 것이라는 점만 밝히겠다.

i18N 추가 기능 작성

프로젝트를 설정하고 나면 roo-hindi-addon이라는 빈 디렉토리가 있을 것이다. 그 디렉토리로 이동하고 roo command을 입력한다. 쉘에서 addon create i18n을 입력한다. 탭 키를 누르면 이 명령에 7개의 속성이 있는 것을 알 수 있다. 7개의 속성 중에서 3개의 속성, 즉 topLevelPackage(새 추가 기능의 최상위 레벨 패키지), locale(이탈리아어의 경우 "it"와 같은 로컬 약어) 및 messageBundle(message_xx.properties에 대한 완전한 경로, 여기서 xx는 로케일 이름)이 필수적이다. 다른 4개의 속성인 language(언어의 전체 이름), flagGraphic(xx.png 파일의 전체 경로, 여기서 xx는 플래그의 이름), description(추가 기능에 대한 설명) 및 projectName(프로젝트의 이름(입력하지 않으면 최상위 레벨 패키지 이름이 사용됨)은 선택적 속성이다.) projectName 속성을 사용하고 그 값이 Google 코드에 호스트된 프로젝트의 이름인지 확인하는 것이 좋을 것이다. 이 경우에는 roo-hindi-addon이 된다.

위에서 언급한 속성 중에서 가장 중요한 것은 변환된 messages.properties 파일을 지정하는 messageBundle이다. messages.properties를 힌디어로 변환하는 가장 쉬운 옵션은 Google 번역과 같은 서비스를 사용하여 각각의 특성을 하나씩 변환한 다음 messages_hi.properties 파일을 쓰는 방법이다. 하지만, Java 특성 파일은 힌디어 문자를 지원하지 않는 ISO-8859-1 인코딩을 사용하기 때문에 이 상황에서는 알맞지 않다. 이 문제점을 극복하기 위해, 필자는 Eclipse ResourceBundle Editor라는 Eclipse 플러그인을 사용했으며 이 플러그인으로 다른 언어에 대한 자원 번들을 변환하고 작성할 수 있다. http://www.nightlabs.de/updatesites/development/ 업데이트 사이트를 사용하여 이 플러그인을 설치할 수 있다. messages.properties 파일을 messages_hi.properties 파일로 변환하는 것은 본 기사의 범위를 벗어나는 주제이다. ResourceBundle Editor 플러그인은 사용하기 쉽다. 해당 언어의 문자가 지원되지 않는 경우에는 ResourceBundle Editor를 사용할 필요가 없다. messages_hi.properties 파일은 목록 2와 같은 형태이다.


목록 2. messages_hi.properties 파일의 샘플

button_cancel = \u0930\u0926\u094D\u0926 
button_end = \u0905\u0902\u0924
button_find = \u0916\u094B\u091C\u0947\u0902
button_home = \u0918\u0930 
...

전체 파일을 볼 수도 있다. messages_hi.properties 파일을 변환했으면 단일 명령 입력만으로 추가 기능을 작성할 준비가 끝난 셈이다. 목록 3에서 Roo 쉘에 명령을 입력하여 힌디어 추가 기능을 작성할 수 있다.


목록 3. 힌디어 추가 기능을 작성하기 위한 명령

addon create i18n --locale hi --topLevelPackage org.xebia.roo.addon.i18n.hindi 
  --messageBundle <location to messages_hi.properties> \
--language hindi --projectName roo-hindi-addon 
  --flagGraphic <full path to flag hi.png>

목록 4는 addon create i18n 명령으로 작성한 아티팩트를 나타낸 것이다.


목록 4. 작성된 아티팩트

Created ROOT/pom.xml 
Created ROOT/readme.txt 
Created ROOT/legal 
Created ROOT/legal/LICENSE.TXT 
Created SRC_MAIN_JAVA 
Created SRC_MAIN_RESOURCES 
Created SRC_TEST_JAVA 
Created SRC_TEST_RESOURCES 
Created SRC_MAIN_WEBAPP 
Created SRC_MAIN_RESOURCES/META-INF/spring 
Created ROOT/src/main/assembly 
Created ROOT/src/main/assembly/assembly.xml 
Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/i18n/hindi 
Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/i18n/hindi/messages_hi.properties 
Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/i18n/hindi/hi.png 
Created SRC_MAIN_JAVA/org/xebia/roo/addon/i18n/hindi 
Created SRC_MAIN_JAVA/org/xebia/roo/addon/i18n/hindi/HindiLanguage.java 

이 명령을 사용하여 Eclipse(m2eclipse 포함)로 가져올 수 있는 Maven 프로젝트 또는 File > Import > Maven > Existing Maven projects를 통해 SpringSource Tool Suite를 통해 가져올 수 있는 Maven 프로젝트를 생성했다. 추가 기능을 수정할 필요가 없으므로 이 프로젝트를 가져올 필요가 없다.

이제 이 추가 기능을 설치하여 프로젝트에서 사용하고 외부에 게시할 수 있다. 그러나 우선은 생성된 코드를 좀 더 정확히 이해하기 위해 몇 가지 아티팩트에 대해 살펴보자.

  • pom.xml — 표준 Maven 프로젝트 구성이다. 생성된 pom.xml에는 미리 구성된 다양한 플러그인이 있으며, PGP를 사용한 아티팩트 서명, Maven 릴리스 플러그인을 사용한 추가 기능 릴리스 및 Maven 번들 플러그인을 사용한 OSGi 번들 작성과 관련된 작업을 수행한다. 또한, Roo 쉘 내에서 추가 기능을 실행하는 데 필요한 OSGi 및 Felix 종속 항목을 프로젝트에 추가한다.
  • assembly.xml — 추가 기능 압축을 위해 Maven 어셈블리 플러그인에서 사용하는 구성을 정의한다.
  • messages_hi.properties — 추가 기능을 작성하는 동안 제공한 메시지 번들 파일로서, 자원 폴더에 복사된다.
  • hi.png — 추가 기능을 작성하는 동안 제공한 플래그 PNG 파일로서, 자원 폴더에 복사된다.
  • HindiLanguage.java — 이 추가 기능에 의해 작성된 유일한 Java 파일로서, 특정 언어에 해당하는 정보를 얻는 데 사용된다. 예를 들어, 로컬 이름, 메시지 번들 자원 파일 등을 제공한다.

애플리케이션에 힌디어 지원 추가

방금 작성한 추가 기능을 사용하여 애플리케이션에 힌디어 지원을 추가할 수 있는 방법을 설명하겠다.

  1. Roo 쉘을 종료하고 mvn clean install 명령을 실행한다. 이 명령을 실행하면 빌드 프로세스 중에 GPG 비밀번호 문구를 묻는 메시지가 표시된다.
  2. Roo 추가 기능이 빌드된 후, 새 명령행을 열고 i18n-hindi-client라는 디렉토리를 작성한다. 우리는 추가 기능을 위한 간단한 클라이언트를 작성할 것이다.
  3. i18n-hindi-client 디렉토리로 들어가서 roo 명령을 입력한다.
  4. Roo 쉘에서 다음 명령을 실행한다. 그러면 간단한 Spring MVC 웹 애플리케이션이 작성된다.

    목록 5. MVC 웹 애플리케이션을 작성하기 위한 Roo 명령
    
    project --topLevelPackage com.shekhar.roo.i18n.client \
    --projectName i18n-hindi-client  
    persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY  
    entity --class ~.domain.MyUser  
    field string --fieldName name --notNull  
    controller all --package ~.web

  5. web mvc language --code de en es it nl sv 명령을 입력하고 탭 키를 누른다.

    힌디어 지원 정보가 보이지 않을 것이다. 이는 우리가 아직 힌디어 추가 기능을 설치하지 않았기 때문이다.

  6. 이 추가 기능을 설치하려면 다음을 입력한다.
    osgi start --url file:///<location to addon target
       folder>/org.xebia.roo.addon.i18n.hindi-0.1.0.BUILD-SNAPSHOT.jar

    그러면 Spring Roo 힌디어 추가 기능이 설치되고 활성화된다. 아래에 나타낸 것처럼 OSGi 번들 정보와 그 상태를 표시하는 osgi ps 명령을 사용하여 추가 기능의 상태를 볼 수 있다.

    [ 95] [Active ] [ 1] roo-hindi-addon (0.1.0.BUILD-SNAPSHOT)

  7. 다시 한 번 web mvc language –code를 입력하고 탭 키를 누른다. 이번에는 힌디어에 대한 코드가 보일 것이다. hi 코드를 선택하면 애플리케이션에 힌디어 지원이 추가된다.
    web mvc language –code hi

  8. Roo 쉘을 종료하고 mvn tomcat:run을 입력한다. 그러면 바닥글에 인도 국기가 나타날 것이다. 그림 2에 나타낸 것처럼 인도 국기를 클릭하면 애플리케이션이 힌디어로 표시된다.

    그림 2. 힌디어 지원
    여러 개의 텍스트 항목이 힌디어로 표시된 브라우저에서 작동하는 애플리케이션을 보여주는 스크린샷

추가 기능이 개발 시스템에서 올바로 작동하는지 테스트한 후, 우리가 작성한 Google 코드 프로젝트에 추가 기능을 밀어 넣을 수 있다.

내부의 작동 방식

우리가 작성한 추가 기능이 작동하는 것을 확인했으므로, 애플리케이션에서 어떻게 힌디어를 지원할 수 있게 되었는지 살펴보자.

  1. roo-hindi-addon은 osgi start 명령을 사용하여 시작되었다.
  2. 추가 기능이 시작되면 addon create i18n command에 의해 작성된 HindiLanguage 클래스가 i18n 추가 기능을 등록하고 등록 취소하기 위한 OSGi 서비스 리스너인 i18nComponent에 등록된다. HindiLanguage 클래스는 Apache Felix에 의해 제공되는 @Component@Service 어노테이션으로 표시된다. 이런 어노테이션은 컴포넌트와 서비스가 Roo 쉘에 등록되어 있고 사용 가능한 상태인지 확인한다.
  3. web mvc install language –code 명령을 입력하고 탭 키를 누른 경우 탭 완료 서비스가 i18nComponent 클래스인 getSupportedLanguages() 메소드를 호출하고, 이 메소드는 i18n 추가 기능의 java.util.Set을 리턴한다. HindiLanguage가 이미 i18nComponent에 등록되었기 때문에 그것도 리턴되었다.

외부에 추가 기능 게시

Roo create 명령이 모든 필수 Maven 플러그인을 설치하여 대부분의 작업을 자동으로 완료했기 때문에 외부에 추가 기능을 손쉽게 게시할 수 있다. 프로젝트 루트 디렉토리로 이동하여 목록 6의 명령을 입력한다.


목록 6. Roo 프로젝트 게시

svn add pom.xml src/ legal/ readme.txt 
svn commit -m "Roo Hindi Addon first version" 
mvn release:prepare release:perform

Maven 릴리스 플러그인에서는 릴리스 버전, 태그 이름 및 개발 버전을 묻는다. 그냥 기본값을 선택하면 계속 진행할 수 있다. Google 코드 프로젝트에 아티팩트를 릴리스하고 게시하는 데 2-3분 정도 걸린다. 이 프로세스가 완료되면 Google 코드 프로젝트에서 해당 추가 기능 아티팩트를 볼 수 있다.

RooBot에 추가 기능 등록

플러그인을 릴리스한 후 s2-roobot@vmware.com으로 이메일을 통해 이를 발송하여 추가 기능 저장소를 등록할 수 있다. 이메일 제목 행에 repository.xml 파일을 포함시켜야 한다. 예를 들어, 방금 작성한 추가 기능의 경우 repository.xml은 http://code.google.com/p/roo-hindi-addon/source/browse/repo/repository.xml이다. RooBot에 등록하는 방법에 관한 자세한 정보는 Spring Roo 문서를 참조한다.


Java 애플리케이션의 모니터링(간단한 추가 기능 작성)

많은 엔터프라이즈 애플리케이션에서는 애플리케이션 성능 병목 지점을 확인하기 위해 Java 애플리케이션을 모니터링하려는 것이 공통적인 요구사항이다. 필자 역시 똑같은 요구사항이 있었기 때문에, 사용 가능한 오픈 소스 솔루션이 있는지 살펴보기로 결정했다. JAMon(Java Application Monitor)은 프로덕션 애플리케이션을 손쉽게 모니터링할 수 있게 해주는 스레드로부터 안전한 고성능 Java API이며 무료로 사용할 수 있다.

웹 애플리케이션에 JAMon 지원을 추가하려면 다음을 수행한다.

  1. pom.xml에 jamon JAR를 추가해야 한다.
  2. 애플리케이션 컨텍스트 파일에서 JamonPerformanceMonitorInterceptor Bean을 정의해야 한다. 샘플 Bean 정의는 목록 7에 있는 코드와 같은 형태이다.

목록 7. JamonPerformanceMonitorInterceptor 코드

	<bean id="jamonPerformanceMonitorInterceptor" 
		class=\
"org.springframework.aop.interceptor.JamonPerformanceMonitorInterceptor"> 
		<property name="trackAllInvocations" value="true"></property> 
		<property name="useDynamicLogger" value="true"></property> 
	</bean> 

	<bean id="autoProxyCreator" 
		class=\
"org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
		<property name="interceptorNames"> 
			<list> 
				<idref bean="jamonPerformanceMonitorInterceptor" /> 
			</list> 
		</property> 
		<property name="beanNames"> 
			<list> 
				<value>speakerController</value> 
				<value>talkController</value> 
			</list> 
		</property> 
	</bean>

이것은 어떤 개발자든 쉽게 기억할 수 있는 간단한 Bean 정의가 아니므로, 이 상용구 구성을 자동화할 수 있다면 좋겠다고 생각했다. 그래서 JAMon 종속 항목을 추가하고 인터셉터 Bean을 연결하는 프로세스를 자동화할 수 있는 추가 기능을 작성하기로 했다. 간단한 추가 기능이나 고급 추가 기능 중 어떤 것이 필요한지 어떻게 결정할까?

  1. 프로젝트에 Maven 종속 항목을 추가하거나 구성 아티팩트를 추가하거나 이 둘 모두 추가하려면 간단한 추가 기능을 사용한다.
  2. 프로젝트에서 기존 Java 형식을 개선하거나 새로운 Java 형식과 AspectJ ITD를 도입하거나 두 가지 모두 수행하는 완벽한 기능을 갖춘 추가 기능을 작성해야 할 때는 고급 추가 기능을 사용한다.

우리의 요구사항은 classpath에 jamon.jar를 추가하고 Bean 정의를 포함할 애플리케이션 컨텍스트 파일을 작성하는 것이다. 기존 컨텍스트 파일을 사용하지 않고 새로운 Spring 애플리케이션 컨텍스트 파일을 작성하는 것이 더 낫다. 그러면 애플리케이션 컨텍스트를 모듈화하는 데 도움이 되기 때문이다. 이런 요구사항으로부터, 어떤 웹 애플리케이션에든 JAMon 지원을 추가할 수 있는 간단한 추가 기능을 작성해야 한다는 점이 분명하게 드러난다.

프로젝트 설정

자국어 지원 추가 기능을 작성할 때와 똑같은 방법으로 프로젝트 설정 작업을 수행할 필요가 있다. 이 프로젝트의 이름을 "spring-roo-addon-jamon"으로 지정하겠다.

간단한 추가 기능 작성

프로젝트를 설정하고 나면 .svn 폴더만 포함한 spring-roo-addon-jamon이라는 디렉토리가 생길 것이다. spring-roo-addon-jamon 디렉토리로 이동하고 Spring Roo 쉘을 시작한다. 그리고 다음 명령을 입력한다.

addon create simple --topLevelPackage org.xebia.roo.addon.jamon \
--projectName spring-roo-addon-jamon

이렇게만 하면 된다. 추가 기능이 작성된 것이다.

생성된 추가 기능 설치

다음 명령을 사용하여 추가 기능을 설치할 수 있다.

osgi start --url file://<Location to addon
    target folder>/org.xebia.roo.addon.jamon-0.1.0.BUILD-SNAPSHOT.jar 

추가 기능을 설치한 후, i18n 추가 기능을 테스트할 때 작성한 것처럼 간단한 클라이언트를 작성한다. 생성된 추가 기능은 다음 두 가지 명령을 제공한다.

  1. say hello는 Roo 쉘에 시작 메시지를 인쇄하는 명령이다. 이 명령은 항상 사용할 수 있으며, Roo 쉘이 작동하는 동안 언제든 이 명령을 입력할 수 있다.
  2. web mvc install tags는 웹 애플리케이션 스캐폴딩 시 생성되는 기본 MVC 태그를 대체한다. 웹 애플리케이션을 작성했을 때만 이 명령을 사용할 수 있다.

생성된 코드 살펴보기

추가 기능을 테스트했으므로, 이제 목록 8에 표시된 것처럼 이 추가 기능에 의해 생성된 파일을 살펴보자.


목록 8. spring-roo-addon-jamon용으로 생성된 파일

Created ROOT/pom.xml 
Created ROOT/readme.txt 
Created ROOT/legal 
Created ROOT/legal/LICENSE.TXT 
Created SRC_MAIN_JAVA 
Created SRC_MAIN_RESOURCES 
Created SRC_TEST_JAVA 
Created SRC_TEST_RESOURCES 
Created SRC_MAIN_WEBAPP 
Created SRC_MAIN_RESOURCES/META-INF/spring 
Created SRC_MAIN_JAVA/com/shekhar/roo/addon/jamon 
Created SRC_MAIN_JAVA/com/shekhar/roo/addon/jamon/JamonCommands.java 
Created SRC_MAIN_JAVA/com/shekhar/roo/addon/jamon/JamonOperations.java 
Created SRC_MAIN_JAVA/com/shekhar/roo/addon/jamon/JamonOperationsImpl.java 
Created SRC_MAIN_JAVA/com/shekhar/roo/addon/jamon/JamonPropertyName.java 
Created ROOT/src/main/assembly 
Created ROOT/src/main/assembly/assembly.xml 
Created SRC_MAIN_RESOURCES/com/shekhar/roo/addon/jamon 
Created SRC_MAIN_RESOURCES/com/shekhar/roo/addon/jamon/info.tagx 
Created SRC_MAIN_RESOURCES/com/shekhar/roo/addon/jamon/show.tagx 

pom.xml, assembly.xml, LICENSE.TXT 및 readme.txt 아티팩트는 i18n 추가 기능에 의해 생성되는 것과 동일하다. 이런 아티팩트에 대해서는 설명했으므로, 여기서 다시 설명하지는 않겠다. 더욱 관심이 가는 아티팩트는 JamonCommands, JamonOperations, JamonOperationsImpl 및 JamonPropertyName이다. 간단한 추가 기능 명령에 의해 생성된 코드를 이해할 수 있도록 하나씩 설명하겠다.

  1. JamonCommands.java:JamonCommands — CommandMarker라는 마커 인터페이스를 구현하고 두 개의 명령(예: 목록 9에 표시된 것처럼 hello 및 web mvc install tags)을 노출하는 클래스이다.

    목록 9. JamonCommands.java:JamonCommands에서 생성된 코드
    
    @Component 
    @Service 
    public class JamonCommands implements CommandMarker { 
            
           @Reference private JamonOperations operations; 
            
           @Reference private StaticFieldConverter staticFieldConverter; 
    
    
           protected void activate(ComponentContext context) { 
               staticFieldConverter.add(JamonPropertyName.class); 
               } 
    
    
           protected void deactivate(ComponentContext context) { 
                   staticFieldConverter.remove(JamonPropertyName.class); 
           } 
            
           @CliAvailabilityIndicator("say hello") 
           public boolean isSayHelloAvailable() { 
                   return true; 
           } 
            
           @CliCommand(value = "say hello", 
    help = "Prints welcome message to the Roo shell") 
           public void sayHello( 
                   @CliOption(key = "name", mandatory = true, 
    help = "State your name") String name, 
                   @CliOption(key = "contryOfOrigin", mandatory = false, 
           help = "Country of origin") JamonPropertyName country) { 
                    
                   log.info("Welcome " + name + "!"); 
                   log.warning("Country of origin: " + (country == null ? \
    JamonPropertyName.NOT_SPECIFIED.getPropertyName() : country.getPropertyName())); 
                   
                   log.severe("It seems you are a running JDK " 
    + operations.getProperty("java.version")); 
                   log.info("You can use the default JDK logger anywhere in your" 
    + " add-on to send messages to the Roo shell"); 
           } 
            
           @CliAvailabilityIndicator("web mvc install tags") 
           public boolean isInstallTagsCommandAvailable() { 
                   return operations.isInstallTagsCommandAvailable(); 
           } 
            
           @CliCommand(value = "web mvc install tags", 
    help="Replace default Roo MVC tags used for scaffolding") 
           public void installTags() { 
                   operations.installTags(); 
           } 
    }
    

    필자는 코드의 자세한 정도를 줄이기 위해 코드 생성기에 의해 생성된 모든 주석을 제거했다. 이 클래스의 중요한 멤버를 살펴보자.

    1. CommandMarker — 모든 명령 클래스는 CommandMarker 인터페이스를 구현해야 하고 @Component@Service 어노테이션으로 어노테이션이 작성되어야 하므로, Roo 쉘에 등록되어 해당 명령을 사용할 수 있게 된다. @Reference로 어노테이션이 작성된 필드는 JamonCommands 클래스의 종속 항목으로서, 기본 Roo OSGi 컨테이너에 의해 인젝션된다. @Component@Service annotations로 어노테이션이 작성되는 모든 클래스는 다른 추가 기능에 인젝션될 수 있다.
    2. Activate 및 Deactivate 메소드activatedeactivate 메소드는 addon install 명령을 사용하여 추가 기능을 설치하거나 addon remove 명령을 사용하여 추가 기능을 제거할 때 호출되는 메소드이다. 이런 메소드를 사용하면 기본 Roo OSGi 컨테이너에 의해 관리되는 추가 기능의 라이프사이클을 끌어들일 수 있다.
    3. @CliAvailabilityIndicator로 어노테이션이 작성된 메소드@CliAvailabilityIndicator로 어노테이션이 작성된 메소드는 사용할 수 없는 명령을 숨기는 데 도움이 되는 메소드이다. 예를 들어, 프로젝트에 Spring Security를 설치하는 security setup 명령은 프로젝트가 웹 애플리케이션일 때까지는 볼 수 없다. 이런 메소드를 가지고 있어야 하는 것은 아니지만, 사용자가 해당 시점에서 이치에 닿지 않는 명령을 실행할 수 없도록 하기 때문에 유용한 점이 있다. 예를 들어, 사용자가 웹 애플리케이션을 작성하기 전에 security setup 명령을 실행하는 것은 이치에 닿지 않는 일이다.
    4. @CliCommand로 어노테이션이 작성된 메소드@CliCommand로 어노테이션이 작성된 메소드는 Roo 쉘에 명령을 등록한다. @CliCommand 어노테이션에는 명령 이름을 정의하는 valuehelp 명령을 입력할 때 표시되는 도움말 메시지를 정의하는 help라는 두 가지 속성이 있다. 모든 명령은 명령의 한 파트로 제공되는 @CliOption 어노테이션을 사용하여 필수 및 비필수 속성을 정의할 수 있다. 예를 들어, say hello 명령에는 name이라는 필수 속성과 country라는 비필수 속성이 있다.
  2. JamonOperationsImpl.java — 명령 클래스는 명령이 실행될 때 몇 가지 조작을 수행할 필요가 있다. 이런 조작은 조작 클래스에 의해 수행된다. JamonOperationsImpl은 pom.xml에 종속 항목을 추가하거나 원하는 위치에 자원을 복사하는 것과 같은 실제 작업을 수행할 책임이 있는 조작 클래스이다. JamonOperationsImpl 클래스는 Spring Roo 프레임워크에서 제공하는 코어 서비스를 사용하여 조작을 수행한다. 예를 들어, 이 클래스는 종속 항목을 추가하기 위해 ProjectOperations 인터페이스를 사용한다. 파일 복사를 위해서는 FileManager 인터페이스를 사용한다. Roo 추가 기능을 좀 더 정확히 이해하기 위해 JamonOperationsImpl 클래스에 대한 코드를 살펴보자(목록 10 참조).

    목록 10. JamonOpertionsImpl
    
    	@Component 
    	@Service 
    	public class JamonOperationsImpl implements JamonOperations { 
    		private static final char SEPARATOR = File.separatorChar; 
    	 
    		@Reference private FileManager fileManager; 
    		@Reference private ProjectOperations projectOperations; 
    
    		public boolean isInstallTagsCommandAvailable() { 
    			return projectOperations.isProjectAvailable() &&
                fileManager.exists(projectOperations.getProjectMetadata().
    			getPathResolver().getIdentifier(Path.SRC_MAIN_WEBAPP,
                    "WEB-INF" + SEPARATOR + "tags")); 
    	} 
    
    		public String getProperty(String propertyName) { 
    			Assert.hasText(propertyName, "Property name required"); 
    			return System.getProperty(propertyName); 
    		} 
    
    		public void installTags() { 
    			PathResolver pathResolver =
                projectOperations.getProjectMetadata().getPathResolver(); 
    			createOrReplaceFile(pathResolver.getIdentifier(
    			    Path.SRC_MAIN_WEBAPP, "WEB-INF" + SEPARATOR +
                        "tags" + SEPARATOR + "util"), "info.tagx"); 
    
    		createOrReplaceFile(pathResolver.getIdentifier(
    			Path.SRC_MAIN_WEBAPP, "WEB-INF" + SEPARATOR +
                    "tags" + SEPARATOR + "form"), "show.tagx"); 
    		} 
    	 
    		private void createOrReplaceFile(String path, String fileName) { 
    			String targetFile = path + SEPARATOR + fileName; 
    			MutableFile mutableFile = fileManager.exists(targetFile) ?
                    fileManager.updateFile(targetFile) :
                    fileManager.createFile(targetFile); 
    			    try { 
    			    	FileCopyUtils.copy(TemplateUtils.getTemplate(getClass(),
                            fileName), mutableFile.getOutputStream()); 
    			    } catch (IOException e) { 
    				throw new IllegalStateException(e); 
    			} 
    		} 
    	}
    

    JamonOperationsImpl 클래스에는 해당 명령을 사용할 수 있는지 검사하는 isInstallTagsCommandAvailable과 대상 프로젝트에 태그를 설치하는 installTags라는 두 개의 중요한 메소드가 있다. 이런 조작을 수행하기 위해 JamonOperationsImpl 클래스는 다음과 같이 몇몇 Spring Roo 코어 서비스와 유틸리티를 사용한다.

    1. ProjectOperations : JamonOperationsImpl 클래스는 ProjectOperations 서비스를 사용하여 프로젝트 사용 가능 여부를 확인하고 태그 폴더의 경로를 가져온다.
    2. FileManager : JamonOperationsImpl 클래스는 FileManager 서비스를 사용하여 특정 경로에 파일이 존재하는지 확인하고 업데이트를 가져오거나 MutableFile을 작성한다. MutableFile은 수정 가능한 파일에 대한 핸들을 표시하는 또 다른 Roo 고유의 클래스이다.
    3. TemplateUtils : TemplateUtils 유틸리티 클래스는 추가 기능 번들에 있는 템플리트 파일(info.tagx 및 show.tagx)에 대한 InputStream을 인식하는 데 사용된다.
    4. FileCopyUtils : FileCopyUtils 유틸리티 클래스는 (TemplateUtils의) 템플리트를 (FileManager의) MutableFile로 복사하는 데 사용된다.

요구사항을 충족하도록 추가 기능 수정

간단한 추가 기능에 대한 첫 번째 섹션에서, 추가 기능이 어떤 Spring MVC 웹 애플리케이션에서라도 JAMon을 구성할 수 있도록 하기 위해 충족시켜야 하는 두 가지 요구사항에 대해 설명했다. 즉, 다음과 같은 요구사항이었다.

  1. pom.xml에 JAMon JAR를 추가할 것
  2. 애플리케이션 컨텍스트 파일에서 JamonPerformanceMonitorInterceptor Bean을 정의할 것

이런 요구사항을 충족시키기 위해, jamon setup 명령을 지원하도록 JamonCommands 클래스를 변경할 것이다. 그런 다음, 요구사항을 충족시키기 위한 실제 작업을 수행하기 위해 jamon setup 명령에 해당하는 한 가지 조작을 추가할 것이다.

JamonCommandsJamonCommands 클래스에는 jamon setup 명령을 사용할 수 있는지 확인하기 위한 isInstallJamonjamon setup 명령 실행 시 JAMon을 설치하기 위한 installJamon이라는 두 개의 메소드만 있을 것이다. (목록 11을 참조한다.)


목록 11. JamonCommands의 코드

@Component 
@Service 
public class JamonCommands implements CommandMarker { 
	 
	private Logger log = Logger.getLogger(getClass().getName()); 

	@Reference private JamonOperations operations; 
	 
	@CliAvailabilityIndicator("jamon setup") 
	public boolean isInstallJamon() { 
		return operations.isInstallJamonAvailable(); 
	} 
	 
	@CliCommand(value = "jamon setup", help = "Setup Jamon into your project") 
	public void installJamon() { 
		operations.installJamon(); 
	} 
}


이것이 바로 JamonCommands 클래스에서 필요한 전부다. jamon setup 명령을 노출하기만 하면 되며, 나머지 작업은 JamonOperationsImpl에게 위임된다. (목록 12를 참조한다.)


목록 12. JamonOperationsImpl의 코드

JamonOperationsImpl

JamonOperationsImpl need to implement two methods – \
isInstallJamonAvailable() and installJamon(). 

The isinstallJamonAvailable() method returns a boolean indicating whether 
command is available at this location or not. I want to enable JAMon only for the web
applications so we need to perform the check whether the project is 
a web application or not. To do that we will write the code as shown below 

	public boolean isInstallJamonAvailable() { 
		return projectOperations.isProjectAvailable() &&
        fileManager.exists(projectOperations.getPathResolver()
			.getIdentifier(Path.SRC_MAIN_WEBAPP, "/WEB-INF/web.xml")); 
	}


목록 12에 표시된 코드에서는 ProjectOperations 서비스를 사용하여 이 위치에서 프로젝트를 사용할 수 있는지 확인하고 web.xml 파일의 경로를 가져온다(web.xml 파일은 웹 애플리케이션용으로만 존재함). FileManager 서비스는 ProjectOperations에 의해 지정된 위치에 web.xml이 존재하는지 확인하는 데 사용된다.

구현에 필요한 두 번째 메소드는 pom.xml에 JAMon 종속 항목을 추가하고 webmvc-config.xml에 Bean 정의를 추가해야 하는 installJamon()이다. 이 메소드에 대한 코드를 쓰기 전에 src/main/resources/org/xebia/roo/addon/jamon 폴더 내부에 configuration.xml이라는 XML 파일을 작성해야 한다. 폴더 구조(org/xebia/roo/addon/jamon)는 Roo 추가 기능의 패키지 구조와 동일하다. configuration.xml은 목록 13에 표시된 것처럼 JAMon 버전 및 JAMon JAR 종속성을 정의한다.


목록 13. configuration.xml의 컨텐츠

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<configuration> 
    <jamon> 
        <properties> 
            <jamon.version>2.4</jamon.version> 
        </properties> 
        <dependencies> 
            <dependency> 
            <groupId>com.jamonapi</groupId> 
            <artifactId>jamon</artifactId> 
            <version>${jamon.version}</version> 
        </dependency> 
        </dependencies> 
    </jamon> 
</configuration>

목록 14에 나타낸 것처럼, pom.xml에 종속 항목을 추가하기 위한 코드를 작성해보자.


목록 14. pom.xml의 컨텐츠

	public void installJamon() { 
		Element configuration = XmlUtils.getConfiguration(getClass()); 
		updatePomProperties(configuration); 
		updateDependencies(configuration); 
	} 

	private void updatePomProperties(Element configuration) { 
		List<Element> properties = \
XmlUtils.findElements("/configuration/jamon/properties/*"
		, configuration); 
		for (Element property : properties) { 
			projectOperations.addProperty(new Property(property)); 
		} 
	} 

	private void updateDependencies(Element configuration) { 

		List<Dependency> dependencies = new ArrayList<Dependency>(); 
		List<Element> jamonDependencies = XmlUtils.findElements(
			"/configuration/jamon/dependencies/dependency", configuration); 
		for (Element dependencyElement : jamonDependencies) { 
			dependencies.add(new Dependency(dependencyElement)); 
		} 
		projectOperations.addDependencies(dependencies); 
	}


이는 pom.xml에 종속 항목을 추가하기 위한 표준 메커니즘이며, 대부분의 Roo 추가 기능에 사용되는 것을 확인할 수 있다. 목록 14에 표시된 코드는 그 자체로 쉽게 이해할 수 있으며 Spring Roo 유틸리티 클래스인 XmlUtils를 사용하여 configuration.xml에서 컨텐츠를 읽은 다음, ProjectOperations 서비스를 사용하여 pom.xml 파일을 업데이트한다.

pom.xml 종속 항목을 추가한 후, JamonPerformanceMonitorInterceptor에 대한 Bean 정의를 포함하게 될 별도의 Spring 구성 파일인 web-jamon-config.xml을 작성할 필요가 있다.


목록 15. web-jamon-config.xml의 컨텐츠

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:mvc="http://www.springframework.org/schema/mvc" \
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-3.0.xsd     
	http://www.springframework.org/schema/context \
http://www.springframework.org/schema/context/spring-context-3.0.xsd     
	http://www.springframework.org/schema/mvc \
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> 


	<bean id="jamonPerformanceMonitorInterceptor" 
		class=\
"org.springframework.aop.interceptor.JamonPerformanceMonitorInterceptor"> 
		<property name="trackAllInvocations" value="true"></property> 
		<property name="useDynamicLogger" value="true"></property> 
	</bean> 

	<bean id="autoProxyCreator" 
		class=\
"org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
		<property name="interceptorNames"> 
			<list> 
				<idref bean="jamonPerformanceMonitorInterceptor" /> 
			</list> 
		</property> 
		<property name="beanNames"> 
			<list> 
				<value></value> 
			</list> 
		</property> 
	</bean> 

</beans>

목록 15에 표시된 web-jamon-config.xml 파일에서는 Spring 기반 애플리케이션에 필요한 모든 것을 구성했다. 이 파일에서 지정하지 않은 유일한 항목은 모니터링할 Bean의 이름이다. 코드 생성기가 이런 Bean의 이름을 예측할 수는 없으므로, 추가 기능 개발자가 지정해야 한다.

이제, 이 web-jamon-config.xml 파일을 WEB_INF/spring 폴더에 복사할 코드를 작성해야 한다. 이 폴더에는 webmvc-config.xml이 있고 web-jamon-config.xml을 가져오기 위해 webmvc-config.xml의 add import 문에 대한 코드가 있다. (목록 16을 참조한다.)


목록 16. pom.xml을 조작하기 위한 코드

public void installJamon() { 
		 
	// update pom.xml code 
	PathResolver pathResolver =

        projectOperations.getProjectMetadata().getPathResolver(); 
	String resolvedSpringConfigPath = pathResolver.getIdentifier(Path.SRC_MAIN_WEBAPP,

        "/WEB-INF/spring"); 
	if (fileManager.exists(resolvedSpringConfigPath + "/web-jamon-config.xml")) { 
			return; 
	} 
	copyTemplate("web-jamon-config.xml", resolvedSpringConfigPath); 
		 
	String webMvcConfigPath = resolvedSpringConfigPath + "/webmvc-config.xml"; 

	new XmlTemplate(fileManager).update(webMvcConfigPath, new DomElementCallback() { 
		public boolean doWithElement(Document document, Element root) { 
			if (null ==
               XmlUtils.findFirstElement\
("/beans/import[@resource='web-jamon-config.xml']",
                   root)) { 
                   Element element = document.createElement("import"); 
		        element.setAttribute("resource", "web-jamon-config.xml"); 
				root.appendChild(element); 
		        return true; 
		    } 
		    return false; 
		   } 
	}); 
		 
} 
	 
private void copyTemplate(String templateFileName, String resolvedTargetDirectoryPath) { 
	
    try { 
	    FileCopyUtils.copy(TemplateUtils.getTemplate(getClass(),
               templateFileName), fileManager.createFile(
	            resolvedTargetDirectoryPath + "/" +
                   templateFileName).getOutputStream()); 
	} catch (IOException e) { 
           throw new IllegalStateException(
               "Encountered an error during copying of resources for Jamon addon.", e); 
    } 
} 

위에서 사용되는 XmlTemplate 클래스는 Spring 웹 플로우 추가 기능에 있는 것이다. 따라서 작성 중인 추가 기능에 웹 플로우 추가 기능에 대한 종속 항목을 추가해야 한다. (목록 17를 참조한다.)


목록 17. 웹 플로우 추가 기능을 위한 종속 항목 추가

	<dependency> 
		<groupId>org.springframework.roo</groupId> 
		<artifactId>org.springframework.roo.addon.web.flow</artifactId> 
		<version>${roo.version}</version> 
      		<type>bundle</type> 
	</dependency>

목록 17에서, roo.version은 현재 사용 중인 Roo의 버전이다.

이 추가 기능에서 마지막으로 할 일은 프로젝트 로깅을 구성하고 로그 레벨을 추적으로 설정하는 것이다. 이 작업은 log4j 추가 기능을 위한 조작 클래스인 LoggingOperations를 사용하여 완료한다. 이 클래스를 사용하려면 목록 18에 표시된 것처럼, 우선 pom.xml 파일에 로깅 추가 기능 종속 항목을 추가해야 한다.


목록 18. 로깅 추가 기능을 위한 종속 항목을 추가하는 XML 코드

	<dependency> 
		<groupId>org.springframework.roo</groupId> 
		<artifactId>org.springframework.roo.addon.logging</artifactId> 
		<version>${roo.version}</version> 
      		<type>bundle</type> 
	</dependency>

pom.xml에 종속 항목을 추가한 후, 다음 행으로 JamonOperationsImpl 클래스에 LoggingOperation을 추가한다.

@Reference private LoggingOperations loggingOperations;

그리고 installJamon 메소드에 다음 행을 추가한다.

loggingOperations.configureLogging(LogLevel.TRACE, LoggerPackage.PROJECT); 

이 추가 기능을 위해 작성해야 할 코드는 이게 전부다. JamonOperationsImpl 클래스에 대한 전체 코드는 목록 19에 표시되어 있다.


목록 19. JamonOperationsImpl에 대해 완성한 코드

@Component 
@Service 
public class JamonOperationsImpl implements JamonOperations { 
       @Reference private FileManager fileManager; 
       @Reference private ProjectOperations projectOperations; 
       @Reference private LoggingOperations loggingOperations; 


       public boolean isInstallJamonAvailable() { 
               return projectOperations.isProjectAvailable() && 
fileManager.exists(projectOperations.getPathResolver()
.getIdentifier(Path.SRC_MAIN_WEBAPP,"/WEB-INF/web.xml")); 
       } 


       public void installJamon() { 
               Element configuration = XmlUtils.getConfiguration(getClass()); 
               updatePomProperties(configuration); 
               updateDependencies(configuration); 
               PathResolver pathResolver = 
projectOperations.getProjectMetadata().getPathResolver(); 
               String resolvedSpringConfigPath = 
pathResolver.getIdentifier(Path.SRC_MAIN_WEBAPP, 
            "/WEB-INF/spring"); 
               if (fileManager.exists(resolvedSpringConfigPath 
+ "/web-jamon-config.xml")) { 
                       return; 
               } 


               copyTemplate("web-jamon-config.xml", resolvedSpringConfigPath); 
                
               String webMvcConfigPath = resolvedSpringConfigPath 
+ "/webmvc-config.xml";


               new XmlTemplate(fileManager).update(webMvcConfigPath, 
new DomElementCallback() { 
                       public boolean doWithElement(Document document, Element root) {
                            if (null == XmlUtils.findFirstElement(
"/beans/import[@resource='web-jamon-config.xml']",
root)) { 
                                 
Element element = document.createElement("import"); 
                                element.setAttribute("resource", "web-jamon-config.xml"); 
                                root.appendChild(element); 
                                return true; 
                             } 
                             return false; 
                       } 
               });
               loggingOperations.configureLogging(LogLevel.TRACE, 
LoggerPackage.PROJECT); 
       } 
        
       private void copyTemplate(String templateFileName, 
String resolvedTargetDirectoryPath) { 
               try { 
FileCopyUtils.copy(
      TemplateUtils.getTemplate(getClass(), templateFileName),
              fileManager.createFile(resolvedTargetDirectoryPath + "/" + 
              templateFileName).getOutputStream()); 
               } catch (IOException e) { 
                       throw new IllegalStateException(
               "Encountered an error during copying of resources for Jamon addon.", e);
               } 
       } 
        
       private void updatePomProperties(Element configuration) { 
               List<Element> properties = XmlUtils
     .findElements("/configuration/jamon/properties/*",configuration);
               for (Element property : properties) { 
                       projectOperations.addProperty(new Property(property));
               } 
       } 


       private void updateDependencies(Element configuration) { 
               List<Dependency> dependencies = new ArrayList<Dependency>(); 
               List<Element> jamonDependencies = XmlUtils.findElements(
           "/configuration/jamon/dependencies/dependency", configuration); 
               for (Element dependencyElement : jamonDependencies) { 
                       dependencies.add(new Dependency(dependencyElement)); 
               } 
               projectOperations.addDependencies(dependencies); 
       } 
        
}

Google 코드 저장소에서 이 추가 기능의 전체 소스 코드를 다운로드할 수 있다. 이제, 방금 작성한 추가 기능을 사용하여 애플리케이션에 JAMon 지원을 추가해보자.

  1. Roo 쉘을 종료하고 mvn clean install 명령을 실행한다. 이 명령을 실행하면 빌드 프로세스 중에 GPG 비밀번호 문구를 묻는 메시지가 표시된다.
  2. Roo 추가 기능이 빌드된 후, 새 명령행을 열고 jamon-client라는 디렉토리를 작성한다. 우리는 추가 기능을 위한 간단한 클라이언트를 작성할 것이다.
  3. jamon-client 디렉토리로 이동하고 roo 명령을 입력하여 Roo 쉘을 연다.
  4. Roo 쉘에서 목록 20의 명령을 실행한다. 그러면 간단한 Spring MVC 웹 애플리케이션이 작성된다.

    목록 20. 간단한 MVC 웹 애플리케이션 작성
    
    project --topLevelPackage com.shekhar.roo.jamon.client --projectName jamon-client 
    persistence setup --provider HIBERNATE --database
    HYPERSONIC_IN_MEMORY
    entity --class ~.domain.MyUser
    field string --fieldName name --notNull
    controller all --package ~.web
    

  5. 이 추가 기능을 설치하려면 다음을 입력한다.
    osgi start --url <a
    href="../../../../">file:///</a><location \
    to addon target folder >
    /org.xebia.roo.addon.jamon-0.1.0.BUILD-SNAPSHOT.jar

    그러면 JAMon 추가 기능이 설치되고 활성화된다. osgi ps 명령을 사용하여 추가 기능의 상태를 볼 수 있다.

  6. jamon setup 명령을 입력하면 애플리케이션에서 구성되는 JAMon을 볼 수 있다. mvn tomcat:run을 사용하여 애플리케이션을 실행하면 모니터링할 Bean을 구성하지 않았기 때문에 콘솔에 어떤 로그도 표시되지 않는다. 목록 21의 코드로 web-jamon-config.xml에서 myUserController Bean을 구성해보자.

    목록 21. web-jamon-config.xml에서 myUserController 구성
    
    	<bean id="autoProxyCreator" 
    		class=\
    "org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
    		<property name="interceptorNames"> 
    			<list> 
    				<idref bean="jamonPerformanceMonitorInterceptor" /> 
    			</list> 
    		</property> 
    		<property name="beanNames"> 
    			<list> 
    				<value>myUserController</value> 
    			</list> 
    		</property> 
    	</bean>	
    

  7. mvn tomcat:run을 사용하여 애플리케이션을 실행하면 Maven 콘솔에 JAMon 로그가 표시된다. 목록 22에 샘플이 나와 있다.

    목록 22. JAMon 로그 샘플
    
    TRACE MyUserController - JAMon performance statistics for method 
    [MyUserController.populateMyUsers]: 
    JAMon Label=MyUserController.populateMyUsers, Units=ms.: (LastValue=187.0, 
    Hits=1.0, Avg=187.0, Total=187.0, Min=187.0, Max=187.0, Active=0.0, Avg 
    Active=1.0, Max Active=1.0, First Access=Wed May 18 15:33:41 IST 2011, Last 
    Access=Wed May 18 15:33:41 IST 2011) 

추가 기능이 개발 시스템에서 올바로 작동하는지 테스트한 후, 우리가 작성한 Google 코드 프로젝트에 추가 기능을 밀어 넣을 수 있다. 외부에 추가 기능을 게시하려면 i18n 추가 기능을 게시할 때와 똑같은 절차를 따른다. 마찬가지로, RooBot에 추가 기능을 등록하려면 i18n 등록 절차를 따른다.


결론

Spring Roo 추가 기능 아키텍처와 자국어 지원 및 간단한 추가 기능 작성법을 살펴보았다. 추가 기능 아키텍처는 Roo를 통해 새로운 기능을 빠르게 추가할 수 있는 유연성을 부여하므로 Roo에 중요한 사항이다. 개발자 입장에서는 이런 추가 기능 아키텍처를 통해 기능이 글로벌하게 구현될 때까지 기다릴 필요 없이 요구사항을 충족시킬 수 있다는 점에서 중요하다. 이후에 어떤 기능이 Roo에 통합되는 경우 비교적 쉽게 구현을 변경하여 사용자 정의 솔루션을 제거할 수 있다.

본 "Spring Roo 소개" 시리즈의 파트 4에서는 고급 및 랩퍼 추가 기능 작성법에 대해 설명하겠다.


참고자료

교육

제품 및 기술

토론

  • developerWorks 블로그를 통해 developerWorks 커뮤니티에 참여할 수 있다.

  • developerWorks 커뮤니티에 참여하자. 개발자가 이끌고 있는 블로그, 포럼, 그룹 및 Wiki를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

필자소개

Shekhar Gulati는 Xebia India에서 근무하는 Java 컨설턴트이다. 그의 엔터프라이즈 Java 경력은 6년이며, Spring, Spring-WS, Spring Roo 등의 Spring 포트폴리오 프로젝트에 폭넓은 경험을 보유하고 있다. 그의 관심사는 Spring Roo, 클라우드 컴퓨팅(주로 Google App Engine, CloudFoundry, OpenShift같은 PaaS 서비스), Hadoop과 같은 Spring, NoSQL 데이터베이스, Hadoop, RAD 프레임워크이다. 그는 JavaLobby, Developer.com, IBM developerWorks 및 개인 블로그(http://whyjava.wordpress.com/)에 활발하게 기사를 게재하고 있다. 트위터(@http://twitter.com/#!/shekhargulati)를 통해 Gulati를 팔로우할 수도 있다

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=오픈 소스
ArticleID=768754
ArticleTitle=Spring Roo 소개, 파트 3: Spring Roo 추가 기능 개발
publish-date=11012011

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.