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

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 에코지오
,

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

기본적으로 메이븐에서 프로퍼티는 실행시 -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 에코지오
,

백업 용도로 또는 어떤 목적이든 메이븐에서 자바소스, 웹소스, 각종 설정파일 등등 내가 원하는 파일만을 포함하는 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 에코지오
,

뽀대나는 CVS저장소 통계 보고서를 보기위해 Maven에 stat-scm 리포트 플러그인을 끼워넣었다.

 <scm>
  <!-- SCM 연결 정보 -->
  <connection>scm:cvs:pserver:anonymous:@111.111.111.111:/SRC:HHHH</connection>
 </scm>
   ... ...
 <reporting>
  <plugins>
   <!-- SCM 통계 리포트 생성 플러그인 -->
   <plugin>
    <groupId>net.sf</groupId>
    <artifactId>stat-scm</artifactId>
    <version>1.2.0</version>
   </plugin>

주기적으로 만들기 위해 Hudson에서 site 골을 걸어놓는다.



Build Now! ................ 헉............

INFO] SCM Connection Type :cvs [INFO] Output Directory :D:\hudson-1.252\home\jobs\MyProject\workspace\target\generated-site\xdoc\statscm\ [INFO] scm log > D:\hudson-1.252\home\jobs\MyProject\workspace\target\generated-site\xdoc\statscm\scm.log [ERROR] Error Getting SCM log. java.io.IOException: CreateProcess: cvs log error=2 at java.lang.ProcessImpl.create(Native Method) at java.lang.ProcessImpl.<init>(ProcessImpl.java:81) at java.lang.ProcessImpl.start(ProcessImpl.java:30) at java.lang.ProcessBuilder.start(ProcessBuilder.java:451) at java.lang.Runtime.exec(Runtime.java:591) at java.lang.Runtime.exec(Runtime.java:464) at net.sf.statscm.SrcManager.log(SrcManager.java:77) at net.sf.statscm.StatScmMojo.executeReport(StatScmMojo.java:200) ... ....

허드슨에 cvs 실행파일 위치도 알려줬는데 뭐가 문제냐고....




cvs.exe를 시스템환경변수 PATH에 추가해 주면 된다.

http://confluence.public.thoughtworks.org/display/CC/Frequently+Asked+Questions#FrequentlyAskedQuestions-faq48

Posted by 에코지오
,

메이븐의 wagon 기능을 통해서 리모트 머신에 파일을 전송할 때 wagon은 CVS 폴더까지 함께 압축해서 전송한다. 따라서 CVS로 관리되는 폴더를 전송하고자 한다면 전송해야할 파일들을 ant copy 등을 이용해서 빌드 결과 폴더(target)로 먼저 복사한 뒤 그것을 전송하는 것이 좋다. wagon은 ant의 defaultexcludes 옵션이 없다....

Ant에서는 고민꺼리가 아닌 것이 Maven에서는 고민이 된다... 메이븐의 배려가 아쉽다....

Posted by 에코지오
,

메이븐에는 wagon이라는 게 있는데 ftp, http, scp, webdav 같은 전송 프로토콜을 추상화한 것인데 site:deploy는 site 파일들을 리모트에 전송하기 위해 wagon 기능을 이용한다.  리모트 경로는 아래처럼 "프로토콜://~" 형식으로 설정하면 되며, 메이븐은 설정된 프로토콜에 적당한 프로바이더를 찾아서 파일을 전송해준다.

  <distributionManagement>
    <site>
      <id>www.yourcompany.com</id>
      <url>scp://www.yourcompany.com/www/docs/project/</url>
    </site>
  </distributionManagement>

site:deploy를 실행하고 콘솔에 찍히는 로그를 살펴보면 

- 로컬에서 .wagon12345.zip 과 같은 임시 zip 파일이 만들어진다
- zip 파일을 리모트 서버로 전송한다. ######## 표시가 늘어나는 식로 전송 진행상태를 보여준다.
- 리모트서버에서 unzip 명령으로 해당 경로에 압축을 풀고, 압축이 다 풀리면 zip 파일을 삭제한다.

그러니까 ant의 scp 타스크나 ftp 타스크가 지정된 fileset에 대해서 파일을 낱개로 하나씩 보내는 반면에,
wagon은 파일에셋을 로컬에서 압축하여 하나의 zip파일로 보낸 뒤 리모트에서 zip을 풀어내는,
나름 효율적인 방식으로 작동한다.

그러나 리모트에서 zip 파일을 푸는 방법에 문제가 있다. 리모트 서버에서 unzip이 없으면 에러가 발생하는 것이다.
실제로 지금 있는 프로젝트의 hp-ux 서버에는 unzip이 설치되어 있지 않았다.
(또 이상한 건 unzip 설치후 ssh 클라이언트로 접속해서 unzip을 실행해보면 잘 실행이 되는데도 불구하고,
wagon으로 파일을 전송해보면 unzip이 없다는 에러가 발생한다. unzip을 /usr/bin에 두어 해결은 했지만,
지금도 잘 이해가 되질 않는다. /usr/bin 이외의 다른 위치에 있으면 unzip이 없다는 에러가 발생한다는게..쫌...)

구걸링을 해보니 메이븐 1.x에서는 압축파일의 포맷과 압축해제 실행파일의 위치를 지정할 수 있게했다고 한다.
즉 tar로 묶는 것도 되고 gunzip으로 zip을 풀 수도 있었단 거다. 그러던 것이 메이븐 2.x에 와서 zip, unzip으로 고정이 돼버렸다.

http://jira.codehaus.org/browse/MSITE-30?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=147326#action_147326

황당한 생각에 wagon-ssh 프로바이더의 소스를 다운받아 까보니
org.apache.maven.wagon.providers.ssh.ScpHelper 클래스의 putDirectory() 메소드에
이렇게 하드 코딩이 돼있다.

executor.executeCommand( "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );

왜 이렇게 fix 시켰을까?  막돼먹은 코딩인가? 아니면 관습의 강요인가?

Posted by 에코지오
,

mvn site 명령을 날리면 site phase 발생전에 compile phase가 먼저 실행되어 황당할 때가 있다.
원래 site 단계는 compile 단계의 실행을 필요로 하지 않는다.

FindBugs 플러그인를 사용할 때 이런 일이 발생하는데, 소스를 분석하기 위해 compile 단계를 실행하는 것 같다.

   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>findbugs-maven-plugin</artifactId>
    <version>1.2</version>
   </plugin>
Posted by 에코지오
,