2011年9月30日 星期五

\Java\jre6\lib\ext\QTJava.zip不應該存在

灌weblogic Server 時,如果出現了下面的錯誤訊息:
Error: “\Java\jre6\lib\ext\QTJava.zip was unexpected at this time.”
解決方法:

去找 環境變數 (我的電腦,右鍵,內容,環境變數) 裡的  %CLASSPATH%
會有 CLASSPATH=.;C:\Program Files (x86)\QuickTime\QTSystem\QTJava.zip
把 它拿掉就可以了

參考來源:http://ora-soa.blogspot.com/2011/06/error-javajre6libextqtjavazip-was.html

2011年9月29日 星期四

SSL X509憑證:SSL的概念


(Public/Private Key 的不對稱加解密法 (Asymmetric Encryption) ,可以把 Public Key 告訴全世界, Private Key 自己秘密保管好,要傳資料給妳的話,只要用妳的 Public Key 加密,全世界就只有妳的 Private Key 才解得開。這種不對稱加解密法雖然很安全,但是加解密的速度很慢。反過來說,傳統的對稱式加解密法 (Symmetric Encryption) ,雖然加解密速度快多了,但是雙方都要握有同一個 Key ,把 Key 傳給對方途中,會有被攔截監聽的危險。) come from  如何製作 SSL X.509 憑證?

Public Key 和 Private Key :用比較白話的說法是:每個人都有兩把鑰匙:
一把是自己留著的叫Private Key (這是死都不可以給別人的);
一把是給別人的叫Public Key(這是要儘量散出去,讓大家使用的)。
更白話的話:就看圖解 = =
未命名

SSL採兩階段的資料加密方法:

第一階段是:先用 Public Key 和 Private Key 做不對稱加解密。傳給對方接下來傳真正資料時,對稱式加解密法要用的 Key 。所以,先傳public key給對方,然後,對方用你給的public key加密—> “一把 對稱式的key” ,傳回來給我,我再用我的private key解讀出來,後來的資料傳輸,的資料加密,就用這把”對稱key”

(Public/Private Key 的不對稱加解密法 (Asymmetric Encryption) ,可以把 Public Key 告訴全世界, Private Key 自己秘密保管好,要傳資料給妳的話,只要用妳的 Public Key 加密,全世界就只有妳的 Private Key 才解得開。這種不對稱加解密法雖然很安全,但是加解密的速度很慢。反過來說,傳統的對稱式加解密法 (Symmetric Encryption) ,雖然加解密速度快多了,但是雙方都要握有同一個 Key ,把 Key 傳給對方途中,會有被攔截監聽的危險。)

第二階段是:用這個對稱式加解密的 Key ,來傳原本要傳的資料。真正傳資料時用的,其實是對稱式加解密法。這個傳資料用的對稱 Key 是用亂數取的,再用 Public/Private Key 法傳給對方,每一次連線時都不一樣。用這種兩階段式的作法, Key 是用不對稱加解密法傳給對方的,不用擔心中途被欄截,也能夠享受合理的加解密速度。

所以總共有幾把Key呢?
答案是三把。Private Key、和由Private Key推得的Public Key、和真正資料傳輸時,用的key

那一把鑰匙是兩邊都有:資料傳輸時,用的key 和,對方的 Public Key

那一把鑰匙是別人都沒有的:Private Key (只用來解密 用 Public Key加密的資料)


參考資料:http://www.imacat.idv.tw/tech/sslcerts.html.zh-tw#sslx509

2011年9月28日 星期三

SSL X509憑證:金鑰(PKI)的概念

SSL x509:為什麼從一把刀(鑰匙) 變成 兩把刀(鑰匙)

很久很久以前,有人把數學的邏輯,運用在網路資料的加密上面,本來資料在網路上,傳來傳去時,走的都是明碼,每個人都看得到你在寫三小給對方。

所以,後來,就有數學家說:我們來用數學的演算法,來加密資料,於是,就有了金鑰,之後,走http的傳輸,都流行用 金鑰 加密,就變成了s (https),來代表這條資料傳輸的通道是有加密的,不會立刻被駭客看光光,所以,就有了:你要送機密的資料給我嗎? 等一下,我先傳一把鑰匙給你,你用這把鑰匙的數學演算邏輯把資料把資料加密後,再傳送給我。以確保資料的安全。

但是,有一天,駭客居然也拿到了我的這把鑰匙了,我傳給我馬子的清涼照,就全部被駭客解密了 = =。

這時候,有個學生剛好在上密碼學,他就碰到了這個問題,就問老穌”說:為什麼不要用兩隻鑰匙呢?  

一支給別人、用來加密;一支自己用,用來解密。

