달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

'프레임워크'에 해당되는 글 22

  1. 2011.01.13 How Twitter Uses NoSQL
  2. 2009.08.21 Roo Relationship 구현하기
  3. 2009.07.29 Roo Demo
  4. 2009.07.14 Spring Roo 간략 소개
  5. 2008.04.16 Artifactory 사용하기
  6. 2008.03.17 Spring, DDD and AspectJ - An Example 1
  7. 2008.03.16 DDD - Inject Repositories, Not DAOs in Domain Entities
  8. 2008.03.15 mvn jetty, cargo
  9. 2008.03.15 mvn test
  10. 2008.03.15 DBUnit 1
2011. 1. 13. 15:46

How Twitter Uses NoSQL 프레임워크2011. 1. 13. 15:46

http://www.readwriteweb.com/cloud/2011/01/how-twitter-uses-nosql.php

Twitter 사용자들은 하루에 12 테라바이트(연 거의 4 페타바이트)의 데이터를 생산한다고 한다.

이러한 방대한 양의 데이터를 사용하는 트위터에서는 아래와 같은 NoSQL들을 사용한다고 한다.

Scribe(https://github.com/facebook/scribe)
- syslog는 더 이상 사용 불가.
- facebook에 의해 오픈소스화된 로그 수집 프레임워크인 scribe를 사용함.
- twitter는 scribe를 이용해서 로그를 hadoop에 저장.

Cloudera(http://www.cloudera.com/)'s Hadoop
- mysql은 분석 업무에 적합하지 않아서 hadoop을 사용

Pig
- hadoop을 사용하는 가장 좋은 방법은 자바를 이용한는 것이지만,
- 자바는 복잡하고 빠르게 반복 작업을 하기에는 적합하지 않다.
- hadoop 위에 구현된 상위 레벨 언어인 Pig(http://pig.apache.org/)를 사용한다.

Hbase(http://hbase.apache.org/)
- hadoop의 상위에 위치. low-latency, data mutability를 위해
- 사용자 검색 기능 강화를 위해 사용

FlockDB(https://github.com/twitter/flockdb)
- real-time, distributed DB.
- twitter에 의해 생성/오픈 소스화됨.
- twitter는 소셜 그래프 분석을 위해 사용함.
- still mysql underneath, but it's very fast

Cassandra(http://cassandra.apache.org/)
- Cassandra(facebook이 만든 NoSQL)는 아직 실험 단계(atomic counting에)이다.

각각에 대해서 살펴 볼 필요가 있을 듯 하다.
:
Posted by codetemplate
2009. 8. 21. 20:14

Roo Relationship 구현하기 프레임워크2009. 8. 21. 20:14

1. ManyToOne
    add field reference

2. OneToMany
    add field set

3. ManyToOne Relationship 구현 방법
Order와 LineItem 경우를 예로 들어보자.

   ENTITY: Order            ENTITY: LineItem
   Set<LineItem> items      Order order

add field set jpa -fieldName items -class Order -element LineItem -mappedBy order
add field reference jpa -fieldName order -class LineItem -type Order -notNull

4. ManyToMany Relationship 구현 방법
n:m relationship을 구현하기 위해서는 3개의 테이블이 필요하다.
Order와 OrderLine이 n:m인 경우

Order
    - OneToMany-> OrderOrderLine

OrderOrderLine
    -orders: @ManyToOne->Order
    -orderlines: @ManyToOne->OrderLine

OrderLine
    -OneToMany->OrderOrderLine

5. Inheritance 구현 방법
new persistent class jpa -name ~.domain.AbstractPerson -abstract
new persistent class jpa -name ~.domain.Vet -extends ~.domain.AbstractPerson -testAutomatically
:
Posted by codetemplate
2009. 7. 29. 11:33

Roo Demo 프레임워크2009. 7. 29. 11:33

http://www.jroller.com/desmax/entry/roo

Roo Demo

1. 프로젝트 디렉토리 생성
msmac ~/tutorial/roo] mkdir pizza
msmac ~/tutorial/roo] cd pizza

2. roo 쉘 실행
msmac ~/tutorial/roo/pizza] roo
    ____  ____  ____  
   / __ \/ __ \/ __ \ 
  / /_/ / / / / / / / 
 / _, _/ /_/ / /_/ /  
/_/ |_|\____/\____/    1.0.0.RC1 [rev 198]


Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.
roo>

3. 프로젝트 생성하기
roo> create project -topLevelPackage com.pizza
...

4. JPA 설치하기
roo> install jpa -provider HIBERNATE -database HYPERSONIC_PERSISTENT 
...

5. 도메인 클래스 생성하기
roo> new persistent class jpa -name ~.domain.Product
...

6. 도메인 클래스에 필드 추가하기
roo> add field string -fieldName name
...

roo> add field number -fieldName price -type java.lang.Double
...

roo> add field boolean -fieldName vegi
...

7. 도메인 클래스에 관계 추가하기
roo> new persistent class jpa -name ~.domain.PizzaOrder
...

roo> add field date jdk -fieldName orderDate -type java.util.Date
...

roo> add field set jpa -element ~.domain.Product -fieldName products
...

8. 컨트롤러와 뷰 추가하기
roo> new controller automatic -name ~.view.ProductController -formBackingObject ~.domain.Product
...

roo> new controller automatic -name ~.view.PizzaOrderController -formBackingObject ~.domain.PizzaOrder
...

9. 실행하기
roo> q
msmac ~/tutorial/roo/pizza] mvn tomcat:run

브라우저 주소창에 http://localhost:8080/pizza를 입력하고 테스트

10. 이클립스에서 프로젝트 불러오기
msmac ~/tutorial/roo/pizza] mvn eclipse:eclipse
eclipse에서 해당 프로젝트 import

Product.java를 열고 @RooToString 라인을 지우고, roo 쉘을 실행하면
msmac ~/tutorial/roo/pizza] roo
    ____  ____  ____  
   / __ \/ __ \/ __ \ 
  / /_/ / / / / / / / 
 / _, _/ /_/ / /_/ /  
