2012年12月27日 星期四

2012年12月26日 星期三

浮點數比較

應該很多人都知道,電腦用二進位的方式在表達浮點數時,時常會有誤差值的存在
例如表達數字 100,如果用浮點數表達時,實際上電腦表達的數字,有可能是 100.0000000001 或者 99.999999999 之類的數字。

2012年12月19日 星期三

回復從 eclipse 上刪除的檔案

今天手殘把程式碼刪掉,用檔案救援軟體掃了半天也掃不到被刪掉的檔案
後來去 Google 發現了 [1],好不容易把檔案弄回來了!
看來 eclipse 上面的刪除,可能不是真的把檔案刪掉,也許是把檔案藏到不知道哪裡去吧..

回復的方法:
在專案上點右鍵 → 選擇「Restore from Local History...」 → 選要回復的檔案。


參考資料:
1、How to recover a deleted file in Eclipse

[轉錄] Java程式設計中“為了性能”需做的26件事

轉錄文~有些大家都在說的事情,還有可以想像會有問題的事情,但也有些不太直覺的,也許可以找個時間真的做一下測試看看。

2012-12-24 補充:
針對第 13 點,也許可以參考看看 [2],好像有些人說會有差,也有些人說 JVM 自己會解決差異。
我自己在我的電腦上做的測試,感覺上好像是差不多....但也許是我測試的方法不對?

(書籤) 動態載入 Class

參考資料:
  1. Java Gossip: 簡介 Class 
  2. 實在不是很喜歡Java載入Class的方法 
  3. Java 鬆綁 (一):利用多型鬆綁類別的依賴關係
  4. Java 鬆綁 (二):利用 Package 與動態載入徹底鬆綁
  5. Java 鬆綁 (三):利用 ServiceLoader 實作動態載入

2012年12月10日 星期一

[筆記] JDK 效率比較:Collection

在看 [1] 這本書時,覺得有些東西應該先寫下來
並且書上的比較方法我覺得還可以換點不同的圖表做比較~
所以花了些時間把書上的統計數據抄錄出來,用 Excel 畫了別的圖。

2012年12月6日 星期四

final 變數的垃圾回收

今天被問到 final 變數被回收的時機,我的想法是應該跟一般變數是一樣的
不過還是去查了一下,從 [1-3] 的討論看來,我的想法應該是沒錯。

2012年11月27日 星期二

Java 數字溢位時的處理

做個小記錄~
目前打算模仿 ConcurrentHashMap 在實作 OCC 的方法,去做出不需要 Exclusive Lock 的 Concurrent Control
所以會需要一直對某個數字做遞增~
但是不太確定 JVM 對於數字遞增到最大值以後到底會如何處理,因此稍微查了一下。

2012年11月13日 星期二

ImageIO 輸出圖檔時的顏色問題

今天發現一個 BUG,當圖片用 PNG 檔時,用程式產生的縮圖顏色會很詭異
經過了一些簡單的測試,初步發現顏色有問題的都是背景有大量白色的圖片
後來就找到 [1] 的討論,其中回應是說因為 JPEG 本身沒有透明度設定
因此當遇到圖片是非 JPEG,但輸出檔案時卻指定為 JPEG,這時產生的檔案就會有問題。

要解決這個問題,一種是用 ImageIO 輸出時不要改變輸出的檔案類型,沒有轉檔就不會有圖片不相容的問題 XD
另一種就是參考 [1] 討論中的各種方法,基本上好像概念都是對透明的東西全部轉換成白色或者某個指定的顏色。

參考資料:
1、Converting transparent gif / png to jpeg using java

2012年11月8日 星期四

提昇 MongoDB 的讀取效能

在我們專案裡面有個 MongoDB 的資料庫,在測試時發現裡面的 19 萬筆資料全倒出來時
花費的時間相當可觀,平均大約是 10~15 秒左右!
但 19 萬筆資料並不算是很龐大的資料,理論上不應該出現這種結果
因此花了些時間查詢有沒有方法可以提昇效率~。

邊查邊測了大概兩天,其實沒查到什麼方法
倒是有發現如果直接在 MongoDB 的 shell 上面輸入同樣的語法時,shell 只要花不到 3 秒就可以取完所有的 document。

從 Thread 接受回傳值:Callable

一般使用 Thread 都會使用 Runnable,但是 Runnable 預設要實作的 run() 回傳值是 void,並且 run 也無法回應 Exception
當外邊有 Thread 需要等其他 Thread 執行完畢後的結果時,常常只能另外寫一個 get() 函式去判斷並取值。
不過在 Java 1.5.0 版開始,額外增加了 Callable,可以允許 Thread 回傳值了。

基本的範例可以直接參考 [1] 的範例~
簡單來說,在宣告 Callable 時,就要先宣告回傳值,然後用 Future 去綁對應的 Callable
最後把 Future 餵給 Thread 去執行~就可以透過 Future.get() 函式取得 Callable 執行完畢後的回傳值。

2012年11月5日 星期一

Thread.join()

之前搞不太清楚 Thread 裡面的 join() 函式的用途,最近終於搞清楚了~
一般來說因為 Thread 是非同步的執行,因此當新的 Thread 被產生出來之後,跟原本的 Thread 的執行順序就不保證了
但有時我們還是會想要等某個 Thread 執行完畢以後,現在的 Thread 再繼續執行
這時就可以利用 join() 函式去等待。

2012年11月1日 星期四

測試 MongoDB 的效能

設定了一個情境,假設我有 100 萬個檔名不同的檔案,隨機放在以下八個資料夾

/user_c/
/user_c/application/
/user_a/log/user_c/
/user_a/log/webserver/
/user_b/
/user_b/application/
/user_b/log/
/user_b/log/webserver/

那麼當我想要知道 /user_c/ 這個資料夾有哪些檔案和資料夾時,MongoDB 可以多快幫我找出來?

用 Java 存取 MongoDB

要透過 Java 連接、插入和讀取 MongoDB,方法其實在官方的文件 [1] 中寫得蠻清楚的~
以下直接轉貼官方的範例。

連接 MongoDB
Mongo m = new Mongo();
DB db = m.getDB( "mydb" );
DBCollection coll = db.getCollection("testCollection");
在產生 Mongo 的實體時,如果沒有輸入任何參數,會自動使用 MongoDB 預設的 localhost:27017 來連接。
有需要的時候可以自行指定位址和連接埠,另外也可以用 ServerAddress 的方式來表達多個 MongoDB 的位址(用於 sharding)。

初始化 Mongo 的實體之後,可以使用 getDB() 來指定要使用的資料庫名稱,然後再使用 getCollection() 選定目前要存取的集合名稱。

2012年10月30日 星期二

在 Windows 初次安裝、使用 MongoDB

這篇是簡易記錄文~記錄一下在 Windows 上安裝和小小測試 MongoDB 的過程。

MongoDB 的安裝和使用方法,在官方網站的文件上其實有說蠻多的,可以參考 [1-2]。

在 Windows 上安裝 MongoDB

在下載好 MongoDB 之後,它會是個壓縮檔,解壓縮之後放在某個自己想放的位置
以官網的教學建議是放在 C:\mongodb,不過放在哪都無所謂~自己方便就好。

PS. 為了方便起見,後面都會以 C:\mongodb 當做是 MongoDB 的所在路徑。

接著要先建立 MongoDB 放資料庫的位置,預設會放在 C:\data\db 這個目錄
因此要用檔案總管或其他方式,把這個目錄建立起來,否則啟動 MongoDB 時它會告知找不到路徑而自行關閉。
如果不想要放在這個目錄,要用以下指令去更改路徑,這裡直接用官網的範例,即要改到 D:\test\mongodb\data:
C:\mongodb\bin\mongod.exe --dbpath d:\test\mongodb\data
如果路徑包含空白,例如路徑是「D:\test\mongo db data」的話,可以用雙引號把完整路徑包起來,例如:
C:\mongodb\bin\mongod.exe --dbpath "d:\test\mongo db data"
基本上這樣就完成 MongoDB 的安裝了(咦?!)
嚴格來說它根本也沒什麼好安裝的,就是壓縮檔抓下來解壓縮就好 XD。

2012年10月29日 星期一

MongoDB Concurrency

MongoDB 的 concurrency 的資料,感覺好像比較難查....

Global Write Lock / Yield Lock (Locking-With-Yield)

