들어가며

이 글은 아래 사이트의 내용을 기초로하여 만들어 졌다. 아래 사이트와 다른점은 한글이라는점과 이클립스가 들어갔다는 점이다. 아래에서 만드는 소스내용도 똑같으니 참고하기 바란다.

http://www.sonatype.com/books/maven-book/reference/public-book.html

여기서 만드는 예제는 yahoo 에서 제공하는 xml 을 이용하여 날씨를 얻어오는 예제이다.

물론 프로젝트 내용은 중요한것이 아니고 maven의 기능을 살펴보는데 초점을 맞추게 될것이다.

 

 

프로젝트 만들기

maven 프로젝트를 생성해서 아래와같이 입력하고 Next 버튼을 누른다.

 

기본적으로 test 를 위해 junit 은 필요할테니까 Add 를 클릭해서 junit 을 검색해서 추가한다.

단 scope 는 test 로 해야 한다는 것을 잊지 말자.

 

 

Dependency 추가

pom.xml 파일을 열면 아래와같은 화면이 나오는데 여기서  Dependencies 탭으로 가서 아래처럼 찾기 아이콘을 클릭한다.

 

이제 찾기 화면에서 아래 라이브러를 검색해서 추가한다.  (단 여기서 log4j 는 이보다 최신버젼이 있지만 그 버젼을 선택할경우 오류가 나는 현상이 있어서 이전 버젼을 선택했다.) 추가한 다음에는 꼭 저장버튼을 클릭해서 적용을 하자.

log4j (1.2.14 )

dom4j

jaxen

velocity

 

 

프로그램 작성

프로그램 작성에 들어가기 전에 이클립스 셋팅을 해야한다. maven 프로젝트를 생성하면 기본적으로 자바 1.4 가 선택이 된다. 프로젝트의 Properties 로 들어가서 Java Compiler 메뉴로 가서 컴파일러 버젼을 최신으로 바꾼다음에 진행하도록 하자.

 

프로그램의 소스파일은 아래와 같다. 여기서 중요한 것은 소스의 내용이 아니기 때문에 소스설명은 생략하도록 하겠다.

net/cranix/Weather.java

net/cranix/Main.java

net/cranix/YahooRetriver.java

net/cranix/YahooParser.java

net/cranix/WeatherFormatter.java

 

<net/cranix/Weather.java>

package net.cranix;

public class Weather {
    private String city;
    private String region;
    private String country;
    private String condition;
    private String temp;
    private String chill;
    private String humidity;

    public Weather() {
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCondition() {
        return condition;
    }

    public void setCondition(String condition) {
        this.condition = condition;
    }

    public String getTemp() {
        return temp;
    }

    public void setTemp(String temp) {
        this.temp = temp;
    }

    public String getChill() {
        return chill;
    }

    public void setChill(String chill) {
        this.chill = chill;
    }

    public String getHumidity() {
        return humidity;
    }

    public void setHumidity(String humidity) {
        this.humidity = humidity;
    }
}

 

<net/cranix/Main.java>

package net.cranix;

import java.io.InputStream;

import org.apache.log4j.PropertyConfigurator;

public class Main {

    public static void main(String[] args) throws Exception {
        // Configure Log4J
        PropertyConfigurator.configure(Main.class.getClassLoader().getResource("log4j.properties"));

        // Read the Zip Code from the Command-line (if none supplied, use 60202)
        String zipcode = "60202";
        try {
          zipcode = args[0];
        } catch( Exception e ) {}

        // Start the program
        new Main(zipcode).start();
      }

    private String zip;

    public Main(String zip) {
        this.zip = zip;
    }

    public void start() throws Exception {
        // Retrieve Data
        InputStream dataIn = new YahooRetriever().retrieve(Integer.valueOf(zip));

        // Parse Data
        Weather weather = new YahooParser().parse(dataIn);

        // Format (Print) Data
        System.out.print(new WeatherFormatter().format(weather));
    }
}

<net/cranix/YahooRetriver.java>

package net.cranix;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.apache.log4j.Logger;

public class YahooRetriever {

    private static Logger log = Logger.getLogger(YahooRetriever.class);

    public InputStream retrieve(int zipcode) throws Exception {
        log.info("Retrieving Weather Data");
        String url = "http://weather.yahooapis.com/forecastrss?p=" + zipcode;
        URLConnection conn = new URL(url).openConnection();
        return conn.getInputStream();
    }
}

<net/cranix/YahooParser.java>

package net.cranix;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.io.SAXReader;

public class YahooParser {

    private static Logger log = Logger.getLogger(YahooParser.class);

