2018年12月11日 星期二

Gradle dependency management

大綱:
緣起
介紹 dependency management
介紹 gradle 與 ant maven 的差別
如何安裝 gradle (如何在 eclipse 安裝 gradle)
簡易介紹 gradle 怎麼使用
附上已經建好的 build.gradle, settings.gradle
說明因為現有的專案架構,還是得需要手動搬檔的部分 (不然可以直接打包成 ear 檔,ear 黨可以讓佈署變得更輕鬆,但 tomcat 基本上不是一個 J2EE server, 所以無法)

後續待完成:
Repository server (nexus)
把 xxxx-util 整個真正的轉換成都寫在 build.gradle 的設定
Write a task for copy xxxx-util ‘s lib to local tomcat server lib folder.

----------------正文開始處---------------
緣起:
為了減少每次新建開發環境的時候,總是要手動針對每個 project  設定 lib 的 build path,除了設定繁瑣外,每次若有需要測試其他版本的 lib 時還需要去下載 lib 且手動丟到相關位置,測完了之後可能會忘記改回原本的版本。如果能使用設定檔除了能夠好維護之外,使用設定讓其自動下載需要的 lib ,還可以將設定使用 SVN 等版控控管,每次版本同步的時候就會知道是否有人使用了新的 lib。

之所以選擇使用 gradle 的原因是因為它可以跟 Ant 有比較好的相容性。可直接套用在現有的專案結構目錄,只要把 gradle 的設定檔放進去專案根目錄之後,再將現有的舊目錄結構設定進去即可。而如果是使用 Maven 的話,雖然 xml 的設定型式好像比較有結構且可以使用 UI 工具清楚管理 lib 的來源,但好像跟 Ant 不太相容,而且用 Maven 的話一定要照它的 convention,可能無法讓現有專案保持原本的結構,而且現在很多的開發也都漸漸趨向使用 gradle 了 (ex. Android Studio ) 所以決定使用 gradle 來試。

如何安裝 gradle:
  1. 到 eclipse marketplace 下載 gradle buildship plugin,或是要手動從 gradle 官網上面下載下來安裝也可以,但沒裝在 eclipse 的話就是要去 cmd 下命令執行,比較麻煩但能夠確保執行方式不會受到萬一 plugin 有 bug 的影響,建議兩邊都可以同時使用看看。build-ship-plugin.png
  2. 直接在原有專案的根目錄下加上 build.gradle, settings.gradle 這兩支檔案,settings.gradle 有時候可以不用加,加不加的原因是要看這個 project 有沒有需要用到 sub project, 如果有才需要加。
  3. 加好了後再對專案按右鍵 : Configure/Add Gradle Nature (只有第一次加設定的時候需要做這步)
  4. 之後每次有更新 build.gradle 之後需要再按右鍵 Gradle/Refresh Gradle Project 讓 eclipse 更新相對應的 project
  5. 替每個專案設定 lib 的相依性,第一步要先指定 lib 的來源 repository, 通常會使用公用的 maven repository 及要給 gradle 使用的 plugin。第二步要在 build.gradle 的 dependency scope 裡面設定相依 lib 的使用時機 (compile, compileOnly, testCompile …)
    build.gradle 範例:
apply plugin: 'java' // 告訴 eclipse 這個 project 是要用 java (JVM) 來做編譯
apply plugin: 'war' // 若此 Project 是 web project 且最後需要被打包成 war 時要用這個 plugin
apply plugin: 'eclipse-wtp' // 表示相關的設定會一併更新到 eclipse 的 wtp 工具(面板)裡,就是在這邊寫好設定之後,打開 eclipse 的 porject properties 一樣可以看到對應的設定,但從 UI 畫面手動設定的就不會更新到 build.gradle 這個設定裡了,所以最好統一在同一處設定才不會造成混淆

repositories {
  mavenCentral()
}

