25 Nisan 2011 Pazartesi

Maven3 (Proje yönetim aracı)-6: Örnek Çok Modüllü Proje

Bu yazıda daha önce Maven3 (Proje yönetim aracı)-4: Örnek Proje(simple-weather) ve Maven3 (Proje yönetim aracı)-5: Örnek Web Projesi(simple-webapp) nde anlattığım örnek projeleri bir araya getirip çok modüllü bir proje oluşturacağız. Proje kodlarını buradan indirebilirsiniz.

Çok modüllü bir proje, içinden sahip olduğu alt modüllere referans veren bir ebeveyn POM ile oluşturulur. Yani ebeveyn projemizi simple-parent/ dizinine koyar ve içine de şu POM'u koyarsak, simple-weather ve simple-webapp adında iki alt modülü olan bir çok modüllü proje elde etmiş oluruz:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.sonatype.mavenbook.multi</groupId>
  <artifactId>simple-parent</artifactId>
  <packaging>pom</packaging>
  <version>1.0</version>
  <name>Multi Chapter Simple Parent Project</name>
 
  <modules>
    <module>simple-weather</module>
    <module>simple-webapp</module>
  </modules>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
          </configuration>
        </plugin>
      </plugins>
   </pluginManagement> 
  </build>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Dikkat ederseniz ebeveyn projemizin paketleme tipi olarak JAR veya WAR gibi herhangi bir arşiv çesidi vermedik. Paketleme çeşidimiz sadece diğer projelere referansla içeren basit bir POM dosyası. Ayrıca bu POM'da modules adında daha önce karşılaşmadığınız bir öğe var: modules Bu öğe altındaki her module öğesi ebeveyn projenin dizini altındaki alt modüllerin dizinlerine işaret eder. Yani bizim örneğimize göre simple-weather ve simple-webapp adında iki alt dizine (modüle) sahibiz.
Bu ebeveny POM tarafından tanımlanmış bütün ayarlar, bağımlılıklar v.s. alt modüller tarafından kalıtım alınır.

simple-weather Alt Modülü
Modülümüzün POM'unu şu şekilde oluşturuyoruz:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.sonatype.mavenbook.multi</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>
  <artifactId>simple-weather</artifactId>
  <packaging>jar</packaging>

  <name>Multi Chapter Simple Weather API</name>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <configuration>
            <testFailureIgnore>true</testFailureIgnore>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement> 
  </build>

  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.5</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Modülün detaylarına inmeden önce burada birşey dikkatinizi çekmeli: parent öğesi. Bu öğe ile modülümüzün ebeveyn projesinin koordinatlarını belirtiyoruz.

Bu modülde şöyle bir yol izleyeceğiz: Tekrar kullanılabilirliği sağlamak için bir tane servis sınıfı yazacağız. Bu sınıf aynen komut satırından çalışan simple-weather projesi gibi girilen bölge koduna ait şehrin hava durumu bilgilerini geri dönecek. Servis sınıfımız işlemlerini gerçekleştirmek için simple-weather projesinin sınıflarını kullanacak.

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

public class WeatherService {

    public WeatherService() {}

    public String retrieveForecast( String zip ) throws Exception {
        // Retrieve Data
        InputStream dataIn = new YahooRetriever().retrieve( zip );

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

        // Format (Print) Data
        return new WeatherFormatter().format( weather );
    }
}

simple-webapp Alt Modülü
Bu modülümüz ise az önce simple-weather'da oluşturduğumuz servis sınıfını kullanıp, ondan aldığı verileri yayınlamak üzere birkaç servlet tanımlayacak. Şimdi öncelikle simple-weather modülünü kullanacağımız için POM'umuzda bu projeyi bağımlılık olarak belirtmeliyiz. POM'un geri kalanı zaten simple-webapp projesininkiyle aynı olacak.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.sonatype.mavenbook.multi</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>

  <artifactId>simple-webapp</artifactId>
  <packaging>war</packaging>
  <name>simple-webapp Maven Webapp</name>
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.sonatype.mavenbook.multi</groupId>
      <artifactId>simple-weather</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Servletimizde ise HTTP isteğinden bölge kodunu okuyoruz ve bir önceki modülde oluşturduğumuz WeatherService sınıfının retreiveForecast() metodunu çağırıyoruz. Dönen cevabı da out.println() ile istemciye yolluyoruz:
package org.sonatype.mavenbook.web;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
 PrintWriter out = response.getWriter();
 out.println( "SimpleServlet Executed" );
        out.flush();
        out.close();
    }
}

Son olarak web.xml'imizde /weather adresine yapılan istekleri karşılamak üzere WeatherServlet'i kaydediyoruz:
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>weather</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.WeatherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>weather</servlet-name>
    <url-pattern>/weather</url-pattern>
  </servlet-mapping>
</web-app>

