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

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

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

뽀대나는 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 에코지오
,
메이븐이 만들어 내는 site 문서를 열어보면 프로젝트 설명이라든가  등등에서 한글이 깨져서 나오기도 하는데

site 문서의 html 소스를 까보면 한글이 깨지는 이유를 알게된다.

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />

html 문서의 charset은 maven-site-plugin의 outputEncoding 설정을 통해 바꿀 수 있다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-site-plugin</artifactId>
    <version>2.0-beta-6</version>
    <configuration>
     <outputEncoding>euc-kr</outputEncoding>
    </configuration>
</plugin>

그러나 이걸로도 한글이 깨지는 걸 피할 수 없는 리포트가 있는데 StatSCM 리포트가 그렇다.
stat-scm 플러그인은 소스버전관리 저장소의 활동내역을 통계 리포트로 생성하는 플러그인이다.
   <plugin>
    <groupId>net.sf</groupId>
    <artifactId>stat-scm</artifactId>
    <version>1.2.0</version>
   </plugin>

여러가지 멋진 통계그래프를 만들어주는 간지나는 플러그인이다. 다만 아쉬운 것이 CVS접속 계정이 한글인 경우
site 인코딩을 euc-kr로 하든 utf-8로 하든 뭔 지랄을 해도 한글이 깨진다는 것이다.



근데 재밋게도 차트안에 들어가는 한글은 안깨지고 잘 나온다. 거참 희한하다.

ps. 위에 실명이 나와서 좀 거시기한데.. 설마 이 글을 보지는 않겠지?
Posted by 에코지오
,