首先是關於在 MongoDB v2.0 以前(含 2.0)的版本,MongoDB 具有兩個特性:global write lock 和 yielding lock。
詳情可以參考看看 [2-4],以下說明大略是出自 [2] 的說法~。

2012年10月26日 星期五

用 Java 產生高品質縮圖的方法

使用原生的 Graphic2D 的函式庫要改變圖片大小不難,不過要產生高品質的縮圖就稍微複雜一點,原因在 [2] 中有描述。
[2] 提到在做圖片的放大縮小時,選擇 RenderingHints 是一件很重要的事情(這是必然的 XD)
當目的是要放大圖時,選項比較簡單:
  • 求快:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
  • 求高品質:RenderingHints.VALUE_INTERPOLATION_BILINEAR
  • 求更高品質:RenderingHints.VALUE_INTERPOLATION_BICUBIC
但如果是縮小圖片時,狀況就稍微複雜一點~

在 Java 中讀取圖片和寫入圖片

稍微看了一下 Java 的圖片相關的資料,在 Java 中是以 java.awt.Image 這個 abstract class 來表達圖片
實際實作圖片的包括 BufferedImage 和 VolatileImage。
一般要把圖片讀取到記憶體中,或者是要把記憶體中的圖片寫入到檔案,都可以透過 ImageIO 來進行。

2012年10月25日 星期四

SQLite Concurrency

SQLite 本身自稱在 concurrency 方面只有有限度的支援
有關 SQLite 如何處理 Lock 和 Concurrency,可以參考 SQLite 官方文件 [1]。
大體上可以理解為 SQLite 的上鎖機制很像是 Java 的 ReadWriteLock
亦即對同一個 SQLite 資料庫而言,允許多個程序同時讀取,但最多只允許一個程序進行寫入
寫入期間就是使用一般的 exclusive lock 將整個資料庫鎖住。
這大概也就是為什麼 SQLite 官方說他們只支援 1 transaction 吧。

2012年10月24日 星期三

HashMap 的特徵

筆記一下 HashMap 的特徵,參考 [1] 的原理說明~
HashMap 會建立一個容器去存放 key 和 value,儲存時是利用 key 的 hashCode(繼承自 Object)去計算位置
在建立容器時,會設定一個指定的長度,長度是根據 HashMap 的 loadFactor 來決定的

2012年10月22日 星期一

字典:Exclusive Lock 與 Optimistic Concurrency Control

Exclusive Lock:當程序要存取某個資源時,先將資源上鎖(禁止除了自己以外的程序存取這個資源),等到自己處理完畢後,再將鎖釋放。

Optimistic Concurrency Control:當程序要存取某個資源時,先假設不會產生衝突,直接進行資料處理。等到資料處理完要寫回資源時,去檢查從讀取資源到現在,中間是否有其他程序曾經修改過這個資源,沒有的話就正常地寫入並結束,但若有的話就放棄所有處理的結果,全部從頭開始。

字典:volatile 關鍵字

原本想找一些比較正式的資料,不過目前還沒有在手邊的書上看到有關這個的資料~。
就 [1-2] 描述有關 volatile 關鍵字,主要是說一般變數宣告的狀況,會在 JVM 當中建立一個專用的 Register 暫存區
存取記憶體時,程序會去該暫存區取得變數資料、對其做修改等動作
但修改的過程對其他程序來說是個黑洞,它們無法「看到」動作的內容,甚至不知道該變數正在被修改。

目前用過最流暢的 Desire Z ROM:LightSense 1.2(Android 2.3.3 + Sense 1.9)

我家有兩支 Desire Z,雖然刷過了好幾種 Desire Z 用的 ROM
包含有原廠的 2.2.1、2.3 更新,還有前面的文章提到的 Runny v6.0、Andromadus beta 5 等等的
扣掉原廠的大概刷過五六種 ROM,Android 2.3.5、4.0.4,有 HTC Sense 或沒有的都用過
但儘管是有些 ROM 號稱有對 Desire Z 特別優化,用起來實際上還是相當地頓!
或許是我們都比較沒耐心吧,像是手機解鎖等個 0.5 秒,要打電話等個 2 秒,我們用了一段時間真的是會有火...
原本是打算等約滿之後直接換手機~但畢竟還有幾個月的時間
正好遇到同事要刷機,心血來潮就去查了 XDA 的 ROM,這次目標是舊版的 Android
最後很幸運地找到了 LightSense 1.2 這個 ROM!(LightSense 1.2 最後更新是 2012 年 5 月)

2012年10月19日 星期五

字典:Atomic

在看 Java 的 concurrent 的教學文章時,看到 Atomic 這個關鍵字
因此稍微查了一下,Atomic 這個字在英文中是「原子」的意思,即在大自然中原子被視為是組成物體的最小單位
在 Java 中,定義一個 Atomic 表示的是這個行為是最小單位,亦即不可被切割,要嘛全部完成、要嘛全部失敗。
Atomic 是一個 package 的名稱 [1],同時也代表著具有 thread-safe 的變數
當一個變數要被修改時,一定包含著三個動作:讀取變數值、修改數值、將修改後的數值存回變數
Atomic 這個詞在 Java 中即表示一系列的動作是可以保證一起被完成而不會被中斷(其實就像是資料庫的交易的概念)
因此能夠確保在 Atomic 的環境之下,不會發生修改時資料不一致的問題。

2012年10月18日 星期四

synchronized、wait() 和 notify() 的觀念與用法 (待續)

在多執行緒的環境中,有時會產生需要造就出類似資料庫的交易環境
也就是某個一系列的動作正在執行時,必須確保執行期間的資料不會被別的執行緒修改。
例如有某個變數 a 的當前數值是 0,然後同時有兩個執行緒 T#1、T#2 進來要存取 a
T#1 進行 a++ 的動作,接著把 a++ 的結果寫回 a
但寫回去之前,T#2 已經讀取了當前的 a(即讀取到 a=0),並且同樣嘗試要做 a++
T#1 寫回去以後,a=1,接著 T#2 又寫回去,a 仍然等於 1
這時就造成資料不一致,亦即 Race condition [1] 的問題發生了
因為理想上 T#1、T#2 個別對 a 做了 a++,應該要產生 a=2 的結果。

2012年10月16日 星期二

在 Linux 中透過 egrep 篩選文字

記錄一下~在使用 cat、tail 等指令時,當遇到想要只顯示包含某些關鍵字的文字行時
可以使用 egrep 來協助篩選,例如:
cat /usr/local/tomcat/logs/catalina.out | egrep "error|ERROR"
上述指令可以篩選出包含 error 或者 ERROR 的文字行。

2012年10月11日 星期四

The serializable class does not declare a static final serialVersionUID field of type long

在 Class 有宣告 implements Serializable 時,eclispe 可能會有 WARNING 跑出來
原理可以參考 [1] 的說明~
實務上想要自動產生某個 long 的 serialVersionUID 時,可以參考 [2] 的回應
在 eclipse 介面上選:"Source" -> "Cleanup..." -> "Custome Profile" -> "Missing Code" -> "Add serial version ID"

2012年10月9日 星期二

在 Java 中計算檔案的 MD5/SHA1 hash

MD5 hash 常用在判斷兩個檔案的內容是否完全相同
一般用來判斷某個檔案是否上傳或下載完整~當然也可以用在檔案複製貼上等等的動作。
之前在做這種需求時,都是直接用 Runtime 呼叫 Linux 的 md5sum
不過總是覺得一直依賴底層 OS 的指令,好像不是個非常完美的作法!
尤其考慮到程式未來可能需要移植到別的平台時,大量使用 Runtime 會導致移植時要花費的功夫暴增。

今天稍微查了一下,原來 Java 原生就有可以計算 MD5/SHA1 hash 的方法!
雖然不清楚 Java 算出來的 hash 值是否跟用其他方式算出來的 hash 值相同,但老實說那並不是很重要 XD
畢竟 MD5 hash 這種東西只要能確認同一個檔案,每次算出來都一樣;不同檔案算出來都不一樣,能達到這個目的就好了!

2012年10月4日 星期四

判斷捲軸位置的 jQuery plugin:waypoint

參考資料:
1、jQuery waypoint

概念上蠻簡單的,waypoint 就是指定某個標籤
當指定標籤出現在瀏覽器的可視範圍內時,該標籤的 waypoint 事件就會觸發~
如果要像 Facebook 一樣讀新資料時,就可以在這個時候送 ajax call 去讀新資料
然後 append 到顯示資料的標籤上~。