Çok Modüllü Projeyi Derlemek
Sıra projelerimizi derlemeye geldi. simple-webapp, simple-weather'a bağımlı olduğu için, daha önce derlenmesi gerekli. Ancak bunu dert etmenize gerek yok, çünkü Maven (Reactor eklentisi) hangisinin daha önce derlenmesi gerektiğini bilecek kadar akıllı. Tek yapmanız gereken ebeveyn projenin olduğu dizinden mvn clean install komudunu çalıştırmak:
~/examples/ch-multi/simple-parent$ mvn clean install
[INFO] Scanning for projects...
[INFO] Reactor build order: 
[INFO]   Simple Parent Project
[INFO]   simple-weather
[INFO]   simple-webapp Maven Webapp
[INFO] ----------------------------------------------------------------------
[INFO] Building simple-weather
[INFO]    task-segment: [clean, install]
[INFO] ----------------------------------------------------------------------
[...]
[INFO] [install:install]
[INFO] Installing simple-weather-1.0.jar to simple-weather-1.0.jar
[INFO] ----------------------------------------------------------------------
[INFO] Building simple-webapp Maven Webapp
[INFO]    task-segment: [clean, install]
[INFO] ----------------------------------------------------------------------
[...]
[INFO] [install:install]
[INFO] Installing simple-webapp.war to simple-webapp-1.0.war
[INFO] 
[INFO] ----------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ----------------------------------------------------------------------
[INFO] Simple Parent Project ............................... SUCCESS [3.041s]
[INFO] simple-weather ...................................... SUCCESS [4.802s]
[INFO] simple-webapp Maven Webapp .......................... SUCCESS [3.065s]
[INFO] ----------------------------------------------------------------------

Tavsiye: Aslında yapmak zorunda olmasanız da mvn komudunu her çalıştırdığınızda clean evresini de ekleyin. Böylece Maven, daha önce oluşturulmuş bütün çıktıları silecek ve "temiz" bir inşa işlemine başlayacaktır.

Uygulamayı Çalıştırmak
Artık bildiğiniz üzere:
~/examples/ch-multi/simple-parent/simple-webapp $ mvn jetty:run
[INFO] ----------------------------------------------------------------------
[INFO] Building simple-webapp Maven Webapp
[INFO]    task-segment: [jetty:run]
[INFO] ----------------------------------------------------------------------
[...]
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: simple-webapp Maven Webapp
[...]
[INFO] Webapp directory = ~/examples/ch-multi/simple-parent/\
                          simple-webapp/src/main/webapp
[INFO] Starting jetty 6.1.6rc1 ...
2007-11-18 1:58:26.980::INFO:  jetty-6.1.6rc1
2007-11-18 1:58:26.125::INFO:  No Transaction manager found
2007-11-18 1:58:27.633::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

Jetty başladıktan sonra http://localhost:8080/simple-webapp/weather?zip=01201 adresine gidip servisi test edebilirsiniz. Sondaki 5 haneli numarayı değiştirip istediğiniz bölgenin hava durumunu öğrenebilirsiniz.
Aslında Türkiye'nin kodlarını da bulup İstanbul'un hava durumunu da öğrenmek lazım ya. Bu da size ödev olsun :)

Kolay Gelsin.

Maven3 (Proje yönetim aracı)-5: Örnek Web Projesi

Merhabalar. Bu yazıda basit bir web uygulamasını Maven kullanarak nasıl yapılacağını anlatacağım. Oluşturacağımız proje bir önceki yazıda anlattığım projeye büyük oranda benzeyecek. Bu yüzden hafizanızı tazelemenize ihtiyacınız olabilir. Hadi başlayalım.

Önce yine şu komut ile proje oluşturulmasını tetikleyin:
mvn archetype:generate -DgroupId=org.sonatype.mavenbook.simpleweb -DartifactId=simple-webapp -Dpackage=org.sonatype.mavenbook
Sorulduğunda proje iskeleti olarak maven-archetype-webapp'ı seçin. İşlem tamamlandığında şuna benzer bir POM oluşacak.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook.simpleweb</groupId>
  <artifactId>simple-webapp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple-webapp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
  </build>
</project>

Bu POM'da bir önceki projeninkinden farklı olarak dikkatinizi çekebilecek 2 şey var. Birincisi projenin paketleme biçimi WAR olarak belirlenmiş. İkincisi ise build öğesi altına finalName öğesini eklenmiş. Bu öğe paketleme evresinde WAR'a son adını vermekte kullanılır. Bu örneğe göre, projemiz target/ dizini altına simple-webapp.war olarak yayınlanacak.

Projeyi Çalıştırmak
Bir önceki projenin aksine bu proje bir web uygulaması olduğu için çalıştırmak için Exec eklentisini kullanamayız. Normal şartlar altında(NŞA) bu iş için Tomcat ya da Jetty gibi bir Servlet Kabı(Container)'nı internetten indirip, ayarlarını yapıp, simple-webapp.war dosyasını webapps/ altına koyup sonra da kabı başlatmanız gerekir. Ancak bunun daha kolay bir yolu var. Maven Jetty eklentisini bu iş için kullanabilirsiniz. Şu POM parçasını projenize eklemeniz yeterli:

<project>
  [...]
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

Sonrasında Jetty eklentisinin Run hedefini kullanarak uygulamayı çalıştırabilirsiniz.

~/examples/ch-simple-web/simple-webapp $ mvn jetty:run
...
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: simple-webapp Maven Webapp
[INFO] Webapp source directory = \
       ~/svnw/sonatype/examples/ch-simple-web/simple-webapp/src/main/webapp
