2012-01-09

如何在 Windows 底下添加系統服務(system service)

以下程式碼呼叫方法:

註冊服務
ruby name.rb reg

註銷服務
ruby name.rb del

服務執行之程式碼位於 service_main 區塊內

require 'rubygems'
require 'win32/service'
require 'win32/daemon'
include Win32

SERVICE_NAME = "RubyService"
SERVICE_DISPLAY_NAME = "A Ruby Service by vegafish"

if ( ARGV[ 0 ] == "reg" )
    Service.create({
        :service_name     => SERVICE_NAME,
        :service_type     => Service::WIN32_OWN_PROCESS,
        :description      => 'A custom service I wrote just for fun',
        :start_type       => Service::AUTO_START,
        :error_control    => Service::ERROR_NORMAL,
        :binary_path_name => 'c:\\ruby187\bin\ruby.exe ' + File.expand_path($0),
        :load_order_group => 'Network',
        :dependencies     => ['W32Time','Schedule'],
        :display_name     => SERVICE_DISPLAY_NAME
   })

    puts( "Service " + SERVICE_DISPLAY_NAME + " Registered." )
    Service.start( SERVICE_NAME )
    exit( 0 )
elsif ( ARGV[ 0 ] == "del" )
    if Service.status( SERVICE_NAME ).current_state == "running"
        Service.stop( SERVICE_NAME )
    end
    Service.delete( SERVICE_NAME )
    puts( "Service " + SERVICE_DISPLAY_NAME + " Removed." )
    exit( 0 )
end

class Daemon
    def service_init
        sleep( 5 )
    end
    
    def service_main
        filecount = 0
        watchfile = "c:\\vegafish.txt"
        while( running?() )
            sleep( 3 )
            if ( File.exists?( watchfile ) )
                filecount++
                File.rename( watchfile, watchfile + filecount.to_s )
            end
        end
    end
end

Daemon.mainloop()

2012-01-08

The Bug of Google Picasa Public URL

If you upload pictures via mobile Google+, your Picasa public URL will be changed* automatically.

This bug is not fixed at 2012-01-08.

* Changed from Gmail account to Google+ account number, e.g. http://picasaweb.google.com/vegafish -> http://picasaweb.google.com/117166741167778447153

2011-10-03

以 Java 實作 Base 64 Encoder

或許有人會覺得奇怪,Java 本身已經有支援 Base 64 Encoder 了,為什麼還要自己重新寫一個? 事實上,Java 平台的 Base64Encoder 需要 import sun.misc.BASE64Encoder,然而這個類別卻不是在所有平台上面都可以正常執行(我個人的經驗是,在 android 執行時會當機)。 以下為 Base 64 Encoder 實作程式碼,歡迎各位朋友切磋學習。
private static String base64_encode( byte[] bytes ) {
        String[] base64 = { "A", "B", "C", "D", "E", "F", "G", "H",
                            "I", "J", "K", "L", "M", "N", "O", "P",
                            "Q", "R", "S", "T", "U", "V", "W", "X",
                            "Y", "Z", "a", "b", "c", "d", "e", "f",
                            "g", "h", "i", "j", "k", "l", "m", "n",
                            "o", "p", "q", "r", "s", "t", "u", "v",
                            "w", "x", "y", "z", "0", "1", "2", "3",
                            "4", "5", "6", "7", "8", "9", "+", "/" };
        String encoded = "";

        int pad_length = 0;
        if ( bytes.length % 3 == 1 )
            pad_length = 2;
        else if ( bytes.length % 3 == 2 )
            pad_length = 1;
        
        byte[] padded = new byte[ bytes.length + pad_length ];

        for( int i = 0; i < bytes.length; ++i ) {
            padded[ i ] = bytes[ i ];
        }
        if ( pad_length == 1 ) {
            padded[ padded.length - 1 ] = 0;
        } else if ( pad_length == 2 ) {
            padded[ padded.length - 1 ] = 0;
            padded[ padded.length - 2 ] = 0;
        }
        
        for( int i = 0; i < padded.length; i += 3 ) {
            encoded += base64[ ( ( padded[ i ] & 255 ) >>> 2 ) ];
            encoded += base64[ ( ( ( padded[ i ] & 3 ) << 4 ) | ( ( padded[ i + 1 ] & 255 ) >>> 4 )  ) ];
            encoded += base64[ ( ( ( padded[ i + 1 ] & 15 ) << 2 ) | ( ( padded[ i + 2 ] & 255 ) >>> 6 )  ) ];
            encoded += base64[ ( padded[ i + 2 ] & 63 ) ];
        }
        
        encoded = encoded.substring( 0, encoded.length() - pad_length );
        
        if ( pad_length == 1 ) {
            encoded += "=";
        } else if ( pad_length == 2 ) {
            encoded += "==";
        }

        return encoded;
    }

2011-09-02

再玩一下 AES 加密解密