2012年10月3日 星期三

B+-tree implementation:JDBM3(2012-10-15 修正)

上一篇文章 B+-tree implementation:JDBM 用了初代(XD)的 JDBM 來實作 B+-tree
不過在追蹤原始碼之後,因為 JDBM3 有特別對 serialization 做調校,因此還是想用看看 JDBM3!

2012年10月2日 星期二

B+-tree implementation:JDBM

前篇文章當中,找了一些有關 B+-tree 的實作函式庫
其中有一些連結提到 JDBM 這個函式庫,因此就來嘗試看看了。

JDBM 本質上其實並不是實作資料結構的函式庫,而是要做一個 Java 版的資料庫系統
具有 B+-tree 只不過是因為他的資料庫系統會用到的關係。
JDBM 目前看起來有分 JDBM 到 JDBM4 總共四個版本,從原始碼的註記來看,JDBM2、JDBM3 似乎換作者了。
而 JDBM [1]、JDBM2 [2] 和 JDBM3 [3] 的差異在哪裡我也不是很確定~
光看專案的描述來說,JDBM2 有做一些效能調校,但調校的部份不知道在哪~
而 JDBM3 看起來好像多了不少 feature,像是它有特別提出它的 serialization 有客製化,可以提昇讀寫的效率

Access restriction on class due to restriction on required library rt.jar?

有時在開發時會遇到這個奇怪的訊息,依照 [1] 的回應中 Nels Beckman 的解釋
是因為某個 Class 名稱可能同時出現在多個 jar 檔裡,因此會讓 JVM 產生混淆
他的解決方法就是在 eclipse 專案的 Build Path 屬性中,把 JRE System Library 刪除掉再重新加回來
這時就會把讀取 JRE 的順序調整成正確的順序了。
而最乾淨完整的解法自然是確保引用的所有 JRE 都是沒有重複的 Class。

參考資料:
1、Access restriction on class due to restriction on required library rt.jar?

2012年10月1日 星期一

分散式系統的架構

根據 [3] 的描述,在分散式系統中,主要分成三種架構:Shared Memory、Shared Disk、和 Shared Nothing

2012年9月27日 星期四

B+-tree implementation

記錄實作 B+-Tree 的函式庫。
網路上要找 source code 很簡單,但是哪一個才是完整的 implementation 就看不太出來
剛剛搜尋時也有找到有人放他自己寫的 B+-Tree,但又特別說他的這個 implementation 有 bug....

字典:序列化 Serialization

Serialization (序列化)主要是把在程式語言中的 Object 實體,轉換成能夠傳送或儲存的形態
例如要把某個 Object 傳送到網路上的另一台電腦,或者要把 Object 暫存到檔案系統上
透過 Serialization 可以把 Object 轉換成可以寫入或傳送的 Stream 型態,就可以進行傳送或儲存
而接收方或者讀取方只要透過 deserialize 的程序,就可以把 Stream 還原成原本的 Object 型態。

2012年9月21日 星期五

整合第三方 DDNS

這篇只是記錄之前在做 DDNS Client 時的心得 XD
大部分的 DDNS 服務其實都是用簡單的 HTTP Post/Get 方法來接受主動更新
所以要實作 DDNS Client 只要知道如何從 Java 模擬 HTTP 的要求即可。
比較重要的其實是如何得知連接的方法~
像 DynDNS、no-ip 這種大型 DDNS 服務,網站上會有一頁直接說明如何送 HTTP 要求
但有些服務就很難看出來,只能從網站上的手動更新的網頁,開瀏覽器的開發者模式去看它送了什麼東西。
這個目前我也還沒找出比較簡單的方法 XD。

2012年9月20日 星期四

Java GUI 的鍵盤監聽事件

在實作 Java GUI 的鍵盤事件時,可能會遇到一直監聽不到的問題
因為 JFrame 如果有子元件,focus 的狀態就會被傳遞到子元件上
此時就會因為 JFrame 一直不會被 focus,因此也不會觸發 JFrame 上的鍵盤事件。

2012年9月19日 星期三

Java 的 String Matching

最近比較悠閒,打算找個時間來想看看要怎麼用 Java 來找專案中沒有再用到的東西。
基本概念就是要搜尋某個資料夾裡的所有檔案
然後對這些檔案的檔名,去其他檔案裡面搜尋,看有沒有被 reference 到。
其實就是 String Matching 的方法~。

2012年9月18日 星期二

JFrame 的關閉事件

記錄一下~
JFrame 關閉時,如果按的是視窗右上角的叉叉,並且設定為 JFrame.EXIT_ON_CLOSE
則按下叉叉時會觸發 WindowListener.windowClosing() 事件。
而如果是程式碼中呼叫 Jframe.dispose() 把視窗消滅
觸發的事件是 WindowListener.windowClosed() 事件。

2012年9月14日 星期五

(暫存) 在 Windows 監視應用程式送出的封包

目前有需要安裝一些程式,然後研究程式是如何發送 HTTP request
我才能模仿他的 request 從我自己的程式發送更新的需求去伺服器~
因為我沒辦法在機器上面裝他們提供的程式,但又必須要能支援他們的服務...。

2012年9月11日 星期二

Java Preference

在 Java 執行時,有時會想要存放一些使用者的設定,但不知道要放在哪裡
Java 中提供了幾種方法來存放,根據 [1] 的解說,主要是透過 Preference 或者 JNDI。
兩者的比較可以參考 [1] 裡面的小標題 Comparing Preferences API to Other Mechanisms 的描述。

在 Java 中讀取專案內的 Image 檔

這個問題剛剛試了很久,也找了不少資料,但還是搞不太清楚什麼狀況可以讀到。
最後成功的方法是以下的寫法。
PS. 這裡我是用在 JFrame 的標題列圖示,以及通知區(System Tray)的圖示。

2012年9月10日 星期一

Java 應用程式取得系統管理員權限的問題 (尚未解決)

先列一下目前找到的資料,不過現在還沒有找到比較好的解法....
目前看到相對較好的解法大概是 [3] 裡面 freecouch 的回應吧
用 launch4j 把 jar 重新包裝成 exe,然後在 Manifest 裡面定義需要管理員權限。

參考資料:
1、java Failed to create new file in windows 7?
2、Java: run as administrator
3、Request admin privileges for Java app on Windows Vista

2012年9月9日 星期日

JFrame 的 Component 的事件

JFrame 的 Component 的滑鼠事件有分成很多種類型,以下列出各個滑鼠事件的意義

Java 拖曳檔案的動作:DnD

在 Java 上要跟其他應用程式交流資訊,可能需要的是透過 Drag & Drop(拖曳)或者 Clipborad(剪貼簿)來交換資料
在 Java 上就是透過 DnD 的方法來實作。
根據 [3] 的官方文件教學,DnD 分成兩個部分:

1、Drag and drop (DnD) support

2、Clipboard transfer through cut or copy and paste

(暫存) Java GUI:在 Windows 通知區域顯示圖示

關鍵字是 java.awt.SystemTray,根據官方文件 [1] 的說明,這應該是 Java SE 6 以上版本才支援的。

參考資料:
1、The Java Tutorials:How to Use the System Tray
1、java编的程序在通知区域显示图标,不在任务栏上显示,就跟qq最小化后一样
3、Open popup(Menu) on task tray icon with left click using java

2012年9月5日 星期三

JDBC 的 Auto-Commit

參考資料:
1、Transactions
2、does Connection commit on close()?
3、JDBC Transaction Example
4、MySQL: InnoDB or MyISAM?

有個小任務是幫忙檢查為什麼在測試時,用我們自己的資料庫連線的 API 要開啟 TRANSACTION 模式
但就算設定了 auto-commit = false 還是會自動 commit
懷疑可能的原因....
1、我們的 API 用的是 executeUpdate() 去執行 INSERT、UPDATE 和 DELETE?(網路上的範例比較多都是呼叫 execute())
2、使用了 TOMCAT 的 JDBC connection pool?

2012年9月4日 星期二

jQuery 處理 checkbox 多選

在可以多選的 checkbox 欄位,在網路上可以找到一些利用 jQuery 的 selection 篩選的方法
但是我試起來好像都有點怪怪的~
例如是有看到有人分享的 selection 方法可以直接檢查 "checked" 這個屬性
但是我的測試好像沒有用:
$("input[name='multi_choice'][@checked]").each(function(value, element) {
  console.log("element: " + $(element).attr("checked"));
});
這樣的程式碼在我的測試裡會全部的 checkbox 都被選到..。
後來查到 [2] 的寫法,就可以順利取道被選取的 checkbox 了。

