開發環境
本文的開發環境為 Windows Vista Enterprise、birt-report-designer-all-in-one-2_2_2、Birt 2.2.2、Java EE 服務器使用 apache-tomcat-6.0.18,當然,您也可以使用 Jboss 等其他 Java EE 服務器。
初識 BIRT
BIRT 是一個 Eclipse-based 開放源代碼報表系統同 JasperReports 類似。它主要是用在基於 Java 與 J2EE 的 Web 應用程序上。BIRT 主要由兩部分組成:一個是基於 Eclipse 的報表設計和一個可以加到你應用服務的運行期組件。BIRT 同時也提供一個圖形報表制作引擎。
BIRT 報表文件的國際化
新建 birt 報表的過程請參考網站的其他文檔,現有一個名為 internationalization_file.rptdesign 報表文件,然後在 layout 視圖點擊報表的空白處如下所示:注意一定是空白處,點擊其他位置是不能出現這個菜單項的。
圖 1. 選擇報表的資源文件
然後點選 resources 標簽頁,點擊 Add 按鈕來加入資源文件或者輸入資源文件名稱。
注意:如果在屬性 Resources 中填寫了 MyResources, 那麼資源文件的名稱要以以下規律命名:
MyResources_en_US.properties、 MyResources_zh.properties 等,資源文件定義符合 Java I18N 格式。後面的 _en_US 或 _zh d 等由參數 __locale 傳遞。那麼資源文件要放到哪裡呢?我們通過在應用的 web.xml 中定義 <param-name>BIRT_RESOURCE_PATH</param-name> 來確定資源文件的位置,這也是指定報表文件路徑的參數。
報表文件國際化的實例演示
現在我們指定了 internationalization_file.rptdesign 報表文件的 Resources 為 MyResources,並建立兩個 property 文件,一個中文資源文件 MyResources_zh.properties, 另一個為英文資源文件 MyResources_en_US.properties,資源文件內容如下:
MyResources_zh.properties:name=\u663E\u793A\u53D8\u91CF
MyResources_en_US.properties:name=show param
將報表文件的字段指定為資源文件中定義的鍵值。
圖 2. 指定對應的鍵值
然後將報表文件及所需的資源文件放到服務器對應的目錄下,如:\webapps\WebViewerExample。在浏覽器中輸入 http://localhost:8080/WebViewerExample/frameset?__report=internationalization_file.rptdesign&sample=my+parameter&__locale=en_US顯示如下:
圖 3. 資源文件為英文的
然後將上面的 URL 中的 __locale=en_US 換成 __locale=zh, 顯示如下:
圖 4. 資源文件為中文的
BIRT-console 的國際化
通常情況下,我們還是用 birt 提供的 console 來展示報表的,如果 console 提供的功能不能滿足我們的要求,在上面做二次開發也是挺簡單的事情,如果重新再開發一個 birt 報表的展示 console,造同樣輪子的做法不是很值得提倡的。回到正題上來,我們怎樣國際化 birt-console 呢?國際化文件放到哪裡?是否可以將國際化文件移到一個可管理的路徑下呢?接下來,我們會一一解開這些疑惑。
Birt 提供了國際化 console 的方法,國際化文件放在一個名為 viewservlets.jar 包裡面,名稱為 Messages.properties。如果我們要實現中文的國際化,是否放入一個中文的資源文件就可以呢?實踐往往是驗證疑惑最有效的方法,實際結果驗證這樣是不行的。下面我們來看一下 birt 實現 console 國際化部分的代碼,類 BirtResources 調用的是 ViewerResourceHandle 類。
清單 1. BirtResources 類
public static String getMessage( String key )
{
ViewerResourceHandle resourceHandle = getResourceHandle( );
if ( resourceHandle != null )
return resourceHandle.getMessage( key );
return key;
}
類 ViewerResourceHandle 繼承自 ResourceHandle 類。
清單 2. ViewerResourceHandle 類
public class ViewerResourceHandle extends ResourceHandle
我們再看 ResourceHandle 類,這個類是組織資源文件的關鍵類:
清單 3. ResourceHandle 類
public ResourceHandle( ULocale locale )
{
String className = this.getClass( ).getName( );
String bundleName = ""; //$NON-NLS-1$
// Create the base message file name formatted like a Java class.
// The Java class loader will search for the file using the same
// algorithm used to find classes.
int index = className.lastIndexOf( '.' );
if ( index > -1 )
{
// e.g: "org.eclipse.birt.report.model.util.Test"
bundleName = className.substring( 0, index ) + ".";
//$NON-NLS-1$
}
bundleName = bundleName + BUNDLE_NAME;
if ( locale == null )
locale = ULocale.getDefault( );
resources = UResourceBundle.getBundleInstance(
bundleName, locale.toString(), this.getClass().getClassLoader() );
assert resources !=
null : "ResourceBundle : " + BUNDLE_NAME + " for " + locale + " not found";
//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
}
從 this.getClass().getClassLoader()可以看出來 console 的國際化文件是放在類路徑下面的,文件的組織形式是文件名稱 +locale,再有資源文件放到 jar 包裡面類路徑下可不是什麼好的方法,那麼我們是否能提供一個外部變量來配置這個資源文件的路徑呢?後面我們會講到。先來看看為什麼中文資源文件放到 jar 包裡面的類路徑下不起作用,明明是有 locale 的,是不是感覺有點奇怪?我們接下來繼續跟蹤,進入類 ReportEngineService 中的方法 createGetParameterDefinitionTask, 看它的方法體:
清單 4. 原 createGetParameterDefinitionTask 方法
public IGetParameterDefinitionTask createGetParameterDefinitionTask(
IReportRunnable runnable, InputOptions options )
{
IGetParameterDefinitionTask task = null;
try
{
HttpServletRequest request =
(HttpServletRequest) options.getOption( InputOptions.OPT_REQUEST );
task = engine.createGetParameterDefinitionTask( runnable );
// set app context
Map context = BirtUtility.getAppContext( request,
ReportEngineService.class.getClassLoader( ) );
task.setAppContext( context );
}
catch ( Exception e )
{
}
return task;
}
我們會注意到服務層的這個入口方法中沒有設置任何與 locale 相關的變量,添加設置 locale 變量的代碼,修改後的 createGetParameterDefinitionTask 如下所示:
清單 5. 修改後的 createGetParameterDefinitionTask
public IGetParameterDefinitionTask createGetParameterDefinitionTask(
IReportRunnable runnable, InputOptions options )
{
IGetParameterDefinitionTask task = null;
try
{
HttpServletRequest request =
(HttpServletRequest) options.getOption( InputOptions.OPT_REQUEST );
task = engine.createGetParameterDefinitionTask( runnable );
task.setLocale(ParameterAccessor.getLocale(request));
// set app context
Map context = BirtUtility.getAppContext( request,
ReportEngineService.class.getClassLoader( ) );
task.setAppContext( context );
}
catch ( Exception e )
{
}
return task;
}
類 ParameterAccessor 感興趣的朋友可以研究一下,他是傳遞入口參數的。如 __action、__nocache、__report 等,參數說明如下。
表 1. 參數描述
參數名 參數說明 參數值 默認值 __format 指定報表輸出格式 html 或 pdf html __isnull 指明一個參數是 null,常用於字符串類型。如果提供參數且值為空: - 對於日期和數字類型,BIRT 會將它們當作 null 處理。 - 對於字符串,BIRT 會將它作為空字符串。因此,為了說明某個字符串是 null,通常寫為:__isnull= 參數。 報表輸入參數名 None. Required. __locale 本地化選項 en-us 或 ch-zh 虛擬機默認 __report 指定 *.rptdesign 文件路徑 None. Required. __svg 指定是否使用 SVG 矢量圖來顯示圖表。管理 console 國際化的資源文件
通過上面講解,我們知道 ViewerResourceHandle 繼承自 ResourceHandle 類,而 ResourceHandle 類是負責組織資源文件的,代碼默認是將資源文件放到類路徑下的,也就是該類的包路徑下。通過以上分析可知,如果我們想改變資源文件的路徑只能有這麼兩種方法。方法一,重寫一個 ResourceHandleNew 類 , 然後讓 ViewerResourceHandle 繼承自 ResourceHandleNew; 方法二,修改 public ResourceHandle( ULocale locale ) 方法,或添加一個新的方法 public ResourceHandleExtent( ULocale locale ),在 ResourceHandleExtent 方法中實現資源文件路徑可以通過外部變量來配置。其實兩種方法各有千秋,不過核心就是都要修改 ResourceHandle 方法來達到管理資源文件的目的。下面我們來重寫 ResourceHandle 方法。
清單 6. 修改後的 ResourceHandle
public ResourceHandle( ULocale locale )
{
if ( locale == null )
locale = ULocale.getDefault( );
// get resource path
String birtResourceFolder = ParameterAccessor.processRealPath( context,
context.getInitParameter( INIT_PARAM_BIRT_RESOURCE_PATH ), null, false );
// 這個地方是需要完善的
String bundleName = birtResourceFolder+"/" + BUNDLE_NAME
+"_"+locale.getLanguage()+"_"+locale.getCountry();
Reader reader = null;
try {
reader = new FileReader(bundleName);
resource = new PropertyResourceBundle(reader);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
INIT_PARAM_BIRT_RESOURCE_PATH 這個參數是不是有點熟悉,就是配置報表文件路徑的變量,是 web.xml 中定義的 <param-name>BIRT_RESOURCE_PATH</param-name> 這個變量的值。至此我們完全可以將 console 的資源文件放到 jar 外面來進行管理了。
經常遇到的一些 birt 相關問題的總結
Birt view 界面顯示紅叉如:
圖 5. 經常遇到的問題之一
解決方法:
viewer.properties 文件的 #base_url=http://127.0.0.1:8080
設置不對,該設置主要應用於代理服務器的情況下,在使用代理服務器後,從 request 裡獲取的 URI 並非真正的 URI,需要在這裡設置。
控制 birt 日志輸出級別
解決方法:
在 web.xml 中設置
<context-param>
<description>
Report engine log level.( ALL|SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST|OFF )
</description>
<param-name>BIRT_VIEWER_LOG_LEVEL</param-name>
<param-value>ALL</param-value>
</context-param>
訪問 http://[yourip]:[yourport]/WebViewerExample/ 然後點擊 View Example 後出現異常
解決方法:
將 common-logging.jar 包添加到 birt-runtime-2_2_2\WebViewerExample\WEB-INF\lib 目錄下。
說明:本文相關代碼的修改只為提供給開源愛好者研究代碼使用,如用於商業用途責任自負。
總結
文章主要講解了如何國際化 birt 的報表文件及 birt 提供的 console 界面,由於 birt 代碼本身的一些局限性,使得 birt console 的國際化文件管理起來有些問題,通過修改相關代碼來達到靈活管理 console 資源文件的目的,通過跟蹤相關類間關系,有助於我們更好的了解 birt 國際化的實現方式。