/_/ |_|\____/\____/    1.0.0.RC1 [rev 198]


Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.
Deleted SRC_MAIN_JAVA/com/pizza/domain/Product_Roo_ToString.aj

이처럼 JIT 방식의 code generation이 지원된다.
:
Posted by codetemplate
2009. 7. 14. 17:34

Spring Roo 간략 소개 프레임워크2009. 7. 14. 17:34

http://phillyspring.ning.com/forum/topics/july-meeting-springsource-roo

위 url에 있는 발표 자료를 간략하게 정리해 봤다.

1. Roo란 ?
spring & java 환경을 위한 RAD(Rapid Application Development) 플랫폼
    - domain-centric design of database entities
    - Auto-scaffolding of UI elements
    - Builds a full stack Spring web application
    - Target build process managed by Maven
java, spring 환경에서 개발할 때 많은 xml 설정을 상당 부분 CoC(Convention over Configuration)으로 변경한 프레임워크.

2. Roo 개발 절차
Roo는 grails/rails 처럼 커맨드 라인 쉘을 제공한다.
이 쉘을 이용해서
     - 메이븐으로 빌드되는 프로젝트를 생성
     - roo와 개발툴에서의 변경이 서로 동기화되는 round-trip을 제공한다.
     - 특정 기능을 제공하기 위해 aspect를 사용한다.
     - aspect를 관련된 클래스 파일에 위빙한다.

3. 사용된 기술들
AspectJ ITD(Inter-Type Declarations)
     - maven/eclipse에서 aspectj 컴파일러를 사용한다.
     - roo 어노테이션된 클래스에 컴파일 타임에 코드(aspect에 구현된)를 추가한다.