[INFO] web.xml file = \
 ~/svnw/sonatype/examples/ch-simple-web/\
simple-webapp/src/main/webapp/WEB-INF/web.xml
[INFO] Classes = ~/svnw/sonatype/examples/ch-simple-web/\
simple-webapp/target/classes
2007-11-17 22:11:50.532::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /simple-webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = \
       ~/svnw/sonatype/examples/ch-simple-web/simple-webapp/src/main/webapp
[INFO] Starting jetty 6.1.6rc1 ...
48
2007-11-17 22:11:50.673::INFO:  jetty-6.1.6rc1
2007-11-17 22:11:50.846::INFO:  No Transaction manager found 
2007-11-17 22:11:51.057::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

DİKKAT: Maven Jetty eklentisini Windows altında çalıştırıyorsanız, yerel deponuzu adında boşluk içermeyen bir dizine almanız gerekebilir. Çünkü böyle durumlarda (örneğin yerel deponun "C:\Documents and Settings\" altında olması durumunda) bazen Jetty'nin başlatılamaması hatası ile karşılabilirsiniz. Çözüm deponuzu adında boşluk içermeyen bir yere alıp, bu yeri de ~/.m2/settings.xml dosyasında tekrar belirtmektir.
Jetty başlatıldıktan sonra, tarayıcınızdan http://localhost:8080/simple-webapp/ adresine gidin. Eğer herşey yolundaysa bir "Merhaba Dünya" görmelisiniz. Karşınıza gelen sayfa maven-archetype-webapp iskeletinin oluşturduğu index.jsp sayfasıdır. Maven ön tanımlı olarak herhangi bir web uygulamasının ana dizinini src/main/webapp/ olarak kabul eder. İşte az önce gördüğümüz index.jsp de bu dizindedir. İçeriğini merak ettiyseniz:


<html>
  <body>
    <h2>Hello World!</h2>
  </body>
</html>

src/main/webapp/WEB-INF altında ise yine çok basit bir web.xml dosyası vardır:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

Servlet Eklemek
Şimdi projemize biraz Java kodu ekleyelim. Öncelikle eğer halihazırda yoksa src/main/java/org/sonatype/mavenbook/web dizinini sonra da içinde SimpleServlet.java sınıfını oluşturun. İçine de şunları yazın:

package org.sonatype.mavenbook.web;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
 PrintWriter out = response.getWriter();
 out.println( "SimpleServlet Executed" );
        out.flush();
        out.close();
    }
}

Servlet'imiz aslında bir çeşit "Merhaba Dünya" servlet'inden başka birşey değil. Bu servlet'i uygulamamız altında çağrılan bir yola bağlamak için web.xml'i kullanacağız. Aşağıdaki web.xml ile servlet'imizi simple/ yoluna bağlıyoruz. Yani ne zaman http://localhost:8080/simple-webapp/simple adresine bir istek yapılsa bu servlet çağırılacak.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
</web-app>

Şimdi de projemize Servlet API'nin bağımlılığını ekleyelim:

<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

Burada bağımlılığın kapsamının provided olduğuna dikkat edin. Bu Maven'a bağımlılığı WAR dosyasının içine koymamasını çünkü servlet kabı tarafından sağlanacağını söyler. Eğer projede JSP etiketleri kullanırsanız JSP belirtim kütüphanesini de classpath'a eklemeniz gerekir.

<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

Şimdi uygulamamamızı çalıştırabiliriz. Uygulama kodları değiştiği için, Jetty'i tekrar çalıştırmamız gerekecek. Eğer Jetty hala çalışıyorsa CTRL+C tuşlarıyla durdurun. mvn clean install komutu ile tekrar inşa edin ve mvn jetty:run ile Jetty'i tekrar çalıştırın.

Tarayıcınızdan http://localhost:8080/simple-webapp/simple adresine gittiğinizde "Simple Executed" çıktısını görmeniz lazım.

Bu yazıda basit bir Java Web uygulaması geliştirdik, uygulmamıza bir servlet ekledik ve servlet kabı içinden çalıştırdık. Şimdi bu temel uzerine kendiniz geliştirmeler yapabilirsiniz. Örneğin POM'a PrimeFaces kütüphanesini ekleyip biraz JSF alıştırması yapın.
Kolay Gelsin.

24 Nisan 2011 Pazar

Maven3 (Proje yönetim aracı)-4: Örnek Proje

Bu yazıda sıfırdan Maven kullanarak bir proje geliştireceğiz. Proje kodlarını buradan indirebilirsiniz.
Projemiz Yahoo Weather RSS servisine bağlanıp girilen koda karşılık gelen bölgenin hava durumu bilgilerini getiren bir konsol uygulaması olacak.
Önce bir önceki yazıda anlattığım maven-quickstart-archive'ı kullanarak bir proje oluşturalım:

mvn archetype:generate -DgroupId=org.sonatype.mavenbook.custom -DartifactId=simple-weather -Dpackage=org.sonatype.mavenbook -Dversion=1.0

