领先一步
VMware 提供培训和认证,助您加速进步。
了解更多在本文中,我将向您展示如何在 SpringSource Application Platform 中运行 Spring Batch 作业。我之前为 JavaOne 做了一个小演示,然后在伦敦 Spring 用户组再次进行了演示,我认为分享一下会很有用。示例代码 在此处。
Bundle 的配置位于META-INF/spring/module-context.xml(这对于 Platform Bundle 是约定俗成的)- Spring DM 会从以下位置拾取所有 XML 文件META-INF/spring。此 Bundle 仅使用 Spring 来配置和启动 HSQL 服务器的实例。
有一个集成测试可用于检查配置。
Eclipse 项目还包含一个用于 HSQL Swing 客户端的启动配置,因此您可以在 GUI 中查看数据库内容。启动它,并使用同一项目中的META-INF/batch-hsql.properties中提供的属性连接到服务器实例(url=jdbc:hsqldb:hsql://:9005/samples)。
Bundle 的配置位于META-INF/spring/module-context.xml照常。这是 Spring Batch 示例中simple-job-launcher-context.xml的一个精简版本。它只需要定义将导出的 Bean,即
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="databaseType" value="hsql" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
唯一其他的配置是针对JobRepository的事务建议(在 Spring Batch 1.1 中不需要)。dataSource 引用来自上面data-sourceBundle 公开的 OSGi 服务。要了解该引用如何导入以及本地服务如何公开到 OSGi Service Registry,我们可以查看META-INF/spring/osgi-context.xml:
<reference id="dataSource" interface="javax.sql.DataSource" />
<service ref="jobLauncher"
interface="org.springframework.batch.core.launch.JobLauncher" />
<service ref="jobRepository"
interface="org.springframework.batch.core.repository.JobRepository" />
<service ref="transactionManager"
interface="org.springframework.transaction.PlatformTransactionManager" />
这是 Spring DM 的一个相当直接的用法。重要的是模块上下文与特定于 OSGi 的上下文是分开的。这允许我们为模块上下文编写集成测试,而无需部署到 Platform。因此,我们有
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobLauncherIntegrationTests {
@Autowired
private JobLauncher jobLauncher;
@Test
public void testLaunchJob() throws Exception {
assertNotNull(jobLauncher);
}
}
测试加载上下文,添加本地数据源定义以替换 OSGi 数据源(参见JobLauncherIntegrationTests-context.xml),然后断言作业启动器可用。您可以像往常一样直接从 Eclipse 运行测试。
...
Export-Package: com.springsource.consulting.batch.support
...
如果您查看此包,您会找到一个便捷类,其他 Bundle 可以使用它来启动作业(SimpleJobLauncherBean)。SimpleJobLauncherBean是一个ApplicationListener,这意味着包含其中一个的任何 SpringApplicationContext都会尝试在启动时(上下文加载时)启动作业。它的工作方式是监听ContextRefreshedEvent,然后尝试启动作业。
try {
jobLauncher.run(job, converter.getJobParameters(parameters));
} catch (JobExecutionAlreadyRunningException e) {
logger.error("This job is already running", e);
} catch (JobInstanceAlreadyCompleteException e) {
logger.info("This job is already complete. "
+ "Maybe you need to change the input parameters?", e);
} catch (JobRestartException e) {
logger.error("Unspecified restart exception", e);
}
启动作业的计划是为每个作业创建一个 Bundle,并让它定义其中一个SimpleJobLauncherBean实例。
将 Bundle 放入正在运行的服务器实例中。它启动得很快,由于作业范围很小,您将立即在批处理元数据中看到效果。在 HSQL Swing GUI 中,您可以执行一些 SQL,例如
SELECT * FROM BATCH_STEP_EXECUTION
并查看结果,类似这样
| STEP_EXECUTION_ID | VERSION | STEP_NAME | ... | STATUS | ... |
|---|---|---|---|---|---|
| 0 | 4 | helloWorldStep | ... | COMPLETED | ... |
这表明作业已执行(并成功完成)。步骤的配置位于META-INF/spring/module-context.xml:
<bean
class="com.springsource.consulting.batch.support.SimpleJobLauncherBean">
<constructor-arg ref="jobLauncher" />
<constructor-arg ref="helloWorld" />
<property name="parameters" value="launch.timestamp=${launch.timestamp}"/>
</bean>
<bean id="helloWorld" parent="simpleJob">
<property name="steps">
<bean parent="simpleStep" id="helloWorldStep">
<property name="commitInterval" value="100" />
<property name="itemReader">
...
</property>
<property name="itemWriter">
...
</property>
</bean>
</property>
</bean>
从上面可以看到,我们有一个常规的 Spring Batch 作业配置(称为“helloWorld”),带有一个步骤。步骤 ID(“helloWorldStep”)已在上面的数据库查询中看到,表明该步骤已执行(一次)。所有步骤所做的就是从平面文件读取数据,将行转换为域对象,然后将它们写入 stdout。您可以通过检查 Platform 主目录中的跟踪日志来查看结果,例如,如果您tail -f serviceability/trace/trace.log | grep -i hello您应该会看到
[2008-05-30 15:57:04.140] platform-dm-11
com.springsource.consulting.batch.hello.MessageWriter.unknown
I Message: [Hello World]
[2008-05-30 15:57:04.140] platform-dm-11
com.springsource.consulting.batch.hello.MessageWriter.unknown
I Message: [Hello Small World]
如果您愿意,可以通过编辑 Bundle 中的文件之一(例如 MANIFEST 或一个 Spring 配置文件)并保存它来再次运行该作业。工具会拾取更改并重新部署 Bundle。此作业的设置方式是,它会使用新的一组参数(使用时间戳)开始每次执行,因此它应该总是成功运行。
为了标记作业的结束,SimpleJobLauncherBean直接抓取封闭的 OSGiBundle实例,并停止它。这是一个相当简单的模型,但它具有 API 定义良好且得到 OSGi 平台普遍支持的好处。原则上,只要容器(SpringSource Application Platform)可以捕获这些 Bundle 事件,它就可以非常灵活地扩展。这些是我们可能会在 Platform 2.0 版本中看到的 Batch Personality 功能。如果您对行为和操作员所需的功能有任何想法,请通过评论本文来帮助我们。
我们可以通过登录 Equinox 控制台来验证作业 Bundle 的状态。如果您转到命令行并键入telnet localhost 2401您应该会看到 Platform 的命令提示符。
osgi>
键入“ss”并按 Enter 键,您将看到已安装的 Bundle 列表。
osgi> ss
Framework is launched.
id State Bundle
...
86 RESOLVED org.springframework.batch.infrastructure_1.0.0
87 RESOLVED org.springframework.batch.core_1.0.0
88 RESOLVED com.springsource.org.apache.commons.lang_2.4.0
97 ACTIVE job.launcher_1.0.0
99 RESOLVED hello.world_1.0.0
osgi>
因此,ID 为 97 的 Bundle 是作业启动器,并且它是活动的。ID 为 99 的 Bundle 是 hello world 作业(您的 ID 可能不同),它已解析,但由于在作业执行完成时已停止,因此未激活。
您可以从 OSGi 命令行重新启动作业
osgi> start 99
osgi> ss
Framework is launched.
id State Bundle
...
86 RESOLVED org.springframework.batch.infrastructure_1.0.0
87 RESOLVED org.springframework.batch.core_1.0.0
88 RESOLVED com.springsource.org.apache.commons.lang_2.4.0
97 ACTIVE job.launcher_1.0.0
99 RESOLVED hello.world_1.0.0
osgi>
作业 Bundle 已恢复到已解析状态,但它已再次执行了作业,您可以从 HSQL GUI 或如前所述从跟踪日志中进行验证。
| STEP_EXECUTION_ID | VERSION | STEP_NAME | ... | STATUS | ... |
|---|---|---|---|---|---|
| 0 | 4 | helloWorldStep | ... | COMPLETED | ... |
| 1 | 4 | helloWorldStep | ... | COMPLETED | ... |
| 2 | 4 | helloWorldStep | ... | COMPLETED | ... |
如果您刚刚尝试过,您可能会发现第二次及后续启动时,数据库中没有任何变化。这是预期的,因为您重新启动了一个成功完成的作业实例,所以它不会再次处理数据。事实上,一个异常是由JobLauncher抛出的,被SimpleJobLauncherBean捕获并记录(因此它会出现在跟踪日志中)。
安装 SpringSource Eclipse 工具后,您需要创建一个服务器实例。转到 File->New->Other... 并找到 Server->Server。选择 SpringSource 和下面的服务器类型,然后使用浏览对话框查找 Platform 安装。
$ find ~/.m2/repository -name \*.jar -exec cp {} bundles/usr \;
无需重启 Eclipse 或其他任何操作。“Bundle Dependencies”类路径容器应包含您刚刚下载的运行时依赖项。当 Problems 视图中的所有 Eclipse 错误(红色的边距标记)都消失后,我们就准备好了。
我非常乐意听到人们有一个更好的方法来做到这一点。其他人已经发展了其他方法,但对我来说似乎都不太方便。实际上,编写一个命令行 Maven 目标很容易,但我还没有见过那个。
原则上,您也完全不需要运行时依赖项的 Maven 本地存储库。您可以打开 Platform 运行时(在 Servers 视图中右键单击并选择 Open),然后直接浏览并下载依赖项到bundles/usr。目前唯一的缺点是(工具团队正在努力改进它),它没有提供任何传递依赖项的视图 - 您必须明确知道需要哪些 Bundle。对于本博客的示例,这很容易,因为 MANIFESTs 都已完全指定了依赖项。当您不确定它们是什么并且必须从头开始创建 MANIFEST 时,情况就更糟了。为此,我目前仍在使用 Q4E。
Application Platform 1.0 发布的大部分重点都在 Web 层,虽然这显然是必不可少的(并且非常棘手),但还有其他事情需要处理。2.0 版本将具有特定的批处理相关功能(Batch Personality),因此我们现在所做的一切都将有助于充实该版本的关键功能需求。因此,如果您有机会尝试一下,并有一些建设性的意见,特别是关于操作方面的,当我们开始构建 Batch Personality 时,它们将非常有用。