메이븐다운(?) 방법으로 웹어플리케이션을 원격서버로 전송하기 위해서 이것저것 찾아보다가 알게 된 거 정리해본다.

1. deploy:deploy
packaging한 artifacts를 리모트의 메이븐저장소로 전송한다. 원격 저장소는  <distributionManagement>엘리먼트의 <repository>에 설정한다. 임의의 파일을 전송하는 건 불가능하다. package 가 war 이고 저장소 경로가 ftp://repository.mycompany.com/repository 라면, 실제로 war 파일은 저장소 layout에 맞춰서 ~/repository/groupId/artifactId/version 디렉토리에 전송된다.

2. site:deploy
 site phase에서 생성된 사이트를 리모트 (웹)서버로 전송한다. 리모트 서버정보는<distributionManagement>엘리먼트의 <site>에 설정한다. inputDirectory 옵션을 통해 target/site 디렉토리가 아닌 다른 디렉토리의 파일들을 전송할 수 있다. 그러나 <site>는 하나만 설정가능하기 때문에 2군데 이상의 서버로 파일들을 전송하는 건 불가능하다.

3. cargo 플러그인
일부 컨테이너에 대해 remote container에 expanded war(war 파일의 압축을 풀어놓은 것. exploded war)를 배포할 수 있다고 cargo 웹사이트에 나온다. 하지만 과연 이게 가능할지는 의구심이 든다. 실제로 작동여부를 테스트해봐야 할 듯하지만, 아마도 war 파일만 원격배포가 가능하지 않을까 싶다. 아직 최신 버전의 상용 WAS에 대한 지원이 미비하다.

4. myfaces의 wagon-maven 플러그인 
임의의 디렉토리 내의 파일들을 원격 서버로 전송한다. 임의의 디렉토리를 2군데 이상의 원격서버에 전송할 수 있다. 실제 프로젝트에서 개발서버 배포를 이 플러그인으로 처리했다. target/webapp 디렉토리의 파일들은 WAS서버로 전송하고, target/htdocs 디렉토리의 파일들은 WEB서버로 전송하도록 말이다. 
아쉽다면, 지정된 디렉토리 내의 모든 파일들을 전송하며 그 디렉토리의 몇몇 파일들만 골라서 전송할 수는 없다는 것이다. 즉 fileset 개념이 없다(사실 이건 이 플러그인의 문제라기 보다 이 플러그인이 이용하는 메이븐 wagon의 문제이다. 몇몇파일만 골라내는 건 ant copy를 써서 target/htdocs처럼 별도의 디렉토리에 전송할 대상만 따로 모아놓으면 해결할 수 있다).
이 플러그인의 장점이라면 디렉토리를 압축하여 전송후 리모트에서 압축을 해제하는 방식의 wagon 기능을 이용하기 때문에 전송할 파일이 많은 경우에 ant의 ftp/scp 타스크를 이용하는 것보다 전송시간이 현저히 줄어든다는 거.
아래는 실제 pom.xml의 일부인데 별거 아닌게 내용이 긴거 같아 맘이 편하지만은 않다.

   <plugin>
    <groupId>org.apache.myfaces.buildtools</groupId>
    <artifactId>myfaces-wagon-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
     <!-- config 배포 작업 -->
     <execution>
      <id>deploy-conf</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-config</id>
       <url>
        scp://${wasserver.username}:${wasserver.password}@${wasserver.ip}:${wasserver.config.dir}
       </url>
       <inputDirectory>${config.home.dir}</inputDirectory>
      </configuration>
     </execution>
     <!-- 웹어플리케이션 배포 작업 -->
     <execution>
      <id>deploy-web</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-web</id>
       <url>
        scp://${wasserver.username}:${wasserver.password}@${wasserver.ip}:${wasserver.web.dir}
       </url>
       <inputDirectory>${web.output.exploded.dir}</inputDirectory>
      </configuration>
     </execution>
     <!-- 웹파일 배포 작업 -->
     <execution>
      <id>deploy-html</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-html</id>
       <url>
        scp://${webserver.username}:${webserver.password}@${webserver.ip}:${webserver.htdocs.dir}
       </url>
       <inputDirectory>${web.output.html.dir}</inputDirectory>
      </configuration>
     </execution>
    </executions>
   </plugin>