Komut satırında sorulduğunda proje iskeleti olarak maven-quickstart-archive'ı seçin. Maven komutu işlemeyi bitirdiğinde simple-weather dizinini oluşturacak ve içine projenin pom.xml dosyasını koyacaktır. Bu dosya:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook.custom</groupId>
  <artifactId>simple-weather</artifactId>
  <packaging>jar</packaging>
  <version>1.0</version>
  <name>simple-weather</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Projenin hedef Java sürümünü Derleme eklentisi aracılığyla Java 5'e ayarlayın:
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  .................
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  .............
</project>

Kod yazmaya başlamadan önce pom'a proje hakkında biraz bilgi girelim:
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
...

  <name>simple-weather</name>
  <url>http://www.sonatype.com</url>

  <licenses>
    <license>
      <name>Apache 2</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
      <comments>A business-friendly OSS license</comments>
    </license>
  </licenses>

  <organization>
    <name>Sonatype</name>
    <url>http://kahveninhatiri.blogspot.com/</url>
  </organization>

  <developers>
    <developer>
      <id>kh</id>
      <name>Kahvenin Hatiri</name>
      <email>kahveninhatiri@gmail.com</email>
      <url>http://kahveninhatiri.blogspot.com/</url>
      <organization>Kahvenin Hatiri</organization>
      <organizationUrl>http://kahveninhatiri.blogspot.com/</organizationUrl>
      <roles>
        <role>developer</role>
      </roles>
      <timezone>+1</timezone>
    </developer>
  </developers>
...
</project>

Sıra geldi projeye birkaç bağımlılık eklemeye. Projemizin XML verileri çözümleme işleri için Dom4J ve Jaxen kütüphanelerini, komut satırına vereceğimiz çıktıyı formatlamak için Velocity kütüphanesini ve günlük kaydı için de Log4J kütüphanesini kullanacağız. Bu bağımlılıkları şu şekilde ekliyoruz:

<project>
  [...]
  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.5</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  [...]
</project>

Peki ben bu bağımlılıkların koordinatlarını nasıl buldum? Bu iş için jarvana.com adresini kullanabilirsiniz.
Şimdi projemizin kaynak kodlarını ekleyelim. Öncelikle proje iskeleti ile gelen App ve AppTest sınıflarını silelim. Projemiz 5 tane sınıftan oluşacak:

  • org.sonatype.mavenbook.weather.Main: Çalıştırılabilir main() metodunu barındıran sınıf.
  • org.sonatype.mavenbook.weather.Weather: Hava durumu nesnesi. Yer, sıcaklık, nem v.b. bilgileri tutan basit bir JavaBean.
  • org.sonatype.mavenbook.weather.YahooRetriever: Yahoo servisine bağlanıp, RSS kaynağından bir InputStream döner.
  • org.sonatype.mavenbook.weather.YahooParser: Yahoo'dan dönen XML i çözümleyip, bilgileri bir Weather nesnesi olarak döner.
  • org.sonatype.mavenbook.weather.WeatherFormatter: Weather nesnelerini alıp, tanımladığımız Velocity temasına göre formatlar.

package org.sonatype.mavenbook.weather;

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; }
}

package org.sonatype.mavenbook.weather;

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 = "02101";
        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( zip );

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

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

package org.sonatype.mavenbook.weather;

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(String 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();
 }
}

package org.sonatype.mavenbook.weather;

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 uris = new HashMap();
        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;
 }
}

package org.sonatype.mavenbook.weather;

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();  
 }
}


Bu sınıfları simple-weather\src\main\java\org\sonatype\mavenbook dizini altında oluşturun.
Şimdi Log4J ve Velocity için ayar dosyalarını ekleyelim. Önce eğer yoksa src/main/ altında resources/ klasörü oluşturun. Bu klasörün içinde log4j.properties dosyası oluşturup içine şunları yazın:

# 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

Bu ayarlar Log4J'nin günlük çıktılarını ekrana hangi formatta basacağı ile ilgilidir.

Sıra velocity'nin ayar dosyasında. Yine aynı klasörde output.vm dosyasını oluşturun ve içine şunları yazın.

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

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

Sizin de tahmin edebileceğiz gibi bu ayar dosyası bir Weather nesnesinin alanlarına erişip (${weather.temp}) verilecek çıktı için bir düzen tanımlar.

Projemizin çalışması için gerekli herşey bu kadar. Şimdi mvn install komutuyla projeyi yerel depoda yayınlayalım.
3. parti bir eklenti olan Exec eklentisi ile projeyi çalıştıralım:
mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main
Bu komut ön tanımlı olan 60202 bölge kodlu Evanston'ın hava durumu bilgilerini ekrana getirecektir. Eğer komut satırından başka bir bölge kodu girmek isterseniz, şöyle yapabilirsiniz.

mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main -Dexec.args="70112"

Maven en başta bizim eklediğimiz 4 bağımlılığın dışında,geçişli bağımlılıkları da classpath'e ekler. Hangi kütüphanelerin classpath'ta olduğunu öğrenmek için, bağımlılıkları çözümleyebilrisiniz. Bunun için gerekli komut:

mvn dependency:resolve
...
[INFO] [dependency:resolve]
[INFO] 
[INFO] The following files have been resolved: 
[INFO]    com.ibm.icu:icu4j:jar:2.6.1 (scope = compile)
[INFO]    commons-collections:commons-collections:jar:3.1 (scope = compile)
[INFO]    commons-lang:commons-lang:jar:2.1 (scope = compile)
[INFO]    dom4j:dom4j:jar:1.6.1 (scope = compile)
[INFO]    jaxen:jaxen:jar:1.1.1 (scope = compile)
[INFO]    jdom:jdom:jar:1.0 (scope = compile)
[INFO]    junit:junit:jar:3.8.1 (scope = test)
[INFO]    log4j:log4j:jar:1.2.14 (scope = compile)
[INFO]    oro:oro:jar:2.0.8 (scope = compile)
[INFO]    velocity:velocity:jar:1.5 (scope = compile)
[INFO]    xalan:xalan:jar:2.6.0 (scope = compile)
[INFO]    xerces:xercesImpl:jar:2.6.2 (scope = compile)
[INFO]    xerces:xmlParserAPIs:jar:2.6.2 (scope = compile)
[INFO]    xml-apis:xml-apis:jar:1.0.b2 (scope = compile)
[INFO]    xom:xom:jar:1.0 (scope = compile)

mvn dependency:tree komutu bağımlılıkları ağaç yapısında gösterir.

mvn dependency:tree
...
[INFO] [dependency:tree]
[INFO] org.sonatype.mavenbook.custom:simple-weather:jar:1.0
[INFO] +- log4j:log4j:jar:1.2.14:compile
[INFO] +- dom4j:dom4j:jar:1.6.1:compile
[INFO] |  \- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO] +- jaxen:jaxen:jar:1.1.1:compile
[INFO] |  +- jdom:jdom:jar:1.0:compile
[INFO] |  +- xerces:xercesImpl:jar:2.6.2:compile
[INFO] |  \- xom:xom:jar:1.0:compile
[INFO] |     +- xerces:xmlParserAPIs:jar:2.6.2:compile
[INFO] |     +- xalan:xalan:jar:2.6.0:compile
[INFO] |     \- com.ibm.icu:icu4j:jar:2.6.1:compile
[INFO] +- velocity:velocity:jar:1.5:compile
[INFO] |  +- commons-collections:commons-collections:jar:3.1:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.1:compile
[INFO] |  \- oro:oro:jar:2.0.8:compile
[INFO] +- org.apache.commons:commons-io:jar:1.3.2:test
[INFO] \- junit:junit:jar:3.8.1:test
...

Eğer proje için düşünülmüş ama sürüm uyuşmazlığı veya diğer nedenlerle seçilmeyen bağımlılıkları da görmek isterseniz, komutu hata ayıklama kipinde çalıştırın:

mvn install -X
...
[DEBUG] org.sonatype.mavenbook.custom:simple-weather:jar:1.0 (selected for null)
[DEBUG]   log4j:log4j:jar:1.2.14:compile (selected for compile)
[DEBUG]   dom4j:dom4j:jar:1.6.1:compile (selected for compile)
[DEBUG]     xml-apis:xml-apis:jar:1.0.b2:compile (selected for compile)
[DEBUG]   jaxen:jaxen:jar:1.1.1:compile (selected for compile)
[DEBUG]     jaxen:jaxen:jar:1.1-beta-6:compile (removed - )
[DEBUG]     jaxen:jaxen:jar:1.0-FCS:compile (removed - )
[DEBUG]     jdom:jdom:jar:1.0:compile (selected for compile)
[DEBUG]     xml-apis:xml-apis:jar:1.3.02:compile (removed - nearer: 1.0.b2)
[DEBUG]     xerces:xercesImpl:jar:2.6.2:compile (selected for compile)
[DEBUG]     xom:xom:jar:1.0:compile (selected for compile)
[DEBUG]       xerces:xmlParserAPIs:jar:2.6.2:compile (selected for compile)
[DEBUG]       xalan:xalan:jar:2.6.0:compile (selected for compile)
[DEBUG]       xml-apis:xml-apis:1.0.b2.
[DEBUG]       com.ibm.icu:icu4j:jar:2.6.1:compile (selected for compile)
[DEBUG]   velocity:velocity:jar:1.5:compile (selected for compile)
[DEBUG]     commons-collections:commons-collections:jar:3.1:compile 
[DEBUG]     commons-lang:commons-lang:jar:2.1:compile (selected for compile)
[DEBUG]     oro:oro:jar:2.0.8:compile (selected for compile)
[DEBUG]   junit:junit:jar:3.8.1:test (selected for test)

Aslında proje oluşturma aşaması burada bitti. Ancak Maven'ın yeteneklerini göstermek için devam edelim ve projemize Birim Testleri ekleyelim.
Öncelikle eğer yoksa src\test\java\org\sonatype\mavenbook sizini oluşturun. Biz burada iki sınıfı test edeceğiz, birincisi YahooParser. Bunun için YahooParserTest sınıfını oluşturun ve içine şunları yazın.

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

import junit.framework.TestCase;

import org.sonatype.mavenbook.weather.Weather;
import org.sonatype.mavenbook.weather.YahooParser;

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() );
 }
}