Roo 커맨드 쉘
     - 훌룡한 빌드 환경을 생성/유지한다.
     - 동적으로 .aj 파일을 생성하고, web UI scaffold를 제공한다.

4. Creating a roo project
     - 디렉토리 생성하기
     - 해당 디렉토리에서 roo 쉘 실행
     -  create project 명령 수행

flibbity-floo:roo-demo krimple$ roo
____ ____ ____
/ __ \/ __ \/ __ \
/ /_/ / / / / / / /
/ _, _/ /_/ / /_/ /
/_/ |_|\____/\____/ 1.0.0.M1 [rev 64]
Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.

roo> create project -topLevelPackage com.chariot.demo.roodemo

Created /Users/krimple/svn-projects/...roodemo/pom.xml
Created SRC_MAIN_JAVA
Created SRC_MAIN_RESOURCES
Created SRC_TEST_JAVA
Created SRC_TEST_RESOURCES
Created SRC_MAIN_WEBAPP
Created SRC_MAIN_RESOURCES/applicationContext.xml
Created SRC_MAIN_WEBAPP/WEB-INF
Created SRC_MAIN_WEBAPP/WEB-INF/roodemo-servlet.xml
Created SRC_MAIN_WEBAPP/WEB-INF/web.xml
Created SRC_MAIN_WEBAPP/WEB-INF/jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/index.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml

5. Installing JPA
'install jpa' 명령을 사용해서 Open JPA, Hibernate, EclipseLink 중 하나의 persistence F/W를 설치한다.

roo> install jpa -provider HIBERNATE -database MYSQL

Created SRC_MAIN_RESOURCES/META-INF
Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
Created SRC_MAIN_RESOURCES/database.properties
please enter your database details in src/main/resources/database.properties
Managed SRC_MAIN_RESOURCES/applicationContext.xml
Managed ROOT/pom.xml

6. Adding a Persistent JPA Entity
‘new persistent class’ 명령을 사용해서 새로운 도메인 클래스를 설정한다.
후에 'add field' 명령을 사용해서 필드를 추가할 수 있다.

roo> new persistent class jpa -name ~.Conference

Created SRC_MAIN_JAVA/com/chariot/roodemo
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference.java
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_Plural.aj
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_Entity.aj
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_Configurable.aj

7. What Got Created?
지금까지의 작업을 수행했다면 Roo는 아래와 같은 파일들을 생성했을 것이다.
     - Conference.java - the file you edit, containing Roo annotations and your properties
     - Conference_Roo_Configurable.aj - adds @Configurable to the entity
     - Conference_Roo_Entity.aj - Adds all JPA persistence methods, an id, and a version
     - Conference_Roo_JavaBean.aj - Adds getters/ setters for all of your private properties

8. Setting up JPA fields
필드나 관계를 추가할 수 있다. 이러한 필드들은 물리적으로 자바 파일에 추가된다. 또 추가된 필드들을 위한 support 메소드들이 자동으로 wired된다.

roo> add field string name
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference.java
Created SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_ToString.aj

roo> add field string description
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference.java
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_ToString.aj

roo> add field date jpa -type java.util.Date -fieldName created
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference.java
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/chariot/roodemo/Conference_Roo_ToString.aj

9. Building a Relationship

roo> new persistent class jpa -name ~.domain.Conference
roo> add field string -fieldName conferenceName -notNull -sizeMin 1 -sizeMax 40
roo> add field date jdk -type java.util.Date -notNull -fieldName startDate
roo> add field date jdk -type java.util.Date -notNull -fieldName endDate

roo> new persistent class jpa -name ~.domain.ConferenceSession
roo> add field string -notNull -fieldName title -sizeMax 80
roo> add field reference jpa -fieldName conference -type com.chariot.demo.contrack.domain.Conference
roo> add field set jpa -element ~.domain.ConferenceSession -fieldName session -class com.chariot.demo.contrack.domain.Conference

