Spring框架 & BIRT

工程 | Josh Long | 2012年1月30日 | ...

作者:Jason Weathersby 和 Josh Long

引言

Eclipse的商业智能和报表工具(BIRT)项目是一个基于流行的Eclipse IDE的开源项目。BIRT项目于2005年夏季发布了第一个主要版本,自成立以来已获得超过一千万次的下载。该项目由Actuate公司启动,该公司使用BIRT作为其许多商业产品的基础。BIRT项目的网站包括介绍、教程、下载和BIRT使用示例。

开发人员使用BIRT在Java/Java EE环境中构建和部署报表。在BIRT 3.7中,提供了一个新的基于POJO的运行时环境,这使得BIRT引擎的部署更加简单。本文讨论了几个使用Spring框架组件的BIRT集成方案。

图1 – BIRT拼图

BIRT项目包含以下关键组件:

  • BIRT Designer - 用于设计报表的开发工具。
  • Web Viewer - 用于部署报表的示例Java Web应用程序。此查看器包含一个JSP标签库,便于与现有Web应用程序集成。
  • BIRT Engines - 用于构建、运行和呈现BIRT报表的引擎。
  • BIRT Charts - 支持构建和运行高度复杂的交互式图表。

Spring框架是一个流行的体系结构和实现方法集合,它使企业Java开发更加容易。该框架的核心部分是依赖注入和面向方面编程。利用这些技术可以减少应用程序基础设施特定部分与作为其主要目标的业务功能之间的耦合。Spring框架由VMware内部的SpringSource业务部门以开源方式开发(最初的SpringSource公司于2009年被VMware收购)。它在Java开发人员社区中得到了广泛应用,并在所有平台上运行,包括VMware支持的Tomcat版本,称为tcServer。

虽然Spring框架提供了许多有用的功能,但大多数关于与BIRT集成的疑问都围绕着Spring MVC和报表中的Spring bean访问。本文涵盖三种方案:

  • 在Spring MVC中集成BIRT引擎
  • 从BIRT查看器访问Spring bean
  • 使用Spring远程访问BIRT报表中的Spring Bean

本文假设读者了解BIRT和Spring框架的编程知识。要了解有关Spring框架的更多信息,请查看“绿色Bean”博客系列

每个方案都附带一个与本文相关的示例,该示例使用以下组件构建和测试:

这些示例旨在说明问题,并且几乎不包含错误检查。

示例代码

在本文中,不同的集成方案都引用了一个通用的Spring bean,它向BIRT报表提供数据。Spring框架包含许多复杂的数据库访问组件,但这个简单的示例对于更复杂的场景来说是一个有用的学习工具。在示例代码中,有两个包: *org.eclipse.birt.spring.example* 和 *org.eclipse.birt.spring.core*。示例包包含简单的代码,可以用你自己的数据访问对象替换。核心包包含一个用于运行和渲染BIRT报表的Spring视图,一个将URL中输入的报表参数转换为相应的BIRT报表参数类型的参数转换器类,以及一个为其他bean提供报表引擎的BIRT引擎工厂。下面显示了示例类,并在本文中使用。核心类将在下一节中描述。

我们从一个简单的POJO开始,它将在我们的BIRT报表中使用。它命名为Car,包含用于描述汽车的简单属性。

 
package org.eclipse.birt.spring.example;

public class Car{
	private String make;
	private String model;
	private String year;
	public String getMake() {
		return make;
	}
	public void setMake(String make) {
		this.make = make;
	}
	public String getModel() {
		return model;
	}
	public void setModel(String model) {
		this.model = model;
	}
	public String getYear() {
		return year;
	}
	public void setYear(String year) {
		this.year = year;
	}
	public Car() {

	}
	public String toString(){
		return "Make:--"+this.make+" Model:--"+this.model+" Year:--"+this.year;
	}
	public String getCarString(){
		return( this.toString() );
	}
}

该示例提供了一个服务类,用于加载多个Car POJO以用于报表目的。

 
package org.eclipse.birt.spring.example;
import java.util.*;

public class CarServiceImpl implements CarService {

	public List getAllCars (){
		Car car1 = new Car();
		car1.setYear("2000");
		car1.setMake("Chevrolet");
		car1.setModel("Corvette");
		Car car2 = new Car();
		car2.setYear("2005");
		car2.setMake("Dodge");
		car2.setModel("Viper");
		Car car3 = new Car();
		car3.setYear("2002");
		car3.setMake("Ford");
		car3.setModel("Mustang GT");
		List cars = Arrays.asList(  car1, car2, car3 ) ;
		return cars ; 
		

	}
}

