Cargo 메이븐 플러그인 설정하기

참조: http://cargo.codehaus.org/Maven2+plugin

상당히 간단하지만, 그 결과는 그다지 간단하지 않은 것 같습니다. 일단 설정은 이렇게 했습니다.

            <plugin>
                <groupId>org.codehaus.cargo</groupId>
                <artifactId>cargo-maven2-plugin</artifactId>
                <configuration>
                    <container>
                        <containerId>tomcat6x</containerId>
                        <home>/apps/apache-tomcat-6.0.18</home>
                    </container>
                </configuration>
            </plugin>

이렇게 하고, cargo:start 골을 실행하면 톰캣 서버를 메이븐에서 실행할 수 있습니다. mvn cargo:start 이런식으로요.

테스트 진행은 다음 순서대로 할 겁니다.

WAR 파일 패키징 -> cargo를 사용하여 톰캣 서버 실행 -> 웹 (통합) 테스트 -> cargo를 사용한 톰캣 서버 정지

이 것을 그대로 메이븐 골로 옮겨 적으면 다음과 같습니다.

mvn package cargo:start test cargo:stop

그러나 이건 예상대로 동작하지 않습니다. 일단, 단위 테스트와 통합 테스트가 분리되어야 합니다. 단위 테스트를 실행하는데 굳이 카고를 이용하여 테스트 서버를 실행할 필요는 없기 때문이죠.

두 번째로 cargo:start를 실행하는 순간, 해당 커맨드 창은 이용할 수가 없습니다. 서버 로그가 뜨고 Ctrl+C를 눌러 서버 실행을 멈추기 전까지는 해당 커맨드 창이 먹통이나 다릅없습니다. 또 다른 콘솔을 띄우고 그 곳에서 테스트를 진행 한 다음 cargo:stop으로 서버 실행을 멈출 수 있습니다.

이 두 가지를 생각해 봤을 때 빌드가 2단으로 구성되어야 할 것 같습니다.

1단에서 할 일은 clean package(이 안에 complie, test 포함) cargo:start
2단에서 할 일은 integration-test, cargo:stop

궁금한 건 이러한 2단 빌드를 제공하는 CI 툴이 있느냐 하는 것과 메이븐으로 이런 구성을 하는 방법입니다. 후자는 금방 찾을 것 같은데 전자는 잘 모르겠네요. 기존 CI 툴의 plan을 잘 짜서 여러 plan끼리 의존성을 주며 실행하도록 할 수 있다면 가능할지도 모르겠고, 아예 하나의 plan으로 이러한 2단 빌드를 구성할 수 있게 해준다면 편리할 것 같습니다.

스프링 인티그레이션 빌드 실패~

소스 받기: svn co https://src.springframework.org/svn/spring-integration/tags/spring-integration-1.0.1.RELEASE

몇일 전 스프링 프레임워크 3.0 빌드도 실패했는데 이번에도 실패하네요. 스프링 인티그레이션 프로젝트도 스프링 프레임워크와 비슷한 프로젝트 구조를 가지고 있으며 동일한 빌드 방법을 사용합니다. 즉 Ant를 사용하여 라이브러리는 Ivy를 사용해서 가져옵니다.

어쨋거나 이번에도 실패하네요.

사용자 삽입 이미지
이번에는 Tsts failed라는데 아마 Tests Failed 겠죠? 흠;; 뭐 걍 빌드 해준 라이브러리만 써도 관계없기는 한데 빌드가 안되니까 참 찝찝하네요.

로컬에서는 무사히 빌드가 되는데, 왜 서버로 올라가서 CI가 돌리면 컴파일 에러가 날까

이럴 때가 정말 황당하고 답답한데, 뭐 방법은 역시 에러 메시지를 잘~~ 살펴보면 됩니다. 아님, 사부님한테 물어보던지요.ㅋㅋ

문제 분석

일단 CI 서버에서 문제가 생기면, 로그 메시지를 보고 어느 Phase에서 에러가 난건지 확인 합니다. 컴파일 에러가 났으면 당연히 compile Phase에서 에러가 난거겠죠. 그럼 로컬에서는 어떤지 로컬에서 compile을 해봅니다. 즉 mvn compile 이라고 하면 되죠. 이 때 서버랑 똑같이 컴파일 문제가 생기면 아주 아주 좋은겁니다. 그런데 이 글의 제목처럼 로컬에서는 잘 돌아간다면, 약간 당황스러워 집니다. 이 때부턴 여태까지의 경험과 로그 분석으로 난국을 헤쳐나가야 합니다. mvn 로그를 보려면, -X를 추가해서 실행하면 됩니다. 서버로 가서 mvn -X compile 이라고 서버에서 실행하고 자세히 들여다 봅니다. 어떤 라이브러리들을 가져왔는지, 플러긴은 제대로 가져왔는지 등등..

문제 해결

0. 로컬에서 빌드가 제대로 돌았다는 사실을 의심해야 합니다.

“왜 서버에선 안 돌아갈까?” 전에.. “왜 로컬에서는 돌아가는거지?” 라는 생각을 먼저 해보는게 문제 해결의 지름길 입니다. ‘서버에서 돌린 스크립트를 그대로 돌렸나??’ 라고 생각해본다거나, 이클립스에서 실행한건 아닌지.. 의심해야 합니다.

1. 인코딩 문제