dependencies {
  // testCompile 表示只有在跑 test case phase 的時候會使用到這個 lib
  testCompile 'junit:junit:4.12'
  
  // compile 表示打包這個專案時,會把下面這個 lib 也打包進去
  compile 'org.apache.commons:commons-lang3:3.1'
  
  // compileOnly 表示只有在 eclipse 開發時會使用到這個 lib, 打包的時候不會打包進來現在的 project,而 compileOnly 通常也意指此 lib 在 runtime 的時候會由 server 或是其他來源提供

   // 下面這些是說此專案需要引用其他 project 的東西,需要搭配 settings.gradle 的來使用
   compileOnly project(':xxxx-util')
   compileOnly project(':xxl-util')
   compileOnly project(':xxl-xxcs-commons')
}

  1. 因為會有一個 project 引用另外一個 project 開發的東西,所以要引用其他專案的時候要另外增加一支 settings.gradle:
/*
// To declare projects as part of a multi-project build use the 'include' method
include 'shared'
include 'api'
include 'services:webservice'
*/
// 此以 xxl- project 為例,宣告會使用到下列其他專案
include 'xxxx-util'
include 'xxl-xxcs-commons'
include 'xxl-util'

project(':xxl-xxcs-commons').projectDir = new File(settingsDir, '../xxl-xxcs-commons')
project(':xxxx-util').projectDir = new File(settingsDir, '../xxxx-util')
project(':xxl-util').projectDir = new File(settingsDir, '../xxl-util')


rootProject.name = 'xxl-'
  1. 因為現有的專案架構設計成需要把 lib 丟到 tomcat lib 的原因,這些人為的設定在 build system 的 plug-in 似乎不在可以控制的範圍 QQ

2017年10月26日 星期四

關於 thread...

寫 java 這麼久了,其實直到這次遇到這個狀況我才真的比較搞得清楚所謂 multithread 是怎麼一回事,以前都只是記得觀念,就是一個 process 可以切分成多個 thread 讓他們同時去跑,但到底哪一個 thread 何時會先執行完畢是無法確定的,反正它們會看起來像是同時在執行的樣子。

原因來自於我要解決一個專案裡面產檔跟清檔的功能,在某個 Java Class 裡面的幾個 method 裡,每次執行開始都要產檔(一堆檔),結束之後需要把檔案全部清掉,每個 method 都要這麼做。

但原本產出來的檔名都一樣,怕萬一某個 method 執行完畢要刪在還沒刪除完之前,下個 method 就起來了,可能會造成檔案資料夾下的檔案會有不完整殘留的問題,然後另外一個同事又提出萬一使用者(它們會使用我們開發出去的這份 class)使用 multi thread 來呼叫我們的 class 時可能會有問題。因為一開始我也不太懂所以就想說那用系統時間或是亂數產生資料夾名稱然後再把產生的檔案們都丟進去就好,結束後在一個個把它們清掉。還在比較 java util 的 Random 跟 Math.random 哪個比較好用且適用時,我家小幫手就突然丟給我一個建議說可以用類似 Thread.getId 這個東東,我想說咦? 好像可以喔,但其實根本沒想過啊我的 class 明明就沒有實作 Runnable 壓,使用一般的呼叫方法 new 起來之後,每個 thread 的 id 都會一樣呀 (都是1, 都是主 main thread)。

後來才發現原來要有不同的 thread id 是要將我們執行的 class 放在 new Thread 裡面,所以要馬就是放進去 thread 的那支 class 需要實作 Runnable,然後這樣呼叫 thread.start() (不是呼叫 thread.run() 哦,一開始我還用錯 = =)

eg.
public final class Mpva2 implements Runnable {
    public void exec() {
        try {
                  Thread.sleep((int) (Math.random() * 1000));
            } catch (InterruptedException e) {
                   e.printStackTrace();

            }
    }

      @Override
      public void run() {
            // TODO Auto-generated method stub
            System.out.println("Thread id:" +           Thread.currentThread().getId());
            exec();
      }

      
}


另外一個 class 用 Thread來呼叫執行 Mpva2:

Thread th1 = new Thread(new Mpva2("1"));
            th1.start();

Thread th2 = new Thread(new Mpva2("2"));
            th2.start();