2012年8月29日 星期三

在 MySQL 中做全文檢索

因為想要能夠提供一個 API,能夠回應資料庫當中哪些資料包含了指定的 key
因此查了一些資料,目前是打算用 MySQL 的 FULL TEXT SEARCH 的功能。

要使用 MySQL 的全文檢索,資料表型態必須是 MyISAM,同時要做全文檢索的欄位型態要是 FULLTEXT
然後對該欄位建立 FULLTEXT 類型的索引。(可參考官方文件 [1] 的說明)
使用時可以使用 MATCH (...) AGAINST (...) 敘述來進行全文檢索。
MATCH (...) 裡面指定的是要做全文檢索的欄位名稱,AGAINST (...) 裡面則是指定要搜尋的關鍵句。
它回應的會是一個數字,表示的是欄位內容跟關鍵句的相似度,如果完全不相似就會回傳 0。

2012年8月27日 星期一

Must qualify the allocation with an enclosing instance of type

今天在寫某個程式時遇到的問題~
eclipse 回報「Must qualify the allocation with an enclosing instance of type」這個錯誤訊息
稍微查了一下,是因為我使用了 inner class,然後又在 static 的函式中嘗試使用 inner class 造成的

2012年8月24日 星期五

清除 StringBuilder 的內容

在 Java 的 StringBuilder 中,並沒有提供 clear() 之類的方法可以清除內容
網路上可以查到兩種方法去清除內容,一個是用 delete(),另一個則是 setLength()
官方文件中似乎沒有提到哪種比較好
根據 [1] 的討論,應該是 setLength() 效率會比 delete() 稍高。

2012年8月23日 星期四

JavaScript 驗證數字

要驗證某個變數的值是不是數字,在 JavaScript 當中好像用 Regular Expression 結果會怪怪的
我試了好幾種不同的 Regular Expression,但用 match() 時都有些奇怪的問題
經由同事的協助,發現 [1] 當中 Joel Coehoorn 的回應~

2012年8月22日 星期三

ArrayList 與 LinkedList 的效率比較 (未完待續)

網路上能夠獲得的資料大概都是說 ArrayList 擅長隨機讀取
而 LinkedList 擅長隨機插入和刪除資料~
不過目前根據我自己跑出來的測試結果,刪除我還沒測試
但插入尾端跟隨機插入,在我的測試當中都是 ArrayList 大勝......囧"
原因不明,不知道是不是我的測試方法有問題~

2012年8月20日 星期一

在 Java 中使用 SQLite Database

SQLite Database 是輕量級的關聯式資料庫
在過去要使用關聯式資料庫,都必須架設像是 MySQL、SQL Server 等等的大型資料庫系統
對於某些會被拿來拿去的移動式程式來說相當不合用
雖然有微軟的 Access 可以達到移動式資料庫的目的,但是 Access 本身並沒有索引的設計
效率不佳,能夠承受的同時使用人數也很少~
現在有了 SQLite Database,相對於 Access 提昇了不少效能
但在 Java 中並沒有內建 SQLite 的 JDBC driver,而 SQLite 官方也沒有提供
因此網路上可以找到很多各自發展的 SQLite JDBC driver。
這裡使用的 driver 是 Xerial 的 JDBC driver [1]
使用原因...看起來他更新比較頻繁,而且用的人好像比較多 XD

2012年8月16日 星期四

(暫存) 在一個 App 當中呼叫另一個 App 的函式

最近可能要接下同事負責的 Android 的程式碼,看到裡面拆成兩個專案
一個是前端的 UI,另一個是後端處理的 API
所以先記錄一下呼叫別的 App 的 API 的方法~

1、How to call one android application from another android application

在 Java 驗證 Public IP 的方法

首先要知道的是,依據 RPC 1918 的規範,Private IP 的範圍如下
  • 10.0.0.0 - 10.255.255.255 (10/8 prefix)
  • 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
  • 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
因此要找出某台電腦上一系列的 IP 裡面哪些是 Public IP,就要把 Private IP 給濾掉。

2012年8月15日 星期三

java.io.IOException: Server returned HTTP response code: 401 for URL

在測試 HTTP 行為時,出現這個錯誤訊息
「java.io.IOException: Server returned HTTP response code: 401 for URL: ....」
參考 [1] 之後發現是因為網站需要使用基本的驗證(即在網址上面串 http://id:password@domain/ 這種形式)
因此需要加入標頭 Authentication 來讓伺服器做驗證。

2012年8月14日 星期二

使用 Tomcat + Jersey 建立 RESTful 服務

用了好一段時間的 RESTful,但還沒有自己從全白的 Linux 架起來過 XD
記錄一下,要用 Jersey 需要以下四個套件:
  • asm-3.1.jar
  • jersey-core-1.1.2-ea.jar
  • jersey-server-1.1.2-ea.jar
  • jsr311-api-1.1.jar
缺少的話 Jersey 會跑不起來,啟動 Tomcat 時就會噴一堆 Exception ~XD

在 ubuntu 設定 SSH 遠端連線

ubuntu 剛安裝完的時候,預設沒有安裝 SSH 的套件,所以無法透過 putty/pietty 遠端連線
可以透過安裝 openssh-server 套件的方式啟用 SSH 連線。
安裝的指令如下
sudo apt-get install openssh-server

參考資料:
1、ubuntu SSH遠端安全連線安裝及設定

2012年8月13日 星期一

2012年8月9日 星期四

從 Java 動態更新 DNS

延續上一篇「在 Java 上使用 dnsjava 查詢 DNS
這篇一樣是用 dnsjava,目標是實作更新 DNS 的程式。

在更新之前,必須先把 DNS Server 的動態更新功能打開,否則一般狀況 DNS Server 是不允許被更改內容的
我這邊測試是架了一台 Windows 2008 R2,在上面加上 DNS 角色。
設定動態更新的地方如圖所示,紅圈圈的位置就是設定動態更新的地方:


由上圖可以看出,這裡假設 DNS Server 管理的網域是 example.com.tw,IP 範圍是 10.0.1.*。

2012年8月3日 星期五

(暫存) Java 與 DNS

參考資料:
1、第十九章、主機名稱控制者: DNS 伺服器
2、Using Java to get detailed DNS information
3、dnsjava examples
4、DNS queries in Java.
5、Developing JNDI-based Applications
6、DNS Service Provider for the Java Naming Directory Interface (JNDI)
7、DynDNS document:DNS update API
8、no-ip document:DNS integrate
9、(ddwrt) DDNS - How to setup Custom DDNS settings using embedded inadyn - HOWTO
10、How to access domain DNS protocol in pure java
11、Accessing the Dusty Corners of DNS with Java

在討論如何透過 Java 連接 DNS 之前,必須先知道 DNS 的運作原理
詳細的說明可以參考鳥哥寫的教學 [1] 。
其中比較需要注意的是一般的 DNS 查詢有分成 NS(Name Server)和 A(Address)
NS 回應的是下一層的 DNS 伺服器位址,A 才是真正的目的地位址。
而如果自己架一個新的 DNS 伺服器,必須向上一層的 DNS 伺服器申請領域查詢授權
也就是在上一層的 DNS 裡面設定 NS,把某個子網域名稱指定給新架的 DNS 伺服器。
假設上一層 DNS 的網域是 abc.com,而我新架了一個 DNS 伺服器,想要管理 xyz.abc.com
這時要先去 abc.com 上申請查詢授權,等 abc.com 的管理員設定了 xyz.abc.com 的 NS。
設定完成以後,如果有 request 在詢問 *.xyz.abc.com 這個網域名稱
abc.com 的 DNS 都會回應 NS,即叫它去問 xyz.abc.com 的 DNS 伺服器。
而如果 abc.com 回應的是 A,則是回應對方說你要的目標就在這個位址,DNS 查詢就結束了。
(更詳細的說明請參閱鳥哥的 19.1.3 小節!XD)

補充:
雖然上面列了一堆參考資料,不過最後還是放棄 JNDI 改用 dnsjava 了 XD
有關 dnsjava 的範例請參閱 dnsjava 標籤

2012年8月1日 星期三

在 Linux 中監控系統資源:使用 dstat

參考資料:
1、dstat
2、How to use dstat to monitor your Linux/UNIX server
3、Linux System monitoring using Dstat
4、http://pkgs.repoforge.org/dstat/

因為需要系統資源吃比較少的程式來收集資訊,所以找到了 dstat 這個工具(官網請參考 [1])!
我目前的執行環境是 Fedora,參考的安裝方法是 [3]

Can't specify target table for update in FROM clause

參考資料:
1、Mysql error 1093 - Can't specify target table for update in FROM clause
2、PHP/MYSQL using an array in WHERE clause

原本我想要在資料表裡面把符合某個條件的值撈出來,然後把他們從資料庫裡刪掉
於是下了類似這樣的 SQL Statement:
DELETE FROM table WHERE id IN (SELECT * FROM table WHERE time < 11111111);
其實這段 SQL Statement 很明顯根本不用用到 SELECT...XD
只是...反正這只是個範例就是了,不要太在意!XD

列舉 HashMap

參考資料:
1、遍历HashMap及获取所有的Key值
2、Java: iterate through HashMap

某些時候會想要對 HashMap 裡面的所有資料做某些處理,這時首先必須先把 HashMap 的內容列舉出來
列舉方法如下:
HashMap<Object, Object> mp = new HashMap<Object, Object>();
Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry)it.next();
    System.out.println(pairs.getKey() + " = " + pairs.getValue());
    it.remove();
}

