领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多在本文中,我将向您展示如何在 SpringSource Application Platform 中运行 Spring Batch 作业。我曾为 JavaOne 和伦敦 Spring 用户组演示过此功能的早期版本,我认为分享它会很有帮助。示例代码在此。
捆绑包配置位于META-INF/spring/module-context.xml(这对于 Platform 捆绑包来说是惯例) - Spring DM 从META-INF/spring中获取所有 XML 文件。此捆绑包仅使用 Spring 配置并启动 HSQL Server 实例。
有一个集成测试可用于检查配置。
Eclipse 项目还包含 HSQL Swing 客户端的启动配置,因此您可以在 GUI 中查看数据库内容。启动它并使用META-INF/batch-hsql.properties中提供的属性(url=jdbc:hsqldb:hsql://127.0.0.1:9005/samples)连接到服务器实例。
捆绑包配置位于META-INF/spring/module-context.xml照常。它是simple-job-launcher-context.xml的简化版本,来自 Spring Batch 示例。它只需要定义将导出的 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-source捆绑包公开的 OSGi 服务。要查看如何导入该引用以及如何将本地服务公开到 OSGi 服务注册表,我们可以查看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
...
如果您查看此包,您会发现一个便利类,其他捆绑包可以使用它来启动作业(SimpleJobLauncherBean)。TheSimpleJobLauncherBean是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);
}
启动作业的计划是为每个作业简单地创建一个捆绑包,并让它定义其中一个SimpleJobLauncherBean实例。
将捆绑包放入正在运行的 Server 实例中。它启动速度很快,并且由于作业的范围很小,您将立即在批处理元数据中看到效果。在 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”)已在上面的数据库查询中看到,表明该步骤已执行(一次)。所有步骤所做的就是从平面文件读取数据,将行转换为域对象,并将它们写入标准输出。您可以通过检查 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]
如果您愿意,您可以再次运行作业,只需编辑捆绑包中的一个文件(例如清单或一个 Spring 配置文件)并保存即可。工具将获取更改并重新部署捆绑包。此作业的设置方式是在每次执行时都使用一组新的参数(使用时间戳)开始,因此它应该始终成功运行。
为了指示作业结束,SimpleJobLauncherBean只需获取封闭的 OSGiBundle实例并停止它。这是一个非常简单的模型,但它具有 API 定义明确且 OSGi 平台普遍支持的优点。原则上,只要容器(SpringSource Application Platform)可以捕获这些捆绑包事件,就可以非常灵活地扩展它。这些是我们可能在 Platform 2.0 版本的 Batch Personality 中看到的特性。如果您对行为应该是什么以及操作员需要哪些功能有任何想法,请通过评论本文来帮助我们。
我们可以通过登录 Equinox 控制台来验证作业捆绑包的状态。如果您转到命令行并键入telnet localhost 2401您应该会看到平台的命令提示符
osgi>
键入“ss”并按回车键,您将看到已安装捆绑包的列表
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 的捆绑包是作业启动器,并且处于活动状态。ID 为 99 的捆绑包是 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>
作业捆绑包已恢复到已解析状态,但它已再次执行作业,您可以从 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 工具后,您需要创建一个服务器实例。转到文件->新建->其他...并找到服务器->服务器。选择 SpringSource 和下面的服务器类型,并使用浏览对话框找到平台安装位置。
$ find ~/.m2/repository -name \*.jar -exec cp {} bundles/usr \;
无需重新启动 Eclipse 或任何其他操作。“捆绑包依赖项”类路径容器应该包含您刚刚下载的运行时依赖项。当“问题”视图(愤怒的红色边距标记)中的所有 Eclipse 错误消失后,我们就可以开始了。
我很乐意听到那些有更好方法的人的意见。其他人已经发展出其他方法,但对我来说,没有一个看起来很方便。实际上,一个命令行 Maven 目标很容易编写,但我还没有看到那个。
从原则上讲,您根本不需要 Maven 本地存储库来存储运行时依赖项。您可以打开平台运行时(在“服务器”视图中右键单击并打开),然后浏览并直接下载依赖项到bundles/usr。目前唯一的弱点(工具团队正在努力改进这一点)是它没有提供任何传递依赖项的视图 - 您必须明确知道需要哪些捆绑包。对于此博客的示例,这很容易,因为所有清单都已完全指定了依赖项。当您不确定它们是什么并且必须从头创建清单时,情况会更复杂。为此,我现在仍在使用 Q4E。
应用平台 1.0 版本的许多重点都放在 Web 层上,虽然这显然是必不可少的(而且交付起来非常棘手),但还有其他事情需要处理。2.0 版本将具有特定于批处理的功能(批处理个性),因此我们现在所做的任何事情都有助于完善该版本的特性需求。因此,如果您有机会尝试一下并提出一些建设性的意见,特别是关于操作方面的意见,它们在我们开始构建批处理个性时会派上用场。