執行的結果就會有不同的 thread id:
Thread id:10
Thread id:11

後來我覺得這件事情大概連我同事也不是很清楚,第一是,我們這個 class 並沒有實作 Runnable,所以照理說別人應該也無法透過 multi thread 來叫用,除非他們在外面再包一層有實作 Runnable 的 class 之類。


一開始實作的時候,我用來命名的檔名 System.currentTimeMillis() 還先用成 static final, 感覺這樣好像比較不會一值浪費資源去取得這個值,可是後來發現如果 class 被 new 的時機很近,也會很容易碰到相同系統時間的狀況,因為現在的 cpu 速度都很快壓,連續 new 兩個同樣的 class instance 系統時間也還是很容易一樣無法分辨。

除此之外,在產檔前也需要先檢查是否檔案已存在,若存在要做適當的處理,看是要刪還是要丟例外跟使用者說,總之是很基本但重要的細節。

題外:不禁想到我昨天上瑜珈課 (嗚嗚~超愛的艾楊格瑜珈老師為何才上了三堂就要走了) 老師說,你們做動作的時候要有覺知,有覺知就是要知道你們在做什麼!如果讓頭腦的速度超過身體,身體就會受傷,所以做的時候要等身體可以了才繼續,不要老是跟著頭腦阿,我覺得常常我做事的時候是很無意識的...



很多不錯的參考:
Difference between a process and a thread in java
如何在 thread pool 取得 thred id?
Program / Process / Thread 的差別

2017年3月7日 星期二

如何計算可用的 IP 數量

最近在工作上遇到一個有趣的小問題,我們要計算在某一個網段內可以被分派的 IP 數量有多少,其實這個函式原本已經有先前的工程師寫了,但因為寫得不是很彈性,也不是以網路IP address的本質去思考來計算的,所以在空檔的時候花了些時間去研究彌補以前沒有好好搞懂的IP address.

以IPv4為主,基本的表示方法是以4個Byte(32bits)來表示每個IP的位址,而通常在每個網段下會有一個網路Id位址跟廣播IP位址是不能夠被一般主機使用的。 一個網域的大小(即可發給電腦的IP數量的範圍)是由subnet mask 來決定的。例如一般最常見到的class C就是最後面的1個Byte全部都是0,而前3個Byte都是1的情況來做 filter(其實就是mask),在這樣的情況下,每一個class C的網段就會有2^8=256個IP 位址,但剛剛說了第一個通常會是給此網域當網路Id,最後一個位址會當廣播用的IP,所以扣掉這兩個位址,可用的就只剩下265-2=254個,這是一般 class C的規則。IP數的範圍取決於一開始subnet設定的大小。

那要怎麼用這些已知的資訊來計算出到底可以用的IP數有多少個呢?這時候還需要再回想關於位元運算的問題,也就是AND, OR這種的位元運算。
通常AND可以想成是「乘法」,而OR可以想成類似「加法」:

AND10
110
000

OR10
111
010

還有利用一個 「~」反向的運算符號就可以算出在某一個範圍內可以被使用的IP數量有多少個,code 在這裡: https://github.com/evelynbaby15/MyWork/blob/master/CountIP.java