为了使此服务在Spring上下文中可用,我们使用一个简单的类,并使用Spring注释进行配置。

 
package org.eclipse.birt.spring.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class BirtDataServiceConfiguration {

	@Bean 
	public CarService carService(){ 
		return new CarServiceImpl(); 

	}
} 

从Spring MVC调用BIRT引擎

将BIRT引擎集成到Spring,特别是Spring MVC中,有很多方法。在此示例中,我们实现了一个Spring视图,该视图使用参数来确定用户所需的报表和输出格式。BIRT报表引擎被注入到视图中以执行报表的实际运行和呈现。

BIRT引擎可以被多个线程使用,但是启动通常比较耗时。理想情况下,我们只希望在应用程序的生命周期中启动一次BIRT引擎。我们还希望在应用程序关闭时正确关闭引擎。考虑到这一点,我们首先创建一个**BirtEngineFactory**类,该类充当工厂bean,其他bean可以使用它来返回BIRT报表引擎的实例。在Spring中,**FactoryBean**接口是复杂对象的智能构造函数。它有一个重要的约定:从其**getObject**方法返回一个可用的对象。

 
package org.eclipse.birt.spring.core;

import java.io.File;
import java.io.IOException;

import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;


/**
Factory bean for the instance of the {@link IReportEngine report engine}.
 */
public class BirtEngineFactory implements FactoryBean, ApplicationContextAware, DisposableBean {  

	public boolean isSingleton(){ return true ; } 

	private ApplicationContext context ; 
	private IReportEngine birtEngine ;	
	private Resource logDirectory ;
	private File _resolvedDirectory ;
	private java.util.logging.Level logLevel ; 

	public void setApplicationContext(ApplicationContext ctx){
		this.context = ctx; 	
	}

	public void destroy() throws Exception {
		birtEngine.destroy();
		Platform.shutdown() ;
	}

	public void setLogLevel(  java.util.logging.Level  ll){
		this.logLevel = ll ;
	}

	public void setLogDirectory( org.springframework.core.io.Resource resource ){
		File f=null;
		try {
			f = resource.getFile();
			validateLogDirectory(f);
			this._resolvedDirectory = f ;
		} catch (IOException e) {
			throw new RuntimeException( “couldn’t set the log directory”);
		} 

 
	}

	private void validateLogDirectory (File f) {
		Assert.notNull ( f ,  " the directory must not be null");
		Assert.isTrue(f.isDirectory() , " the path given must be a directory");
		Assert.isTrue(f.exists() , "the path specified must exist!");	
	} 

	public void setLogDirectory ( java.io.File f ){ 
		validateLogDirectory(f) ;
		this._resolvedDirectory = f; 
	}

	public IReportEngine getObject(){ 

		EngineConfig config = new EngineConfig();
		
		//This line injects the Spring Context into the BIRT Context
		config.getAppContext().put("spring", this.context );
		config.setLogConfig( null != this._resolvedDirectory ? this._resolvedDirectory.getAbsolutePath() : null  , this.logLevel);
		try {
			Platform.startup( config );
		}
		catch ( BirtException e ) {
			throw new RuntimeException ( "Could not start the Birt engine!", e) ;
		}

		IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
		IReportEngine be = factory.createReportEngine( config );
		this.birtEngine = be ; 


		return be ;
	}

	@Override
	public Class getObjectType() {
		return IReportEngine.class;
	}
}

此类还实现了**DisposableBean**接口,以便在应用程序关闭时可以正确关闭引擎。当Spring ApplicationContext关闭时,Spring将对所有实现DisposableBean的bean调用**DisposableBean#destroy**方法。我们的实现调用destroy方法。最后,此类实现**ApplicationContextAware**接口以接收Spring **ApplicationContext**的实例。我们将ApplicationContext存储起来,然后使用以下代码将其传递给BIRT报表引擎:

 
		EngineConfig config = new EngineConfig();
	
		//This line injects the Spring Context into the BIRT Context
		config.getAppContext().put("spring", this.context );

此代码允许从BIRT脚本和表达式中访问Spring上下文对象。