현재까지 패턴을 적용하여 임의의 파일/디렉토리를 내 맘대로 전송할 수 있는 방법은 메이븐에서는 antrun을 이용하는 방법밖에 없는 듯싶다. maven의 wagon api와 file management api 이용하여 직접 플러그인을 만드는 것도 재밌을거 같다.

Posted by 에코지오
,
지금 몸담고 있는 회사의 빌드관련 문서에는 build와 deploy를 이렇게 정의하고 있다.

- build : 소스를 실행가능한 모듈로 변환하는 것
- deploy : 빌드되어 실행가능한 결과물을 컨테이너에서 인식가능한 곳에 배치하는 것

build는 별로 어렵지 않지만 deploy는 곰곰이 생각할 것이 많다. 내용상으로는 install과 비슷하고, 번역용어가 distribution과도 헷갈린다.

실행가능한 모듈을 실행 가능한 곳에 위치시키고 설정하는 것. install 아닌가?  그러나 OS위에서 작동하는 소프트웨어에 대해서는 대개 install이라 하고, WAS같은 컨테이너에서 작동하는 어플리케이션이나 재사용 레포지토리에 저장되는 서비스모듈에 대해서는 deploy라고 구별하는 것 같다 (이게 소프트웨어와 어플리케이션/라이브러리의 차이에서 오는 개념구분인지도 모르겠다).

deploy 및 관련된 deployables, distribution의 개념에 대해 개인적으로 다시 정리해보았다.

* deployables(deployable application)
- 정의 : WAS같은 컨테이너 상에서 실행가능한 어플리케이션.
- 설명 : 자바 환경이라면 war,ear,jar 같은 것들을 말한다. deployables는 소프트웨어 distribution의 한가지 형태라고 봐도 될듯하다. 근데 이걸 뭘로 번역할지는? 배포물? 배포가능 어플리케이션?

* deploy (deployment)
- 정의 : deployables를 실제 컨테이너에 배치(arrange? allocate?)하는 작업. 
- 설명 : 간단히 말하면 어플리케이션을 WAS 상에 설치(install)하는 것이다. deploy는 전개,배치,배포 등으로 다양하게 번역되며 보통 디플로이 또는 배포라고 부른다. 그러나 배포라는 표현은 distribution과 혼동을 일으키기 쉽기 때문에 개인적인 의견으로는 배치가 더 맞는 표현이 아닐까 생각하지만, 역시나 배치라고 불렀을 때 batch와 발음이 같아서 또다른 혼동을 일으킬 수 있다.

* distribution
- 정의: 누군가에게 전달(delivery)하기 위해 패키징된 소프트웨어. (또는 그것을 전달하는 행위)
- 설명 : 사전적으로는 분배,배분,배포의 의미를 가지며 보통 배포본, 배포판으로 번역한다.

ps1. IBM 프로젝트 자동화 아티클에서는 deployment를 전개로, distribution을 배포로 번역했다. 헷갈리게시리...-.-;

ps2. 위의 deploy 정의는 웹어플리케이션 개발 영역에서나 통하는 정의이고 이전에 언급했듯이 메이븐에서는 아티팩트를 저장소에 올리는 걸 deploy라고 부른다. 그래서 메이븐 처음 접하면서 오해하게 되는 게, 메이븐이 웹어플리케이션을 WAS에 배포하는 것까지 간단하게 해준다고 생각한다. 천만에 만만에 콩떡인거다.

ps3. 소스를 컴파일/빌드하여 생긴 결과물을 뭐라고 부르는게 좋을까?
빌드결과물? 빌드산출물? 배포본? 에셋? 바이너리? 실행파일번들? 빌드모듈?
개인적으로는 빌드산출물이 맘에 든다.

ps3. deployables. 이건 deployable thing을 표현한 단어인데, cargo에서 아이디어를 따왔다. 실제 이런 단어가 있는건 아닌거 같다.
Posted by 에코지오
,

흔히 개발서버나 테스트서버에 웹어플리케이션을 배포할 때는 프로젝트의 모든 빌드된 파일(class,jsp,html,...)을 한꺼번에 배포한다.

그러나 운영서버 반영은 상황이 다르다. 운영서버에는 전체 어플리케이션 파일을 한꺼번에 배포하지 않고 테스트가 통과된 검증된 파일만 반영해야 하기 때문이다. 또한 대개 업무팀에서 특정 기능 또는 화면을 골라서 운영에 반영해달라고 요청이 들어온다.

전체 프로젝트 소스에서 일부만 운영에 반영하기 위해 어떻게 하는게 최선일까?

