2011年11月9日 星期三

JAI TIFF Split 很慢的解決方式

這個是協助外包廠商解決 Java 使用 JAI 去切割 TIFF 檔的分頁很慢的問題

TIFF 檔基本上可以分為 單檔單頁 , 或是 單檔多頁
因為廠商寫出來的切檔程式速度很慢 , 因此本於能夠盡快導入系統的責任感 , 進行了協助測試



TIFFSeparater Ver 3.0

這個也有變快 , 沒有使用到 NIO , FileChannel ; 取消 FineInputStream ;
因為再次測試的結果 , 發現跟 NIO 也沒有太大的影響 , 所以應該是  Stream 的問題


//ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", fis, null);  //java.io.FileInputStream  這個版本很慢

ImageDecoder decoder = ImageCodec.createImageDecoder("tiff" , stream , null);//com.sun.media.jai.codec.SeekableStream

                                                                                                      //com.sun.media.jai.codec.FileSeekableStream         (A)都在可以接受的速度

                                                                                                      //com.sun.media.jai.codec.ByteArraySeekableStream   (B) 都在可以接受的速度

--------------------------------
TIFFSeparater.java


import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.StopWatch;

import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFDirectory;
import com.sun.media.jai.codec.TIFFEncodeParam;

public class TIFFSeparater {
        public static void splitImage(Collection imgFiles, String forms, File destPath) throws IOException {

        System.out.println("Enter split methd ");
        StopWatch stopWatch = new StopWatch();

        stopWatch.start();
        FileUtils.cleanDirectory(destPath);
        stopWatch.stop();
        System.out.println("FileUtils.cleanDirectory Spend time: " + stopWatch.getTime()/1000f);
        stopWatch.reset();


        String[] form_id = forms.split(";");
        ArrayList file_list = new ArrayList();
        for (File imgFile : imgFiles) {  //for1
           stopWatch.start();
        //FileInputStream fis = new FileInputStream(imgFile);         
        //FileChannel fisChannel = fis.getChannel();
        //ByteBuffer buffer = ByteBuffer.allocate((int)fisChannel.size());
            //fisChannel.read(buffer);
            //fisChannel.close();
            //SeekableStream stream = new ByteArraySeekableStream(buffer.array());
       
        SeekableStream stream = new FileSeekableStream(imgFile);
       
            //ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", fis, null);
        ImageDecoder decoder = ImageCodec.createImageDecoder("tiff" , stream , null);
        stopWatch.stop();
             System.out.println("ImageCodec.createImageDecoder(" + imgFile + ") Spend time: " + stopWatch.getTime()/1000f);
            stopWatch.reset();
       
       
                int page_count = decoder.getNumPages();
                for(int i = 0; i < page_count; i++) {   //for2
               
                       
                    File dest = new File(destPath, UUID.randomUUID().toString() + ".tif");
                   stopWatch.start();
                    RenderedImage ri = decoder.decodeAsRenderedImage(i);
                   stopWatch.stop();
                    System.out.println("decoder.decodeAsRenderedImage( " + i + " ) Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();

                   stopWatch.start();
                    TIFFDirectory td = (TIFFDirectory)ri.getProperty("tiff_directory");
                   stopWatch.stop();
                    System.out.println("ri.getProperty(tiff_directory) Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();

                   stopWatch.start();
                    TIFFEncodeParam tep = new TIFFEncodeParam();
                     stopWatch.stop();
                  System.out.println("new TIFFEncodeParam() Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();

                   stopWatch.start();
                    tep.setExtraFields(td.getFields());
                     stopWatch.stop();
                  System.out.println("td.getFields() Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();

                   stopWatch.start();
                    tep.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
                     stopWatch.stop();
                  System.out.println("ep.setCompression Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();


                   stopWatch.start();
                    FileOutputStream fos = new FileOutputStream(dest);
                    FileChannel fosChannel = fos.getChannel();
                   
                     stopWatch.stop();
                  System.out.println("new FileOutputStream(" + dest +") Spend time: " + stopWatch.getTime()/1000f);
                   stopWatch.reset();




                   stopWatch.start();                 
                   ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", fos, tep);
                 
                        encoder.encode(ri);
                    fos.flush();
                    fos.close();
                        IOUtils.closeQuietly(fos);               
             
                     stopWatch.stop();
                  System.out.println("new FileOutputStream(" + dest +") Spend time: " + stopWatch.getTime()/1000f);

                   stopWatch.reset();


                    file_list.add(dest.getName());
                }  //for2
                //IOUtils.closeQuietly(fis);
}        //for1
       
        FileUtils.writeLines(new File(destPath, "Context.dat"), file_list, "\r\n");
        System.out.println("Exit split method");
    }

public final static void main(String args[]) throws Exception {

if (args == null || args.length < 2)
{
System.err.println("Check your agrument is correct!!");
System.exit(-1);
}
Collection tifs = new ArrayList();
StringBuilder formsBuilder = new StringBuilder();
String filesString [] = args [0].split(";");
for (String string : filesString) {
tifs.add(new File(string));
formsBuilder.append( string + ";");
}

// tifs.add(new File("D:/111252f58002242b.tif"));
// tifs.add(new File("D:/111252f58002242d.tif"));
// tifs.add(new File("D:/111252f58002242c.tif"));

System.out.println(formsBuilder.toString());
System.out.println(args[1]);
splitImage(tifs,formsBuilder.toString(),new File(args[1]));

}

}


2010年5月8日 星期六

處理 UBUNTU 檢視 RAR 壓縮檔案內檔案名稱為亂碼的問題

處理 UBUNTU 檢視 RAR 壓縮檔案內檔案名稱為亂碼的問題

(每次 UBUNTU 有新版時 , 我都會重灌 , 但是有些時候  ,有些設定就會忘記 , 所以補記在此)

檢查你的 UBUNTU 是否有安裝套件 , rar 及 unrar
有的話 , 請一定要移除

要解壓縮 RAR , 請安裝以下套件:
(1) p7zip-full
(2) p7zip-rar

安裝完成後 , 再去開啟 RAR檔案時 , 就可以看到 RAR裡面的檔案名稱已經可以正常顯示了

剛剛裝完 UBUNTU 10.04 暫時沒有發現甚麼大的問題

2010年2月21日 星期日

企管小故事 - 老子 第七十九章 - 有德司契 , 無德司徹

企管小故事 - 老子 第七十九章
今晚去洗衣服 , 順便看看書 , 老子他說續集
其中看到此篇 , 頗有感觸 , 所以特別記錄下來
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
老子 第七十九章
和大怨 , 必有餘怨 ,
安可以為善 , 是以聖人執左契 , 而不責於人
(大意是說 , 要化解大怨 , 但是一定會有小怨 , 小怨有可能又會變成大怨
所以聖人只掌握大原則 , 而不求人人無怨)


有德司契 , 無德司徹
(大意是說: 聖人之道講的是領導階層的大政治家 , 只把握原則 , 不求管理
          無德的統治 , 就是到處設規則)
天道無親 , 常與善人
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
這篇跟管理學 , 領導學 , 還有ISO標準等等可以扯的上邊...

所以管理是甚麽 , 領導又是甚麽?
有些公司拼命的設定一大堆的制度規則 , 然後公司或是集團大點的,
還要搞個集團的規定 , 子公司的規定 , 甚至是部門的規定 ,
各部門或是事業群還要搞個內規 , 外規(說一套 , 作一套)
訂了那麼多規定 , 能不能保護客戶免於危機 ? 看一下這次金融危機的起源 ,
每次金融危機事件出現之後 , 就會有單位出來針對金融危機事件訂定一大堆規則規定 ,
於是乎 , 規定越來越多 , 但是呢 , 只有一有事件出現都是大問題 ,
不是說 , 定規定可以保護客戶或是保護公司嗎 ? 為甚麽問題越出越大包??

所以看到這裡 , "有德司契 , 無德司徹"
當一間公司在推行任何 ISO或是安控制度時 , 即便你訂了一萬條的規定 , 但是只要有那麼一個人搞漏洞 , 馬上就會破功
與其搞得萬分複雜 , 還不如掌握大原則 ,
與其規範這個不能作 , 那個不能作 , 還不如養成大家的觀念 , 觀念建立了 , 大家就能夠自行判斷什麽可以作 , 甚麽不能作
而不是死板板的把能作的也變成不能作

管理之道是要訂定鉅細靡遺的規定還是掌握大原則 ?

每個人的看法各有不同 , 或許作法也不同~

2010年2月18日 星期四

加班 & 不加班



加班 & 不加班

首先要聲明的是 我對這兩種並沒有特定的偏好或厭惡

重點是企業以及企業主管本身會不會嘴裡說得跟實際做的是兩回事

去逛書店時 , 看到一個日本人寫的關於不加班的書籍 , 當然要作這種事 ,一定是由上而下推行

特別是在日本這種工作狂的社會體系 , 裡面提到

作者每天一到下班時間就把燈關掉 , 一開始 , 員工也是不能適應 , 但是沒有員工會違反規定把燈打開....


反回頭看看國內 , 到底有多少心口不一的企業體 ?
例如: 告訴你 , 不要加班  , 但是在面試的時候會問你 , 如果假日加班或者是常常需要加班時 , 你能不能配合 ?

例如: 告訴你 , 不要加班 , 但是當你準時下班時 , 會有其他部門或是員工出現雜音: 某某人是否工作太過清閒 , 為何都可以準時下班 ....之類的 , 然後主管還會特別為了這些雜音來跟你打招呼...

例如: 告訴你 , 不要加班  , 但是升遷時 , 是以常常在加班的人升為主管


再次聲明的是 我對這兩種並沒有特定的偏好或厭惡

但是檢視一下國內有多少企業跟主管的說與作彷彿得了精神分裂症一般背道而馳...

這種令人無所適從的情況存在與否 , 也是檢視你所在的企業體與主管是屬於甚麽等級的方法之一

2010年2月16日 星期二

給不動腦主管看的企管小故事 - 老猴子新猴子吃香蕉的故事

以下關於猴子的實驗 , 不是我寫得 , 所以特此聲明

~以下是引用自網路上的猴子的實驗~(到底誰是原創者 , 我也弄不清楚 , 說明是引用)
行為學家做了一個實驗,他們把五隻猴子關在一個籠子裡,
在籠子高處掛了一串猴子最愛吃的香蕉,另外一邊則安裝了一個灑水器,如果香蕉被碰到,就會讓灑水器自動噴水。

實驗開始,有一隻猴子聞到香蕉的芬芳,受不了誘惑,猛力跳起想要拿到這垂涎已久的食物,
結果灑水器發揮作用,把其他的猴子都淋溼了,溼猴子遭受到無妄之災,就把剛剛那隻好吃的猴子痛打一頓。

沒多久,另外一隻猴子也想嚐試看看,於是牠又縱身一跳,灑水器又再次把其他四隻淋溼了,
這四隻猴子二話不說又把牠痛扁一頓,而打最用力的是原先的第一隻猴子,當然牠後來也學乖了。

接下來的猴子都在想,自己應該不會那麼倒楣,也分別去嚐試,結果被打得一個比一個慘,
於是猴子們都得到了共識,只要有人去拿香蕉,大家都要跟著遭殃,而任何一隻猴子都從此不敢有此意圖。

接下來,行為學家將灑水器拿掉,並且更換一隻新猴進去,這新猴子看到香蕉興奮不已,馬上想去拿來大快朵頤,不料牠一靠近,就被其餘四隻老猴子痛打一頓,

行為學家繼續依次換掉所有的猴子,每次新猴子要幹同樣的事時,結局都是鼻青臉腫,

最後籠子裡是五隻新的猴子,香蕉還一直掛在那裡,而猴子卻搞不清楚,為什麼不能去碰它?

~~~以上是引用自網路上的猴子的實驗~~

以上引用結束 , 網路上關於這個實驗的故事版本很多 , 大致上就是描述 , 猴子在某種制約狀況下養成了習慣 , 導致新猴子老猴子都變成了習慣 , 但是不知道這個原因是什麼

為什麼標題要說 這是給不動腦主管看的企管小故事 - 老猴子新猴子吃香蕉的故事

因為這個故事常常被拿去引用在企業界 , 大致上就是說 , 公司的老員工會阻止新員工去作某些
應該作 , 可以作 , 或是創新的行為 ,

感覺上就是把所有的過錯統統變成是公司的老員工的錯 , 似乎老員工就是糟糕~

但是像這樣的企管故事還有不少主管拿出來轉寄 ,

我只能說這是給不動腦主管看的企管小故事 ,


首先要知道 , 老員工的在變成老員工之前 , 也是新員工 , 他們習慣的養成 , 也是由以前的老員工培養

很多時候新員工被老員工打壓或是給予錯誤的教育時 , 主管就在旁邊 , 但是卻是放任如此的行為 , 久而久之 , 新員工就是如此被養成 , 然後當新員工變成老員工時 , 就是用他學到的那一套去教新員工 ,

慢慢地 ,

就變成了 , 好像主管如果要推展新事物受到阻力 , 好像都是老員工的錯 ~

但是要追根究底 , 要問的是 : 公司的主管是死了還是瞎了 , 還是無能 ,

所以如果你的主管也曾經轉寄過老猴子新猴子的實驗給你看過的話 , 只能說你的主管也在不動腦主管的行列中

請先想想 , 主管的責任是什麼 , 管理公司要什麼 , 作什麼 , 而不是憑著老員工去作

所以當主管把公司或是部門無法創新無法改革的錯誤推到老員工(老猴子)
身上的時候  , 反應出來的不過就是主管無能 , 根本不懂得管理

想一下公司/部門的制度/規定哪一項不是經過主管同意的 ?
但是如果到最後卻變成公司或是部門無法創新無法改革的原罪時 ,

主管在做什麼?


2010年1月31日 星期日

UBUNTU - Java GUI 中文顯示不正常 - 出現方框




UBUNTU 8.10 / UBUNTU  9.10 - Java GUI 中文顯示不正常 - 出現方框

因為最近重裝 Notebook ,
重新安裝 Sun JDK 6.0 , 搭配另一個使用Java 開發的軟體結果
發現Java GUI 程式有些中文字顯示不正常  , (只有某些中文字出現方框 , 不是全部出現方框)
但是直接使用gedit去看資料檔的內容又是對的 ,

所以就猜測是 JRE , JDK 的字型設定不正常 ;
測試之後果然如此 ,


解法:
開啟這個字型設定檔
sudo gedit /usr/lib/jvm/java-6-sun/jre/lib/fontconfig.properties


找到 這兩行

filename.-arphic-ar_pl_shanheisun_uni-medium-r-normal--*-*-*-*-p-*-iso10646-1=/usr/share/fonts/truetype/arphic/uming.ttf
filename.-arphic-ar_pl_uming_uni-medium-r-normal--*-*-*-*-p-*-iso10646-1=/usr/share/fonts/truetype/arphic/uming.ttf

(就是把  uming.ttf 改成 uming.ttc , 因為原先的設定是 uming.ttf , 但是我發現我的電腦上根本沒有這個檔案 ,  只有 uming.ttc 就是這樣)
修改成
filename.-arphic-ar_pl_shanheisun_uni-medium-r-normal--*-*-*-*-p-*-iso10646-1=/usr/share/fonts/truetype/arphic/uming.ttc
filename.-arphic-ar_pl_uming_uni-medium-r-normal--*-*-*-*-p-*-iso10646-1=/usr/share/fonts/truetype/arphic/uming.ttc

然後存檔結束 ,

重新啟動我的 Java GUI 程式 , 中文顯示又回復正常了...

那隻Java GUI 程式是 ThinkingRock (是基於 GTD概念的工作管理程式 , 不錯用)

2009年11月25日 星期三

Chrome OS 輸入中文

Chrome OS 輸入中文

因為 Chrome OS 沒有內建中文輸入法 ,

所以使用 Chrome Browser 要在 Web 上輸入中文時 ,


可以使用這個網頁來輸入中文 (支援注音 , 上面雖然說只支援 Firefox , 但是 Chrome Browser 也支援 )
http://people.linux.org.tw/~pofeng/ov-webime/zhuyin-kb.html

使用 Ctrl 鍵切換輸入法狀態 , 其他的都跟使用注音輸入法一樣

2009年11月15日 星期日

OpenProj 專案管理工具軟體

OpenProj 專案管理工具軟體



原始發表日期: 2008/08/10 13:22
又是一個專案管理工具軟體

介紹它 , 有幾個特色:
首先它是 Open Source  , 免費
接著據說它是用 Java 撰寫的
然後它可以直接開啟 MS Project 的專案檔( .mpp) ,這個是先前介紹過的其他專案管理工具所沒有的
既然它是使用 Java 撰寫的 , 當然就可以跨平台 , 所以它也支援 Windows , Mac , Linux

它的官方網站 : http://openproj.org/

請點選畫面右方的 OpenProj 下方的 Download 連結去下載 ,
使用 UBuntu 可以選擇   openproj_1.3-1.deb 的版本下載 ,
使用 Windows 可以選擇 openproj-1.3.msi 來下載
下載完成後直接點兩下就可以安裝

在UBuntu 安裝完成之後 , 啟動軟體的連結
應用程式->辦公->OpenProj 這樣就可以啟動軟體

可以直接開啟 MS Prjoect 的專案檔(.mpp)



以下這個範例是來自於MS Project Template 的網站
匯入後 , 看起來該有的都有




可以另存成 MS Prjoect 的 XML 匯出檔


又是一個可以讓我們在 UBuntu上脫離MS 工具掌控的好幫手....

2009年9月27日 星期日

ExtJS -實戰經驗談001- 處理 TextArea 多行資料問題

ExtJS -實戰經驗談001- 處理 TextArea 多行資料問題

(書上沒說的 , 自己有碰到的 , 並且解掉的問題 , 就順便記下來)
(太基本的 , 請自己去看書)

最近在使用 ExtJS 開發某個 Web ,
其中我使用了 TextArea , TextArea 可以輸入多行資料並且允許你按 Enter 鍵換行...

背端使用 ASP 接收 ExtJS的 Form 傳送過來的TextArea的資料 , 也成功的存入資料庫

但是要從資料庫讀取 TextArea 的資料並且轉成 JSON的資料格式傳回前端給 ExtJS處理時 ,
發現一直出現錯誤...  <怎麼知道找出錯誤的? 待續 - ExtJS -實戰經驗談002- 除錯工具 >
原因是  : 前端的 ExtJS 在處理 傳回的 JSON資料時 , 不能接受跟處理 資料內容中有包含換行符號的資料 ;


那怎麼辦?
(1)
在被端要傳回資料的 ASP 在要把資料轉成 JSON格式傳回前 ,
將資料中的換行符號( \r\n) 給取代掉 代換成


(2)
在前端接受的ExtJS 中 , 把街收到的 JSON 格式資料 ,
重新把 收到的資料中有
的重新換成 (\r\n) , 然後處理過的資料 , 重新塞值到 ExtJS 的 Form 中的
TextArea 中 , 就可以完成由資料庫中還原的動作


注意A:
(1) 只有在還原到前端的的 ExtJS 的 Form 的 Text Area 時需要代換 (
為 (\r\n))
(2) 如果是要放在 ExtJS 的Grid 中的 , 就不需要特別還原
為(\r\n)

注意B:
換行字元大不同....

如果你是使用 IE , 同樣是網頁中的 Form 的 TextArea , 如果是有多行內容的 ,
傳到背端(Server 端) , 收到的換行字元會是 \r\n

但是你是使用 FireFox , 同樣是網頁中的 Form 的 TextArea , 如果是有多行內容的 ,
傳到背端(Server 端) , 收到的換行字元會是 \r (少掉一個 \n)
所以在動作(1)作還原處理的代換動作時 , 要注意是代換 \r\n 還是代換 \r 為

軟體介紹-7-Zip-免費壓縮工具

可以取代 WinZip & WinRAR 的免費軟體
7-ZIP
下載路徑: http://sourceforge.net/projects/sevenzip/
支援以下壓縮格式:
7z, ZIP, CAB, RAR, ARJ, LZH, CHM, GZIP, BZIP2, Z, TAR, CPIO, RPM ,DEB
可以取代 WinZIP , WinRAR 等工具
有 Windows 介面可以使用 , 支援壓縮檔加密(可以設定密碼)等操作 , 也有命令模式
它本身的壓縮格式 7z 更是號稱比 zip 的壓縮率更高 , 也就是壓縮出來的檔案會更小 , 不過如果你選擇 7z 格式的話 , 別人也需要使]用 7-zip 才能解壓縮

可以試用看看

Java - LDAPBROWSER (待撰寫)

Java - LDAPBROWSER (待撰寫)

Lean Six Sigma -談MIS部門的浪費模式

Lean Six Sigma -談MIS部門的浪費模式

眾所周知的 Lean 談的是精實 , 把不必要的浪費給去除
一般來說都是以流程的精實為第一要務
但是在企業內部還有很多是屬於商務流程以外的浪費
其中以MIS部門的浪費為最 ,
特別是當企業越大時 , MIS部門的浪費可能也是最大的
以下要談的跟Lean Six Sigma 無關 , 純粹只是就 Lean 這件事來談
所以你如果是要看Lean Six Sigma 的很抱歉 , 可以離開了
但是如果你是要看 , 避免不必要的浪費的 , 可以繼續往下看

浪費模式一: 搖擺不定的軟體平台或是產品選擇
這種浪費模式 , 很常見 , 常見於一些基本但是必要的軟體產品
例如像 MS Exchange vs Domino Notes
這兩套產品是拿來做 Mail Server的基本功能 ,
但是你會發現有些公司會一下用這個 , 一下用那個...
<不相信 , 你去看看微軟或是IBM的軟體產品的使用公司的簡介中就會發現
  當然 , 因為是競爭關係 ,
所以你看的不是 Exchange 取代 Domino
就是 Domino取代 Exchange
賣產品總是這樣老王賣瓜自賣自誇 , 就軟體供應商來說這樣的行為很常見 ,
但是當你(們的公司)就是消費者是付錢的公司時 , 你就要想想 ,
當初你買 Exchange 要付一次購買費用 , 然後轉換到 Domino 又要付一次購買費用
然後不同產品之間要轉換 , 就必須還要付出轉換的成本
這些行為根本就是浪費...
>
浪費模式二: 使用了多套同質性產品於公司內部
有可能看到的是例如: Database / Flow / web / 開發工具 相關軟體
例如使用了 MS SQL , 又買了IBM DB2 , 又買了 Oracle , 或是又買了 Sybase等等
買了多種都是要拿來做資料庫的軟體 , 感覺有點像是
你買了3台不同廠商的電視機 (都是要拿來看電視的 ,
 不同廠牌的電視可能小功能會有所不同 , 但是拿來看電視這個功能是一定不會變得)
事實上就軟體的購買而言 , 軟體除了初次的購買建置費用還有每年要付出去的維護費用
(一般來說是購買費用的15%吧)
所以當你公司有了多套同性質的軟體時, 每年要付出去的維護費用就是一筆錢 ,
再說 , 一套產品買進來後 ,
你還必須要有對該產品精通或是熟悉的人進行管理或者是微調維護等等作業
意思就是如果如上述的資料庫的例子來說 , 你們必須要有熟悉MS SQL/IBM DB2/Oracle/Sybase 等等的人員
也就是說必須要有這些熟悉的人員 ,
如果沒有 , 要麼就是再聘人 , 要麼再外訓 , 要麼請顧問
這些都是額外的費用 , 要同時搞多種不同的資料庫
在談到Lean時 , 請好好想想有沒有必要讓這麼多種不同的資料庫產品同時存在於公司內部

以下談的可能會跟上述的模式一與模式二有重疊
浪費模式三: 因為公司內部沒有人會用那個產品所以換產品
這個在大型企業中的MIS部門也是常見的事 , 當會的人走掉之後 ,
如果後續的接手者不會 , 則有可能那個軟體產品在公司內部會因為沒有人使用導致形同作廢
當然最後則只好買個新的產品進來公司用
<為什麼不訓練人? 別問我>
浪費模式四: 因為公司要求廠商客製化產品 , 所以廠商有新版本產品時 , 公司無法跟著昇級
台灣有很多公司都是要求產品要客製化 ,
但是當軟體廠商特別為你們公司從Source Code 源頭開了一個特殊版本的客製化版時
也就代表著你們買的那套軟體可能無法享受的原廠軟體有新的 fix 更新 , 或是出了新版本時,
你們公司都無法使用
除非你們公司要再花一次錢請原廠針對新版本幫你們重新客製化一套新版本
我的建議是:一般來說 , 對於原廠軟體盡量不要直接對原始的Source Code 去動手
                     而是要去尋求 plug-in 或是 extension 或者是 interface 去連接
                     建置客製化功能

浪費模式五: 在自行開發與外包開發中搖擺不定
這個在大型企業中也很常見 ,
想要自行開發就要養人 , 必須在內部培訓或是引入有能力的人員
不然就是要導求外包廠商來進行開發
這兩種都可以 , 但是最糟糕的狀況會是搖擺不定
好像離題了 , 剛剛談的不是 公司裡面的軟體產品的問題嗎 ?
怎麼會有自行開發或是外包的問題?
舉 ERP 的例子來說好了
如果你的公司導入了某套ERP ,
當公司內部有新功能要建置時 , 一種是自行開發 , 一種是找外包廠商來開發
這個又會牽扯到上述的 浪費模式一/二/三/四的狀況
因為政策搖擺不定 , 所以可能應該要培訓的人員沒有培訓 ,
然後外包廠商又吃定你沒有人可以用, 所以獅子大開口,
然後這時可能才想要自行養人來開發 , 可是可能等到訓練完到能夠上手精通 , 可能黃花菜都涼了
最後可能就會發生模式三 , 沒人會用所以要換新產品...
<好像這樣 問題就解決了 , 問題就這樣不斷的出現不斷的循環>
有些銀行就很乾脆 , 就是外包 , 擬定嚴格跟好的政策去處理, 而不是搖擺不定的政策

浪費模式六:因為人員對軟體產品的不熟悉或是沒有善用,而購買新的產品達到既有產品所提供的功能
這樣的浪費模式跟浪費模式二很相近但是原因不同
這種情形是人員在不熟悉或是精通產品的情況下,最容易發生。
舉例來說:有的公司會特別去購買 LDAP 產品 , 但是以微軟家族中 至少有兩個部分提供LDAP功能
而且是很多企業會用到 , 那麼為何還要去購買?  同樣的狀況也會出現在 Portal Server 或是 Content Manager Server (不是幫微軟打廣告 , 而是他們的某些產品提供了不少功能 , 但是常常有很多人沒有去挖出來去 , 導致要用A功能時 , 大家就要另外去買 另一套產品 , 要用B功能時 , 又去買另一套產品)

上述這些是在物價不斷上漲 , 全球企業都在尋求減少浪費的觀念下 ,
在MIS部分所可能會發生的浪費與內耗
如果你的公司出現了這種狀況 , 就表示有浪費出現了
檢查點1: 你的公司是否存在同一性質但是的多套不同產品軟體
               Database: MS SQL , IBM DB2 , Oracle, Sybase
               APServer: WebLogic , Jboss , WebSphere
               WebServer: IIS , Apache
               Mail Server: MS Exchange / Domino Notes
               Process/Flow: ...
<注意是你的公司是大企業而不是軟體公司產品展示會 , 相同性質的產品買那麼多套做甚麼 , 浪費...>
 
檢查點2: 你的公司使用的產品是否有要求要特別客製化而導致未來無法跟著原廠進行fix或是升級
檢查點3: 你的公司是否有在近幾年內 ,同一性質但是多套不同產品之間進行轉換
                <而且未來還有可能會再轉換>
                <這年頭沒人要你從一而終 , 但是不斷的轉換軟體 , 只是會讓你的投資浪費掉>
檢查點4: 你的公司是否曾有因為內部沒有人員會使用某軟體而直接購買新產品去取代的
                <這一點 , 可能檯面上沒有人會承認 , 但是一定曾經在某些公司發生過>
如果你的公司出現過這幾點 , 你或許應該好好評估一下,
因為這些行為直接或是間接導致的浪費有多少
最後一點 , 上述內容絕對都有真實案例 ,
<像模式一 , 軟體轉換 , 印象中是科學園區的某廠商 , 在微軟或是 IBM 的網站上都有>
<模式四 , 跟模式五 , 導入ERP 的公司可能會碰到>

但是請千萬不要替你們公司自己對號入座 , 或是想說是公司內部的某某某寫的
因為我提到的都是在網路上可以看得到的通例

提出上述例子 , 只是要說明當全球潮流講求 Lean 不浪費時 ,
也請檢視一下你們公司的MIS部門是否也是造成浪費的始作俑者
這些浪費如果被好好檢視跟處理之後 , 又可以省下一筆錢
------------------------------------------------------------------------------------------------------------------------------
當然還是要來推銷一下 Open Source , 有很多公司想省錢 ,
明明已有夠品質的open 軟體可以使用
但是卻又一定要花錢買一些open source 就有的功能軟體
最常見的大概是 Office 系列吧 ,  不一定要用 MS Office 用OpenOffice 也很棒
壓縮軟體有 7-zip 可以取代 WinZip , WinRAR
字典軟體有 StarDict(星際譯王)可以取代Dr.Eye ,
大企業特別要小心的是因為員工使用非法軟體而被抓到的問題 , 一旦上新聞 , 可就對企業的形象有損 ,
(經過一定的評估程序)導入Open Source 軟體可以大幅的降低這類問題
(而且根本不用擔心 , 因為購買的軟體授權不足而被抓 , 員工即便在家中的電腦也可以使用Open Source 的軟體 )
這些通通不用錢 , 你們公司還在花錢買類似的軟體嗎?

未完 , 內容待續-PDF 檔案壓縮

未完 , 內容待續-PDF 檔案壓縮

關於java 程式編譯與執行的一些基本常識

網路上很多人常常會有甚麼出現錯誤訊息要別人幫忙解決的

在這裡先說一個基本觀念 對的方式 可能只有一種到兩種 但是錯誤的方式卻會有百百種

甚麼意思呢 譬如常見的問題是:
"我使用了 XXXDatabase 的 JDBC , 但是執行的時候出現 class not found , 我有設定 CLASSPATH "

觀念一: 請把外部套件放在正確的位置
(假設如果你不是在 IDE 環境下執行)
如果你有使用 JDBC Driver (xxx.jar) 那麼請把 xxx.jar 放在你的 JDK\jre\lib\ext 目錄之下
根本不需要去設定 CLASSPATH

如果是在 IDE環境(Eclipse , JBuilder , ....) 那麼設定方式又不同

觀念二: 請分清楚 編譯時期跟執行時期的錯誤訊息
編譯時期 , 指的就是你寫好原始碼 (xxx.java)
然後要下 javac xxx.java 將原始碼編譯成 class 的過程稱為編譯時期

執行時期 , 指的是你已經成功的編譯產出了 xxx.class 檔
然後要下 java xxx 去執行的時候

常常有人會攪混掉 , 明明編譯的時候就已經出錯了 , 他還會告訴你 我打 java xxx 也會出錯
大哥 , 編譯的時候出錯表示根本不會產出 xxx.class 那麼你打 java xxx 當然會出錯


在這邊常常碰到的一個問題之一是: 我的編譯會過 , 但是執行時會出現找不到 class (EX: JDBC Driver)
這裡要注意的是:
剛剛提到兩個名詞一個是 編譯 ---> 請找 jdk\lib 這個目錄去放你的外部 jar 檔
一個是 執行 ---> 請找 jdk\jre\lib\ext 這個目錄去放你的外部 jar 檔

何謂 外部套件或是外部 jar 檔 , 只要是你引用別人的 不是你自己的程式碼的都算
當然上面的設定是 以相關的 library 是以 jar 檔的形式存在

另外還有不少人會犯的毛病是 , 在文件中會有 "假設" , "如果" 出現的話 表示這些是有條件的
例如 我上面提到 (假設如果你不是在 IDE 環境下執行)
就表示如果你是在IDE環境下的話 相關說明需要另外找


java 的設定很簡單
當你安裝完 JDK

你只需要設定以下幾個內容
JAVA_HOME 環境變數 設定為 JDK 安裝目錄(EX: C:\jdk_1.6.0 )
PATH 環境變數 在最前面加上 JDK 安裝目錄(下的bin (EX: C:\jdk_1.6.0\bin;<後面是原來的PATH內容> )
CLASSPATH 環境變數 在一開始只需要設定 .; 這樣就夠了 ,
然後你的 jar 檔要放在上面所提
剛剛提到兩個名詞一個是 編譯 ---> 請找 jdk\lib 這個目錄去放你的外部 jar 檔
一個是 執行 ---> 請找 jdk\jre\lib\ext 這個目錄去放你的外部 jar 檔
這兩個地方就夠了

如果你做到這些基本設定之後基本上 80%那些阿貓阿狗的問題都不會有

錯誤的方式百百種 對的方式只有一兩種

當然上面說得是java 程式的基本開發設定
如果你是使用AP Server 那麼設定又不太一樣 需要是各家廠商產品而定

用什麼像什麼

用什麼像什麼

你真的有好好的去了解你的開發工具嗎?
以下是我聽過我們公司IT部門的不少人員的說法 , 針對一些軟體工具
(工具的類型包含 程式語言開發工具 , DB Server , APServer , OOAD 設計工具 , ....
反正類型多的是)
(1)某某工具很爛 , XXX工具比較好用
(2)某某工具沒有YY功能 , XXX工具有YY功能
(3)某某工具的費用比較貴 , XXX工具比較便宜


以下是我個人的看法
(1)某某工具很爛 , XXX工具比較好用
那麼是不是換了XXX工具你就一定會用 , 而且用的比較好

(2)某某工具沒有YY功能 , XXX工具有YY功能
錯 ,
某某工具一直都有YY功能 ,
只是我們的人從來沒有好好的自我學習如何使用某某工具
是我們的人不知道某某工具有YY功能 , 而不是某某工具沒有YY功能

(3)某某工具的費用比較貴 , XXX工具的費用比較便宜
貴與便宜 , 那要看你的人是否有充分的壓榨出這個工具的所有功能來決定
假設 1000 萬的工具A有 1000 種功能 ,
而 20 萬的工具B有50種功能
但是不要錢的工具C有 49 種功能 ,
如果單就價格來比較 , 你要選擇那一種工具 ???

有經驗的人都會知道 , 選擇軟體工具價格不是唯一項目(還有很多項目需要考量的) ,
那麼是誰故意只把費用當作比較依據的???



舉例來說: 使用以下幾種工具都可以編輯網頁
DreamWeaver  ,  InterDev ,  Notepad , UltraEdit
如果有人完全不管用的是什麼工具 , 他純粹就是把工具當作 Notepad 來使用
那麼在這種人的手裡 , 使用DreamWeaver  ,  InterDev ,  Notepad , UltraEdit 統統都是當作 Notepad 來用 , 這種人根本就沒有好好善用他的開發工具

要記住: 用什麼要像什麼
如果你用的DreamWeaver , 請你把它當作DreamWeaver來用 , 好好壓榨這個工具的每一項功能 , 而不是把它當作 Notepad 來用 , 又說這工具很不好用
千萬不要把 DreamWeaver 用的像是 Notepad 又來說這工具很低級什麼的
對於這種人 , 你不要期望他會了解某某開發工具提供了多少種便利的開發功能
他永遠只會告訴你開發網頁很難 , 所以要多少工時 , 然後工具永遠的很難用
他希望要換另一種工具 , 可能市面上 90%的工具 , 他都用過了 , 在他的手中 , 不管是哪一種工具 , 工具始終很難用 ,
但是其他真正的網頁編輯人員可能只會某一種工具 ,
但是她們可能會善用那種工具的 80%的功能

以上只是舉例 ,
我最常看到的是
J2EE 的開發工具 , 目前有的是 RAD/WSAD , JBuilder , Eclipse , NetBeans 等等
DB Server , 目前有的是 DB2 UDB , Oracle , MS SQL 等等
AP Server , 目前有的是 WebSphere , WebLogic , JBoss , SunOne , Resin 等等

在公司用了某一套開發工具(或是DB Server , AP Server)之後 ,
就會有人出來說 XXX工具比較好用 , 開發軟體速度比較快 , 然後功能多 , 價格便宜等等
而這些出來說話的人 , 對於公司目前使用的這套開發工具完全沒有好好研究透徹
你以為她們出來說XXX工具比較好用 ,
就表示她們真的有好好研究過XXX工具嗎?

你以為公司真的改用XXX工具 ,
就表示她們真的會使用XXX工具嗎?
就表示她們真的會好好使用XXX工具的功能嗎?
就表示她們真的會讓她們的開發軟體速度變快嗎?

我可以告訴你 , 這些答案幾乎都是否定的 ,
因為她們用什麼就不像什麼 , 用什麼爛什麼(只要被她們用過的 , 都變成是她們口中的爛產品)
請問一下到底是產品的錯? 還是使用的人的錯?
真正會用的人 , 不論他拿到上述的哪些軟體 , 他都會好好的去研究 , 去學習 , 去壓榨出這個軟體的每一項功能與價值 , 然後用於工作上
不會用的人則是用什麼爛什麼
這也就是為什麼 ,
同樣的程式語言 或 Server 有人可以開發出系統 , 並且販賣或者是架設出專業網站
而有的人只會抱怨某某程式語言或 Server 很爛 , 什麼都做不出來

我不知道其他人是否也有看到類似的狀況?
我希望大家記住一句話: 用什麼要像什麼
以此作為本文的結尾

安裝設定Java相關軟體或套件注意事項

安裝設定Java相關軟體或套件注意事項
1.盡量不要安裝在有目錄路徑中包含空白字元的目錄之下
2.安裝目錄的名稱不要使用過長的名稱
有些軟體的設定會設定 CLASSPATH  , 有時候就會導致軟體啟動失敗 ...
可以試著將相關套件所在目錄改成較短的目錄名稱...再重新啟動軟體就會成功
(同理 , 包含空白字元的目錄名稱也有可能會引起啟動軟體失敗)

Java 程式透過公司Proxy連線到Internet

這個應該是不少在大公司上班的程式開發人員會問的問題 .
我們的公司要上網 , 必須透過 proxy , 而且要有申請過的Domain ID 跟 Password 才能連上網路去

那麼Java 程式或是Server軟體(Ex: Tomcat , Jetty) 要如何才能透過 proxy 連上 internet ?

基本的屬性:
http.proxy.host
http.proxy.port http.proxy.username
http.proxy.password
接著就看你要把它以程式的方式去設定或是以屬性的方式去設定
舉例來說:
 如果你原來要執行的Java 程式指令如下
java myDemoApp
加上相關屬性設定之後
java  -Dhttp.proxy.host=myProxyServerName -Dhttp.proxy.port=80 -Dhttp.proxy.username=myDomain\myUserID -Dhttp.proxy.password=myPassword    myDemoApp
(或者是在你的 Tomcat 啟動的 .bat 或是 Jetty 啟動的 .bat 中去修改加上上述的設定)

在這裡假設
http.proxy.host=myProxyServerName
http.proxy.port=80
http.proxy.username=myDomain\myUserID
http.proxy.password=myPassword
你可以使用 -D 去設定JVM的執行參數

或者你可以在你的程式碼中使用以下指令
System.setProperty("http.proxy.host" ,  "proxy.hinet.net");
System.setProperty("http.proxy.port" ,  "80");
System.setProperty("http.proxy.username", "myUserName");
System.setProperty("http.proxy.password", "myUserPassword");
在這裡只是舉例 , 所以 你要記得換成你使用的 proxy Server , port , username 跟password

這些設定記得看你的需求而定 , 甚至有其他的變化方式
這樣你的程式就可以在公司有proxy server的情況上 , 也能連上 internet

在網頁中同時顯示 Html 內容 及 PDF 內容

在網頁中同時顯示 Html 內容 及 PDF 內容

重點應該是加上粗體的部份
其中 PDF 來源可以是 WEB Server 上的 PDF File
<param name="src" value="demo1234.pdf"/>

或是 ASP 抓取資料庫內容回傳的 PDF 內容
  <param name="src" value="showpdf.asp?pdfID=bb980124567"/>


因為 html 原始碼無法正常放上  奇摩部落格 ,  所以只好手動把 半形英數字 換成 全形英數字
有需要測試的人 , 請自行把 全形英數字重新還原成 半形英數字



以下是 html 內容 (有需要 ,你也可以換成 asp 的寫法)

<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
        <title>showdata       
       
<body>   
    name:xxxx<br/>
    age:12<br/>
   
        <object classid="clsid:ca8a9780-280d-11cf-a24d-444553540000" id="pdf1" height="100%" width="100%">
         <param name="_version" value="327680"/>
         <param name="_extentx" value="2646"/>
         <param name="_extenty" value="1323"/>
         <param name="_stockprops" value="0"/>       
         <param name="src" value="showpdf.asp?pdfid=bb980124567"/>
   
  



ASP 讀取儲存在資料庫欄位中的 PDF 原始範例

ASP 讀取儲存在資料庫欄位中的 PDF 原始範例

範例假設如下:
database name: DEMODB
database user id: DEMOUser
database user id: DEMOPassword
table name:MY_PDF_TABLE
            field1 :    pdfID               - varchar , 20 , primary key
            field2:     pdf_content  - blob         , 1M                      
ODBC Name: DEMODB             

以下挑重點解說:
(1) SQL 指令:
set rs = conn.Execute("SELECT PDF_CONTENT FROM MY_PDF_TABLE WHERE pdfID='"+pdfID+"'")

(2)asp 部份:
      Response.Buffer =true
      Response.Clear()

(3)設定 MIME TYPE
下面這行是黨你的內容是 pdf 時所要設定的 mime type ,
所以當你的檔案是其他的格式 , 請修改對應的 mime type
      Response.ContentType="application/pdf"          

(4)Browser 開啟方式: 強制另存檔案 或是 線上開啟檔案
(a) 如果你希望回應到前端 browser 是要強制另存檔案 請用下面這一行
'Response.AddHeader "Content-Disposition" , "attachmentfilename="+pdfID+".pdf"

(b) 如果你希望回應到前端 browser 是要線上開啟檔案 請用下面這一行
Response.AddHeader "Content-Disposition" , "inlinefilename="+pdfID+".pdf"

(5)傳回檔案的二進位串流 
回應前端資料使用 BinaryWrite 把二進位串流傳回 
dim pdfSize
pdfSize = rs("PDF_CONTENT").ActualSize          
Response.BinaryWrite rs("PDF_CONTENT").getChunk(pdfSize)

(6)這一篇雖然是叫做
儲存在資料庫欄位中的 PDF 原始範例
但是如果你的 DB 中放的是其他格式的檔案內容也一樣可以通用 ,

假設你放的是 JPG
那麼請修改上述的
(3) MIME TYPE  , 請改為 jpg 的 MIME TYPE-->  image/jpeg
(4) 開啟方式的檔案名稱的副檔名 , 請改為 .jpg
     

以下是完整程式碼 , 有執行過 , 確認沒有問題的程式碼


測試方式
開啟瀏覽器: 輸入 以下網址
http://localhost/demo1/ShowPDF.asp?pdfID=BBB9998804



(假設: Windows Server , IIS ,
你建立一個虛擬目錄為 demo1 ,
資料庫中有一筆紀錄 pdfID 為 :  BBB9998804 )


===ShowPDF.asp===
<%@Language="VBScript"%>

<%
    Response.Charset="utf-8"  
    Response.Expires=0
    dim pdfID
    dim pdfID1
    dim pdfID2
    pdfID1= Request.Form("pdfID")                 'POST - 由 input html 中 form 傳入參數
    pdfID2 =Request.QueryString("pdfID")   'GET  - 直接輸入參數在網址列
        if (pdfID1 <> "" ) then pdfID=pdfID1
    if (pdfID2 <> "" ) then pdfID=pdfID2
   'DB Operation
   dim conn
   dim rs
   dim haveData
       haveData=false
   set conn = Server.CreateObject("ADODB.Connection")
       conn.ConnectionString = "Provider=MSDASQL.1;Password=DEMOPassword;Persist Security Info=True;User ID=DEMOUser;Data Source=DEMODB"
       conn.Open()
   set rs = conn.Execute("SELECT PDF_CONTENT FROM MY_PDF_TABLE WHERE pdfID='"+pdfID+"'")
   on error resume next
       rs.MoveFirst()       
   do while NOT rs.EOF
      haveData = true
      Response.Buffer =true
      Response.Clear()
      Response.ContentType="application/pdf"          
      'Response.AddHeader "Content-Disposition" , "attachmentfilename="+pdfID+".pdf"
       Response.AddHeader "Content-Disposition" , "inlinefilename="+pdfID+".pdf"
      dim pdfSize
          pdfSize = rs(PDF_CONTENT").ActualSize          
      Response.BinaryWrite rs("PDF_CONTENT").getChunk(pdfSize)
      rs.MoveNext()
    loop
   if (NOT haveData)  then Response.Write "查無資料
"
   conn.Close()   
%>

Velocity - 使用備忘錄


Velocity - 使用備忘錄
Velocity 是拿來作模板套版使用 , 你可以把常用的某些內容透過模板定義的方式 , 由它幫你轉換
直接來看主程式
以下範例 , 是假設你要寄送一堆電子帳單給客戶 , 但是有可能電子帳單的內容會調來調去
(User 特別是User 的主管 , 總是會在最後一刻改變心意 , 告訴你 , 某段的內容要換掉某個字 , 甚至是標點符號的全形半形都會要你改)

以下是使用講解 , 只把重點挑出來 , 完整程式碼 , 請看最下方有附上
完成的測試範例包含
(1)
TestMainApp1.java
(2)
example1.vm
(3)
example1_big5.vm

以下針對
TestMainApp1.java 作重點講解
假設你有一個method , 會根據你傳入的資料,去進行套版 , 然後回傳套表之後的結果

public static String getMailVM(Properties pData) throws Exception{
String s1=null;
VelocityEngine ve=new VelocityEngine();

Properties p = new Properties();
//以下是告訴套表程式 , 你的套版檔案放在哪個目錄

p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "/home/demo/wk_myDemoLab/MyDemoVelocity/src");
//p.setProperty("runtime.log", "velocity.log");
ve.init(p);
//這個是放我們傳入的變數內容

VelocityContext context = new VelocityContext(pData);

Template template = null;

//以下是抓取套檔案 , 且要用哪種編碼方式讀取
//注意 , 如果你的套版檔案存檔時是用UTF8編碼 , 請用UTF8讀取
//注意 , 如果你的套版檔案存檔時是用big5編碼 , 請用Big5讀取
//不然就會造成 , 顯示時有可能只有變數會正常顯示 , 其他部份都是亂碼
// template = ve.getTemplate("example1.vm","UTF8");
template = ve.getTemplate("example1_big5.vm","big5");

StringWriter writer=new StringWriter();
//合併套版及變數 ,

if ( template != null) template.merge(context, writer);
writer.flush();
writer.close();
//將套版結果轉出成字串

s1=writer.toString();
ve=null;
context=null;
template =null;
return s1;
}


public static void main(String[] args) throws Exception {
//設定會給套版使用的變數 , 需要跟套版檔案配對

Properties p1=new Properties();
p1.setProperty("cust_name", "錢多多");
p1.setProperty("bank_name", "大台北郵局");
p1.setProperty("account_num", "200-123-456");
p1.setProperty("yr", "98");
p1.setProperty("mn", "06");
p1.setProperty("dy", "15");
String s1= getMailVM(p1);
System.out.println(s1);
}

}
======執行結果 , 粗體部分是我們傳入的變數=========
親愛的 錢多多 ,您好:
您的帳單將於980615日自您的大台北郵局200-123-456進行扣款。

客戶服務部
======================================
前面提到User 或是User的主管總是很多變 , 如果哪一天User 又想要變變花樣
把電子帳單的訊息改變甚至是要由 Text 變成HTML 怎麼辦?
在沒有增加變數的情況下 , 你只要打開 example.vm然後根據User要的 , 重新調整這個套版就可以了
完成不需要打開原始程式碼重新修改編譯....

把會變的部份 , 盡可能抽離出來 , 讓"修改"遠離你的原始程式碼,

以下是完整程式碼範例

=====example1.vm , 注意是要存檔為 UTF8編碼==================
親愛的 $cust_name ,您好:
您的帳單將於$yr年$mn月$dy日自您的$bank_name$account_num進行扣款。

客戶服務部

=========================================================
=====example1_big5.vm , 注意是要存檔為 Big5編碼=============

親愛的 $cust_name ,您好:
您的帳單將於$yr年$mn月$dy日自您的$bank_name$account_num進行扣款。

客戶服務部

==================================================================
TestMainApp1.java
==================================================================

package com.demo.velocity.examples;

import java.io.StringWriter;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

public class TestMainApp1 {
public static String getMailVM(Properties pData) throws Exception{
String s1=null;
VelocityEngine ve=new VelocityEngine();

Properties p = new Properties();
p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "/home/demo/wk_myDemoLab/MyDemoVelocity/src");
//p.setProperty("runtime.log", "velocity.log");
ve.init(p);
VelocityContext context = new VelocityContext(pData);

Template template = null;
// template = ve.getTemplate("example1.vm","UTF8");
template = ve.getTemplate("example1_big5.vm","big5");

StringWriter writer=new StringWriter();
if ( template != null) template.merge(context, writer);
writer.flush();
writer.close();
s1=writer.toString();
ve=null;
context=null;
template =null;
return s1;
}
public static void main(String[] args) throws Exception {
Properties p1=new Properties();
p1.setProperty("cust_name", "錢多多");
p1.setProperty("bank_name", "大台北郵局");
p1.setProperty("account_num", "200-123-456");
p1.setProperty("yr", "98");
p1.setProperty("mn", "06");
p1.setProperty("dy", "15");
String s1= getMailVM(p1);
System.out.println(s1);
}

}