10. Entity Validation
JSR-303 annotation에 의해 entity를 validate할 수 있다.
    @NotNull, @Nullable, @Size, @DecimalMin, @DecimalMax, etc...
자동 생성되는 Controller에는 이러한 Bean Validation API를 통해 validation을 호출하는 코드가 포함된다.

javax.validation.Validation.
    buildDefaultValidatorFactory().getValidator().
    validate(conference))

11.  Validation Examples

@Size(min = 5, max = 30, message = "Please enter a name between {min} and {max} characters.")
private String name;

@Size(min = 10, max=2048, message = "{description.required}")
private String description;

@Temporal(TemporalType.TIMESTAMP)
@Column(insertable=true, updatable=false)
@NotNull
private Date created;

12. Configuring logging

roo> configure logging -level TRACE
Created SRC_MAIN_RESOURCES/log4j.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml

<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
</context-param>

#Updated at Tue Jun 09 09:21:45 EDT 2009
#Tue Jun 09 09:21:45 EDT 2009
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
log4j.rootLogger=TRACE, stdout, R
log4j.appender.R.File=application.log
log4j.appender.R.MaxFileSize=100KB
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.R=org.apache.log4j.RollingFileAppender

13. Generating a Controller

roo> new controller automatic -name com.chariot.roodemo.domain.ConferenceController
Created SRC_MAIN_JAVA/com/chariot/roodemo/domain/ConferenceController.java
Created SRC_MAIN_JAVA/com/chariot/roodemo/domain/ConferenceController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/images
Created SRC_MAIN_WEBAPP/images/banner-graphic.png
Created SRC_MAIN_WEBAPP/images/springsource-logo.png
Created SRC_MAIN_WEBAPP/images/list.png
Created SRC_MAIN_WEBAPP/images/show.png
Created SRC_MAIN_WEBAPP/images/create.png
Created SRC_MAIN_WEBAPP/images/update.png
Created SRC_MAIN_WEBAPP/images/delete.png
Created SRC_MAIN_WEBAPP/styles
Created SRC_MAIN_WEBAPP/styles/roo.css
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/header.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/footer.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/includes.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/dataAccessFailure.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/uncaughtException.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/conference
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/conference/list.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/conference/show.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/conference/create.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/conference/update.jsp
Created SRC_MAIN_WEBAPP/WEB-INF/jsp/menu.jsp
Managed SRC_MAIN_WEBAPP/WEB-INF/jsp/menu.jsp

19.  Example Aspect: show
In ConferenceController_Roo_Controller.aj:

@org.springframework.web.bind.annotation.RequestMapping(
value = "/conference/{id}", method = org.springframework.web.bind.annotation.RequestMethod.GET)
public java.lang.String ConferenceController.show(
    @org.springframework.web.bind.annotation.PathVariable("id") Long id,
    org.springframework.ui.ModelMap modelMap) {
    if (id == null) throw new IllegalArgumentException("An Identifier is required");
    modelMap.addAttribute("conference", com.chariot.roodemo.domain.Conference.findConference(id));
    return "conference/show";
}

Uses findConference in Conference_Roo_Entity.aj

public static com.chariot.roodemo.domain.Conference Conference.findConference(java.lang.Long id) {
    if (id == null) throw new IllegalArgumentException(
        "An identifier is required to retrieve an instance of Conference");
    javax.persistence.EntityManager em = new Conference().entityManager;
    if (em == null) throw new IllegalStateException(
        "Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects
        library?)");
    return em.find(Conference.class, id);
}

20. Get it working!
mvn jetty:run이나 mvn tomcat:run을 통해서 웹 어플리케이션을 실행할 수 있다.
Hit the webapp url and get a menu of choices

21.  IDE Support
Roo는 SpringSource Tool Suite(STS)를 사용하면 보다 편하게 사용할 수 있다. STS는 다음과 같은 편의를 제공한다.
    - AJDT AspectJ editing support is very good
    - Roo Console available via Right-click if installed