로그를 보다가 UTF-8 뭐시기.. 인코딩 뭐시기 하는 메시지가 쭉~~~~~~~~~뜨는 경우가 있습니다. 이 때는 소스 파일 인코딩이랑 서버의 인코딩이 맞지 않아서 그런데, 리눅스의 경우 export LANG=ko_KR.eucKR 이런식으로 인코딩을 변경할 수 있으니, 인코딩을 변경한 다음에 다시 빌드를 해봅니다. 되면 귿!

2. 라이브러리 참조 문제

사용자 삽입 이미지
필요한 라이브러리들을 제대로 참조 했는지, 내가 원하던 버전을 참조하고 있는지 확인합니다. -X 옵션을 주면 볼 수 있으니까 잘 보면서 확인합니다.

2-1. 원하던 라이브러리가 없다.

<dependency> 엘리먼트 안 에 정의한 <scope>를 의심해보시기 바랍니다. <scope>에 test라고 입력했다면, 소스 코드 compile 시점이 아니라 테스트 코드 compile 시점에 참조하게 됩니다. 따라서 mvn compile 할 떄는 해당 라이브러리가 없겠죠.

2-2. deps 들이 꼬였다.

deps가 꼬일 일이 없으면 좋겠지만, 간혹… 정말 간혹.. 꼬일 수도 있습니다. managed deps 들이랑, transient deps들이 잘못해서 꼬이면 정말 찾기가 힘들지만, 어떻게 하겠습니까? 로그를 보면서 찾아야 합니다. 찾으면 그 담엔 제외 시키던지, 아니면 새로 deps를 정의해서 nearest first 전략을 활용하면 됩니다.

저의 에피소스

문제는 금요일 저녁에 발생했는데, 해결은 방금 전에 했습니다. 똑같은 컴파일 에러.. 똑같은 상황… 10번도 넘게 빌드를 해봤지만, 문제 원인은 쉽게 떠오르질 않고, 왜 자꾸 spring에 있는 mock 패키지를 못찾는 다고 하는 건지.. 로컬에선 잘 돌아갔는데.. mvn compile을 해도 돌아갔는데.. 대체 왜 이러니…

조금 전 연결이 된 사부님에게 문제 상황을 말씀 드렸더니, 저번에도 겪은 문제(deps가 꼬인 경우)가 아니겠냐고, -X 옵션으로 확인해보라는 대답을 들었습니다. 속으로는 ‘아.. 안 되는데.. 그 문제면 정말 귀찮은데…’ 이러면서 확인해 봤더니, 컴파일에러에서 찾지 못한다는 그 클래스를 가진 라이브러리가 없었습니다. ‘뭐지.. 왜 spring-test.jar가 없지?’ 제가 돌린 프로젝트는 스프링 테스트를 확장한 클래스(테스트 클래스가 아니라 그냥 일반 소스 코드로써의 클래스)를 가지고 있었기 때문에 spring-test.jar가 필요했습니다. ‘올커니… scope 때문이구나.’ 아니나 다를까. pom.xml을 열어봤더니, 해당 라이브러리가 test 스콥으로 잡혀있었습니다. 해당 라인을 지워버리니까 빌드는 무사히 성공~

Ant 빌드로 테스트 실행하기

참조 : http://www.java2s.com/Code/Java/Ant/Junittest.htm

1. JUnit jar 파일을 Ant의 lib 폴더에 추가

먼저 junit.jar 나 junit-4.4.jar 파일을 ANT_HOME의 lib 폴더에 넣어줍니다.  이 일도 태스크로 작성할 수 있는데, 굳이 이 일을 타겟으로 지정해서 할 필요는 없을 것 같아서 뺏습니다.

2. 빌드 작성

빌드 파일을 작성합니다. 제가 작성한 테스트의 경우에는 xml파일이나 프로퍼티 파일을 필요로 하기 때문에 해당 파일들도 build 디렉터리로 복사해주는 작업이 prepare에 추가됐습니다.

<property name=”dir.build” value=”build” />
    <property name=”dir.test” value=”test” />
    <property name=”dir.src” value=”src” />

    <!– JUnit –>
    <target name=”prepare”>
        <mkdir dir=”${dir.build}” />
        <copy file=”${dir.test}/memberData.xml” todir=”${dir.build}” failonerror=”true” />
        <copy file=”${dir.test}/testDatabase.properties” todir=”${dir.build}” failonerror=”true” />
    </target>

    <target name=”compile” depends=”prepare” description=”Compile all source code.”>
        <javac srcdir=”${dir.test}” destdir=”${dir.build}”>
            <classpath refid=”base-classpath” />
        </javac>
        <javac srcdir=”${dir.src}” destdir=”${dir.build}”>
            <classpath refid=”base-classpath” />
        </javac>
    </target>

    <target name=”junit” depends=”compile”>
        <junit printsummary=”on” fork=”false” haltonfailure=”false” failureproperty=”tests.failed” showoutput=”true”>

            <classpath refid=”base-classpath” />
            <formatter type=”brief” usefile=”false” />

            <batchtest>
                <fileset dir=”${dir.test}”>
                    <include name=”**/*Test.java” />
                </fileset>
            </batchtest>
        </junit>

        <fail if=”tests.failed”>
        *******************************************************
        *******************************************************
        One or more tests failed. Check the output…
        *******************************************************
        *******************************************************
        </fail>
    </target>

3. 빌드파일 실행하기
사용자 삽입 이미지사용자 삽입 이미지