上次在這篇「玩了一下AES加密解密」裡面寫的程式碼有些缺點,就是加密過後的結果是 byte[] 而不是 String,對於 Java 的操作上不是很方便。看了幾位高手的博文,自己做了些改良,同時也將程式碼放上來拋磚引玉。主要的改良是透過 BASE64Encoder 和 BASE64Decoder 將 byte[] 轉為可讀的英文字母 + 數字字串,這在網址的傳遞上也有好處,不需要變成%xx%xx的 UTF-8 形式,更容易偵錯與閱讀,也不減少其安全性。
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder; 

public class Base64AES {

    public static String decrypt( String key, String text ) throws Exception {
        byte [] results = decrypt( key.getBytes(), new BASE64Decoder().decodeBuffer( text ) );
        return new String( results );
    }

    public static String encrypt( String key, String text ) throws Exception {
        byte[] results = encrypt( key.getBytes(), text.getBytes() );
        return new BASE64Encoder().encode( results );
    } 
    
    public static byte[] encrypt( byte[] key, byte[] msg ) throws Exception {
        if ( key.length != 16 ) {
            throw new IllegalArgumentException( "Key length should be 16." );
        }
        SecretKeySpec spec = new SecretKeySpec( key, "AES" );
        IvParameterSpec ivSpec = new IvParameterSpec( key );
        Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
        cipher.init( Cipher.ENCRYPT_MODE, spec, ivSpec );
        return cipher.doFinal( msg );
    }
    
    public static byte[] decrypt( byte[] key, byte[] msg ) throws Exception {
        if ( key.length != 16 ) {
            throw new IllegalArgumentException( "Key length should be 16." );
        }
        SecretKeySpec spec = new SecretKeySpec( key, "AES" );
        Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
        IvParameterSpec ivSpec = new IvParameterSpec( key );
        cipher.init( Cipher.DECRYPT_MODE, spec, ivSpec );
        return cipher.doFinal( msg );
    }
    
    public static void main( String[] args ) throws Exception {
        String msg = "This is so easy.";
        
        System.out.println( "原字串: " + msg );
        System.out.println( "加密後: " + encrypt( "vegafish12345678" , msg ) );
        System.out.println( "解密後: " + decrypt( "vegafish12345678", encrypt( "vegafish12345678" , msg ) ) );
    }

}
執行結果如下:
原字串: This is so easy.
加密後: KczRl5uSsrgfwWRKpfR5FoKEIVU0jPvxX1VsEYl/jl0=
解密後: This is so easy.

2011-08-31

少子化的感想

少子化對臺灣所造成的影響,就是未來20年的經濟不景氣甚至倒退、生產力被侵蝕、到消亡的地步,已經有很多書籍探討,如《人口減少經濟時代》(ISBN 978-986-788942-3),在此就不多做論述了。

政府為何要人民生育?因為我們追求的是經濟成長率,我們一輩子都活在成長的年代,不管是營業額、GDP。一兩季的營業額負成長,即使是大如台積電的公司,員工、股東們會怎麼看?同樣的,當經濟負成長時,政府就得要擴大公共支出、舉債建設,以便提振景氣。

然而之前的經濟成長是怎麼來的呢?簡言之,龐氏騙局(Ponzi scheme)。從前的人民靠著新加入的勞動人口來推動經濟成長,加入的人比退出的人多,經濟就成長。而臺灣的經濟並不是靠高附加價值產業起家的,加入的人比退出的人少,經濟當然會衰退。

如果公司的營業額負成長達兩年、四年甚至十年,這種股票我們會想買嗎?很明顯的未來,我們已經看到了,只是從沒想過。那麼,我們已經在社會裡面的人要怎麼辦呢? :)

因為人民的短視近利,政客靠著生育補助、教育補助、老人照顧等等,就可以騙到選票。但人民卻忘了最基本的事情:為什麼要領生育補助、老人照顧的錢?

「因為我們和子女、父母相處的時間被剝奪了」

民主國家政府,很擅長利用數字混淆視聽。不論央行和政府如何唬爛,在世界上許多專業的投資者都是用國際金價當成購買力指標,直到現在,各國的幣值、甚至償債能力仍還是用黃金做為結算單位。

1990 臺灣人均收入 TWD 223,860 元,換算美金 USD 8,325 元,購買力相當於 20.8 oz 黃金
2011 臺灣人均假設 TWD 600,000 元,換算美金 USD 20,000 元,購買力相當於 11.4 oz 黃金

臺灣現在的實質薪水,比1990的薪資少了一半,所以雙薪家庭才能負擔一個20年前一個正常家庭的生活。在20年前的傳統是,爸爸出門賺錢,媽媽在家顧小孩、照顧爸媽、公婆。現在則是夫妻兩人去上班,小孩丟給爸媽、保母。這種環境下,不要說注重生活品質的人不想生了,就連想花時間陪小孩的人也沒辦法生。

會有這種「實質薪資下降」的結果原因很多,這邊的空間不夠寫(誤)。人民希望政府達到經濟成長的目標->政府為了達到目標,創造出有利企業賺錢的環境->企業為了追求獲利,會為了穩定的低毛利來源,放棄了投資高附加價值的產品,如創意、設計、藝術產業,甚至為了賺錢壓榨勞工。

我只能說臺灣人的現狀,是自找的啊。