하지만 STS 사용시 mvn eclipse:eclipse를 빈번하게 수행해야 하고, F5를 자주 눌러야 한다.
이런 불편은 eclipse에서 mvn eclipse:eclipse를 external command로 등록하고 수행 후에 프로젝트를 refresh하도록 설정하면 해결된다.

22.  Resources
Roo JIRA: http://jira.springframework.org/browse/ROO
Roo Forum: http://forum.springsource.org/forumdisplay.php?f=67

:
Posted by codetemplate
2008. 4. 16. 10:39

Artifactory 사용하기 프레임워크2008. 4. 16. 10:39

1. 설치하기

http://www.jfrog.org/sites/artifactory/latest에서 최신 버전을 다운로드하고 적절한 디렉토리에서 압축을 푼다.

2. 설정하기
$ARTIFACTORY_HOME/etc에 있는 artifactory.config.xml 파일을 아래과 같이 수정한다.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://artifactory.jfrog.org/xsd/1.1.0"
xsi:schemaLocation="http://artifactory.jfrog.org/xsd/1.1.0
http://www.jfrog.org/xsd/artifactory-v1_1_0.xsd">
<localRepositories>
<localRepository>
<key>daum-internal-repository</key>
<description>daum internal repository</description>
<handleReleases>true</handleReleases>
<handleSnapshots>true</handleSnapshots>
</localRepository>
<localRepository>
<key>third-party-repository</key>
<description>third party repository</description>
<handleReleases>true</handleReleases>
<handleSnapshots>true</handleSnapshots>
</localRepository>
</localRepositories>
<remoteRepositories>
<remoteRepository>
<key>ibiblio</key>
<handleReleases>true</handleReleases>
<handleSnapshots>false</handleSnapshots>
<excludesPattern>org/artifactory/**,org/jfrog/**</excludesPattern>
<url>http://repo1.maven.org/maven2</url>
</remoteRepository>
<remoteRepository>
<key>codehaus-snapshots</key>
<blackedOut>true</blackedOut>
<handleReleases>false</handleReleases>
<handleSnapshots>true</handleSnapshots>
<excludesPattern>org/artifactory/**,org/jfrog/**</excludesPattern>
<url>http://snapshots.repository.codehaus.org</url>
</remoteRepository>
<remoteRepository>
<key>apache-m2-snapshots</key>
<handleReleases>false</handleReleases>
<handleSnapshots>true</handleSnapshots>
<url>http://people.apache.org/repo/m2-snapshot-repository</url>
</remoteRepository>
<remoteRepository>
<key>apache-snapshots</key>
<handleReleases>false</handleReleases>
<handleSnapshots>true</handleSnapshots>
<url>http://people.apache.org/maven-snapshot-repository</url>
</remoteRepository>
</remoteRepositories>
</config>

3. 실행하기
$ARTIFACTORY_HOME/bin에서 artifactory.sh를 수행한다. 이 스크립트는 jetty를 이용해서 $JAVA_HOME 환경 변수 설정이 필요하다.
8081 포트로 artifactory가 실행된다.

4. settings.xml
~/.m2/settins.xml 파일을 아래와 같이 설정한다.
<settings 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/xsd/settings-1.0.0.xsd">
<profiles>
<profile>
<id>gaia</id>
<repositories>
<repository>
<id>central</id>
<url>
http://media-install:8081/artifactory/repo
</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>snapshots</id>
<url>
http://media-install:8081/artifactory/repo
</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>
http://media-install:8081/artifactory/repo
</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>snapshots</id>
<url>
http://media-install:8081/artifactory/repo
</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

5. maven에서 artifactory 연결하기
mvn 실행시 settings.xml에서 정의한 프로파일을 정의하기 위해 '-Pgaia'를 지정하면 된다.

6. 정상적으로 동작하는지 확인하기
http://media-install:8081/artifactory로 접속 admin/password로 로긴한다. 왼쪽 메뉴에서 "Browsing/Repositories Tree"를 클릭해서 artifactory들이 보이면 정상 동작하는 것이다.

7. 3rd party artifact나 사내 artifact 등록하기
왼쪽 메뉴에서 "Deploy an Artifacts" 클릭. "찾아보기" 버튼으로 deploy할 artifact 선택하고, "Upload!" 버튼 클릭. 다음에 나오는 화면에서 groupId, artifactId 확인하고 적절하게 설정하고 deploy한다.

:
Posted by codetemplate
2008. 3. 17. 22:13

Spring, DDD and AspectJ - An Example 프레임워크2008. 3. 17. 22:13

아래와 같은 코드를 예로 들어보자.

public class Customer {

private Long accountId;

public Long getAccountId() {
return accountId; }
}

public void setAccountId(Long accountId) {
this.accountId = accountId;
}
...
}

public class CustomerServiceImpl {
public Double getNetWorth(Customer customer) {

Long accountId = customer.getAccountId();

Account account = accountRepository.getAccountById(accountId);

return account.getValue();
}

}

위의 예는 전형적인 Anemic Domain Model이다(행위는 서비스 레이어에만 있고, 도메인 객체는 단순히 데이터 컨테이너 역할만 한다).

이를 해결하고자 할 때 부딪히는 가장 큰 문제는 Cutomer객체가 어떻게 Account객체를 얻느냐는 것이다. 이 문제에 대한 해결책은 아래와 같은 3가지가 있을 수 있다.

  1. CustomerServiceImpl이 Account를 얻어서 Customer객체에 전달한다. 이 경우 CustomerServiceImpl은 여전히 2개의 비즈니스 로직에 참여하게 되서 위 코드와 별반 다를바가 없게 된다.
  2. Cutomer객체가 service locator를 통해 능동적으로(actively) AccountRepository를 얻는다. 이 방법은 Customer 객체가 ArticleRepository와 service locator에 종속성(dependency)을 갖게 해서 transparent한 해결책이라 할 수 없다.
  3. DI를 통해 ArticleRepository를 Customer 객체에 주입한다.

위 방법 중에 3안이 가장 이상적인 방안으로 보인다.

3안은 아래와 같이 구현될 수 있다.

@Configurable("smartCustomer")
public class SmartCustomer extends Customer {
private AccountRepository accountRepository;

public AccountRepository getAccountRepository() {
return accountRepository;
}

public void setAccountRepository(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}

public Double getNetWorth() {
Account account = accountRepository.getAccountById(getAccountId());
return account.getValue();
}
}

@Configurable은 해당 클래스가 새로운 인스턴스가 생성될 때 Spring에 의해 DI되어야 한다는 표식이다. @Configurable이 붙은 클래스를 스프링에 등록해 주면 스프링은 그 객체가 생상될 때 그 객체가 필요로 하는 bean들을 주입해준다. 이를 위한 설정 파일은 아래와 같다.

<bean id="smartCustomer" abstract="true" class="org.springdallasug.aspectj.domain.SmartCustomer" scope="prototype" >
<property name="accountRepository" ref="accountRepository" />
</bean>

그리고 LTW로 DI가 발생하도록 하기 위해 JVM 실행 옵션에 아래 라인을 추가한다.

-javaagent:PATH_TO_JAR/aspectjweaver-1.5.0.jar

Pros and Cons
나도 같은 생각이지만 이 글의 필자도 도메인 객체에 DI를 적용하는 것이 맞는지 의문이라고 한다. 예제와 같은 스타일로 구현하게 되면 도메인 객체는 자신의 역할 수행(net worth 계산)을 위한 완벽한 로직을 갖게 되는 장점을 갖는다. 하지만 도메인 객체가 리파지토리를 사용함으로써 이상한 설계가 된다.
보다 풍부한(richer) 도메인 모델이 되지만 복잡해질 수도 있는 보다 많은 종속성이 도메인 모델에 생긴다.

구현 후기
- service 메소드가 인자로 Customer를 받으면 동작하지 않는다. SmartCustomer를 받아야만 제대로 동작한다.

참고자료

:
Posted by codetemplate

Domain 객체에 Repository를 inject해야 하나 ?

먼저 DAO와 Repository의 차이를 알아보자.

presentation <--> service <--> domain <--> data access

이런 관계에서 DAO는 data access layer에 속한다.
DAO는 CRUD 연산을 encapsulate하는데, ORM이 없는 상황에서는 RDB와 OO Technique 간의 impedence를 처리한다.

Eric Evans는 "객체는 더 이상 <자신의 의미나 상호작용에서의 역할 지원>과 관련된 코드가 없을 때 까지 정제되어야 한다"라고 한다.

@Configurable("organization")
class Organization {
// implementation needs to be injected
private EmployeeDAO employeeDao;
// ..
// ..
public List<Employee> getOutstationEmployees() {
List<Employee> emps = employeeDao.getEmployeesByAddress(corporateOffice);
List<Employee> allEmps = employeeDao.getAllEmployees();
return CollectionUtils.minus(allEmps, emps);
}
// ..
}

위의 코드에서 employeeDao를 통해 데이터를 준비하는 것은 Oraganization의 역할이 아니다. 이런 상세한 로직은 data access layer에 가까운 다른 추상화에 속해야 한다.

가장 이상적인 추상화 수준은 EmployeeRepository이다.

Repository의 역할은
- data accessors(Dao)와 상호작용을 추상화하는 분리된 레이어
- 도메인 모델에 "business interface"를 제공한다.

이런 경우 Employee aggregate(Employee 클래스는 Employee aggregate를 구성하는 Adress, Office 등과 같은 클래스와 연관을 갖을 수 있다)에 대해 하나의 Repository를 사용한다.

Aggregate의 리파지토리는 도메인 언어로 도메인 모델에게 data access 서비스를 제공하기 하기 위해 필요한 모든 Dao들과 상호작용하는 책임을 갖는다. 도메인 모델은 리파지토리 덕에 데이터 레이어로부터의 결과를 수집하는 것과 같은 상세 구현과 분리/유지될 수 있게된다.

public interface EmployeeRepository {
List<Employee> getOutstationEmployees(Address address);
// .. other business contracts
}

public class EmployeeRepositoryImpl implements EmployeeRepository {

private EmployeeDAO employeeDao;

public List<Employee> getOutstationEmployees(Address address) {
List<Employee> emps = employeeDao.getEmployeesByAddress(corporateOffice);
List<Employee> allEmps = employeeDao.getAllEmployees();
return CollectionUtils.minus(allEmps, emps);
}
}

리파지토리와 Dao의 주요 차이점은 아래와 같다.

  • Dao는 리파지토리 하위의 추상화 단계로써 데이터베이스로부터의 데이터를 읽어오기 위한 배관 코드(plumbing code)를 가질 수 있다. 데이터베이스 테이블 마다 하나의 Dao를 사용하고, 하나의 도메인 타입이나 aggregate에 대해 하나의 리파지토리를 사용한다.
  • 리파지토리가 제공하는 인터페이스/계약(contract)은 순수 "도메인 중심적"이고 같은 도메인 언어를 사용한다.
리파지토리는 도메인 구조물이다.

리파지토리는 도메인의 ubiquitous 언어를 사용한다. 그래서 리파지토리가 제공하는 인터페이스는 도메인 모델에 속한다. 하지만 리파지토리의 구현은 테이블 종속적인 메소드나 Dao를 사용하기 위한 배관코드를 갖는다. 그래서 순수 도메인 모델은 리파지토리 인터페이스에만 종속적이어야 한다. 마틴 파울러는 이를 위해 "Separated Interface" 패턴(http://www.martinfowler.com/eaaCatalog/separatedInterface.html - 리파지토리 인터페이스는 도메인 패키지 정의하고 구현은 다른 패키지. 클라이언트는 상세 구현을 완전히 몰라도 된다. Separated Interface는 Gateway를 위한 훌룡한 plug point가 된다.)을 추천했다.

도메인 모델에 리파지토리를 Inject하는 것은 아래와 같이 단순하다.

@Configurable("organization")
class Organization {
private String name;
private Address corporateOffice;
// .. other members

// implementation needs to be injected
private EmployeeRepository employeeRepo;

// ..
// ..

public List<Employee> getOutstationEmployees() {
return employeeRepo.getOutstationEmployees(corporateOffice);
}
// ..
}

하이버네이트 같은 ORM을 사용하게 되면 transparent persistence로 인해 Dao가 불필요해진다.

이러한 방식으로 구현된 어플리케이션을 수행하기 위해서는 아래와 같이 jvm에 옵션을 추가해야 한다.

-javaagent:/Users/msbaek/.m2/repository/org/springframework/spring-agent/2.5.2/spring-agent-2.5.2.jar

eclipse에서 아래 화면과 같이 설정하면 테스트 수행시마다 설정을 할 필요가 없어진다.

Javaagent-Options

질의응답
Q1. 리파지토리 대신 Dao에서 Aggregate를 처리하면 되지않나 ?
A1. Dao는 높은 재사용성을 제공하는 fine grained 메소드를 제공. 리파지토리는 도메인 구조물로써 도메인 언어를 사용하고 Aggregation에 대한 coarse grained 메소드를 제공. Dao는 "outstation(출장소)"라는 도메인 언어의 해석을 포함면 안된다.

참고자료

  • http://debasishg.blogspot.com/2007/02/domain-driven-design-inject.html
:
Posted by codetemplate
2008. 3. 15. 01:25

mvn jetty, cargo 프레임워크2008. 3. 15. 01:25

tomcat에 인스톨하기

mvn jetty:run-war: jetty로 웹 어플리케이션 실행

mvn cargo:start -Dcargo.wait=true: 톰캣에 어플리케이션을 디플로이한다. http://localhost:8081/applicationName-version

maven 2 tomcat plugin 설정(mvn tomcat:run, mvn tomcat:run-war)

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>tomcat-maven-plugin</artifactId>

<configuration>

<path>/</path>

</configuration>

</plugin>

이미 설치된 톰캣을 사용하고 할 경우 아래의 설정을

<container>

<containerId>${cargo.container}</containerId>

<!--home>${cargo.container.home}</home-->

<zipUrlInstaller>

<url>${cargo.container.url}</url>

<installDir>${installDir}</installDir>

</zipUrlInstaller>

</container>

다음과 같이 변경한다.

<container>

<containerId>${cargo.container}</containerId>

<home>${cargo.container.home}</home>

</container>
:
Posted by codetemplate
2008. 3. 15. 01:24

mvn test 프레임워크2008. 3. 15. 01:24

mvn test

-Dtest=ClassName(not fully-qualified)

-Dsurefire.useFile=false: test 오류를 파일이 아닌 콘솔에서 보고자 할 때

-Dmaven.surefire.debug: 5005번 포트로 디버그 포트를 열고 대기(suspend=y)할 때

:
Posted by codetemplate
2008. 3. 15. 01:22

DBUnit 프레임워크2008. 3. 15. 01:22

dbunit을 이용해서 데이터베이스 export하기

mvn dbunit:export

target/dbunit/export.xml에 export됨. -Ddest 파라미터로 위치 변경 가능

repopulate database

mvn dbunit:operation

mvn hibernate3:hbm2ddl dbunit:operation

creates and populates your database

:
Posted by codetemplate