另外 [1] 有提到另一種列舉方法是透過呼叫 HashMap 的 keySet().toArray()
不過似乎有效率上的差異,詳情可以自行參考 [1] 的內容。

2012年7月27日 星期五

(暫存) 限制下載速度

參考連結:
1、程式碼分享 - 限制下載速度

最佳化 Java File I/O

[1] 這篇文章是研究怎樣讀取檔案最快的開箱文 (?!)
一開始先說明了 Java 常用的 14 種讀取檔案的方法,並簡單說明主要的優點(例如有沒有 thread-safety)
然後開始做各種方法的效率測試。

2012年7月26日 星期四

使用 Java 發送 HTTP Request

要在程式裡面模擬瀏覽器的行為是很常見的需求
不管是要做爬蟲,還是是要無縫連接網際網路的服務,都需要利用程式來實作瀏覽器的動作。
雖然上週才剛發一篇 Android HTTP POST
但因為 Android 函式庫本身已經內含 Apache 的 HttpClient 套件,所以實作起來稍微簡單一點
而在純 Java Application 的環境中,如果沒辦法用 Apache 的套件的話,就必須自己用別的方法處理了。

參考資料:
1、java HttpURLConnection來實作get及post動作
2、HTTPURLConnection

2012年7月24日 星期二

自行觸發 jQuery Validation 的表單驗證

這是個無聊的記錄 XD
當遇到有任何需要,必須強制由程式碼自行觸發 validator 的驗證事件時
可以直接呼叫 valid() 函式,同時 valid() 會回應驗證結果(true/false)。
$("#form").valid();


參考資料:jQuery Validation

在 Linux 上偵測系統頻寬

原本有找到 nmon 這個工具可以在 Linux 上偵測頻寬
在 Linux 監控系統資源:nmon for Linux
不過它好像只有直接執行 nmon 觀察,才會有 peak 的數據出現
如果要輸出成檔案時,nmon 只會輸出當下的流量數據,這樣就無法達成原本的需求了。
在看 nmon 輸出的資料時,發現在網路的部份它似乎也只是去分析 /proc/net/dev 提供的資訊
例如現在去看 /proc/net/dev,顯示累計傳輸了 N Bytes
一秒之後再看一次 /proc/net/dev,會顯示累計傳輸 N+M Bytes
因此可以算出這 1 秒的流量是 M Byte/s。
既然 nmon 實際上也只是輸出 /proc/net/dev 的數據,那其實直接自己去查詢 /proc/net/dev 就好了!

2012年7月20日 星期五

在 Linux 監控系統資源:nmon for Linux

官方網站:nmon for Linux
線上文件:Document

要把監視的結果輸出成檔案可以用:
nmon -f -N -m 輸出資料夾
nmon 就會在指定的資料夾裡面產生輸出的檔案
輸出檔名的格式是 _YYYYMMDD_HHMM.nmon。

不過目前覺得輸出的資料太多了,我只需要其中幾個值而已
所以還在搜尋要如何只讓 nmon 輸出少部分的資料....
如果沒辦法的話,可能就只能在看輸出的記錄時自己寫篩選器了。

2012年7月18日 星期三

2012年7月14日 星期六

(暫存) 實現像 Google Play 那樣左右滑動換頁的方法

可以使用 ViewPager:http://developer.android.com/reference/android/support/v4/view/ViewPager.html

2012年7月11日 星期三

筆記:JVM 的記憶體管理

JVM 規範的記憶體空間結構如下圖:

註:圖片轉載自 [2]

Heap
Heap 用來儲存物件的實例以及陣列值,可以視為所有用 new 關鍵字建立的物件,記憶體都會分配在 Heap 區域中。
Heap 預設的最小值是實體記憶體的 1/64,但小於 1GB;最大值是實體記憶體的 1/4,但小於 1GB。
最大值可以透過 -Xmx 參數來設定;最小值則透過 -Xms 設定。

當 JVM 目前的 Heap 空間使用率超過 60% 時,JVM 會自動增加 Heap 的空間;
而當使用率低於 40% 時,JVM 會自動減少 Heap 的空間。
要改變比例的話,可以透過 -XX:MinHeapFreeRatio、-XX:MaxHeapFreeRatio 改變比例
要注意的是參數是用閒置比例來計算的,因此假設 MinHeapFreeRatio 設定為 30% 的話
是表示閒置記憶體低於 30%,亦即記憶體使用率超過 70%,JVM 會增加 Heap 的記憶體空間。

在 Java 1.2 版以後,為了提昇 Heap 記憶體的使用效率,JVM 使用了分代管理(Generations)的機制來管理 Heap 記憶體空間,即將 Heap 分成三個世代,如下圖。



註:圖片轉載自 [6]

(1) 新生代(Young Generation)

(2) 舊生代(Old Generation)

(3) 方法區(Permanent Generation)
方法區存放類別的資訊(名稱、修飾符)、靜態變數、常數(定義為 final 的變數)等等。
Permanent Generation 在一定條件下會被 GC,但一般狀況下 GC 不會影響到 Permanent Generation。
而且根據 [6] 的描述,看起來官方似乎也不建議 GC 方法區的樣子?(不太確定,其實官方也只是用詞看起來不太建議,但並沒有明說 XD)
根據 [3] 回應的描述,Permanent Generation 當中會存放 class 對應到 Heap 裡的位置,如下圖。



註:圖片轉載自 [4]

在 JVM 當中可以透過 -XX:PermSize 及 -XX:MaxPermSize 來指定大小,預設最小值為 16MB、最大值為 64MB。


參考資料:
1、[書目] Java 分散式處理實務精要:奠定雲端基礎的 63 個思考術
2、JVM内存模型
3、In Java is Permanent Generation space garbage collected?
4、Presenting the Permanent Generation
5、Pick up performance with generational garbage collection
6、Improving Java Application Performance and Scalability by Reducing Garbage Collection Times and Sizing Memory Using JDK 1.4.1

JVM学习总结(转载)

2012年7月8日 星期日

2012年6月28日 星期四

Desire Z 刷 ICS ROM

原本刷 R6 的 ROM,用 Android 2.3.5 + Sense 3.5
不過用起來實在太頓了,所以後來想說乾脆放棄 HTC Sense,找看看別的 ROM
因此這次選上了今天才剛進入 RC 的 CyanogenMod 9.0.0-RC1

圖片來源:xda-developers

2012年6月27日 星期三

在 Windows 底下跑 Tomcat

參考資料:在Windows 安裝 Tomcat 6 測試站

在 Windows 底下安裝 Tomcat,真正要做的只有:
1、安裝 JDK
2、設定環境變數
3、下載並執行 Tomcat

byte array 與 hexadecimal 字串互相轉換

記錄這篇是因為加解密的結果會是 byte array,但是我的資料必須透過 HTTP 協定傳遞
所以必須把 byte array 轉換成字串,傳送出去之後還要能再轉回原本的 byte array 來解密。
根據 Encrypting and decrypting large data using Java and RSA 的建議
要把 byte array 轉成 hexadecimal 的字串,在網路上傳送才不容易有問題。

參考資料:
1、Converting A String To Hexadecimal In Java
2、How to convert Hex String to Bytes and viceversa in Java?