另外最近也發現一個不錯的電腦數值系統的基礎教學網站 (Ryan's tutorials)

2014年12月8日 星期一

寫 java 時,該有的良好習慣

以前的自己其實不是一個很會寫程式的人。但有機會能夠每天參考厲害前輩們寫過的程式,慢慢的邊看邊學,進而能夠因此考到 SCJP,真的要感謝很多人也很開心。

以下列出一些自己在學習過程中,好的老師曾經提醒過的重點:
  1. 在 eclipse 畫面中,盡量把 method 行數限至在 50 行以內,能夠一個畫面就看完這整個 method 在做什麼,太長的 method 盡量將其整理分開成更小的單元,盡量讓一個 method 只專注在一件事,例如它是控制流程、或是處理資料細節等。
  2. 針對每個 public method 寫下簡潔易懂的註解說明,這對於維護的人跟使用的人都相當重要。
  3. 善用 checkStyle、findBugs 、codeCoverage 這些幫助讓 code 更好的工具,不符合就修到好,盡量修到好,愛用 ctl+shift+f 排版,愛用 ctl+shift+o 整理 import。
  4. 良好的變數命名習慣,寧願使用較長的變數名稱也不要使用短到讓人不知道是在做什麼的變數名稱,目前我們的習慣是用 lower camel case。
  5. 在 class 中,若 method 沒用到 member,可以將 method 標成 static。變數可以的話也標記 final。
  6. log 分層原則,記錄 vo 值的盡量記成 trace ,重要的才記成 info 或 debug,以免現場記錄太多不必要的資訊。
  7. Synchronized 因為即使使用單元測試也很難測,所以要記錄 log 以便在實際環境執行時觀察在 multi threads 下是否有正確運作。讀 cache 或清 cache 也盡量記一行有被呼叫到的 log。


2014年11月14日 星期五

設計一個簡單的檔案清理模組

我們 team 開發了多個專案,這些專案每天在現場環境都會因為功能被使用而記錄了很多 log ,長期下來現場系統組給的空間就會慢慢的不夠,然後就會被念...= =,可是用人工定期去檢查這個問題再手動清檔又是一件很蠢的事,為了要能夠保持空間不被過多的 log 占用,於是我就被給了這個任務來處理這個問題。

(當時心想,哇~我真的可以開始設計模組了嗎?...真是不可思議 XD)

第一步就是先將自己所有能夠想到的解法都寫下來,然後對其做優缺點分析 (就像 SWOP 那樣,但沒那麼麻煩就是),接著請教前輩看看這樣子方法是不是可行,再把想法畫出來然後就開始動手實作,最後做出來也將它順利的套上各個專案了,真的滿開心的!

 看大圖
看大圖

手動 install local lib to local .m2

最近在試著更新我們某個專案使用的第三方 lib,但卻發現在網路上找到的最新版 lib 卻一直無法更新到內部的 repository 上,原因是因為對方(官方)並沒有把最新的釋放出來 (放到官方 repository 上),所以就試著手動將自己下載下來的 lib 安裝進 maven 的目錄,這樣我們專案的 maven 才有辦法抓對我需要的測試 lib。

步驟:
1. 先下載 maven 的手動工具 (不是附在 eclipse 的 plugin) http://maven.apache.org/download.cgi

2. 下載後要做的就是設定 maven 的家 (home) 等相關環境變數。
確定 maven 已經可以在 windows console 下面使用了 --->  mvn --version。

3. 之後則下下列指令,在這邊示範的是要裝新版的 smartgwt-skins.jar
mvn install:install-file -Dfile=C:\Users\evelyn\Downloads\smartgwt-skins.jar -DgroupId=com.smartgwt -DartifactId=smartgwt-skins -Dversion=4.1 -Dpackaging=jar

4. 看到 build success 之後可以去 .m2 下看看應該已經裝好了。

5. 再到 eclipse update pom 就可以使用了。

參考的指令:
mvn install:install-file
-Dfile=<path-to-file>-DgroupId=<group-id>-DartifactId=<artifact-id>-Dversion=<version>-Dpackaging=<packaging>-DgeneratePom=true

Where: <path-to-file>  the path to the file to load
   <group-id>      the group that the file should be registered under
   <artifact-id>   the artifact name for the file
   <version>       the version of the file
   <packaging>     the packaging of the file e.g. jar

JUnit unrooted problem

常常發生的一個怪現象,就是我要 run 自己本身的 tesecase 時,eclipse 卻跟我說找不到自己本身要跑的這個 class (unrooted ...Compilation failure), 原因可能是因為之前有對本身這個專案做了 maven-clean,
但是 JUnit 的 plug-in 沒有再重新產生出這個 tesecase 的 byte code(<---吳先生解釋)
所以要再執行一次  maven: test-compile 這個指令,之後再 run tesecase 即可。


如果還是一直出現錯誤,就將 project/build automatically 先關掉,再 clean 一次 project 試試看。


結論就是:
try to clean & rebuild your project.