以上,就是金鑰的演化流程。

 

要做憑證,第一步要先了解:
1。什麼是 Public Key 和 Private Key (Public Key Infrastructure /Private Key Infrastructure)
2。資料在傳輸時,Public Key 和 Private Key 的使用時機

(待續…)

2011年9月27日 星期二

SQL Server 更改欄位的資料型別

目前資料庫中有一個紀錄費率的欄位是FLOAT。因為FLOAT本身不是精準的數字,造成比對時的困擾,所以把他改成DECIMAL。以下是T-SQL的語法:

ALTER TABLE FeeRate ALTER COLUMN Rate DECIMAL(8,6); 

改完後發現欄位從原本不可空值變成可空值,原來他是整個重新定義欄位,所以:

ALTER TABLE FeeRate ALTER COLUMN Rate DECIMAL(8,6) NOT NULL;

2011年9月22日 星期四

使用Spring JdbcTemplate產生序號(流水號)

常常會有碰到需求是要 按照一定的規則產生流水號
通常,這種序號會有兩種呈現方式:
第一種是:在新增資料時,就要先有序號在畫面上
第二種是:資料實際要進資料庫時,才去取號
比較常見的三種產生序號的做法是:

  1. 寫 stored procedure,確保不會同時有兩個程式在執行
  2. 在insert的sql中寫序號產生的邏輯
  3. 將序號產生的邏輯抽出來共用,需要的時候再呼叫它

不論是那一種呈現方式,或那一種序號產生的做法,最重要的都是:
「如何避免序號重複 及 重複的處理」
所以,在建立資料庫表格的欄位時,序號的欄位一定要是 unique或primary key
那當序號有重複時,就可以做進一步的處理:
如讓程式拋出exception、或是重新再取一次序號
JDBC偵測的方式可參考這篇文章:JDBC 欄位資料重複的偵測方式

以下是informix資料庫,搭配spring JdbcTemplate 並用上述的第三種做法,把它抽出來做成元件。JdbcTemplate會將JDBC的錯誤解譯後在丟出比較恰當的例外。在這裡的程式會判斷是否丟出DataIntegrityViolationException。
序號的規則是:兩碼英文字 + 三碼中國年 + 二碼月份 + 五碼流水號的做法,再加上判斷每個月的流水號要再重新開始。

資料庫的規格是:

   1: CREATE TABLE foo (
   2:     id INT PRIMARY KEY,
   3:     serial_number NVARCHAR(16) UNIQUE,
   4:     time_created DATETIME YEAR TO SECOND DEFAULT CURRENT YEAR TO SECOND
   5: );
   6: CREATE SEQUENCE foo_seq; 




class-1。序號新增的程式



   1: public class SerialNumberedInserter {
   2: /**
   3: * 看有幾種不同的序號邏輯,都要繼承這個介面
   4: */
   5: public interface SerialNumberGenerator {
   6:     String generate();
   7: }
   8: /**
   9: * 建構子,可以自訂重覆取號的最大次數
  10: * @param jdbcTemplate 對資料庫存取的jdbcTemplate
  11: * @param generator 產生流水號的邏輯,實作Generator介面
  12: * @param maxRetry 最多重複嘗試產生流水號的次數
  13: */
  14: public SerialNumberedInserter(JdbcTemplate jdbcTemplate,
  15:                         SerialNumberGenerator generator,
  16:                         int maxRetry) {
  17:     this.jdbcTemplate = jdbcTemplate;
  18:     this.generator = generator;
  19:     this.maxRetry = maxRetry;
  20: }
  21: /**
  22: * 建構子,預設可以重覆取號三次
  23: * @param jdbcTemplate 對資料庫存取的jdbcTemplate
  24: * @param generator 產生流水號的邏輯,實作Generator介面
  25: */
  26: public SerialNumberedInserter(JdbcTemplate jdbcTemplate,
  27:                     SerialNumberGenerator generator) {
  28:     this(jdbcTemplate, generator, 3);
  29: }
  30:  
  31:  
  32: /**
  33: * @param sql 要執行新增的sql,如:
  34: *     INSERT INTO foo (id, serial_number) VALUES( ?, ? )
  35: * @param param jdbcTemplate要執行sql時需要的參數,如: 
  36: *     Object[] param = new Object[]{ foo_seq, "" ); 序號的地方要傳空值。
  37: * @param serialNumberPos 序號欄位在param中的第幾位(第一位是0)。
  38: * @param retries 目前因失敗已重複嘗試產生序號的次數
  39: */ 
  40:  
  41: private void tryInsert(String sql, Object[] param,
  42:                 int serialNumberPos, int retries) {
  43:     param[serialNumberPos] = generator.generate(); //取號
  44:     try {
  45:         jdbcTemplate.update(sql, param);
  46:     }
  47:     catch (DataIntegrityViolationException ex) {
  48:         if (retries < maxRetry) {
  49:         tryInsert(sql, param, serialNumberPos, retries + 1);
  50:     }
  51:     throw ex;
  52:     }
  53: }
  54:  
  55:  
  56: /**
  57: * 產生序號,並執行資料新增
  58: * @param sql 要執行新增的sql,如:
  59: *     INSERT INTO foo (id, serial_number) VALUES( ?, ? )
  60: * @param param jdbcTemplate要執行sql時需要的參數,如: 
  61: *     Object[] param = new Object[]{ foo_seq, "" ); 序號的地方要傳空值。
  62: * @param serialNumberPos 序號欄位在param中的第幾位(第一位是0)
  63: */ 
  64:  
  65: public String insert(String sql, Object[] param, int         serialNumberPos) {
  66:     tryInsert(sql, param, serialNumberPos, 0);
  67:     //如果有需要將產生的序號,存到另一個TABLE當FK時,可以將序號傳回
  68:     return param[serialNumberPos].toString();
  69: }
  70:  
  71: }