1. FTP 프로그램 등에서 그냥 반영할 파일을 골라서 업로드하는 방법.
난 지금껏 이 방법을 써왔다. 물론 시스템오픈 전에 막바지 구축단계에서.

2. 무조건 최신 소스를 받아서 반영.
소스저장소의 중심개발축(HEAD, Trunk)에 소스를 받아서 빌드하고 그냥 전부 일괄로 반영. 배째라식.

3. 중심개발축 소스를 태깅하고 태깅된 소스를 받아서 반영.
태깅은 전체소스에 다 태그를 붙이기 때문에 반영할 소스가 아닌 것도 반영될 수 있다. 정말로 반영을 원하는 것만 가져오려면 운영에 반영할 소스들만 커밋하게 해야 하는데 이게 쉽지 않다.

4. 운영 반영용 브랜치(릴리스브랜치)를 따서 반영하는 방법
릴리스 브랜치를 만들고 중심개발축에서 운영에 반영할 소스만 골라서 릴리스 브랜치에 병합한 뒤
릴리스브랜치 소스를 커밋한다. 이후 운영반영용 빌드는 릴리스 브랜치의 소스만 취합해서 빌드,테스트하고 운영서버에 반영하는 식이다.

관련 책이나 자료를 보면 4번 방법을 많이 적용하는 것이 정석이라고 나온다.
그러나 내 경험도 그렇고 내 주위에는 1,2번 방법으로 흔히 처리한다. 내가 SM 경험이 없고 SI만 해봐서 그런 걸수도 있고...

시스템 오픈 전에는 그냥 1,2번 방식으로 하고 오픈 후 안정화되고 나면 4번 적용하는게 맞는걸까?

Posted by 에코지오
,

메이븐에 대해 많은 사람들이 토로하는 불만중의 하나가 프로퍼티를 외부 파일에서 읽어오는 기능이 없다는 점이다.

기본적으로 메이븐에서 프로퍼티는 실행시 -Dxxx=yyy 식으로 넘겨주거나 <properties> 엘리먼트에 <xxx>yyy</xxx> 식으로 설정할 수 있다.

Ant는 간단히 <property file="jdbc.properties" /> 식으로 외부 프로퍼티 파일에서 값을 읽어올 수가 있는데 메이븐에는 이런 간단한 것조차 안된다는 점이 많이 실망스럽기도 하다.

그래서인가 누군가 properties-maven-plugin이라는 플러그인을 만들었는데 http://snapshots.repository.codehaus.org 레포지토리에서 받을 수 있다.
(2007년 이후로 별다른 업데이트는 없다...)

   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>properties-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <executions>
     <execution>
      <phase>initialize</phase>
      <goals>
       <goal>read-project-properties</goal>
      </goals>
      <configuration>
       <files>
        <file>jdbc.properties</file>
       </files>
      </configuration>
     </execution>
    </executions>
   </plugin>

그러나 기대는 아직 이르다. 이 플러그인에는 치명적인 문제가 있다. 
properties 파일에 설정한 프로퍼티를 플러그인 및 의존성의 설정(configuration) 항목에 사용할 수 없다는 것이다.
젠장.

Posted by 에코지오
,

보통은 pom.xml 빌드스크립트를 프로젝트 root에 포함하지만, 개발자에게 노출되는게 싫다거나 하는 등의 이유로 외부로 빼내고 싶을 때가 있다.

그렇다면 허드슨은 프로젝트 외부에 있는 pom.xml을 이용해서 프로젝트를 빌드할 수 있는가?
허드슨에서 pom.xml의 위치를 절대 경로로 줄 수 있는가?

만약 job 타입이 maven 타입이면 불가능하다. 무조건 workspace에 대한 상대경로여야 한다. 그러니까 닥치고 프로젝트 안에만 있어야 한다는 거다. ../../my/path/pom.xml 이런 거 안통한다.

다행히 job 타입이 free style이면 pom.xml을 workspace 밖에 두는 것이 가능하다.
http://www.nabble.com/Dynamic-Views-of-Clear-Case--%3E-workspace-empty-!-td16350453.html

Posted by 에코지오
,

Hudson을 이용한다면 scm 저장소로부터 (메이븐 타입의) 프로젝트를 체크아웃/업데이트 받아서 특정 goal/phase를 실행하는 일은 Hudson의 몫이다. 그러니까 보통은 메이븐타입 프로젝트의 pom.xml로는 프로젝트 자체를 scm에서 체크아웃/업데이트 받는 일은 안한다.