接下来,我们创建一个运行和渲染报表的Spring视图。此视图期望注入BIRT报表引擎。该视图在请求中搜索ReportName和ReportFormat参数以确定要运行的报表和所需的格式。还搜索请求中的报表参数名称。如果找到这些报表参数,则将其设置为相应的值。下面介绍了此视图的一部分。有关更多信息,请参阅示例代码。

 
/**
 * BirtView is used to run and render BIRT reports.
 * This class expects the request to contain a ReportName and ReportFormat
 * parameter. In addition Report parameters are automatically searched for in the
 * the request object.
 */
public class BirtView extends AbstractView {


	public static final String PARAM_ISNULL = "__isnull";
	public static final String UTF_8_ENCODE = "UTF-8"; 

	private IReportEngine birtEngine;
	private String reportNameRequestParameter = “ReportName” ; 
	private String reportFormatRequestParameter = “ReportFormat” ; 
	private IRenderOption renderOptions ; 

	public void setRenderOptions(IRenderOption ro) { 
		this.renderOptions = ro;
	} 
	
	public void setReportFormatRequestParameter( String rf ){ 
		Assert.hasText( rf , “the report format parameter must not be null”) ;
		this.reportFormatRequestParameter = rf ;
	}

	public void setReportNameRequestParameter ( String rn ) { 
		Assert.hasText( rn , “the reportNameRequestParameter must not be null”) ;
		this.reportNameRequestParameter = rn ; 
	}

	protected void renderMergedOutputModel(
			Map map, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		String reportName = request.getParameter( this.reportNameRequestParameter );
		String format = request.getParameter( this.reportFormatRequestParameter );
		ServletContext sc = request.getSession().getServletContext();
		if( format == null ){
			format="html";
		}
		IReportRunnable runnable = null;
		runnable = birtEngine.openReportDesign( sc.getRealPath("/Reports")+"/"+reportName );
		IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(runnable);
		runAndRenderTask.setParameterValues(discoverAndSetParameters( runnable, request ));


		response.setContentType( birtEngine.getMIMEType( format ));
		IRenderOption options =  null == this.renderOptions ? new RenderOption() : this.renderOptions;		
		if( format.equalsIgnoreCase("html")){    
			HTMLRenderOption htmlOptions = new HTMLRenderOption( options);
			htmlOptions.setOutputFormat("html");
			htmlOptions.setOutputStream(response.getOutputStream());
			htmlOptions.setImageHandler(new HTMLServerImageHandler());
			htmlOptions.setBaseImageURL(request.getContextPath()+"/images");
			htmlOptions.setImageDirectory(sc.getRealPath("/images"));
			runAndRenderTask.setRenderOption(htmlOptions);

		}else if( format.equalsIgnoreCase("pdf") ){
			PDFRenderOption pdfOptions = new PDFRenderOption( options );
			pdfOptions.setOutputFormat("pdf");
			pdfOptions.setOption(IPDFRenderOption.PAGE_OVERFLOW, IPDFRenderOption.FIT_TO_PAGE_SIZE);
			pdfOptions.setOutputStream(response.getOutputStream());
			runAndRenderTask.setRenderOption(pdfOptions);
		}else{

			String att  ="download."+format;
			String uReportName = reportName.toUpperCase(); 
			if( uReportName.endsWith(".RPTDESIGN") ){ 
				att = uReportName.replace(".RPTDESIGN", "."+format);
			}	
			response.setHeader(	"Content-Disposition", "attachment; filename=\"" + att + "\"" );
			options.setOutputStream(response.getOutputStream());
			options.setOutputFormat(format);
			runAndRenderTask.setRenderOption(options);
		}
		runAndRenderTask.getAppContext().put( EngineConstants.APPCONTEXT_BIRT_VIEWER_HTTPSERVET_REQUEST, request );
		runAndRenderTask.run();	
		runAndRenderTask.close();		

	}
	public void setBirtEngine(IReportEngine birtEngine) {
		this.birtEngine = birtEngine;
	}
.
.

要设置Spring MVC(并在我们的应用程序中使用此新视图),我们需要创建一个带有@EnableWebMVC注释的Spring @Configuration类。要覆盖Spring MVC机制的部分内容,我们可以简单地扩展一个基类 -WebMvcConfigurerAdapter - 并连接到适当的回调方法。此类是一个常规的Spring @Configuration类,正如我们之前在配置服务时看到的那样。我们使用@ComponentScan注释来告诉Spring将我们两个包中带注释的bean注册到Spring上下文中。接下来,我们覆盖**addViewControllers**方法以告诉Spring,以“/reports”结尾的URL应该委托给新的Birt Spring MVC **View**。birtView和BIRT引擎作为bean创建,并设置BirtView bean的**birtEngine**属性。

 
package org.eclipse.birt.spring.example;

import org.eclipse.birt.spring.core.BirtEngineFactory;
import org.eclipse.birt.spring.core.BirtView;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.BeanNameViewResolver;

@EnableWebMvc 
@ComponentScan( {"org.eclipse.birt.spring.core", "org.eclipse.birt.spring.example"})
@Configuration
public class BirtWebConfiguration  extends WebMvcConfigurerAdapter  {

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/reports").setViewName("birtView");

	}