class-2。序號邏輯的程式



   1: /**
   2: * 都要實作 "序號邏輯"
   3: */
   4: public class SerialNumberGenerator implements SerialNumberedInserter.SerialNumberGenerator {
   5:     JdbcTemplate jdbcTemplate;
   6:     String tableName;
   7:     String serialNumberName;
   8:     String timeCreatedName;
   9:     String functionName
  10: /**
  11: * 建構子,在這裡將序號產生需要的參數都傳進來
  12: */
  13: public SerialNumberGenerator(JdbcTemplate      jdbcTemplate, //spring jdbcTemplate
  14:                String tableName, //資料表的名稱
  15:                String serialNumberName, //你取的序號欄位的名稱
  16:               String timeCreatedName, //資料建立的時間的欄位名稱
  17:                String functionName) { //序號的前兩碼功能代碼
  18:        this.jdbcTemplate = jdbcTemplate;
  19:        this.functionName = functionName;
  20:        this.tableName = tableName;
  21:        this.serialNumberName = serialNumberName;
  22:        this.timeCreatedName = timeCreatedName;
  23: }
  24:  
  25:  
  26: /**
  27: * 把資料庫中,目前最大的序號,找出來,informix找不到時,會回傳0
  28: *
  29: * 解析SQL:
  30: * MAX(%3$s) -> MAX(serialNumberName):
  31: *        先把最大筆的資料找出來,這樣資料庫的效能會比較好
  32: *
  33: * CAST( SUBSTR(MAX(%3$s), -5) AS int) -> 
  34: *        把序號 TW1000900001 取出來為 00001,並轉為數字
  35: *
  36: * timeCreatedName >= MDY(MONTH(TODAY), 1, YEAR(TODAY)):
  37: *        大於每個月1號,每個月流水號要從新開始
  38: *        (注意:這是INFORMIX的寫法,其它資料庫未必符合)
  39: *
  40: * %3$s LIKE ? -> serialNumberName LIKE TW10009%:
  41: *        這樣寫會讓效能比較好,不需要特別拆字去比較
  42: */
  43: private static String COUNT_SQL = 
  44:     "SELECT CAST(SUBSTR(MAX(%3$s), -5) AS int) FROM %1$s" +
  45:     " WHERE %2$s >= MDY(MONTH(TODAY), 1, YEAR(TODAY))" +
  46:     " AND %3$s LIKE ?" ;
  47:  
  48:  
  49: /**
  50: * 實作 "序號邏輯.序號產生" 的方法
  51: */
  52: public String generate() {
  53:     String serialNumber = null;
  54:     String prefix;
  55:     int count;
  56:     Calendar cal = Calendar.getInstance();
  57:     int year = cal.get(Calendar.YEAR) - 1911; //改成中國年
  58:     prefix = String.format("%1$s%2$03d%3$tm", functionName, year, new Date() ); //java.util Formatter
  59:     //取得目前資料庫中最大的序號
  60:     count = jdbcTemplate.queryForInt(
  61:     String.format( COUNT_SQL, tableName, timeCreatedName, serialNumberName),prefix + "%");
  62:     return String.format("%s%05d", prefix, count + 1); //回傳目前資料庫最大的序號加1,沒有資料會回傳0
  63: }
  64: }