하지만 허드슨을 이용하지 않고 허드슨의 일을 대신해서 프로젝트 소스를 scm으로부터 받아와서 빌드/배포 goal을 실행해야 하는 상황이라면 어떻게 해야할까?

scm에서 체크아웃/업데이트하고 프로젝트 안의 pom.xml에 대해 goal을 실행하는 쉘스크립트를 짜는 것도 한가지 방법일 수 있지만 이러한 작업을 하는 또다른 pom.xml을 만드는 것도 가능하다. maven-scm-plugin 플러그인을 이용하면 된다.

 <scm>
  <connection>scm:svn:https://127.0.0.1:8443/svn/XXX</connection>
 </scm>
 ... ...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-scm-plugin</artifactId>
    <version>1.1</version>
    <configuration>
     <connectionType>connection</connectionType>
     <scmVersionType>tag</scmVersionType>
     <scmVersion>${scm.version}</scmVersion>
     <checkoutDirectory>workspace</checkoutDirectory>
     <workingDirectory>workspace</workingDirectory>
     <skipCheckoutIfExists>true</skipCheckoutIfExists>
     <username>user</username>
     <password>user</password>
     <goals>clean test war:inplace</goals>
     <profiles>env-staging</profiles>
    </configuration>
 </plugin>

위처럼 pom.xml을 만들어 놓고 scm:bootstrap 을 실행하면 메이븐은 checkoutDirectory로 지정된 workspace에 프로젝트 소스를 체크아웃 받는다. 체크아웃 받은 뒤에 프로젝트 root의 pom.xml에 대해서 <goals/>에 나열된 goal을 실행해준다.

mvn -Dscm.version=RB123 scm:bootstrap
Posted by 에코지오
,
단위 테스트케이스 소스가 없는 프로젝트를 메이븐으로 빌드할 때 test phase(단계)는 불필요한 것이 명백하다.

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
      <skip>true</skip>
    </configuration>
  </plugin>

또는 maven.test.skip 프로퍼티 값을 true로 설정하면 test phase에 바인딩된 surefire:test 골의 실행을 skip할 수 있다.

메이븐에서 deploy는 개발 결과 아티팩트(jar,war,....)를 리모트의 메이븐 저장소에 등록하는 것을 의미한다.
공통라이브러리나 오픈소스 라이브러리를 개발한다면 이게 deploy의 의미에 합당할지도 모르겠다.
그러나 아마도 대부분의 JEE 기반 웹시스템 개발 프로젝트에서 deploy는 웹어플리케이션을 WAS에 배치하는 것을 의미한다. test 단계를 skip한 것처럼 deploy 단계에 기본으로 바인딩된 deploy:deploy 골도 skip할 수 있다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-deploy-plugin</artifactId>
    <configuration>
     <skip>true</skip>
    </configuration>
</plugin>

이렇게 deploy:deploy를 skip해놓고 anttrun 등으로 WAS에 배포본을 올리는 작업을 deploy phase에 바인딩하면 deploy가 진정 내가 원하는 deploy로 바뀌게 된다.

한가지 또 아쉬운게 있다면 deploy 단계 이전에 실행되는 install 단계도 skip할 수 있으면 좋겠는데 안타깝게도 install:install 모조에는 skip 옵션이 없다. 젠장.
Posted by 에코지오
,
메이븐 POM 레퍼런스 문서의 Directories 섹션을 보면 *Directory 경로는 프로파일에서 바꿀 수 없다고 나온다.

The set of directory elements live in the parent build element, which set various directory structures for the POM as a whole. Since they do not exist in profile builds, these cannot be altered by profiles.

그러니까 sourceDirectory, testSourceDirectory 엘리먼트는 profile 엘리먼트하위의 build에는 존재하지 않기 때문에 프로파일에서 변경이 불가하다는 것이다.

  <build>
    <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
    <outputDirectory>${basedir}/target/classes</outputDirectory>
    <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
    ...
  </build>
</project>


과연 바꿀 수 없을까? 바꿀 수 있다!

