2015年1月26日

Gradle(9)- 品質保證


我們需要利用一些工具來保持程式的穩定,好的程式是容易維護、花費的成本比較低。本章介紹一些工具可以搭配Gralde來達到公司的軟體政策並制定一些標準。例如Checkstyle, JDepend, PMD, FindBugs, CodeNarc, and Sonar。




(1) Checkstyle plugin




在Gradle腳本可以載入checkstyle,當它執行build任務時,check 任務也會執行,

2015年1月25日

Gradle(8) 最後一哩路-發佈版本

通常在Java專案會把程式打包為JAR、ZIP檔或是其他檔案,在Gradle中打包程式任務為build<configurationName>且上傳的任務為upload<configurationName>。我們定義

(1) 版本庫上傳

  • jar檔上傳

apply plugin: 'java'
archivesBaseName = 'sample'
version = '1.0'

repositories {
    flatDir {
        name 'uploadRep'
        dirs   'upload'
    }
}

uploadArchives {
        repositories {
       
       //檔案上傳的設定環境為uploadRep
        add project.repositories.uploadRep
     
        //額外jar上傳目的檔
        flatDir {
            dirs 'C:\\libs'
        }
    }
}

  • Maven版本上傳

apply plugin: 'java'
apply plugin: 'maven'

archivesBaseName = 'sample'
group =  ‘com.sample’
version = '1.0'