	@Bean 
	public BirtView birtView(){ 
		BirtView bv = new BirtView(); 
		bv.setBirtEngine( this.engine().getObject() );
		return bv; 
	}


	@Bean public BeanNameViewResolver beanNameResolver(){ 
		BeanNameViewResolver br = new BeanNameViewResolver() ;
		return br; 
	} 

	@Bean
	protected BirtEngineFactory engine(){ 
		BirtEngineFactory factory = new BirtEngineFactory() ;  
		//Enable BIRT Engine Logging
		//factory.setLogLevel( Level.FINEST);
		//factory.setLogDirectory( new FileSystemResource("c:/temp"));

		return factory ; 
	}


}

如前所述,在描述BIRT引擎工厂类时,我们提供了来自BIRT上下文的Spring **ApplicationContext**的引用。BIRT上下文对象只不过是一个对象映射,BIRT引擎将其提供给下游进程,例如BIRT表达式、BIRT事件处理程序等。BIRT使用Rhino JavaScript引擎来处理BIRT表达式并评估JavaScript事件处理程序。BIRT上下文预先加载到脚本环境中,其中包含Rhino引擎对象。此对象允许报表开发人员检索Spring bean,并使用以下语法在表达式或事件脚本中使用它:

 
var mypojo = spring.getBean("carService");
mypojo.getAllCars() ().get(0);

如果你不想将Spring上下文注入BIRT应用程序上下文,你总是可以使用类似于以下代码的JavaScript来访问BIRT表达式和JavaScript事件处理程序中的Spring bean:

 
importPackage(Packages.org.springframework.context);
importPackage(Packages.org.springframework.web.context.support );
//ServletContext
var sc = reportContext.getHttpServletRequest().getSession().getServletContext();
//ApplicationContext 
var spring = WebApplicationContextUtils.getWebApplicationContext(sc);
var mypojo = spring.getBean("carService");
this.text = mypojo.getAllCars().get(0).getMake();

要运行此Web应用程序,请将下载的BIRT 3.7.1运行时birt-runtime-3_7_1\ReportEngine\lib目录中的JAR文件添加到webapp的WEB-INF/lib中。你还需要在WEB-INF/lib中使用Spring Framework下载中的以下JAR文件:

  • cglib-nodep-2.2.2.jar
  • org.springframework.aop-3.1.0.RELEASE.jar
  • org.springframework.asm-3.1.0.RELEASE.jar
  • org.springframework.beans-3.1.0.RELEASE.jar
  • org.springframework.context.support-3.1.0.RELEASE.jar
  • org.springframework.context-3.1.0.RELEASE.jar
  • org.springframework.core-3.1.0.RELEASE.jar
  • org.springframework.expression-3.1.0.RELEASE.jar
  • org.springframework.web.servlet-3.1.0.RELEASE.jar
  • org.springframework.web-3.1.0.RELEASE.jar

与本文附带的示例具有以下web.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>

 <servlet>
   <servlet-name>springandbirt</servlet-name>
   <servlet-class>
     org.springframework.web.servlet.DispatcherServlet
   </servlet-class>
   <init-param>
     <param-name>contextClass</param-name>
     <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
   </init-param>   
   <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>org.eclipse.birt.spring.example.BirtWebConfiguration</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
   <servlet-name>springandbirt</servlet-name>
   <url-pattern>/reports</url-pattern>
 </servlet-mapping>

  <welcome-file-list>
    <welcome-file>
      index.jsp
    </welcome-file>
  </welcome-file-list>
</web-app>