Bu sınıftaki JUnit detaylarına girmeyeceğim, bu kısmı JUnit'i anlattığım bir yazıda açıklarım belki. Burada YahooParser sınıfının gelen XML bilgisini doğru çözümleyip çözümlemediği kontrol ediyoruz. Bunun önce sınayacağımız bir veriye ihtiyacımız var. Bu veriyi classpath'taki ny-weather.xml dosyasından alıyoruz. (birazdan bu dosyayı classpath'e koyacağız.) ve bu dosyadaki değerlerle karşılaştırıyoruz. Hepsi doğruysa test hata vermeden tamamlanacaktır.

İkinci testimiz WeatherFormatter sınıfı için. Bunun için WeatherFormatterTest sınıfını aynı dizinde oluşturun.

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

import org.apache.commons.io.IOUtils;

import org.sonatype.mavenbook.weather.Weather;
import org.sonatype.mavenbook.weather.WeatherFormatter;
import org.sonatype.mavenbook.weather.YahooParser;

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( IOUtils.toString( expected ), formattedResult );
 }
}

Bu sınıfta WeatherFormatter ın Weather nesnelerini doğru biçemlendirip biçimlendirmediği sınıyoruz. Bunun için karşılaştıracağımız bir kaynağa ihtiyacımız var. Bu kaynağı format-expected.dat dosyasından alıyoruz (bu dosyayı da birazdan oluşturacağız.).
Burada açıklamam gereken bir ayrıntı daha var. G/Ç işleriyle uğraşmamak için bir InputStream i String olarak dönen Apache Commons un commons-io kütüphanesini kullanıyoruz. Aslında bunu elle de yapabilirdik ancak test kapsamlı bağımlıkları göstermek için bu kütüphaneyi kullanıyoruz. Şimdi bu bağımlılığı pom'a ekleyelim.


  ...
  
    ...
    
      org.apache.commons
      commons-io
      1.3.2
      test
    
    ...
  


Şimdi daha önce söz verdiğimiz dosyaları ekleyelim. Önce eğer yoksa src/test/resources dizinini oluşturun.
format-expected.dat dosyasını oluşturup içine şunları yazın.

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

Temperature: 39
Condition: Fair
Humidity: 67
Wind Chill: 39
*********************************

ny-weather.xml dosyasını oluşturup içine şunları yazın.