parent build 엘리먼트에서 소스 디렉토리의 경로를 프로퍼티로 설정하고 프로파일에서 프로퍼티를 재정의하면 된다.

 <properties>
  <java.src.dir>src</java.src.dir>
  <scripts.src.dir>scripts</scripts.src.dir>
  <test.src.dir>test</test.src.dir>
  <java.output.dir>web/WEB-INF/classes</java.output.dir>
  <test.output.dir>bin/test</test.output.dir>
 </properties>

 <build>
  <sourceDirectory>${java.src.dir}</sourceDirectory>
    <scriptSourceDirectory>${scripts.src.dir}</scriptSourceDirectory>
    <testSourceDirectory>${test.src.dir}</testSourceDirectory>
  <outputDirectory>${java.output.dir}</outputDirectory>   
    <testOutputDirectory>${test.output.dir}</testOutputDirectory>
  ...
  </build>
  
 <profiles>
   <profile>
    ... ...
   <properties>
      <java.src.dir>src2</java.src.dir>
      <scripts.src.dir>scripts</scripts.src.dir>
      <test.src.dir>test2</test.src.dir>
      <java.output.dir>/weblogic/deploy/app/WEB-INF/classes</java.output.dir>
      <test.output.dir>bin/test2</test.output.dir>
   </properties>
  </profile>

메이븐에서는 만약 profile별로 다른 소스 디렉토리 구조를 가져야한다면 별도의 프로젝트로 분리할 것을 권장하고 있다. 그 말이 맞는 것같다.

Posted by 에코지오
,

백업 용도로 또는 어떤 목적이든 메이븐에서 자바소스, 웹소스, 각종 설정파일 등등 내가 원하는 파일만을 포함하는 zip 파일을 만들고 싶을 때가 있다.

maven-assembly-plugin이 그 일을 해준다.

   <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
     <descriptors>
      <descriptor>.assembly.xml</descriptor>
     </descriptors>
    </configuration>
   </plugin>

이 플러그인의 한가지 귀찮은 점은 assembly descriptor 라는 파일을 만들어야한다는 것인데 이쯤에서 또다시 Ant가 그리워지는 순간이기도 하다.

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
  <id>src-backup</id>
  <formats>
    <format>zip</format>
  </formats>
  <fileSets>
    <fileSet>
      <directory>.</directory>
      <outputDirectory></outputDirectory>
      <useDefaultExcludes>true</useDefaultExcludes>
      <excludes>
        <exclude>target/**</exclude>
        <exclude>bin/**</exclude>
        <exclude>build/**</exclude>
      </excludes>
    </fileSet>
  </fileSets>
</assembly>

mvn  assembly:assembly를 실행하면 {project.build.finalName}-src-backup.zip 파일이 떨궈진다.
닪순히 파일명을 {project.build.finalName}.zip 으로 하고 싶으면 descriptor 에서 <id>를 생략하면 된다.
또는 appendAssemblyId 설정을 false로 하거나.

* assembly:assembly와 assembly:single의 차이
- assembly:assembly : package phase가 먼저 실행된다. 이 goal은 phase에 바인딩하면 안된다.(못한다?)
- assembly:single : assembly 작업만 실행된다. 이 goal은 phase에 바인딩 가능하다.

Posted by 에코지오
,
1. (maven을 사용한다면) pom.xml에 보고자 하는 report 플러그인을 설정
   <!-- FindBugs 리포트 생성 플러그인 -->
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>findbugs-maven-plugin</artifactId>
    <version>1.2</version>
    <configuration>
     <threshold>Low</threshold><!-- High, Default, Low, Ignore -->
     <xmlOutput>true</xmlOutput>
    </configuration>
   </plugin>
   <!-- PMD 리포트 생성 플러그인 -->
   <plugin>
    <artifactId>maven-pmd-plugin</artifactId>
    <configuration>
     <rulesets>
      <ruleset>/rulesets/basic.xml</ruleset>
      <ruleset>/rulesets/unusedcode.xml</ruleset>
     </rulesets>
     <sourceEncoding>${maven.compiler.encoding}</sourceEncoding>
     <targetJdk>${maven.compiler.source}</targetJdk>
    </configuration>
   </plugin>

2. 허든슨에서 관련된 허드슨 플러그인을 설치. (Manage Hudson > Manage Plugins)



3. 허드슨에서 빌드시 maven의 site goal을 실행하도록 설정하고 리포트 publish 설정(빌드 Configure 화면). 필요시 pmd.xml, findbugs.xml 같은 리포트 xml 위치 설정.


4. 이제 빌드작업 화면에서 차트를 볼 수 있다~

Posted by 에코지오
,