程式碼高𠅙

顯示具有 java 標籤的文章。 顯示所有文章
顯示具有 java 標籤的文章。 顯示所有文章

2014/06/18

Java: JSON Library

Below is a list of major JSON format processing libraries for java. Check it out.

  • JSON Processing
    • Summary
      • JSR 353: Java API for JSON Processing - Reference Implementation
    • Comment
      • the java version official implementation
  • JSON in Java (json.org)
    • Summary
      • json.org official implementation
    • Comment
      • The API requires you to load the entire document into a String. This is somewhat cumbersome to use and inefficient for large documents. But it's a DOM model so you can get a lot done with very little code. 
  • Jackson JSON Processor
    • Summary
      • High-performance JSON processor.
    • Comment
      • Jackson ones to be the fastest json processing library in the old age.
      • Has many features with modules approach.
      • Jackson has grown into a huge collection of data type processing library
  • Boon JSON
  • google-gson 
    • Summary
      • A Java library to convert JSON to Java objects and vice-versa
    • Feature
      • Provide simple toJson() and fromJson() methods to convert Java objects to JSON and vice-versa
      • Allow pre-existing unmodifiable objects to be converted to and from JSON
      • Extensive support of Java Generics
      • Allow custom representations for objects
      • Support arbitrarily complex objects (with deep inheritance hierarchies and extensive use of generic types)
  • json-simple
    • Summary
      • JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. 
    • Feature
      • Full compliance with JSON specification (RFC4627) and reliable (see compliance testing)
      • Provides multiple functionalities such as encode, decode/parse and escape JSON text while keeping the library lightweight
      • Flexible, simple and easy to use by reusing Map and List interfaces
      • Supports streaming output of JSON text
      • Stoppable SAX-like interface for streaming input of JSON text (learn more)
      • Heap based parser
      • High performance (see performance testing)
      • No dependency on external libraries
      • Both of the source code and the binary are JDK1.2 compatible
    • Comment
  • genson - A fast & extensible Java <> Json library
    • Summary
      • Genson is an open-source library doing conversion from Java to Json and Json to Java. Genson targets people who want an extensible but also configurable, fast, scalable and easy to use library. 
    • Feature
      • Easy to use, fast, highly configurable, lightweight and all that into a single small jar!
      • Full databinding and streaming support for efficient read/write
      • Support for polymorphic types (able to deserialize to an unknown type)
      • Does not require a default no arg constructor and really passes the values not just null, encouraging immutability. It can even be used with factory methods instead of constructors!
      • Full support for generic types
      • Easy to filter/include properties without requiring the use of annotations or mixins
      • You can apply filtering or transformation logic at runtime on any bean using the BeaView mechanism
      • Genson provides a complete implementation of JSR 353
      • Starting with Genson 0.95 JAXB annotations and types are supported!
      • Automatic support for JSON in JAX-RS implementations
      • Serialization and Deserialization of maps with complex keys
    •  Comment
      • Looks good!
 Want to use XPath with JSON? see below.
  • Commons JXPath
    • Summary
      • The org.apache.commons.jxpath package defines a simple interpreter of an expression language called XPath. JXPath applies XPath expressions to graphs of objects of all kinds: JavaBeans, Maps, Servlet contexts, DOM etc, including mixtures thereof.  
  • json-path 
    • Summary
      • Java JsonPath implementation

2014/03/27

Java Practice: Shutdown Hook

今日 Daily Java Practice 如下,請問其中 runTime.addShutdownHook 的作用。

public class Main implements Runnable {
    public void run() {
        System.out.println("Shutting down");
    }

    public static void main(String[] arg) {
        Runtime runTime = Runtime.getRuntime();
        Main hook = new Main();
        runTime.addShutdownHook(new Thread(hook));
    }
}

2014/03/24

Java Practice: Enum

今天來玩玩 enum, 以下透過一程式,說明 enum 如何把物件導向程式的三個特質封裝、繼承跟多型完美的結合在一起。
善用 enum,可以大幅減少程式碼。
以下請說明:
  • 程式輸出訊息。
  • 為何輸出此訊息。
  • S, M, L, XL ... 與 Size 的關係,是類別跟實體的關係,還是父類別跟子類別的關係?

public class SizeIterator {
    public static void main(String[] args) {
        Size[] sizes = Size.values();
        for (Size s : sizes) {
            s.showName();
            s.showType();
        }
    }
}