class-3。需要取序號的程式


   1: public void insert(BeanObj bean) {
   2:     //新增資料的sql
   3:     String sql = "INSERT INTO foo (id, serial_number )" +
   4:                 " VALUES ( foo_seq, ? )";
   5:     String function = "TW";
   6:     /*
   7:     * 產生序號,請傳:
   8:     * 1、JdbcTemplate
   9:     * 2、資料庫table名稱
  10:     * 3、資料庫中序號的欄位名稱
  11:     * 4、資料庫中資料建立日期的欄位名稱、
  12:     * 5、功能代碼(序號所需要的代碼,可以多個 = =)
  13:     */
  14:     SerialNumberGenerator generator = new SerialNumberGenerator (jdbcTemplate,"table_name", "serial_number", "time_created", function);
  15:     //對應到新增資料時需要傳的參數
  16:     Object[] param = new Object[]{""};
  17:   
  18:      //請將序號的欄位,傳空格進來
  19:     SerialNumberedInserter inserter = new SerialNumberedInserter(jdbcTemplate, generator);
  20:     
  21:     inserter.insert(sql, param, 0);
  22:     //0的話是指:序號是在你組的 新增資料的sql中的第幾個位置,我是把它放在最前面,就第一個問號(?)的地方
  23:     //實際執行新增的程式,請傳入:1你組好的新增sql,2新增的資料(param),3指定序號是在param中的第幾個,(從0開始)
  24: }





備註:java.util 的 Formatter 功能真的很強大,有空可以好好研究一下

Excel資料欄顯示工作表名稱

Excel欄位可以設定成自動顯示工作表(worksheet)的名稱,譬如說一個成績單檔案中,一張工作表代表一個學生的成績,工作表的名稱設成學生的名字。這時在工作表中某個欄位輸入下面的內容就可在那個爛位直接顯示該學生的名字:
=MID(CELL("filename", A1),FIND("]", CELL("filename", A1)) + 1, 255)

說明
CELL
這裡面最重要的就是CELL函數。CELL可以回傳很多種關於Excel檔案中某欄位的相關資料。如果第一個參數是"filename",他就會回傳這個檔案的完整路徑及工作表名稱的字串,其中完整路徑會用中括號包起來,如下:
[C:\Book1.xls]Sheet1
上面字串的意思是在C:\Book1.xls檔案中叫做Sheet1的工作表。

CELL第二個函數是指定回傳哪一個資料欄的相關資料,因為我們只是要取得工作表的名稱,所以哪一個欄位不太重要,我們就直接指定A1,也就是左上角第一個欄位。如果沒有指定資料欄,這個函數會回傳最後一個被更動欄位的相關資料。

MID
MID是拿來擷取部分字串的函數,我們使用MID從CELL回傳的字串中擷取工作表名稱。第一個參數是要開始擷取的位置(從第幾個字開始抓),第二個參數是指定要抓多長的字串。我們用FIND找到右邊中括號(])是在字串中第幾個位置,然後指定要抓255長度的字串。因為Excel工作表名稱不得超過31個字,所以擷取255個字是綽綽有餘。當字串已被擷取到最後一個字後,就不會再繼續抓了。

FIND
FIND就是在字串中找出另一個字串在裡頭的位置。第一個參數是要找到的內容,第二個參數是被搜尋的字串。

2011年9月19日 星期一

如何在portal(portlet)中取得 HttpServletRequest

portal中,為了每個獨立的portlet能獨立處理自已的邏輯,所以在request中就區分了兩種request:javax.portlet.ActionRequest 和 javax.portlet.RenderRequest。
那這時如果要取得:javax.servlet.http.HttpServletRequest 的參數的話:就要寫:
javax.servlet.ServletRequest servletRequest = (javax.servlet.ServletRequest) request.getAttribute("javax.servlet.request");
再把 servletRequest cast HttpServletRequest,就可以了, 如下:
HttpServletRequest request = (HttpServletRequest) servletRequest;

補充:
如果要把這個抽出來寫一個 讓RenderRequest和ActionRequest 共用的method
因為:
portlet的 RenderRequest 是繼承 javax.portlet.PortletRequest

public abstract interface javax.portlet.RenderRequest extends javax.portlet.PortletRequest

ActionRequest 是繼承 ClientDataRequest ,它又繼承 PortletRequest
public abstract interface javax.portlet.ActionRequest extends javax.portlet.ClientDataRequest
public abstract interface javax.portlet.ClientDataRequest extends javax.portlet.PortletRequest

   private HttpServletRequest getHttpRequest(PortletRequest request) {
        javax.servlet.ServletRequest servletRequest = (javax.servlet.ServletRequest) request.getAttribute("javax.servlet.request");
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        return httpRequest;
    }

這樣就可以在 processaction和doview,這樣寫

HttpServletRequest httpRequest = getHttpRequest(request);