2012年6月26日 星期二

在 Java 使用加密演算法(三):使用 RSA 加解密長資料

在前一篇文章「在 Java 使用加密演算法(二)」中雖然已經有用 RSA 加解密的程式碼
但那段程式碼沒有辦法加解密長度稍長的資料(大概是 100 多個 bytes 以上)
稍微查詢了一些資料,目前個人的了解應該是受限於 RSA 是個 block cipher
block cipher 本身的設計就是一次只用來加解密很小的資料。
網路上雖然討論在 Java 中使用 RSA 的文章很多
但多數似乎都假定看的人應該要知道 RSA 的 "一次加密" 只能加密很短的資料,就很少在文章中提到這件事....。
PS. "一次加密" 指的就是執行一次 RSA 的動作。

2012年6月25日 星期一

在 Java 使用加密演算法(二):使用 RSA 加密與解密(最後修改:2015-10-11)

利用 RSA 加密演算法來加密字串的範例如下~
這裡總共分成兩段程式碼,分別是 Main.java 以及 myEncryption.java
Main.java 是主要程序;myEncryption.java 進行加解密。

2012年6月19日 星期二

在 Java 使用加密演算法(一):產生與儲存 RSA 的金鑰(2017-07-19 調整)

正在嘗試要實作公開金鑰的加密演算法,目前搜尋了一些資料,原本有在疑惑應該選用哪個演算法
不過在搜尋到 [4] 以後,就決定還是用 RSA 了。

參考資料
1、JAVA 上加密演算法的實作範例
2、Generate Public and Private Keys
3、Save/Load Private and Public Key to/from a file
4、RSA Versus DSA and EL GAMAL
5、有關如何安全地儲存 key 的討論,可以看看:How to securely store a PrivateKey in code

這裡分成三個部分來討論,首先是要如何在 Java 裡面產生 RSA 的 Public Key 和 Private Key
以及把產生的兩把 Key 都儲存成檔案,再把它從檔案裡讀出來,最後才是真正把資料加密和解密。

生成金鑰

生成金鑰和讀寫金鑰可參考以下的程式碼,基本的程式碼是參考 [3],只有做小幅度的修改而已。
程式碼分為以下幾個 method:

  • main():程式的主要進入點,除了產生金鑰以外,也會負責呼叫其他 method。
  • printKeyPair():單純為了將產生的金鑰印出。而因為金鑰會是以 byte 形式存在的東西,因此必須透過某些編碼轉成能夠被印出來的字串。這裡是使用 Base64 編碼。
  • saveKeyPair():將金鑰分別輸出到指定路徑底下,並產生 “public.key” 和 “private.key” 兩個檔案。
  • loadKeyPair():從指定路徑中尋找 “public.key” 和 “private.key” 兩個檔案,並將他們載入成 KeyPair 型態到程式中。

以下就分別是各個 method 的原始碼以及相關的說明。

main()
public static void main(String[] args) throws Throwable {
  String path = "D:/encrypt";

  SecureRandom random = new SecureRandom();
  random.setSeed("test".getBytes());

  // Generate the key pair (public key and private key).
  KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
  // Specify that the key should be 1024-bit.
  keygen.initialize(1024, random);
  KeyPair generatedKeyPair = keygen.generateKeyPair();

  // Print the key. The key will be encoded by Base64.
  log.trace("Generated key pair:");
  printKeyPair(generatedKeyPair);

  // Store the key as files.
  log.info("Output the key pair to {}", path);
  saveKeyPair(path, generatedKeyPair);

  // Load the keys
  log.info("Loaded Key Pair from {}", path);
  KeyPair loadedKeyPair = loadKeyPair(path, "RSA"); // Load the keys from files

  // Print the loaded key pair to ensure that they are exactly the same.
  log.trace("Loaded key pair:");
  printKeyPair(loadedKeyPair);
}

4~5 行是用指定的字串去產生金鑰,透過這個方法的話,只要指定的字串是同一個,產生的金鑰就會是同一個。
這裡指定的字串是 "test"。
7~11 行是產生金鑰的程式碼,其中第 10 行的 1024 是指要產生的金鑰長度為 1024-bit
長度設得越長,產生金鑰所需要的時間也越長~
我的電腦產生 512 bits 的金鑰一瞬間就出來了,1024-bit 要大概一兩秒
2048-bit 就要稍微等個三秒之類的吧...(秒數是純感覺,沒有做過任何精準的測試)

11 行將 KeyPair 產生出來,也就是同時產生 Private Key 和 Public Key,會被存在同一個物件裡。
然後後面就依序是先把產生的金鑰印出來,接著把金鑰輸出成檔案
(預期輸出位置是 D:\encrypt\public.key 和 D:\encrypt\private.key)
然後再從檔案中把金鑰讀進來,再印出來,可以用眼睛確認跟輸出前是否長得一樣。

printKeyPair()

public static void printKeyPair(KeyPair keyPair) {
  log.trace("  public key: {}",
      Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
  log.trace("  private key: {}",
      Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
}

這裡很單純,就只是利用 Java 原生支援的 Base64 編碼器,把金鑰的內容輸出成文字印出來而已。

saveKeyPair()

public static void saveKeyPair(String path, KeyPair keyPair) throws IOException {
  PrivateKey privateKey = keyPair.getPrivate();
  PublicKey publicKey = keyPair.getPublic();

  // Store Public Key.
  File fileForPublicKey = Paths.get(path, "public.key").toFile();
  log.trace("Public key will be output to '{}'.", fileForPublicKey);
  
  X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
  try (FileOutputStream fos = new FileOutputStream(fileForPublicKey)) {
    fos.write(x509EncodedKeySpec.getEncoded());
  }

  // Store Private Key.
  File fileForPrivateKey = Paths.get(path, "private.key").toFile();
  log.trace("Private key will be output to '{}'.", fileForPrivateKey);
  
  PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
  try (FileOutputStream fos = new FileOutputStream(fileForPrivateKey)) {
    fos.write(pkcs8EncodedKeySpec.getEncoded());
  }
}

5 ~ 7 行是產生路徑,實際執行起來,這裡產生的路徑會是 D:\encrypt\public.key。
9 ~ 12 行是把 Public Key 的內容輸出到檔案 D:\encrypt\public.key。

同樣地,接著 15 ~ 16 行也是產生路徑,實際執行時這個路徑會是 D:\encrypt\private.key。
18 ~ 21 行則是把 Private Key 的內容輸出到檔案 D:\encrypt\private.key。

loadKeyPair()

// Load the keys from files.
public static KeyPair loadKeyPair(String path, String algorithm)
    throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {

  // Initiate the factory with specified algorithm.
  KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  
  // Read public key from file.
  File fileForPublicKey = Paths.get(path, "public.key").toFile();
  log.trace("Public key will be loaded from '{}'.", fileForPublicKey);
  
  PublicKey publicKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPublicKey)) {
    byte[] loadedBytes = new byte[(int) fileForPublicKey.length()];
    fis.read(loadedBytes);
    
    X509EncodedKeySpec spec = new X509EncodedKeySpec(loadedBytes);
    publicKey = keyFactory.generatePublic(spec);
  }
  
  // Read private key from file.
  File fileForPrivateKey = Paths.get(path, "private.key").toFile();
  log.trace("Private key will be loaded from '{}'.", fileForPrivateKey);
  
  PrivateKey privateKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPrivateKey)) {
    byte[] loadedBytes = new byte[(int) fileForPrivateKey.length()];
    fis.read(loadedBytes);
    
    PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(loadedBytes);
    privateKey = keyFactory.generatePrivate(privateKeySpec);
  }

  return new KeyPair(publicKey, privateKey);
}

這裡程式碼看起來好像比較長,但其實邏輯跟前一段 saveKeyPair() 沒什麼差別
就是組成路徑,然後讀檔案,再把讀入的 bytes 轉成金鑰的物件。因為流程上大同小異,這裡就先忽略細節了 XD

以上述的 method 全部放在同一個 Class 合併起來執行的話,會跑出類似下面這樣的 log 輸出:

2017-07-19 22:58:44,252 | Generated key pair:
2017-07-19 22:58:44,254 |   public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB
2017-07-19 22:58:44,260 |   private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y//PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE
2017-07-19 22:58:44,260 | Output the key pair to D:/encrypt
2017-07-19 22:58:44,261 | Public key will be output to 'D:\encrypt\public.key'.
2017-07-19 22:58:44,262 | Private key will be output to 'D:\encrypt\private.key'.
2017-07-19 22:58:44,263 | Loaded Key Pair from D:/encrypt
2017-07-19 22:58:44,263 | Public key will be loaded from 'D:\encrypt\public.key'.
2017-07-19 22:58:44,264 | Private key will be loaded from 'D:\encrypt\private.key'.
2017-07-19 22:58:44,264 | Loaded key pair:
2017-07-19 22:58:44,264 |   public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB
2017-07-19 22:58:44,264 |   private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y//PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE
透過 RSA 加密與解密

請參考下篇文章:在 Java 使用加密演算法(二):使用 RSA 加密與解密

附錄:對稱金鑰加密(symmetric-key algorithm)

原本弄錯了以為 AES 也是屬於公開金鑰加密,不過後來查到的程式碼覺得怪怪的
於是仔細查了一下 AES,發現它應該是對稱金鑰加密的演算法才對~(參閱下面參考資料中的連結 6)
也就是發送端與接收端共用同一個 secret key。

AES 破解的資訊可參考:New Attack on AES
微軟在 2011 年 8 月發現可以加快破解 AES 的方法,例如以 AES-128 來說,可以將複雜度降低到 2126.1
不過其實 2126.1 還是很多 XD,也就是目前來說 AES 還是安全性可接受的加密方法~。

參考資料:
6、AES encryption, what are public and private keys?
7、Using AES with Java Technology
8、【java】AES加密解密|及Base64的使用
9、Java AES Encrypt & Decrypt Example(加解密)
10、JAVA AES 加密步骤解释

2012年5月11日 星期五

申請 Amazon Appstore 的帳號

Amazon Appstore 有每日免費的 App 可以下載~。

嗯,一句話點明為什麼會想要申請 Amazon Appstore 的帳號 XD
之前曾經有中斷好一陣子,因為 Amazon 限制一定要美國的 IP 才能購買
今天突然發現好像限制又取消了?

2012年5月10日 星期四

deluge 無法開啟的解決方法

這兩天我的 ubuntu 從 11.10 升級到 12.04 LTS
然後才突然發現我的 deluge 不知道什麼時候開始掛掉了
重新安裝 deluge 和 deluged 好幾次都沒有解決
狀況是打開 deluge 介面後,畫面的左右邊都一片空白,然後連線管理員開啟常駐後也連不上
搞了好一陣子,最後找到這篇文章:

2012年5月1日 星期二

(暫存) Google Geocoding API

Google Geocoding API
https://developers.google.com/maps/documentation/geocoding/?hl=zh-TW

地理編碼是指將地址 (例如 1600 Amphitheatre Parkway, Mountain View, CA) 轉換成地理座標 (例如緯度 37.423021 和經度 -122.083739) 的程序,而您可以使用這些座標來放置標記或設定地圖位置。Google Geocoding API 可讓使用者直接透過 HTTP 要求存取 Geocoder。另外,此服務能讓您執行反向作業 (將座標轉換為位址);此程序也就是「反向地理編碼」。

Google Place API
1、Android Development – Part 1: Using Google’s Places API to Develop Compelling Location Based Mobile Applications
2、Get list of places surrounding user's current location

找出以標記為中心一公里之內的地點

這個問題其實是在看論壇時看到的
因為自己之後要寫的東西也會用到,所以就先來查了一下
不過一查才發現這是相當複雜的問題呀 XD