    public Weather parse(InputStream inputStream) throws Exception {
        Weather weather = new Weather();

        log.info("Creating XML Reader");
        SAXReader xmlReader = createXmlReader();
        Document doc = xmlReader.read(inputStream);

        log.info("Parsing XML Response");
        weather.setCity(doc.valueOf("/rss/channel/y:location/@city"));
        weather.setRegion(doc.valueOf("/rss/channel/y:location/@region"));
        weather.setCountry(doc.valueOf("/rss/channel/y:location/@country"));
        weather.setCondition(doc.valueOf("/rss/channel/item/y:condition/@text"));
        weather.setTemp(doc.valueOf("/rss/channel/item/y:condition/@temp"));
        weather.setChill(doc.valueOf("/rss/channel/y:wind/@chill"));
        weather.setHumidity(doc.valueOf("/rss/channel/y:atmosphere/@humidity"));

        return weather;
    }

    private SAXReader createXmlReader() {
        Map<String, String> uris = new HashMap<String, String>();
        uris.put("y", "http://xml.weather.yahoo.com/ns/rss/1.0");

        DocumentFactory factory = new DocumentFactory();
        factory.setXPathNamespaceURIs(uris);

        SAXReader xmlReader = new SAXReader();
        xmlReader.setDocumentFactory(factory);
        return xmlReader;
    }
}

<net/cranix/WeatherFormatter.java>

package net.cranix;

import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;

import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

public class WeatherFormatter {
    private static Logger log = Logger.getLogger(WeatherFormatter.class);
    public String format(Weather weather) throws Exception {
        log.info("Formatting Weather Data");
        Reader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream("output.vm"));
        VelocityContext context = new VelocityContext();
        context.put("weather", weather);
        StringWriter writer = new StringWriter();
        Velocity.evaluate(context, writer, "", reader);
        return writer.toString();
    }
}

 

소스를 조금 설명하자면 dom4j 라이브러리로 yahoo 에서 제공하는 xml 파일을 읽어서 파싱한 다음 velocity 라이브러리로 출력을 하게 되는 것이다. 여기서 콘솔출력은 log4j 에 의해 이루어진다.

 

 

RESOURCE 추가

그러나 이렇게 소스만 만들어놨다고 제대로된 결과를 기대하기는 어렵다. 왜냐하면 log4j 같은 경우는 설정 파일이 필요하고 velocity 는 템플릿 파일이 필요하다 이런 resource 를 추가하는 방법을 알아보도록 하겠다.

 

모든 리소스들은 resource 디렉토리에 위치시키면 된다. 이 프로그램에서 필요한 리소스는 아래와 같다.

log4j.properties

output.vm

 

<log4j.properties>

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-4r %-5p %c{1} %x - %m%n

<output.vm>

*********************************
Current Weather Conditions for:
${weather.city}, ${weather.region}, ${weather.country}

Temperature: ${weather.temp}
Condition: ${weather.condition}
Humidity: ${weather.humidity}
Wind Chill: ${weather.chill}
*********************************

디렉토리의 형태는 아래와 같다.

 

 

실행하기

실행하기 위해서는 Run As-maven build 를 클릭한다음 아래와같이 셋팅하고 Run 을 클릭하자.

 

아래와 같은 결과가 나오면서 우리가 만든 프로그램이 제대로 실행되고 있는 것을 알 수 있다.

[INFO] Searching repository for plugin with prefix: 'exec'.
[INFO] Attempting to resolve a version for plugin: org.codehaus.mojo:exec-maven-plugin using meta-version: LATEST
[INFO] Using version: 1.1.1 of plugin: org.codehaus.mojo:exec-maven-plugin
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - net.cranix:weather:jar:0.0.1-SNAPSHOT
[INFO]
[INFO] Id: net.cranix:weather:jar:0.0.1-SNAPSHOT
[INFO] task-segment: [exec:java]
[INFO] ------------------------------------------------------------------------
[INFO] [exec:java]
0    INFO  YahooRetriever  - Retrieving Weather Data
359  INFO  YahooParser  - Creating XML Reader
561  INFO  YahooParser  - Parsing XML Response
639  INFO  WeatherFormatter  - Formatting Weather Data
*********************************
Current Weather Conditions for:
  Evanston, IL, US
Temperature: 70
   Condition: Fair
    Humidity: 73
  Wind Chill: 70
*********************************
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Sun Aug 16 15:44:18 KST 2009
[INFO] Final Memory: 4M/12M
[INFO] ------------------------------------------------------------------------

 

 

Dependency 정보 확인하기

이렇게 maven 으로 프로젝트를 만들었다면 Dependency 정보를 쉽게 확인할 수 있다.