<?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/*http://weather.yahoo.com/forecast/10002_f.html</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/*http://weather.yahoo.com/forecast/10002_f.html</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 />
<a href="http://us.rd.yahoo.com/dailynews/rss/weather/New_York__NY/*http://weather.yahoo.com/forecast/10002_f.html">Full Forecast at Yahoo! Weather</a><BR/>
 (provided by The Weather Channel)<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><!-- p7.weather.re3.yahoo.com compressed/chunked Sat Nov 10 17:57:31 PST 2007 -->

Şimdi testlerimizi çalıştırabiliriz. Aslında mvn package veya mvn install komutlarını çalıştırınca bu evrelerden önce olan test evresi de çalıştırılır. Ancak sadece test evresini çalıştırmak isterseniz mvn test komutunu da kullanabilirsiniz. Bu komutu çalıştırınca Maven Surfire eklentisi testlerinizi çalıştıracaktır ve testler hakkında detaylı bilgileri ${basedir}/target/surefire-reports altına koyacaktır. Eğer bir şeyler ters giderse buraya bakın :)

Eğer hatalı testlerin inşa işlemini bölmesini istemiyorsanız Bunu iki şekilde belirtebilirsiniz. İlki pom'a şu parçayı ekleyerek:

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <testFailureIgnore>true</testFailureIgnore>
        </configuration>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

İkincisi komut satırından:
mvn test -Dmaven.test.failure.ignore=true

Eğer bir inşada testleri atlamak isterseniz bunu da yine iki şekilde yapabilirsiniz. İlki pom'a şu parçayı ekleyerek:

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

İkincisi yine komut satırından:
mvn install -Dmaven.test.skip=true
...
[INFO] [compiler:testCompile]
[INFO] Not compiling test sources
[INFO] [surefire:test]
[INFO] Tests are skipped.
...

Kolay Gelsin.

23 Nisan 2011 Cumartesi

Maven3 (Proje yönetim aracı)-3: maven-archetype-quickstart İskeleti

Technorati Etiketleri: ,,,,,
Bu bölümde örnek bir Maven projesi oluşturup detaylarıyla inceleyeceğiz. Anlatacaklarım daha önce yayınladığım Maven Rehberi belgesindeki bilgilerin üzerine kurulacak. Eğer okumadıysanız şimdi okumanın tam zamanı.

Komut satırından mvn archetype:generate komutunu girerek, etkileşimli kipte proje oluşturmaya başlayın. Listelenen proje iskeletleri arasından maven-archetype-quickstart'ı seçin ve istenilen bilgileri girin. Projenizin artifacId'sini simple olarak adlandırırsak, Maven komutu çalıştırdığınız dizinde simple/ adında bir dizin oluşturacaktır. Bu dizinin yapısı:

simple/[1]
simple/pom.xml[2]
      /src/
      /src/main/[3]
          /main/java
      /src/test/[4]
          /test/java

Bu yapıyı incelersek oluşturulan her proje için şunların yapıldığı sonucuna varabiliriz:
  • Projenin artifactId'si ile aynı adda bir dizin: simple/.[1]
  • İçinde POM.xml.[2]
  • Projenin kaynak kodlarının koyulacağı src/main dizini.[3]
  • İçinde groupId ile aynı paket yapısının içinde App adlı basit bir Java programı.
  • Test kodlarının koyulacağı src/test dizini ve içinde App programının testi olan AppTest.[4]
Bu projeyi inşa etmek için komut satırından POM un bulunduğu dizine geçin ve mvn install komutunu çalıştırın. Bu komut projeyi derler, testlerini çalıştırır, paketler ve yerel deponuza kaydeder. Ayrıca target/ dizini altına simple-1.0-SNAPSHOT.jar dosyasını koyar.

maven-archetype-quickstart proje iskeleti Maven'ın tanımladığı en basit proje yapısını oluşturur. Bu projede sade bir POM, App adında bir Merhaba Dünya uygulaması ve bunun AppTest adında bir testi bulunur.

mvn install komutu simple projesi üzerinde İnşa Yaşam Döngüsünün install evresini çalıştırır. İnşa Yaşam Döngüsü bir projeyi inşa etmek için teker teker çalıştırılan(geçilen) bir dizi evredir(phase). Maven'ın işletebileceği birçok yaşam döngüsü vardır. Ancak en önemlisi projenin kontrolü, derlenmesi, paketlenmesi, yayınlamasını da içeren İnşa'dır. Eklenti hedefleri yaşam döngüsü hedeflerine bağlanabilir. Maven döngüde ilerlerken her evreye bağlanmış hedefleri sırayla çalıştırır. Her evrenin sıfır veya daha fazla hedefi olabilir. Örneğin bizim projemiz JAR çıktısı verdiği için package evresine jar:jar hedefi bağlanmıştır. Eğer WAR çıktısı verseydi war:war hedefi çalıştırılacaktı.



Maven'da bir evreyi çalıştırmak, belirtilen evre ve öncesindeki bütün evrelerin çalıştırılmasına neden olur. Örneğin mvn package komutu çalıştırıldığında Maven package ve öncesindeki her evre için resimde görülen hedefleri çalıştırır. Çalıştırılacak hedeflerin seçimi projenin ayarlarının belirtildiği POM dosyasındaki girdilere göre değişir.

maven-archetype-quickstart iskeleti projenin testlerini çalıştırmak için kullanılmak üzere JUnit-3.8.1 için bir bağımlılık tanımlar. mvn install komutunu çalıştırdığınızda Maven, bu bağımlılığı internetten indirip yerel deponuza JUnit'in koordinatlarını da göz önünde bulundurarak koyar. Junit-3.8.1 in koordinatları şöyledir: junit:junit:3.8.1. Eğer ~/.m2/repository/ altına bakarsanız, bu şekilde bir dizin yapısı oluşturulduğunu ve içine de junit-3.8.1.jar ın yerleştirildiğini görürsünüz: /junit/junit/3.8.1/junit-3.8.1.jar

Maven bağımlılıklar için jar'ın dışında bağımlılığın pom.xml dosyasını ve indirmenin doğru yapılıp yapılmadığını kontrol için SHA-1 dosyalarını da indirir. Bağımlılığın pom'u geçişli bağımlılıkları bulabilmek için indirilir.

18 Nisan 2011 Pazartesi

Maven3 (Proje yönetim aracı)-2: Türkçe Maven Rehberi

Sonatype'ın birkaç tane CC lisanslı Maven kitabı var. Maven konusundaki Türkçe kaynak sıkıntısını gidermek adına Maven: The Complete Reference kitabının giriş ve orta seviye için önemli gördüğüm yerlerini Türkçeye çevirerek bir rehber hazırladım.
Maven Rehberi
Faydalı olması dileğiyle...

3 Nisan 2011 Pazar

Maven3 (Proje yönetim aracı)-1: Giriş ve Kurulum

Merhabalar. Bu yazımızda piyasada çok kullanılan bir proje yönetim aracı olan Maven’ı işliyoruz.

Maven bir proje yönetim aracıdır. “Ben projelerimi zaten kendim (ya da IDE’m vasıtasıyla) yönetebiliyorum. Bunun için bir araca ne gerek var?”, demeyin. Bu yazı dizisi çaylak Javacılara hitap ettiği için muhtemelen projeleriniz Maven kullanmanızı gerektirecek büyüklükte değildir. Kurumsal projeler ise bazen o kadar büyük olabiliyor ki, Maven gibi araçlara ihtiyaç duyulabiliyor.

Maven ne yapar?

Maven en başta projeleriniz (tipine göre) için bir standart dizin yapısı tanımlar. Bu sayede, örneğin siz Netbeans’te çalışıyorken Netbeans’in oluşturduğu dizin yapısı ile arkadaşınız Eclipse’de çalışıyorken Eclipse’in oluşturduğu dizin yapısı veya bunların kullandığı özel ayarlar yüzünden projelerin taşınabilirliğinin olmamasının önüne geçer.

Kurumsal Java projeleri o kadar büyük olabilir ki, bazen 100’lerce 3. Parti kütüphane jar’ı kullanmanız gerekebilir. Bu jarları internetten birbirleriyle uygun sürümlerine göre indirip IDE’nizde kütüphaneleri koyduğu yere koymanız, sonra ayrı ayrı javadoc ve kaynak kodlarını ayarlamanız ve bunları proje üzerinde geliştirme yapan herkesin standart bir şekilde uygulaması angarya olur. Öyle ki projeyi geliştirmekten çok yönetimine kafa patlatırsınız. Maven buna da çözüm getirir. Maven’ın yaklaşık 200 GB büyüklüğünde çevrimiçi bir deposu vardır. Siz Maven’a projenizde hangi kütüphaneyi kullanmak istediğinizi söylersiniz, o da bu depodan jarları indirip sizin yerel deponuza kaydeder ve projenin classpath’ını bu jarları da dâhil edecek şekilde değiştirir.

Maven projenin yönetimi için standart bir yaşam döngüsü tanımlar: Temizle-Derle-Test Et-Çalıştır-Yayınla… gibi. Siz çeşitli eklentilerle (plugin) bu döngüye müdahale edebilir, projenizin gerekliliklerine göre değiştirebilirsiniz. Maven’ın çekirdeği aslında çok basittir. Çekirdek sadece gerekli jarları internetten nasıl indireceğini ve XML dosyalarıyla yapılan ayarların nasıl uygulanacağını bilir. Bu yüzden çok hafiftir (lightweight). Eklentilerle Maven’ı ayarlamak size kalır.

Maven projeler için kendi modelini tanımlar. Projenin ayar dosyasına girdiğiniz verileri (tanım, geliştiriciler, lisans, bağımlı olduğu diğer projeler v.s.) çok çeşitli işlerde kullanabilir. Bu açıklamaları kullanarak tek bir komutla projeye özel bir web sitesi bile oluşturabilir.

Kurulum

Maven 3’ü http://maven.apache.org/download.html adresinden indirebilirsiniz. Kurulumunu sadece Windows’a göre anlatacağım. Çünkü zaten diğer işletim sistemlerini kullananlar biraz sonra anlatacağım ayarları kendi sitemlerinde nasıl yapacakları biliyorlardır.

İndirdiğiniz sıkıştırılmış dosyayı C:\ dizinine açın. Bu dizinde şuna benzer bir klasör oluşmalı: C:\apache-maven-3.0.3

Bilgisayarıma sağ tıklayıp Özellikler’e tıklayın (ya da Denetim Masası’ndan Sistem’i açın). Gelişmiş sekmesinde Çevre değişkenlerine (environment variables) tıklayın. Sistem değişkenleri (system variables) altında Yeni’ye tıklayın. Değişken adına: M2_HOME , değerine de C:\apache-maven-3.0.3 girin, Tamam’a tıklayın.

Aynı pencerede tekrar Yeni’ye basın. Bu sefer değişken adına M2 değerine de %M2_HOME%\bin girin.

Yine aynı pencerede Path değişkenine tıklayıp, düzenle’yi seçin. Değer kısmının en sonunda ; yoksa ekleyin, sonra da %M2% ‘ yi ekleyin.

Yine ayrı pencerede JAVA_HOME değişkeni yoksa ekleyin ve değerine JDK’nın bulunduğu dizinin yolunu verin. Örneğin C:\Program Files\Java\jdk1.6.0_24

Sonra Path değişkenine üstteki gibi %JAVA_HOME% ‘yi ekleyin.

Windows+R tuşlarına basıp cmd ‘yi çalıştırın. Komut satırından mvn –v ‘yi çalıştırarak kurulumunuzu sınayın.

Kurulum Ayrıntıları

Maven’ın yalın 3.0.3 sürümü yaklaşık 2.9 MB boyutunda. Bu kadar küçük olmasının nedeni, gerektiğinde ihtiyacı olduğu eklentileri kendi deposundan indirebiliyor olmasıdır.

Maven kurulum dizini şu dosya ve klasörleri içerir:

LICENSE.txt 
NOTICE.txt 
README.txt 
bin/ 
boot/ 
conf/ 
lib/ 

Bunlardan, bin/ içinde Maven’ı çalıştıran mvn komut kümesi (batch)’i barındırır. boot/ içinde Maven’ın çalıştırıldığı bir Sınıf Yükleyici (Class Loader)’yi oluşturan plexus-classworlds-2.4.jar dosyası vardır. conf/ klasöründe Maven’ın global ayarlarını bulunduran settings.xml’i barındırır. lib/ içinde de Maven çekirdeğini oluşturan maven-core-3.0.3.jar dosyası bulunur.

Yerel Maven Deposu ve Ayarları

Maven’ı projelerinizde ilk kullanmaya başladığınızda, işletim sisteminizin kullanıcı dizininde (Win XP: C:\Documents and Settings\Foobar\.m2 ya da Linux: /home/foobar ya da genel adıyla ~/) .m2 adında bir klasör oluşturur. Bu klasör sizin yerel ayarlarınızın ve kütüphane (jar) deponuzun (repository) bulunduğu dizindir. Bu dizinde iki tane girdi olacaktır:

~/.m2/settings.xml : Kullanıcıya özel Maven ayarlarının bulunduğu dosya.

~/.m2/repository: Yerel Maven deposu. İnternetten indirilen jarlar buraya konulur.

Eğer Maven’ın ayarlarını değiştirmek isterseniz ~/.m2/settings.xml dosyasında değişiklik yapmanız daha doğru olacaktır.


Devamı gelecek…