所有/reports URL都路由到Spring DispatcherServlet。index.jsp有两个链接可以运行两个报表示例。一个报表使用BIRT样本Derby数据库,另一个访问Car Service bean,如下面的代码所示:

<html>
<head>
</head>
<body>
<h1>BIRT Report</h1>
<p>
<a href="https://127.0.0.1:8080/springandbirt/reports?ReportName=TopNPercent.rptdesign">click here to run BIRT Report</a><br>
<a href="https://127.0.0.1:8080/springandbirt/reports?ReportName=SampleSpring.rptdesign">click here to run BIRT Report that calls a Spring Bean</a>
</p>
<%= new java.util.Date() %>
</body>
</html>
图1 – 查看BIRT Spring MVC示例输出

从BIRT查看器访问Spring Bean

在许多情况下,BIRT用户希望将Spring IOC容器添加到BIRT查看器。查看器是一个基于AJAX的Java Web应用程序,用于运行和呈现报表。查看器支持分页、目录和导出到其他格式(如PDF)。以这种方式部署时,开发人员需要访问Spring容器中的bean以进行报表处理。此方案非常易于实现。

从 BIRT 官网下载并解压 BIRT Runtime。BIRT 查看器位于 runtime 下载包的 WebViewerExample 目录中。要将查看器部署到 Tomcat,用户只需将 WebViewerExample 目录复制到 tomcatinstall/webapps 目录即可。大多数用户会将此文件夹重命名为更相关的名称,例如 BirtViewer。BIRT 官网提供了有关将查看器部署到其他应用服务器的更多详细信息。安装查看器后,可以将 Spring ContextLoaderListener 添加到查看器的 web.xml 文件中。为此,请在 web.xml 文件中的上下文参数下方添加以下监听器条目。

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

您还可以指定一个上下文参数来指向 Spring 上下文类,如下面的代码所示。

   <context-param>
     <param-name>contextClass</param-name>
     <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
   </context-param>   
   <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>org.eclipse.birt.spring.webviewer.example.BirtDataServiceConfiguration</param-value>
   </context-param>

在这种情况下,我们不需要 BirtWebConfiguration 类,因此我们将 contextConfigLocation 指向 BirtDataServiceConfiguration 类。

此处使用了本文第一节中描述的相同 POJO 类(只是包名不同),它们需要位于 WEB-INF/classes 目录中,或者该包需要位于 JAR 文件中并放置在查看器的 WEB-INF/lib 目录中。最后,将 Spring Framework 下载中的以下 JAR 文件添加到 WebViewer 的 WEB-INF/lib 目录。

  • cglib-nodep-2.2.2.jar
  • org.springframework.aop-3.1.0.RELEASE.jar
  • org.springframework.asm-3.1.0.RELEASE.jar
  • org.springframework.beans-3.1.0.RELEASE.jar
  • org.springframework.context.support-3.1.0.RELEASE.jar
  • org.springframework.context-3.1.0.RELEASE.jar
  • org.springframework.core-3.1.0.RELEASE.jar
  • org.springframework.expression-3.1.0.RELEASE.jar
  • org.springframework.web.servlet-3.1.0.RELEASE.jar
  • org.springframework.web-3.1.0.RELEASE.jar

要在 BIRT 表达式或 BIRT JavaScript 事件处理程序中访问 carService bean,请使用以下语法。

 
//BIRT label report item onCreate Script
importPackage(Packages.org.springframework.context);
importPackage(Packages.org.springframework.web.context.support );
var sc = reportContext.getHttpServletRequest().getSession().getServletContext();
//ApplicationContext 
spring = WebApplicationContextUtils.getWebApplicationContext(sc);
var mypojo = spring.getBean("carService");
this.Text = mypojo.getAllCars().get(0).getMake();
图 2 - 构建 BIRT 表达式

在设计器中预览报表时,BIRT 表达式或脚本会失败。为防止失败,请将表达式或脚本包装在“if”语句中,如下面的代码所示。

 
if( !reportContext.getHttpServletRequest().getAttribute("attributeBean").isDesigner()){
//Access Bean 
} 
图 3 - 在 BIRT 查看器中显示 Spring bean 输出

使用远程访问 BIRT 报表中的 Bean

本文的前两节重点介绍了将 BIRT 引擎和查看器与 Spring 框架集成。虽然这些方法运行良好,但假设您需要访问位于单独上下文或另一台机器上的 Spring bean。可以使用 Spring Remoting 来实现此功能。