pom.xml 파일을 열어서 Dependency Graph 탭을 클릭하고 새로고침 버튼을 클릭하면 아래와 같이 그래프 형태로 표시되는 것을 볼 수 있다.

 

아래와 같이 Dependency Hierarchy 를 클릭해서 트리 형태로 볼 수도 있다.

 

 

 

테스트 소스 추가하기

여기서는 유닛 테스트를 위해 commons-io 라이브러리를 사용한다. pom.xml 파일을 열어서 dependency 탭으로 가서 commons-io 라이브러리를 검색해서 추가하자. 단 테스트를 위한 것이기 때문에 scope 를 test 로 설정하자.

 

테스트를 하기 위해서는 test 디렉토리에 테스트 파일이 위치해야 하며 여기서 사용할 테스트파일은 아래와 같다.

test/net/cranix/YahooParserTest.java

test/net/cranix/WeatherFormatterTest.java

 

<test/net/cranix/YahooParserTest.java>

package net.cranix;

import java.io.InputStream;

import junit.framework.TestCase;

public class YahooParserTest extends TestCase {

    public YahooParserTest(String name) {
        super(name);
    }

    public void testParser() throws Exception {
        InputStream nyData = getClass().getClassLoader().getResourceAsStream(
                "ny-weather.xml");
        Weather weather = new YahooParser().parse(nyData);
        assertEquals("New York", weather.getCity());
        assertEquals("NY", weather.getRegion());
        assertEquals("US", weather.getCountry());
        assertEquals("39", weather.getTemp());
        assertEquals("Fair", weather.getCondition());
        assertEquals("39", weather.getChill());
        assertEquals("67", weather.getHumidity());
    }
}

 

<test/net/cranix/WeatherFormatterTest.java>

package net.cranix;

import java.io.InputStream;

import org.apache.commons.io.IOUtil;

import junit.framework.TestCase;

public class WeatherFormatterTest extends TestCase {

    public WeatherFormatterTest(String name) {
        super(name);
    }

    public void testFormat() throws Exception {
        InputStream nyData = getClass().getClassLoader().getResourceAsStream("ny-weather.xml");
        Weather weather = new YahooParser().parse(nyData);
        String formattedResult = new WeatherFormatter().format(weather);
        InputStream expected = getClass().getClassLoader().getResourceAsStream("format-expected.dat");
        assertEquals(IOUtil.toString(expected).trim(), formattedResult.trim());
    }
}

 

 

테스트 RESOURCE 추가하기

테스트를 위한 이 프로그램 역시 그냥은 실행되지 않는다. 리소스 파일을 추가해 줘야 하는데 테스트 리소스 파일은 test/resources 디렉토리에 위치해야 한다.

test/resources/format-expected.dat

test/resources/ny-weather.xml

<test/resources/format-expected.dat>

*********************************
Current Weather Conditions for:
New York, NY, US

Temperature: 39
Condition: Fair
Humidity: 67
Wind Chill: 39
*********************************
<test/resources/ny-weather.xml>
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0"
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
<channel>
<title>Yahoo! Weather - New York, NY</title>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/New_York__NY/</link>
<description>Yahoo! Weather for New York, NY</description>
<language>en-us</language>
<lastBuildDate>Sat, 10 Nov 2007 8:51 pm EDT</lastBuildDate>

<ttl>60</ttl>
<yweather:location city="New York" region="NY" country="US" />
<yweather:units temperature="F" distance="mi" pressure="in" speed="mph" />
<yweather:wind chill="39" direction="0" speed="0" />
<yweather:atmosphere humidity="67" visibility="1609" pressure="30.18"
rising="1" />
<yweather:astronomy sunrise="6:36 am" sunset="4:43 pm" />
<image>
<title>Yahoo! Weather</title>

<width>142</width>
<height>18</height>
<link>http://weather.yahoo.com/</link>
<url>http://l.yimg.com/us.yimg.com/i/us/nws/th/main_142b.gif</url>
</image>
<item>
<title>Conditions for New York, NY at 8:51 pm EDT</title>

<geo:lat>40.67</geo:lat>
<geo:long>-73.94</geo:long>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/New_York__NY/\</link>
<pubDate>Sat, 10 Nov 2007 8:51 pm EDT</pubDate>
<yweather:condition text="Fair" code="33" temp="39"
date="Sat, 10 Nov 2007 8:51 pm EDT" />
<description><![CDATA[
<img src="http://l.yimg.com/us.yimg.com/i/us/we/52/33.gif" /><br />
<b>Current Conditions:</b><br />
Fair, 39 F<BR /><BR />
<b>Forecast:</b><BR />
Sat - Partly Cloudy. High: 45 Low: 32<br />
Sun - Sunny. High: 50 Low: 38<br />
<br />
]]></description>
<yweather:forecast day="Sat" date="10 Nov 2007" low="32" high="45"
text="Partly Cloudy" code="29" />

