基于 Eclipse 的 Birt 国际化的分析与改进
2010-03-30 00:00:00 来源:WEB开发网开发环境
本文的开发环境为 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 矢量图来显示图表。 SVG 矢量图形是一种 XML 格式的文本文件,在 IE 下需要安装 Adodb SVG Viewer 插件才能浏览。 | true 或 false | 安装插件后浏览时 frameset 下默认 true,run 下默认 false;不安装插件时默认为 false |
管理 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 国际化的实现方式。
更多精彩
赞助商链接