uploadArchives {
     repositories {
         mavenDeployer {
            repository(‘C:\\libs’)
        
     }
}

(2)打包檔案
在通常我們開發網頁程式,會將JAVA專案打包成Web application Archive (WAR) or Enterprise Archive (EAR)。如果對Web開發不了解請您參考
Gossip@openhome
Gradle網頁架構預設

  • src/main/webapp/WEB-INF  環境配置檔(web.xml)
  • src/main/resources    資源架構
  • src/main/                   class架構
  • 打包WAR檔

Gradle任務War方法用來將Java專案打包成war檔,

war屬性或方法

說明

webInf 用來定義WEB-INF的內容,
範例:
webInf {
         //將resources裡的文件複製到WEB-INF
         from 'src/main/resources’
}
classpath 如果為jar或是zip檔會自動複製到WEB-INF/lib中,其他檔案就複製到WEB-INF/classes
範例:
classpath sourceSets.main.runtimeClasspath
classpath fileTree('libs')
webXml 用來定義web.xml
 

使用WAR pluging的預設環境來打包war檔,會比較開發。

apply plugin: 'war'
version = '1.0'

repositories {
      mavenCentral()
}

configurations {
     extraLibs
    }

dependencies {
      providedCompile 'javax.servlet:servlet-api:3.0'
      providedRuntime 'webcontainer:logging:1.0'
      extraLibs 'sample:lib:2.1'
}

war {
      classpath configuration.extraLibs
      webXml = file('src/main/webXml/web-dev.xml')
      baseName = 'gradle-webapp'
}

2015年1月24日

Gradle(7) 軟體品質把關-測試


開發程式最重要的事情就是測試我們的程式,Gradle支援JUnit和TestNG測試框架。我們開始學習如何自動化的測試程式。

(1)測試之道
在gradle預設測試的目錄為src/test/java/<test_package>,如果你要修改測試的目錄可以參考下列範例

sourceSets {
    test {
          java {
              srcDir 'testSrc'
           }
   }}


  • Junit

dependencies {
        testCompile group: 'junit', name: 'junit', version: '4.+'
       
}

$ gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
BUILD SUCCESSFUL

  • TestNG
dependencies {
       testCompile 'org.testng:testng:6.5.1'       
}

範例一 寫一個計算機,可以做四則運算(加、減、乘、除)並做測試

image

Gradle.build

apply plugin: 'java'

repositories{
    flatDir{
        dirs 'C:\\Program Files\\Apache Software Foundation\\Tomcat 7.0\\lib'
        
    }
    mavenCentral()

}

dependencies {
        compile group: 'org.springframework', name: 'servlet-api'
        compile group: 'org.springframework', name: 'jsp-api'
        testCompile group: 'junit', name: 'junit', version: '4.+'
       
}
   

計算機的檔案: src/main/java/org/gradle/main/Calculator.java
實作計算機的基本操作。

package org.gradle.main;

public class Calculator {
   
    public int sum(int a, int b){
        return a+b;
    }
   
   
    public int minus(int a, int b){
        return a-b ;
    }
   
    public int  multiply(int a, int b){
        return a*b ;
    }
   
    public int  divide(int a, int b){
        return a/b ;
    }
   
}

測試計算機 :src/main/java/org/gradle/test/CalculatorTest.java

package org.gradle.test;

import org.gradle.main.Calculator;
import org.junit.Assert;
import org.junit.Test;

public class CalculatorTest {
   
    @Test
    public void testSum(){
        final Calculator calculator = new Calculator() ;
        int sum = calculator.sum(1, 1) ;
        int expectSum = 2 ;
        Assert.assertEquals("1+1=",expectSum, sum);
       
    }
   
    @Test
    public void testMinus(){
        final Calculator calculator = new Calculator() ;
        int sum = calculator.minus(1, 1) ;
        int expectSum = 0 ;
        Assert.assertEquals("1-1=",expectSum, sum);
       
    }
   
    @Test
    public void testMultiply(){
        final Calculator calculator = new Calculator() ;
        int sum = calculator.multiply(5, 10) ;
        int expectSum = 50 ;
        Assert.assertEquals("5*10=",expectSum, sum);
       
    }
   
    @Test
    public void testDivide(){
        final Calculator calculator = new Calculator() ;
        int sum = calculator.divide(10, 5) ;
        int expectSum = 2 ;
        Assert.assertEquals("10/5=",expectSum, sum); 
    }


}

執行指令 : $ > gradle test

測試結果:
由測試報告通常放置在build/reports/tests裡面
image

Gradle(6) 相依管理-抓取程式版本



(2)定義相依規則
當我們定義好哪裡抓取的規則後,接下來我們會介紹實際抓取jar檔的動作規則設定,在Gralde中會把抓取的規則寫在dependencies{ }區塊中。

由下面的範例我們可以知道,dependencies區塊會定義抓取{組織}{檔名}{版本}規則讓它可以抓取,complie方法會比complie group更簡潔和方便。

apply plugin: 'java'

repositories {
       mavenCentral()
}

dependencies {
 compile group: 'org.springframework', name: 'spring-core', version: '3.1.1.RELEASE'
 
compile 'org.springframework:spring-core:3.1.1.RELEASE’
}

如果我們可以將jar檔的組織與版本可以使用變數定義,會讓我們開發專案時候,更容易維護gradle的腳本與彈性

ext{
   springVersion : ‘'3.1.1.RELEASE'’
   springGroup : ‘'org.springframework'’
}
dependencies {
 compile group: '$springGroup’ , name: 'spring-core', version: '$springVersion'
 

}

遞移相依(Transitive Dependency) wiki解說
當管理多個版本相依時,發生遞移相依時不需要下載可以設定為transitive = false

  • 排除相依性

dependencies {
    compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.6.4', transitive: false

}

  • 排除部分相依

dependencies {
       compile('org.slf4j:slf4j-simple:1.6.4') {
       exclude 'org.slf4j:slf4j-api'
       }
}

程式版本動態
我們可以設定一個版本的範圍,來抓取jar檔案的範圍,下列符號

符號

說明

[1.0, 2.0] 版本在1.0~2.0之間
[1.0, 2.0[ 版本在1.0~2.0之間,但是不等於2.0
[1.0, ) 版本大於1.0以上
(, 2.0] 小於或是等於2.0
2.+ 大於2.0以上

 

apply plugin: 'java'

repositories {
     mavenCentral()
}

dependencies {
     compile group: 'org.springframework', name: 'spring-core',
     version: '[3.0, 3.1['
}

 

 

 

 

 

Gradle(5) 相依管理-抓取版本庫設定

當我們開發程是會使用third-party或是函式庫,這些jar之間版本相依不同會造成錯誤,我們可以利用Gradle來管理這些jar,讓我們開發程式更容易管理參照不同版本的jar檔。

  • 相依環境設定

Maven和Ivy都有相依管理的機制,然而Gralde是使用Ivy API的相依管理的函式,但是也加入一些額外的功能。ralde


(1) 版本庫(Repositories)
版本庫裡面定義函式模組之間的路徑方式,Gradle可以去定義如何存取方式、版本等等,例如使用HTTP、SSH或是file system或是其他協定來存取程式。它定義很多的環境可以使用或是可依照你需求去定義環境。Gradle是繼承Maven或是Ivy的方式來管理相依性。

版本類型

方法

說明
Maven repository   使用Maven方式去存取遠端版本庫
Maven central repository mavenCentral() 使用Maven方式從Maven官方版本庫去抓取程式
Maven local repository mavenLocal() 使用Maven方式從自己本機端抓取版本庫,如果你先在本機定義Maven的版本庫
Ivy repository   使用Ivy方式去抓取本機端或是遠端
Flat directory repository flatDir() 直接抓取本機端或是遠端

 

案例一、從Maven官方抓取檔案

repositories {
         mavenLocal()
         mavenCentral()
}                 

案例二、自訂版本庫抓取的方式

repositories {
     maven {
           // Name is optional. If not set url property is used
           name = 'Main Maven repository'
           url = 'http://20.1.10.50/spring'
           artifactUrls ''http://20.1.10.51/spring'
           artifactUrls 'http://20.1.10.51/spring/snapshot-jar'

    } 
          mavenRepo(name: 'Snapshot repository', url: '20.1.10.50/spring')
}

說明: 本案例Maven版本庫和實際jar檔放置的位置不同,因為本例url只有定義metadata的描述檔,所以需要額外定義jar檔的位置artifactUrls 。


案例三、需要驗證才可以讀取版本庫

repositories {

    maven(name: 'Secured repository') {
           credentials {
                username = 'username'
                password = 'password'
    } 
           url = 'http://20.1.10.50/spring'
           artifactUrls ''http://20.1.10.51/spring'
           artifactUrls 'http://20.1.10.51/spring/snapshot-jar'
       }
}

或是定義gradle.properties參數檔

repositories {

    maven(name: 'Secured repository') {
           credentials {
              // $USER_HOME/.gradle/gradle.properties
               username = usernameSecuredRepo
               password = passwordSecuredRepo  
       }
 
           url = 'http://20.1.10.50/spring'
           artifactUrls ''http://20.1.10.51/spring'
           artifactUrls 'http://20.1.10.51/spring/snapshot-jar'
       }
}

案例四: 、自訂規則抓取,開發jsp需要本機的tomcat的servlet-jar和jsp-api檔,利用gradle來抓取jar檔。

需要用到flatDir的方法,它可以當作arguments或closure使用,下面是檔案命名的樣式,根據這些樣事才做讀取

• [artifact]-[version].[ext]
• [artifact]-[version]-[classifier].[ext]
• [artifact].[ext]
• [artifact]-[classifier].[ext]

apply plugin: 'java'

repositories{
    flatDir{
        dirs 'C:\\Program Files\\Apache Software Foundation\\Tomcat 7.0\\lib'
        
    }
}

dependencies {
        compile group: 'org.springframework', name: 'servlet-api'
        compile group: 'org.springframework', name: 'jsp-api'
}

image


2015年1月21日

Gradle(3)–– 檔案處理




我們在開發軟體時,我們時常需要對檔案和目錄作處理,例如我們需要複製檔案到另外一個目錄理或是需要移動某些檔案到其他目錄。常用的型態

type 說明
copy 複製
zip 壓縮




檔案操作

(1)檔案顯示: 我們使用gradle的file()它用來解析檔案的目錄和檔案,file()是使用java.io.File的物件來操作。
範例一、 txt目錄中有兩個檔案為a.txt和b.txt,可以顯示txt底下所有的檔案
File txt = file('txt/')
for(File f : txt.listFiles()){
    println(f.getName())
}
image
範例二、使用Url和Uri操作URL url = new URL('file: D:\\gradle\txt')
URI uri = new URI(‘file: D:\\gradle\txt’)

(2) 檔案樹(file tree): 我們利用fileTree()函式顯示目錄和ZIP的檔案樹狀結構,我們也可以利用pattern來做檔案的過濾。matching方法閉包用來自訂方法include、includes、exclude和excludes來自訂過濾的樣式

pattern :
  *  -> 符合任何字串樣式
  ?  –> 符合單一字串樣式
**  –> 符合任何檔案或是目錄的字串樣式

範例一、顯示txt目錄底下的java檔image
def treeFiles = fileTree(dir:'txt').matching{
                    include '**.java'
                }

treeFiles.visit{FileVisitDetails fileDetails ->
    if(fileDetails.directory){
        println "directory: ${fileDetails.relativePath}"
    }else{
        println("filename: ${fileDetails.name}")
    }
}

說明: 利用fileTree建構子指定目錄,利用matching的閉包(closure)過濾只有.java的檔案。
(3) copying files : 檔案操作需要複製檔案,使用Copy的閉包來操作檔案
方法:
from() :從哪裡複製檔案
to() : 複製到哪裡
include() : 包含條件
exclude: 排除條件

範例一、將txt目錄複製到copyTxt目錄裡面

task copyFile(type: Copy){
    from 'txt'
    into 'copyTxt'
}

#> gradle  copyFile
範例二、將txt目錄只有複製.java的檔案到copyTxt目錄裡面
task copyFile(type: Copy){
    from 'txt'
    into 'copyTxt'
    include '**.java,**.gif'
}

#> gradle  copyFile

(4) Renaming files : 更改檔案名稱


範例一、副檔名為txt會更改為text

task renameFile(type: Copy){
    from 'txt'
    rename{
        String fileName ->
        if(fileName.endsWith('txt')){
            String org = fileName - ".txt" + ".text"
        }
    }

    into 'renameTxt'
}
(4)打包操作(Archiving files) : 當很多檔案需要打包到Zip、Jar、War、Tar等等,我們使用Gradle作歸檔操作需要檔名的設定如[baseName]-[appendix]-[version]-[classifier]。
範例一、 把txt目錄檔案打包成ZIP
task archiveFiles(type: Zip){
    from 'txt'
    into 'zip'
   
    // Create output filename.
    baseName = 'txtFile'
    appendix = 'archive'
    extension = 'zip'
    version = '1.0'
    classifier = ''
}
說明: 打包為txtFile-archive-1.0.zip
範例二: 打包為tar的檔案,利用GZIP的方式壓縮
task archiveTar(type: Tar){
    from 'txt'
    into 'tar'
   
    // Create output filename.
    baseName = 'txtFile'
    appendix = 'archive'
    extension = 'tar.gz'
    version = '1.0'
    classifier = ''
    compression = Compression.GZIP // or Compression.BZIP2
}

2015年1月18日

Gradle(2)–– 專案與任務



前言

在Gradle中,專案和任務是重要基本慨念,專案(projects)可以說是一個目的或是專案,例如: 開發一個函式庫、網頁應用程式或是複製某個檔案到目錄中等等。任務(task)可以是專案的最小單位元,也就是專案可以由很多任務所完成,任務區塊中有很多動作(action)所組成,這些動作可以自行定義。

Project 屬性


屬性 說明
name 唯讀,需要透settings.gradle設定
parent 唯讀
version 專案版本
description 專案說明

任務


(1)基本

我們由上面可以知道任務是構成專案的主要元素,我們開始示範一個簡單的範例,來做解說,在gradle專案裡面預設專案檔名為build.gradle,寫完建構腳本後,需要執行建構指令,在執行指令時,只要輸入first任務就會執行腳本內定義的任務並執行裡面的動作。如果當你儲存成不同檔名需要加上 --build-file的參數來指定檔名。

預設執行檔名: build.gradle

project.description = 'first project'
task first << {
    println 'Running first task for project ' + project.description
}

程式說明 : 屬性為 description是用來說明這個專案的註解,print函式是顯示console的訊息
image


這些動作是比較常用到的方法:
動作 說明
doFirst 會被先執行
doLast 會最後直行
<< 附加元素
defaultTasks
範例一、doLast和doFirst範例
// defaultTasks用來定義將會被執行的任務,只有first區塊會被執行
// 會先執行doFirst,在執行doLast

defaultTasks "first"
version= "1.0"
description = " test "
task first {
    doLast{
        print "1 last execute "
    }
    doFirst{
        print "1 first execute "
    }
   
}
task secound {
    doLast{
        print "2 last execute "
    }
   
    doFirst{
        print "2 first execute "
    }
   
}

結果: 1 first execute 1 last execute



(2) 任務的寫法差異

project.description = 'first project'
task first  {
    doFirst{
        println 'Running first task for project '
    }
}
task second  {
    doLast { Task task ->
        println 'test '+${task.name} +' for project'
    }
}
task third << {
    th_task ->
        println 'test'+th_task.name  +' for project'
   
}
任務first的寫法是可以定義動作(action)內容,使用doFirst為動作串列的開始資料。
任務second把宣告task物件當做方法來使用
任務third會使用"<<"符號,首先說明符號意義,它會將動作區塊附加到third任務的後面。

(3)任務順序
當任務有相關係時,例如Task1 需要先執行,才能做Task2
Task1 –> Task2

檔名 : dependTask.gradle
說明: 在Gradle語言中可以使用dependsOn方法定義任務間的相依,Gradle是使用lazy的載入方式,也就是我們可以把相依的任務定義在後面。
image
範例二、第一個任務需要先執行第二個才會被執行,利用dependsOn
task first {
    doLast{
        print "1 last execute "
    }
    doFirst{
        print "1 first execute "
    }
}
task secound {
    doLast{
        print "2 last execute "
    }
    doFirst{
        print "2 first execute "
    }
}

first.dependsOn secound

結果: :secound
2 first execute 2 last execute :first
1 first execute 1 last execut
範例三、
task first {
    doLast{
        print "task 1 last execute "
    }
   
    doFirst{
        print "task 1 first execute "
    }
   
}
task secound {
    doLast{
        print "task 2 last execute "
    }
   
    doFirst{
        print "task 2 first execute "
    }
   
}
task third {
    doLast{
        print "task 3 last execute "
    }
}
task tool {
    println "loading a tool! "
}
first.dependsOn secound
secound.dependsOn third
third.mustRunAfter tool
結果
loading a tool!
:third
task 3 last execute :secound
task 2 first execute task 2 last execute :first
task 1 first execute task 1 last execute


(4) 設定預設任務(setting default task)
我們可以預設腳本執行的任務順序,方便我們規劃執行的方式

defaultTasks 'first', 'second'
task first {
    doLast{
        print "It is 1 "
    }
}
task second {
    print "It is 2 "
}
說明 : defaultTasks 是用來使用預設任務的執行。
image