虽然任何 Spring Remoting 技术或其他机制(如 SOAP 或 REST)都可以工作,但本节介绍如何使用 Spring 的基于 HTTP Invoker 的服务导出器。要实现此示例,我们首先构建一个示例 Web 应用程序,其中包含一个向远程客户端提供 Car POJO 的 Car Service bean。

对于远程访问,我们首先定义 Car POJO,如下面的代码所示。

 
package org.eclipse.birt.spring.remoting.example;

public class Car implements ICar{

	private static final long serialVersionUID = 1L;
	private String make;
	private String model;
	private String year;
	public String getMake() {
		return make;
	}
	public void setMake(String make) {
		this.make = make;
	}
	public String getModel() {
		return model;
	}
	public void setModel(String model) {
		this.model = model;
	}
	public String getYear() {
		return year;
	}
	public void setYear(String year) {
		this.year = year;
	}
	public Car() {
		this.year = "2000";
		this.make = "Chevrolet";
		this.model = "Corvette";
	}
	public String toString(){
		return "Make:--"+this.make+" Model:--"+this.model+" Year:--"+this.year;
	}
}

接下来,我们实现 Car Service。

 
package org.eclipse.birt.spring.remoting.example;
import java.util.Arrays;
import java.util.List;

import org.eclipse.birt.spring.remoting.example.Car;
import org.eclipse.birt.spring.remoting.example.CarService;
public class CarServiceImpl implements CarService{

public List getAllCars(){
	Car car1 = new Car();
	car1.setYear("2000");
	car1.setMake("Chevrolet");
	car1.setModel("Corvette");
	Car car2 = new Car();
	car2.setYear("2005");
	car2.setMake("Dodge");
	car2.setModel("Viper");
	Car car3 = new Car();
	car3.setYear("2002");
	car3.setMake("Ford");
	car3.setModel("Mustang GT");
	List cars = Arrays.asList(  car1, car2, car3 ) ;
	return cars ; 

	
}
}

最后,我们实现 BirtDataServiceConfiguration 文件来处理 Spring 上下文的 Java 配置。

 
package org.eclipse.birt.spring.remoting.example;

import java.util.HashMap;
import java.util.Map;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;


@Configuration
public class BirtDataServiceConfiguration {

	@Bean 
	public CarService carService(){ 
		 
		return  new CarServiceImpl();
	}

	@Bean 
	public HttpInvokerServiceExporter myServiceExporter(){ 
		HttpInvokerServiceExporter hse = new HttpInvokerServiceExporter();
		hse.setService( this.carService()) ;
		hse.setServiceInterface( CarService.class); 
		return hse; 
	}

	@Bean
	public SimpleUrlHandlerMapping myUrlMapping(){

		SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();         
		Map urlMap = new HashMap();         
		urlMap.put("/carService", myServiceExporter());                  
		mapping.setUrlMap(urlMap);         
		mapping.setAlwaysUseFullPath(true);         
		return mapping; 		
	}


}

此类仅将 /carService URL 映射到一个 HttpInvokerServiceExporter 对象,该对象将 carService bean 暴露给远程客户端。

接下来,我们可以为该应用程序创建一个使用 Spring DispatcherServlet 的 web.xml 文件,如下面的代码所示。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>

 <servletgt;
   <servlet-name>springandbirt</servlet-name>
   <servlet-class>
     org.springframework.web.servlet.DispatcherServlet
   </servlet-class>
   <init-param>
     <param-namegt;contextClass</param-name>
     <param-valuegt;org.springframework.web.context.support.AnnotationConfigWebApplicationContextlt;/param-value>
   </init-param>   
   <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>org.eclipse.birt.spring.remoting.example.BirtDataServiceConfiguration</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
   <servlet-name>springandbirt</servlet-name>
   <url-pattern>/carService</url-pattern>
 </servlet-mapping>

  <welcome-file-list>
    <welcome-file>
      index.jsp
    </welcome-file>
  </welcome-file-list>

</web-app>