(圖片取自維基百科

座標跟距離的問題有分成兩種:
1、知道兩點的座標,如何求出兩點之間的距離(以公尺或者公里為單位)
2、以某點為中心點,附近有很多座標不同的其他點,如何得知距離在 XXX 公尺/公里之內的點有哪些

1、知道兩點的座標,如何求出兩點之間的距離(以公尺或者公里為單位)

第一點在網路上討論好像比較多,主要問題在於地球是個球體
所以在緯度 0 度的地方,經度移動 1 度所走的實際距離,跟在緯度 60 度的地方移動 1 度經度所走的距離實際上是不同的。

Google Map API 入門教學(二):在 Map 上加入標記

要在 Google Map 上加入標記,需要利用的是 google.maps.Marker 這個物件。
延續 Google Map API 入門教學(一)的例子,可以將 JavaScript 加上以下的語法:

google.maps.event.addListener(currentMap, "click", function(event) {
  // Setting of marker
  var optionOfMarker = {
    position: event.latLng,
    map: currentMap,
    title: event.latLng.toString()
  };

  // Show marker in the place of mouse clicks
  mapMarker = new google.maps.Marker(optionOfMarker);
});

2012年4月26日 星期四

(暫存) chunks

1、https://www.google.com/#hl=zh-TW&site=&source=hp&q=java+upload+chunk&oq=java+upload+chunk&aq=f&aqi=&aql=&gs_nf=1&gs_l=hp.3...2279.7765.0.8039.17.17.0.4.4.0.148.413.12j1.13.0.dKajhJiFq18&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=3af3822da289a1e4&biw=1920&bih=965

2、Trying to upload in chunks

3、Upload a video file by chunks

4、Chunked transfer encoding

5、W3C:Chunked Transfer Coding

觀察 JVM 讀取了哪些 CLASS

我們的環境中 JRE 是有被刪減過的,所以有時寫新功能時
會出現 ClassNotFoundException 或者是 NoClassDefFoundError 之類的錯誤訊息
通常這是表示 JVM 在函式庫中找不到指定的 CLASS。

為了要儘快發現少了哪些東西
可以在 tomcat 上(我們的環境是 web 的,所以 Java 是跑在 tomcat 上面)加參數
也就是去修改 /bin/catalina.sh。

2012年4月24日 星期二

Google Map API 入門教學(一):將 Google Map 放入網頁

Google Map API 目前已經是 v3 版本了,不需要特別申請 API,不過規定跟之前一樣,不能將 Google Map API 用在需要付費的地方。

Google 官方文件其實已經有蠻不錯的入門教學,不過說明個人覺得稍微少了點
所以自己在學習的同時也順便來記錄一下!

要在網頁上顯示 Google Map,最基礎需要的就是一個 HTML 網頁
然後網頁中去執行 Google 提供的 JavaScript API 在網頁上顯示地圖。
網頁的部份其實後續很少在改,因為動作幾乎都是靠 JavaScript 在做的。

553 Mail from must equal authorized user

今天在尋找免費且有支援 TLS/SSL 加密的 SMTP 服務時,測試跑出以下的錯誤訊息:

553 Mail from must equal authorized user

原本以為是帳號密碼打錯之類的,還重打了幾次帳號~

2012年4月22日 星期日

2012年4月20日 星期五

Invalid byte 1 of 1-byte UTF-8 sequence.

今天遇到 dom4j 丟出以下的 Exception

org.dom4j.DocumentException: Invalid byte 1 of 1-byte UTF-8 sequence.

查了一下網路之後,得到的結果是因為讓 dom4j 的 SAXReader 讀取 XML 時
XML 在標頭宣告自己的編碼是 UTF-8,但實際上檔案儲存時並不是用 UTF-8。
後來發現我用 NotePad++ 儲存的 XML 檔忘了選編碼了
要在 NotePad++ 的工具列上選 [編碼][編譯成 UTF-8 碼] 或者 [編譯成 UTF-8 碼 (檔首無 BOM)]

2012年4月19日 星期四

在 Java 中刪除 HTML tag 的屬性內容

當需要用 Java 把 HTML tag 的屬性內容刪除時,可以利用類似以下的方法:
line = line.replaceAll("<html([^<]*)>", "<html>");

MySQL 效能調教相關知識

先留一些資料下來。

1、關於 MySQL的記憶體使用量計算

轉貼文章內提到的資訊:
MySQL 記憶體需求量的計算
min_memory_needed = global_buffers + (thread_buffers * max_connections)

(1) global_buffers 包含:

key_buffer
innodb_buffer_pool
innodb_log_buffer
innodb_additional_mem_pool
net_buffer

(2) thread_buffers 包含:

sort_buffer
myisam_sort_buffer
read_buffer
join_buffer
read_rnd_buffer

2012年4月18日 星期三

透過 Exchange Server 用 JavaMail 寄信

跟之前不同的地方在於,要透過 Exchange Server 好像必須提供 Authenticator,程式碼必須修改以下的部份:

JavaMail 相關資源

先記錄一下 JavaMail 相關的一些資源

1、JavaMail API - FAQ
FAQ 當中有提到使用 JavaMail 連接 Exchange Server 時,帳號要打的內容必須是 domain_name/windows_account/mail_account,不過目前尚未實際側試過...。
2、JavaMail quick start
3、JavaMail Properties

在 jQuery 檢查 checkbox 的點選狀態

用 jQuery 一般要檢查一個 checkbox 是不是有被點選
通常都直接檢查有沒有 checked 屬性,範例如下:
$(function() {
  $("#checkbox").change(changeEventForEnabled);
});

function changeEventForEnabled() {
  if($("#checkbox").attr("checked")) {
    $("#text").removeAttr("disabled");
  }
  else {
    $("#text").attr("disabled", true);
  }
}
上面的目的是當 id="checkbox" 的元件被點選時,會把 id="text" 的欄位開啟(刪除 disabled 屬性)
反之則是把 id="text" 的欄位關閉(刪除 disabled 屬性)

2012年4月2日 星期一

在 Windows Server 2008 安裝 Exchange Server 2010

安裝圖文教學可參考 [1]
基本上只要按照圖文教學一步一步做,就可以把 Exchange Server 2010 裝好了!

2012年3月23日 星期五

在 ubuntu 11.10 上執行 .jar

假設 ubuntu 完全是剛灌好的狀態,會需要先安裝 JRE
然後把 eclipse 上的專案匯出成 jar 檔,設定 Manifest 以後再丟到 ubuntu 上執行。
以下就一步一步記錄我的執行流程!

安裝 JRE

因為目前我的網路環境不知道為什麼抓不到 sun-jdk,所以還是先用 ubuntu 原本的 openjdk
openjdk 6 的安裝方法:

sudo apt-get install openjdk-6-jdk

2012年3月22日 星期四

在 ubuntu 11.10 安裝 Apache2 + PHP5 + MySQL + phpMyAdmin

聽說舊的 ubuntu 版本可以裝 Lamp,不過好像 ubuntu 11 沒有 Lamp 可以安裝
所以只能自己一個一個裝了~
原本找到的網路資料現在一時找不到.....
但下的指令蠻簡單的:

sudo apt-get install apache2 php5 mysql-server

以上指令會把 Apache2、PHP5、MySQL 都裝進來
其中我裝進來的版本分別如下。

  • Apache2:Apache/2.2.20 (Ubuntu)
  • MySQL:5.1.61-0ubuntu0.11.10.1
  • PHP:5.3.6-13ubuntu3.6

有可能需要測試下面這個指令 XD

sudo apt-get install libapache2-mod-php5

2012年3月21日 星期三

提昇資料庫效率的方法:Partition

原本要找的是 AUTO_INCREMENT 爆掉的問題該怎麼處理
意外找到這個可以提昇資料庫效率的方法~
詳細說明可以直接看下列的參考資料

參考資料:
1、通過分區(Partition)提升MySQL性能(一)
2、通過分區(Partition)提升MySQL性能(二)
3、mysql 的 partition 與 auto_increment

在 [1] 的效能測試中,建了兩個資料表,一個使用 Partition、一個沒使用
然後用隨機產生的資料填入 800 萬筆資料,測試找出時間是 1995 年間的所有資料
在沒有做 Partition 的資料表中花了 38 秒,有做 Partition 的則只花了 3 秒多~
當資料有顯著的分區效果時,做 Partition 可以降低搜尋資料的時間~

不過後來又找到 [3],同時使用 AUTO_INCREMENT 跟 Partition 時 MySQL 可能會有問題....
目前暫定的作法是使用 Partition、放棄 AUTO_INCREMENT
用其他 INDEX 來盡可能達到類似 AUTO_INCREMENT 的 PRIMARY KEY 效果~

2012年3月20日 星期二

在 Java 中寫入 XML 檔案:使用 dom4j

dom4j 官方網站:http://dom4j.sourceforge.net/
在 Java 存取 XML 的方法好像有不少,會選 dom4j 是因為據說它效率還不錯,而且用法簡單!

以下直接舉一個寫入 XML 的例子
我是先建立一個 XMLConstructor 的 Class,然後在 main 去呼叫它~

2012年3月15日 星期四

使用網路爬蟲(Crawler)擷取網頁資料:以 niocchi 為例

這個標題寫得很像論文的標題 XD
不過總之目標是要抓網頁內容下來。

Open Source 的 Web Crawler 可以參考 [1]
網路上比較多人使用的好像是 Heritrix,不過我看了一下他的資料還是不太會用 XD
Crawler4j 原本要使用,但後來也不知道怎麼用,所以又繼續 survey 了
最後我選擇的是 niocchi....原因嘛...其實沒什麼特別的原因 XD
主要優點大概是它號稱使用 NIO 來進行 crawling,還有它最後更新時間是 2011 年!XD

niocchi 官方網站可參考 [2]。

2012年3月2日 星期五

安裝 jProfiler6 到遠端的 Linux 主機

jProfiler 簡介就免了~
我的主要目的是想追蹤 JVM 上的 thread 狀況
看那些我開出來的 thread 是不是在任何情況都會正確地被關閉。

公司給的 JProfiler 6.2.4 有包含 Windows 的 GUI 跟 Linux 的 jprofiler_linux_6_2_4.tar.gz
安裝時首先要先把遠端機器上的 tomcat 關掉,接著按照以下的流程:

2012年2月15日 星期三

(暫存) PreparedStatement

PreparedStatement 的基本優點是程式碼好讀(不用自己拼字串)、提供一定程度的 SQL injection 防禦,還有有些資料說 PreparedStatement 效率比較高。

相關資源:
1. 官方文件
2. PreparedStatement & Statement 的差異
3. PreparedStatement 真的比 Statement 快嗎?

效率問題可以參考看看第三個連結,SELECT 狀況下大多是 Statement 比較快一點,不過下面回應有人說 PreparedStatement 主要是快在非 SELECT 的指令。(但話說資料庫最常用的指令應該是 SELECT 吧~XDDD)

範例程式碼可以參考:Using the Prepared Statement Twice

Cron Expression

參考資料:Lesson 6: CronTrigger

雖然說上面的參考資料是 Quartz 的文件,不過查 Wikipedia 時,查到的 Cron Expression 表達方法是一樣的,所以....應該 Linux 上的 cron 跟 Quartz 的 CronTrigger 用的都是同一套標準吧!(也許 Cron Expression 是公定的標準?我不太清楚 XD)

Cron Expression 的規則就看 Wiki 吧,這裡要特別記錄的是使用「/」記號的規則
在分鐘的欄位設定 3/20 表示的是「every 20th minute of the hour, starting at minute three」
也就是從第三分鐘開始,每個第 20 分鐘執行一次
而設定 /35 表示的並不是每隔 35 分鐘執行一次,而是同義於 0-59/35
也就是每個小時的 0 分開始算第 35 分鐘執行一次。
亦即實際執行時會是例如 1:35、2:35、3:35、.....,實際上會變成每小時執行一次。

PS. 這是我目前對 Cron Expression 的理解,如果有錯誤請指正!

2012年2月6日 星期一

Java 複製目錄

Java 內建沒有檔案複製的 API,原本想直接用 Linux 的 cp -r
(因為我們公司的環境是在 Linux 上跑 Java 的 Web Application)
但怎麼試都試不出在 Java 裡面透過 Runtime 來下 cp 可以把某個資料夾內所有檔案複製到指定目錄
好像是因為 Runtime 不能用 * 這種字元,然後改成寫 shell script 從 Java 呼叫也還是有問題
所以只好自己寫一個~
其實說是自己寫一個,因為實在懶得寫 XD,所以直接找了人家寫好的 code 來小改一下!

參考資料:JAVA COPY整個資料夾
整體結構跟上面連結的程式碼一模一樣,只是改了一點點 error handling 跟習慣的變數用法。

2012年1月11日 星期三

Java 定時執行 (2):將 ExecutorService 產生的 Thread 加入 ThreadGroup

前一篇文章提到,我選用 ExecutorService 的主因是比較好控制 Thread
而控制的方法是把要產生的 Thread 都加入到我指定的一個 ThreadGroup
如此一來我只要用 ThreadGroup.activeCount() 就可以知道定時執行產生的 Thread 還有幾個正在執行
也可以用 Thread.enumerate(new Thread[ ]) 把 ThreadGroup 中的 Thread 列舉出來。

參考資料:How to use ThreadFactory in Java