enum Size {
    S {
        @Override
        public void showName(){
            System.out.println("I am too small to show to others!");
        }
    }, M, L, XL, XXL, XXXL;
    public void showName() {
        System.out.println(this.name());
    }
    public void showType() {
        System.out.println(this.getClass().getName());
    }
}

2014/03/19

Java Practice: Object Serialization

今天的 Java Practice 如下, 請服用:
  1. Coper 類別的功用為何?
  2. Coper:: <T> copyInstance(T obj) 方法使用了 Java Generic 的技巧,請解釋這裡採用型別參數的好處?
  3. main 方法中印出的兩個 User 物件,內容是否相同? 若有差異,原因為何?
abstract class Copier {
    @SuppressWarnings("unchecked")
    public static <T> copyInstance(T obj) throws IOException,
            ClassNotFoundException {
        ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
        ObjectOutputStream outStream = new ObjectOutputStream(outBytes);
        outStream.writeObject(obj);
        outStream.close();
        ByteArrayInputStream inBytes = new ByteArrayInputStream(outBytes
                .toByteArray());
        ObjectInputStream inStream = new ObjectInputStream(inBytes);
        T copied = (T) inStream.readObject();
        inStream.close();
        return copied;
    }
}

class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private Date date = new Date();
    private String username;

    private transient String password;

    public User(String name, String pwd) {
        username = name;
        password = pwd;
    }

    public String toString() {
        String pwd = (password == null) ? "(n/a)" : password;
        return "logon info: \n   username: " + username 
            + "\n   date: " + date
            + "\n   password: " + pwd;
    }
}

public class CopierTest {
    /**
     * @param args
     * @throws Exception
     * @throws IOException
     */
    public static void main(String[] args) throws Exception {
        User jack = new User("Jack", "magic");
        System.out.println(jack);
        User copyOfJack = Copier.copyInstance(jack);
        System.out.println(copyOfJack);
    }
}  

2014/03/18

Java Practice: Random


練習一下使用 Java Random 類別。
以下題目,請試著寫出最後 min, max 印出來的最可能數值。
public class MathRandomTest {
 public static void main(String[] args) {
    Random rand = new Random();
   
   int n = 10;
    int min = Integer.MAX_VALUE;
    int max = Integer.MIN_VALUE;
   
    for(int i = 0; i < 100; i ++){
      int r = rand.nextInt(n + 1);
      min = Math.min(min, r);
      max = Math.max(max, r);
    }
    System.out.printf("Min = %d, Max = %d", min, max);
 }
}  

2014/03/17

Java Practice: Generics

從今天開始,預計把發給敝公司新人的 Java Practice 題目,release 在此給大家參考。Java 的初學者可以試著練習看看,老手也可溫故知新喔!

首先是 Java Generics 之應用,請在 ________ 填入正確宣告,使程式可以正常運作:
class TwoTypeParas <T,V> {
    T ob1;
    V ob2;

    TwoTypeParas(T o1, V o2) {
        ob1 = o1;
        ob2 = o2;
    }

    void showTypes() {
        System.out.println("Type of T is " + ob1.getClass().getName());
        System.out.println("Type of V is " + ob2.getClass().getName());
    }

    T getob1() {
        return ob1;
    }

    V getob2() {
        return ob2;
    }
}

public class TwoTypeParaTest {
    public static void main(String args[]) {
        TwoTypeParas______ tgObj = new TwoTypeParas______(88, "Generics");
        tgObj.showTypes();

        int v = tgObj.getob1();
        System.out.println("value: " + v);

        String str = tgObj.getob2();
        System.out.println("value: " + str);
    }
}

2013/12/16

ETL 概念、應用及工具簡介

企業裡面常有資料處理、轉換或整合的需求。好的資料管理工具雖然不一定能讓你上天堂,不好的工具卻肯定會讓你住病房。「ETL 資料轉換作業」是「資料管理」中最基礎的應用,本篇就來說說 ETL 的基本概念,它有何應用,以及市場上常見的工具。