该 Web 应用程序需要 Spring Framework 中 WEB-INF/lib 目录下的以下 JAR 文件。

  • cglib-nodep-2.2.2.jar
  • org.springframework.aop-3.1.0.RELEASE.jar
  • org.springframework.asm-3.1.0.RELEASE.jar
  • org.springframework.beans-3.1.0.RELEASE.jar
  • org.springframework.context.support-3.1.0.RELEASE.jar
  • org.springframework.context-3.1.0.RELEASE.jar
  • org.springframework.core-3.1.0.RELEASE.jar
  • org.springframework.expression-3.1.0.RELEASE.jar
  • org.springframework.web.servlet-3.1.0.RELEASE.jar
  • org.springframework.web-3.1.0.RELEASE.jar
  • aopalliance.jar (Spring 依赖项)

现在可以构建和部署该应用程序。

接下来,我们需要构建一个远程客户端来访问前面构建的应用程序。在此示例中,它只是一个 JAR 文件,我们可以将其与 BIRT 查看器一起包含,以便单独的上下文访问 carService。

客户端 jar 应包含 CarService 接口和 CarPojo/ICarPojo 类/接口。除了这三个类之外,我们还需要一个配置类来处理 Spring 上下文的 Java 配置。

此类使用 AnnotationConfigApplicationContext 类来指定一个 Java 类来处理 Spring 上下文的配置。ContextConfig 类如下所示,它使用 HttpInvokerProxyFactoryBean 来连接服务器并检索 car service bean。

 
package org.eclipse.birt.spring.remoting.client.example;

import org.eclipse.birt.spring.remoting.example.CarService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;

@Configuration
public class ContextConfig {

	@Bean
	public HttpInvokerProxyFactoryBean client() {     
		HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();     
		proxy.setServiceInterface(CarService.class);     
		proxy.setServiceUrl("https://127.0.0.1:8080/springandbirtremote/carService");
		return proxy;
	} 

}

此配置文件定义一个充当 HTTP invoker 代理的 bean。服务 URL 定义了前面定义的远程服务器的位置,服务接口定义了返回的对象。要构建客户端 JAR 文件,请将为服务器添加的相同 JAR 文件添加到类路径。

要从 BIRT 查看器调用 car service,请在 BIRT 表达式生成器或 JavaScript 事件处理程序中使用以下 JavaScript 代码片段,如下面的代码所示。

 
importPackage(Packages.org.springframework.context);
importPackage(Packages.org.springframework.web.context.support );
var sc = reportContext.getHttpServletRequest().getSession().getServletContext();
//ApplicationContext 
var spring = WebApplicationContextUtils.getWebApplicationContext(sc);
var mypojo = spring.getBean("client");
mypojo.getAllCars().get(0).getMake();

将构建远程服务器部分中描述的所有 jar 文件添加到 BIRT 查看器的 WEB-INF/lib 中。此外,将远程客户端 jar 文件添加到 BIRT 查看器的 WEB-INF/lib 中。最后,将以下内容添加到已部署查看器的 web.xml 中。这些设置已在本文的“从 BIRT 查看器访问 Spring Bean”部分中进行了讨论。请注意,contextConfigLocation 已更改为 ContextConfig 类。

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

   <context-param>
     <param-name>contextClass</param-name>
     <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
   </context-param>   
   <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>org.eclipse.birt.spring.webviewer.example.ContextConfig</param-value>
   </context-param>

这种方法允许已部署的 BIRT 查看器访问单独的机器/上下文以访问远程 bean。最后需要注意的是,如果您使用中间 Java 类来访问远程 Bean,请使用以下代码。

 
.
.
private final CarService carPojoService;

	public CarPojoClient(){
		final ApplicationContext context = new AnnotationConfigApplicationContext( ContextConfig.class);      
		this.carPojoService = (CarService) context.getBean("client");   
.
.

结论

Spring 和 BIRT 框架在 Java 社区中非常流行。许多 BIRT 技术用户也使用 Spring 功能作为其企业应用程序代码的一部分,来处理诸如数据源、报表展示和访问控制等功能。这些只是结合使用 Spring Framework 和 BIRT 的一些好处。本文介绍了集成 BIRT 和 Spring 的方法。虽然还存在其他场景,但这篇文章可以作为学习工具,帮助您开始同时使用这两种技术。

附件

希望利用本文中描述的集成类的开发人员可以使用下面的 SpringandBirtCoreJar.zip 下载。有关示例,请参阅 SpringBirtArticleSamples.zip 下载。

SpringandBirtCoreJar.zip SpringBirtArticleSamples.zip

获取 Spring 新闻通讯

关注 Spring 新闻通讯

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部