<yweather:forecast day="Sun" date="11 Nov 2007" low="38" high="50"
text="Sunny" code="32" />
<guid isPermaLink="false">10002_2007_11_10_20_51_EDT</guid>
</item>
</channel>
</rss>

 

최종 디렉토리는 아래와 같다.

 

 

 

테스트 실행

이제 테스트 환경이 갖추어졌다.

Run As-maven test 를 실행하자. 아래와 같이 테스트가 되는 것을 볼 수 있다.

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - net.cranix:weather:jar:0.0.1-SNAPSHOT
[INFO]
[INFO] Id: net.cranix:weather:jar:0.0.1-SNAPSHOT
[INFO] task-segment: [test]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [surefire:test]
[INFO] Surefire report directory: D:\cranix\work\workspace-wtp2\weather\target\surefire-reports

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running net.cranix.YahooParserTest
0    INFO  YahooParser  - Creating XML Reader
171  INFO  YahooParser  - Parsing XML Response
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.407 sec
Running net.cranix.WeatherFormatterTest
266  INFO  YahooParser  - Creating XML Reader
282  INFO  YahooParser  - Parsing XML Response
282  INFO  WeatherFormatter  - Formatting Weather Data
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.172 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Sun Aug 16 16:55:20 KST 2009
[INFO] Final Memory: 2M/6M
[INFO] ------------------------------------------------------------------------

 

 

 

Dependencies 까지 포함시켜서 패키징하기

이 과정을 assembly 과정 이라고 하는데 제대로 실행되도록 하려면 셋팅을 해야한다. pom.xml 파일을 열어서 아래 부분을 추가한다.

<project>
[...]
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>

 

그 다음 Run As-maven assembly:assembly 를 실행한다. 아래와같은 화면이 나오면서 패키징을 하게된다.

[INFO] [jar:jar]
[INFO] Building jar: D:\cranix\work\workspace-wtp2\weather\target\weather-0.0.1-SNAPSHOT.jar
[INFO] [statemgmt:end-fork]
[INFO] Ending forked execution [fork id: 574707759]
[INFO] [assembly:assembly]
[INFO] Processing DependencySet (output=)
[INFO] Expanding: D:\cranix\work\maven\repository\log4j\log4j\1.2.14\log4j-1.2.14.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1429370463.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\maven\dom4j\1.7-20060614\dom4j-1.7-20060614.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1955766376.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\jaxme\jaxme-api\0.3\jaxme-api-0.3.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.165260721.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\jaxen\jaxen\1.1.1\jaxen-1.1.1.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1217840646.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\dom4j\dom4j\1.6.1\dom4j-1.6.1.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.3570191.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\jdom\jdom\1.0\jdom-1.0.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1685521809.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\xerces\xmlParserAPIs\2.6.2\xmlParserAPIs-2.6.2.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.305584397.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\xerces\xercesImpl\2.6.2\xercesImpl-2.6.2.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1558042758.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\xom\xom\1.0\xom-1.0.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.2049563553.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\com\ibm\icu\icu4j\2.6.1\icu4j-2.6.1.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.796405737.tmp
[INFO] Expanding: D:\cranix\work\maven\repository\xalan\xalan\2.6.0\xalan-2.6.0.jar into C:\Users\cranix\AppData\Local\Temp\archived-file-set.1324133154.tmp

이 작업은 현재 종속된 모든 라이브러리를 하나의 jar 파일로 묶는 작업으로 시간이 조금 걸린다. 완료가 되면 target 디렉토리에 모든 종속된 라이브러리의 클래스가 포함된 weather-0.0.1-SNAPSHOT-jar-with-dependencies.jar 파일이 생성된 것을 볼 수있다.

 

마치며

왠만해서 pom.xml 파일을 직접 에디트 하지 않았으면 했지만 아직 m2eclipse 플러그인 에서는 비쥬얼하게 위와같이 assembly 설정을 할수있는 방법이 없는거 같다. 하여튼 마지막 assembly 과정은 maven 의 백미 라고 볼 수 있겠다.

' > maven' 카테고리의 다른 글

maven-jetty-plugin 포트 설정하기  (3291) 2009.09.03
maven 에서 encoding 설정하기  (844) 2009.09.03
eclipse maven 으로 tomcat 어플리케이션 만들기  (73) 2009.08.17
eclipse 에서 maven  (755) 2009.08.16
maven 이란  (37) 2009.08.15
by cranix 2009. 8. 16. 17:17