ETL 是什麼

  • ETL 代表資料由資料來源端擷取 (Extract) 出來,經過轉換 (Transform),再載入 (Load)到目的端的資料傳輸過程。
  • Extract -- 從各種不同的資料來源中,將資料擷取出來。傳統上資料來源是來自於交易型資料庫。
  • Transform -- 對資料進行適當的轉換,如型態轉換、字串相連、彙總運算等等。這個步驟在 ETL 中不一定要執行。
  •  Load -- 將資料載入目的端;目的端通常是為了報表產製及商業智慧分析而最佳化的資料倉儲。


 

 ETL 的應用情境

  • 同步不同資料庫的資料。
  • 資料清理,移除錯誤資料。
  • 將資料載入資料倉儲以便後續分析, 如進行 OLAP 或 Data Mining 之應用。

 

使用 ETL 工具的優勢

使用 ETL 工具最大的優勢,在於可把傳統上需要透過手工方式開發的資料轉換程式碼,轉為由 ETL 工具去進行設定或設計,如此可大量減低資料轉換作業的建置時間及成本。

目前大部分開發人員還是使用自行開發的指令,來處理資料的整合作業。要透過這種方式來建立商業智慧系統,需要開發人員需具備一定的專業能力,還要費時費力的進行開發及除錯。

對於資料庫管理人員而言,若不使用外部工具,要連通不同種類的資料來源並不簡單。況且一旦外部資料來源改變,或者要加入新的資料來源,那這種勞心勞力自行開發的工作又要整個重來一次。使用 ETL 工具,將能簡化這種繁複瑣碎的工作。而良好的資料轉換流程之圖形介面設計工具,甚至可將這類操作變成樂趣。

 

ETL 工具簡介

以下來說說一些業界比較常用的 ETL 工具:

Informatica PowerCenter

  • Informatica 公司所提供的同名商業化套裝資料整合軟體。
  • 建立於 1993 年,主要業務即為資料整合。
  • 依據 Gartner Dataquest 的報告,它在資料整合領域具有最高的市占率。
  • 它提供了大量的套件供企業進行系統整合,並能連接到大量的新一代或既有的資料來源。
  • 提供良好的作業設計及監控介面,功能強大,但人員必須經過訓練才能上手。
  • 單價昂貴,顧問服務費用高。
  • 無縫整合著名的「下推優化」ELT 方法,使用資料庫的服務資源來進行資料轉換作業,效能優良,可以應用於大規模的系統。

    Pentaho Kettle (PDI)

    • Pentaho 是一家商業化的公司,約始於 2001 年,他們基於 Java 開發了一套開源 BI 套件,使用者社群人數眾多。 其中包含了一個 ETL 軟體,稱為 Kettle (或稱為 Pentaho Data Integration/PDI)。
    • 透過中介資料趨動 (metadata-driven) 的方式來設計資料轉換流程,而其使用介面功能完善且易於操作。不需像 Informatica 那樣大的前期投入。
    • 其作業設計及轉換使用了兩種腳本文件:Transformation 和 Job。Transformation 進行資料的轉換作業,以 Javascript 撰寫,Job 則用來控制整個作業流程。
    • 具有作業分派的功能 (需要一個像 cron 的分派器),可以透過分散式部署建立 Pentaho 群集,並能運行部署在 slave servers 上的遠程任務。
    • 擁有資料品質管理功能,可以撰寫自定義的 SQL 查詢,Javascript,和正則表達式。 
    • 從 4.3 版開始,改採 Apache 授權。參考 Pentaho changes ETL license for big data push -- Kettle 4.3, available under the Apache License Version 2.0, can ingest, output, manipulate and report on data from Apache Cassandra, Hadoop HDFS, Hadoop MapReduce, Apache Hive, Apache HBase, MongoDB and Hadapt’s Adaptive Analytical Platform.

    Talend

    • Talend 是一個開源碼的資料整合軟體, 大概啟始於 2006 年 10 月。
    • 採用程式碼產生器的方式來建立資料轉換作業,它將產生的 Java 或 Perl 程式碼佈署於伺服器執行。
    • 其 GUI 操作介面是建立在 Eclipse RCP 平台之上。
    • 使用者社群人數沒有 Pentaho 多,但在國外有許多商業公司進行支援。像著名的 Jaspersoft (Jasper Report 開發者) ETL 工具就是採用 Talend ETL。

    Trinity

    • Netpro 公司所出產的商業化資料管理整合軟體。
    • 包含 Data Management (即 ETL)、JCS (作業控制系統,用來排程及分派作業),以及 Metadata (管理資料源與資料轉換作業會用到的資料庫、資料表及欄位資訊) 三個部分。
    • 其最新版支援 BigData Management, 能連接到 Hadoop HDFS, Hbase 等 BigData 資料儲存體,亦能執行 Pig 與 HiveQL 操作。
    • 提供以 Web 為基礎的操作介面,資料流程設計工具及作業執行監控工具都能透過 Java Web Start 即時自網頁上即時啟動,整體配套措施相當完善。
    • 採分散式多 Agent 架構,可以簡易的 scale-out.
    • 還包含 Data Profile, Impact Analysis, Data Protection 等各種資料管理軟體,實用性相當高。

    2011/09/06

    用 Java Annotation 簡化命令列參數處理

    撰寫 Java 命令列程式,免不了要處理命令列參數。常見的作法是跑個 for loop, 裡面用 if ... else 去判斷要執行的選項。這樣的作法當然可以達到目的,不過比較不具有程式的美感。

    其實在 Web 上,同樣的問題也存在,就是怎麼把 URL 對應到特定的 servlet, action 或 controller 的方法上。這不管是在 Spring Framework 3 Web MVC, Servlet 3 或是 Jessey 中,都有一套近似的解法,就是使用 @Annotation。

    OK, 那我們何不如法泡製,也用 @Annotation 來處理命令列參數呢? 我設計的命令列規範如下:
    • 將命令列參數區分成「選項」及「選項參數」。選項如 -start, -stop;選項參數如 -start localhost 18080 中,未加 dash 符號的字串,即紅字部分。
    • 一個命令列可以接受多個選項及多個選項參數。
    • 支援將選項綁定到主程式裡面的方法,以下我稱之為「選項常式」。
    • 支援將選項參數綁定到選項常式的參數上。
    • 支援自動列出所有選項及其說明。
    • 自動檢查選項參數是否正確。 
    • 支選整數型態的選項參數。
    基於這個規範,我設計出的這組 Command line API,可以透過類似以下方式使用:

    package com.isong.cmdexe;
    
    /**
     * Usage example:<pre>
     * java -jar ds.jar -start localhost 2011
     * java -jar ds.jar -start localhost 2011 -sm
     * java -jar ds.jar -stop
     * </pre>
     * @author edwardsayer
     */
    public class DummyServer {
        /**
         * @param args
         */
        public static void main(String[] argv) {
            new CmdExecutor(new DummyServer()).exec(argv);
           
        }
       
        // execute: java -jar ds.jar -start localhost 2011
        @CommandArg(value = "-start ip port", desc = "start the server instance.")
        public void doStart(String ip, int port){
            System.out.println("Starting server at " + ip + ":" + port + "...");
        }
       
        // execute: java -jar ds.jar -sm 
        @CommandArg(value = "-sm", desc = "start session monitor.")
        public void startSessionMonitor(){
            System.out.println("Starting session service...");
        }
       
        // execute: java -jar ds.jar -stop 
        @CommandArg(value = "-stop", desc = "stop the server instance.")
        public void doStop(){
            System.out.println("Stoping...");
        }
    }
    

    從以上的程式碼片段,可以知道這組 Command line API 提供了一個 CmdExecutor 類別, 以及一個 @CommandArg annotation。

    @CommandArg 會在主程式的選項常式上,加上標注訊息,包括選項名稱,以及選項描述。

    而CmdExecutor 類別則依據 @CommandArg 所提供的資訊,綁定使用者輸入的參數。另外,若使用者未輸入任何參數,CmdExecutor 則顯示參數提示訊息,這些訊息,其實是我們透過 @CommandArg 加諸在每個選項常式上的訊息。範例如下:

    Available options:
    -start ip port
        start the server instance.
    -sm
        start session monitor.
    -stop
        stop the server instance.
    

    接下來,就來了解 CmdExecutor 及 @CommandArg 的實作方式。先看看 @CommandArg 的程式碼:

    @Retention(RetentionPolicy.RUNTIME)
    public @interface CommandArg {
        String value();
        String desc() default "";
    }
    

    可以看到 @CommandArg 包含兩個屬性。value 用來設定選項名稱,而 desc 用來設定選項描述。

    CmdExecutor 較為冗長,程式碼如下:

    public class CmdExecutor {
        private Map cmdMap;
        private StringBuffer cmdDocs;
        private Object target;
        
        public CmdExecutor(Object target){
            this.target = target;
        }
        
        public int exec(String[] args){
            System.out.println("args = " + Arrays.asList(args));
            initCommands();
            if (args.length == 0) {
                showCommands();
                return 0;
            }
            
            for (int i = 0; i < args.length; i++) {
                if (args[i].startsWith("-")) {
                    String option = args[i];
                    Method method = cmdMap.get(option);
                    if (method == null) {
                        System.out.println("Unknown options: " + option);
                        showCommands();
                        return 1;
                    }
                    try {
                        Class[] types = method.getParameterTypes();
                        Object[] paras = new Object[types.length];
                        for (int j = 0; j < types.length; j++) {
                            if ( ++i >= args.length || args[i].startsWith("-")) {
                                System.out.println("Invalid option parameter count: " + option);
                                showCommands();
                                return 1;
                            }
                            if(types[j].isAssignableFrom(String.class))
                                paras[j] = args[i];
                            else if(types[j].isAssignableFrom(int.class) 
                                    || types[j].isAssignableFrom(Integer.class))
                                paras[j] = Integer.valueOf(args[i]);
                            else
                                throw new IllegalArgumentException("Invalid command parameter type: " + method.getName());
                        }
                        method.invoke(target, paras);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } 
                }
            }
            return 0;
        }
        
        private void initCommands(){
            if (cmdDocs != null)
                return;
            
            cmdDocs = new StringBuffer("Available options:\n");
            cmdMap = new HashMap();
            Method[] methods = target.getClass().getMethods();
            for (Method method: methods) {
                CommandArg argCmd = method.getAnnotation(CommandArg.class);
                if (argCmd != null) {
                    cmdDocs.append(argCmd.value() + "\n    " + argCmd.desc() + "\n");
                    cmdMap.put(argCmd.value().split(" ")[0], method);
                }
            }
        }
        
        private void showCommands(){
            System.out.println(cmdDocs);
        }
    }
    

    以上主要程式邏輯放在 exec 這個方法上,它會透過呼叫 initCommands 方法,建立所有選項常式之描述 (cmdDocs) 及對應 (cmdMap)。完成後,就是實際去解析命令列參數。如果沒有任何參數,就透過 showCommands 顯示說明。如果有,就進行選項與主程式方法的對應。
     

    2009/02/24

    如何取得 Web 應用程式的根目錄 Context URL

    我們常常在開發 Web 應用程式時,會有需要取得 Context URL。例如,為了透過這個 URL 取得某些資源。Context URL,也就是 Web 應用程式根目錄的 URL。例如,給定一個 URL 如下:

        http://test.com:8080/AppContext/path/to/your/page
    

    則 Context URL 指的是:

        http://test.com:8080/AppContext   
    

    如果我們都是以相對路徑 (相對於根目錄或或目前路徑) 的方式來存取資源,那直接使用 Context URL 的必要性就比較低了。不過有時候並無法使用相對路徑,例如:

    • 因為配合 API 的呼叫,你不曉得這個 API 會在虛擬路徑的第幾層呼叫,因此就無法提供相對路徑。
    • 因為配合某種 code gen 機制,你要傳入一個 URL。
    • 因為採用了某種 URL dispatch 的機制,導致你網頁相對路徑發生偏差。

    當然還有其他原因。而面對這個問題的解法,傳統上是在 web.config 或 web.xml 裡面去定義一個類似 HOST_URL 的參數來解決。但缺點是,每次 deploy 時,就得變更 HOST_URL 的設定,除了顯得不便之外,因會增加 API 與環境變數的相依性,實在不能算是很好的做法。因此透過程式自動抓取 ContextURL 的作法,便有其必要。

    現在,我們假設我們的 ContextURL 就是以上的形式,而不是 WebServer 的根目錄 (例如 http://test.com:8080/),那麼取得 Context URL 的作法,在一般情況下並不難。首先看看 ASP.NET/C# 版本:

       int idx = Request.Url.AbsoluteUri.IndexOf(Request.ApplicationPath);
       string serviceUri = Request.Url.AbsoluteUri.Substring(0, idx); // 伺服器 URL
       string applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL
    

    JSP/Java 版本如下:

       int idx = request.getRequestURL().toString().indexOf(request.getContextPath());
       String serviceUri = request.getRequestURL().substring(0, idx); // 伺服器 URL
       String applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL
    

    不過有些情況比較特殊,例果透過某種 URL dispatch 的機制來存取網頁,你會發現用以上方式取得的 URL,與 Client 端發送的 URL 並不相同。我們以 IBM Tivoli Access Manager 為例,它的 URL 組成格式如下:

          http://sso.com/JUNCTION/AppContext/path/to/your/page
    
    • sso.com: 是一個用來作 singal sign-on 的網站
    • JUNCTION: 用來試別要將 client 端 request dispatch 到後端哪個實發處理的網站
    • /AppContext/path/to/your/page: 後端處理要求的網頁或程式,實際位址可能是 http://192.168.1.111:8080 /AppContext/path/to/your/page 之類的。

    也就是說,當 Client 端發送 http://sso.com/JUNCTION/AppContext/path/to/your/page 這個請求時,經過 Tivoli Access Manager 轉送後,Server 端實際取得的服務位址是 http://192.168.1.111:8080/AppContext/path/to/your/page。

    為了解決這個問題,我們需要動用到 HTTP Header 裡面的一個參數 -- referer。referer 是指目前存取這個網頁,是透過哪個網頁連結過來的。有了這層認識,我們可以將上面的程式碼改成如下。 ASP.NET/C# 版本:

       if(IsPostBack) {
          idx = Request.UrlReferrer.AbsoluteUri.IndexOf(Request.ApplicationPath);
          serviceUri = Request.UrlReferrer.AbsoluteUri.Substring(0, idx); // 伺服器 URL
          applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL
       }
    

    JSP/Java 版本如下:

       if(request.getParameter("IsPostBack").equals("true")){
          idx = request.getHeader("referer").indexOf(request.getContextPath());
          serviceUri = request.getHeader("referer").substring(0, idx); // 伺服器 URL
          applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL
       }
    

    如果你是直接在瀏覽器的網址列輸入 URL,或是點選書籤/我的最愛直接連結,那 referer 的值會是 null。因此,我們可以透過檢查這個網頁是否是 post back 網頁,來確保 referer 有值。注意一下,JSP 裡面並沒有預設的 IsPostBack 檢查機制,你要自己做。

    另外,有些防火牆或防毒軟體也可能會改寫 Client 端發出的 referer,使得 Server 上取得的值為 null。若是這種情況,可以試著透過 client 端的 Javascript,取得 document.location.href 的值,將它指定給某個隱藏表單欄位,在 post 時傳給 Server 端來加以解決。

    2009/01/23

    Java SE 6 Update 10 裡面的 Nimbus Look and Feel

    Java 視窗應用程式在過去給部分人留下著刻版印象,總是認為那是跑得慢,看來醜的應用程式。不過,這真的只是刻版印象。個人在過去曾經以 Swing 套件,開發了 4 年以上的 Java Client 端應用程式,無論是在啟動速度、執行效能及視覺美觀上,Swing 介面均能達到令人滿意的程度。

    而事實上不論是 Sun 或是 Java 社群也一直在 Look and Feel (就相當於佈景主題--Theme 的概念) 下了相當心血。在 Java SE 6 Update 10 裡面,就包括了一個 Nimbus Look and Feel,這是一個簡潔、洗練、具現代感的 Look and Feel。我們先來看看 SwingSet3 在 Windows XP 中的呈現樣式:

    SwingSet3Nimbus

    是不是還不賴呢? 我們展現一下 Metal Look and Feel 比較一下:

    SwingSet3Metal

    除了視覺上的輕巧之外,Nimbus Look and Feel 在實質上也是相當輕巧的,因為你在畫面上所看到的各個元件,包括按鈕、捲軸、下拉選單及標題的漸層及陰影等效果,全部是用 Java 2D 向量方式 (而不是透過 bitmap 圖檔) 畫出來的,因此才占用了 56 KB 的空間。

    設定 Swing 應用程式使用 Nimbus Look and Feel

    這樣漂亮的 Look and Feel,並沒有隨著 Java SE 6 Update 10的到來,而成為 Swing 預設的 Look and Feel。開發人員必須加以設定,才能在應用程式中以它作為佈景主題。隨著需求的不同,設定方式至少有三種。

    在應用程式中進行設定--透過程式碼指定,你所要使用的 Look and Feel。這種方式只會將主題套用到應用程式本身,這只需要一行程式碼:

    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");

    或者,也可以在啟動 Java 程式時,加入以下的 java 啟動參數:

    -Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

    如果你想將 Nimbus Look and Feel 設定成整台電腦中,所有 Swing 程式的預設主題,則可以在 /lib/swing.properties 這個檔案中,加入底下這行:

    swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

    如果上述的設定檔不存在,你可以自行新增一個。

    在 jEdit 中設定 Nimbus Look and Feel

    除了以上方式之外,有些應用程式可以透過 UI 介面,讓你設定應用程式的 Look and Feel。例如 jEdit 的這個以 Java 開發而成的編輯器,就可以在 Utilities/Global Options... 主選單所開啟的 Options 視窗中,選擇 Appearance 設定 Look and Feel。以下是套用 Nimbus 之後的外觀:

    jEditNimbus

    附上一張預設樣式 (Metal) 作比較:

    jEditDefault

    設定 NetBeans 使用 Nimbus Look and Feel

    一直覺得 Sun 是一家有著偏執傾向的公司,他們似乎有著一種,不管別人如何批評,想要做的,便會全力以赴做到好的企業精神。因此這幾年來在 NetBeans IDE 上的進展是有目共睹的 (雖然,我還是 Eclipse 的愛用者啊! ),而我也因此相當看好 JavaFX 未來的發展…。OK,回歸正題,我想社群裡面 NetBeans 的愛好者應該也不少,要設定 NetBeans 使用 Nimbus Look and Feel 也很簡單,只要找到 NetBeans 的安裝目錄,在 etc 子目錄下的 netbeans.conf 設定檔中,找到 netbeans_default_options 設定,在設定中加上 --laf Nimbus 即可。例如,我的設定是這樣子的:

    netbeans_default_options="-J-Dorg.glassfish.v3.installRoot=\"C:\java\glassfish-v3-prelude-b15b\" -J-Dcom.sun.aas.installRoot=\"C:\java\glassfish-v2ur2\" -J-client -J-Xverify:none -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Dnetbeans.logger.console=true -J-ea -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true --laf Nimbus"

    以下是套用 Nimbus 之後的外觀:

    NetBeansNimbus

    再附上預設外觀看看:

    NetBeansDefault

    其實,隨著應用的不同,你很難找到一套一體適用於所有應用情境的 Look and Feel,不過,這又是另一個主題了,我就此打住。

    2008/11/11

    Google Translate 非官方翻譯 API

    之前為了將系統的多語化,其中一個議題就是要產生各種語系的訊息資源檔。繁簡中文的對譯,只要透過 Word 就行了,要是英日語,就得人工作業。靈機一動,想說何不借助既有的翻譯服務,為 resource 檔提供預設的簡中、英、日翻譯。

    就這樣,找到了一些應用 Google Translate API 的討論。試用了之後,多少都有語系編碼的問題。我最後實作了一個版本,可以在輸入正體中文時,正常產出簡中、英、日語系的翻譯。

    一開始我是採用 C# 語言測試,程式碼如下:

    public static string TranslateText(string fromLang, string toLang, string msg)
    {
      string target = "http://www.google.com/translate_t?ie=UTF-8&oe=UTF-8&text={2}&langpair={0}|{1}";
      string url = String.Format(target, fromLang, toLang, msg);
      WebClient webClient = new WebClient();
      webClient.Encoding = System.Text.Encoding.UTF8;
      string result = webClient.DownloadString(url);
      string sign = "<div id=result_box dir=\"ltr\">";
      result = result.Substring(result.IndexOf(sign) + sign.Length);
      result = result.Substring(0, result.IndexOf("</div"));
      return result.Replace("<br>", "\n");;
    }
    

    後來又做了一個 Java 的版本,程式碼如下:

    public static String translateText(String fromLang, String toLang, String msg) throws IOException{
      String target = "http://www.google.com/translate_t?ie=UTF-8&oe=UTF-8&langpair=%s|%s&text=%s";
      String url = String.format(target, fromLang, toLang, URLEncoder.encode(msg, "utf-8"));
      String result = getUrlContent(url);
      String sign = "<div id=result_box dir=\"ltr\">";
      result = result.substring(result.indexOf(sign) + sign.length());
      result = result.substring(0, result.indexOf("</div"));
      return result.replaceAll("<br>", "\n");
    }
    
    public static String getUrlContent(String url) throws IOException {
      URL u = new URL(url);   
      HttpURLConnection conn = (HttpURLConnection)u.openConnection();   
      conn.setRequestProperty("User-agent","Mozilla/4.0");
      conn.setRequestProperty("Content-Language","UTF-8" );
      conn.connect();
      return slurp(conn.getInputStream());            
    }
    
    public static String slurp (InputStream in) throws IOException {
      StringBuffer out = new StringBuffer();
      byte[] b = new byte[4096];
      for (int n; (n = in.read(b)) != -1;) {
        out.append(new String(b, 0, n, "UTF-8"));
      }
      return out.toString();
    }
    

    測試程式長得像這樣:

    public static void main(String []argv) throws IOException{
      System.out.println(GoogleTranslate.translateText("zh-TW", "zh-CN", "書不同文、車不同軌、世界不大同!"));
      System.out.println(GoogleTranslate.translateText("zh-TW", "en", "書不同文、車不同軌、世界不大同!"));
      System.out.println(GoogleTranslate.translateText("zh-TW", "ja", "書不同文、車不同軌、世界不大同!"));          
    }
    

    稍微說明一下我的測試心得。

    首先,就是使用 Google Translate 時,最好是明確指定你所用的輸入編碼及輸出編碼,即上面 URL 連結中的 ie=UTF-8&oe=UTF-8。若不明確指定,Google 可是會依據你的輸入語言,而決定輸出編碼。例如,你輸入的語言是 "zh-CN",它預設會採用 gb2312 編碼回應。這會造成處理上的困擾。

    其次,由於 .NET 與 Java 平台內建皆採用 UTF-8 編碼,因此在我們指定輸入編碼及輸出編碼時,當然是優先考量 UTF-8。

    最後,如果你用 Eclipse 做測試,你可能會發現 console 裡有部分亂碼。請依照下圖,在 Run/Run Configurations... 裡面做設定,將 Console Encoding 指定為 UTF-8:

    eclipse-console-utf8

    最後就能產出正常的顯示了。

    eclipse-console-utf8-2

    2008/10/15

    glassfish-v2 JSP request 中文參數亂碼解法

    最近幾天用 Microsoft Visual Web Developer 2008 Express Edition 練習 ASP.NET + ADO.NET,順便把 Java 陣營兩大 IDE Eclipse 及 NetBeans 拿來比較一下開發的易用性…

    心得是,以 NetBeans 用 JSF + JPA 開發網站,開發的效率並不下於 ASP.NET + ADO.NET。而 Eclipse 的 JPA 工具(稱為 Dali Java Persistence Tool) 的功能其實也相當完善,例如,它可以讓你由 Java class 產生 db table,也可以讓你由 table 產生 entity class。唯一在使用性上不足之處,是 Eclipse 出廠設定較 NetBeans 薄弱。例如,為了使用 JPA,你必須自行下載一個 JPA 的一個實作,而為了使用 JSF,你也必須自行下載一個 JSF 的一個實作。下載後還要自行設定… 需要看一點功夫才設定的起來。而 NetBeans 如果下載的是 Web & Java EE 版,預設就能直接開發 JSF + JPA 程式,甚至連範例專案都含在裡面,deploy 一下,直接就能 run 了。

    以上都是雜話,以下是正文,但正文不會很多。

    在測試 NetBeans 開發 Web 網站時,遇到一件有趣的事。就是以 NetBeans JSP web application 的預設組態,deploy 到 glassfish-v2 上,輸入中文時,取得的資料會是亂碼。但若是以 JSF 來開發 web application 便不會有中文亂碼的問題。

    glassfish 中文亂碼的問題,可參考 FaqHttpRequestParameterEncodingServlet Character Encoding,在 sun-web.xml 中加入 default-charset="UTF-8" 得到解決,如下所示:

       <sun-web-app>
         <locale-charset-info default-locale="">
           <locale-charset-map locale="" charset=""/>
           <parameter-encoding default-charset="UTF-8"/>
         </locale-charset-info>
       </sun-web-app>
    

    (注意,在 NetBeans 裡面,預設將 JSP 及 Java 檔都以 UTF-8 存檔,且 JSP 皆加入 <%@page pageEncoding="UTF-8"%> 指示)。

    但 JSF 沒有中文亂碼問題,雖然這是好事,但反道讓我有些不解。用 Google 查詢一下,原來在 JSF 規範裡面有特別提到要如何解析當前 request 參數的編碼,Tips for JSF character encoding 有精要的描述。有了這些資訊,便不需再透過撰寫 filter 來解決編碼問題了。

    Technorati : , , , ,