Apache Turbine 快速入门与集成示例
以 Apache Turbine 7.0 完整工程讲解快速入门:archetype 生成、pom 与 web.xml 配置、自建 Screen 页面、Pipeline 与 Intake 实操、Security 登录体系与堆验证。
Apache Turbine 快速入门与集成示例
本文以 Apache Turbine 7.0 为例,演示从零创建完整工程、自建页面到核心概念实操的全过程,环境为 OpenJDK 17 + jakarta.servlet 6.1.0 + Maven 3.9.9 + MariaDB,全部回显来自真实服务器实测。
一、快速入门总览
| 步骤 | 内容 | 命令/文件 |
|---|---|---|
| 1 | 生成工程骨架 | mvn archetype:generate |
| 2 | 初始化数据库 | systemctl/mysql + mvn integration-test |
| 3 | 启动应用 | mvn jetty:run |
| 4 | 验证默认页与登录 | curl |
| 5 | 自建 Screen 页面 | HelloTurbine.java + HelloTurbine.vm |
| 6 | 理解 Pipeline/Intake/Security | 配置文件与代码走读 |
前置:JDK 17、Maven ≥ 3.9.6、MariaDB 已安装并启动(安装步骤见本专栏安装指南两篇)。
二、创建完整工程
2.1 生成工程
cd /root
mvn archetype:generate \
-DarchetypeGroupId=org.apache.turbine \
-DarchetypeArtifactId=turbine-webapp \
-DarchetypeVersion=7.0 \
-DgroupId=com.example \
-DartifactId=turbine-demo \
-Dversion=1.0.0 \
-Dpackage=com.example \
-DinteractiveMode=false
实测回显(尾部):
[INFO] Project created from Archetype in dir: /root/turbine-demo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
2.2 pom.xml 全文
archetype 生成的 pom.xml 如下(省略许可证头注释,其余为实测文件原文,属性 turbine.core 即 Turbine 版本):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.apache.turbine</groupId>
<artifactId>turbine-parent</artifactId>
<version>14</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>turbine-demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>My Turbine Web Application</name>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<targetPath>../torque/schema</targetPath>
<filtering>true</filtering>
<directory>src/main/torque-schema</directory>
<includes><include>*.xml</include></includes>
</resource>
</resources>
<testSourceDirectory>src/test/java</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<configuration>
<skipTests>${skipTests}</skipTests>
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compile.source}</source>
<target>${maven.compile.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.torque</groupId>
<artifactId>torque-maven-plugin</artifactId>
<version>${torque.version}</version>
<configuration/>
<executions>
<execution>
<id>torque-om</id>
<phase>generate-sources</phase>
<goals><goal>generate</goal></goals>
<configuration>
<packaging>classpath</packaging>
<configPackage>org.apache.torque.templates.om</configPackage>
<sourceDir>${project.basedir}/src/main/torque-schema</sourceDir>
<options>
<torque.database>auto</torque.database>
<torque.om.package>com.example.om</torque.om.package>
</options>
<outputDirMap>
<modifiable>${project.build.sourceDirectory}</modifiable>
</outputDirMap>
</configuration>
</execution>
<execution>
<id>torque-sql-mysql</id>
<phase>generate-sources</phase>
<goals><goal>generate</goal></goals>
<configuration>
<packaging>classpath</packaging>
<configPackage>org.apache.torque.templates.sql</configPackage>
<sourceDir>${project.basedir}/src/main/torque-schema</sourceDir>
<defaultOutputDir>${project.build.directory}/generated-sql/torque/mysql</defaultOutputDir>
<defaultOutputDirUsage>none</defaultOutputDirUsage>
<options><torque.database>mysql</torque.database></options>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/src/main/webapp/WEB-INF</directory>
<filtering>true</filtering>
<targetPath>WEB-INF</targetPath>
<includes><include>jetty-ee10-env.xml</include></includes>
</resource>
<resource>
<directory>${project.basedir}/src/main/webapp/META-INF</directory>
<filtering>true</filtering>
<targetPath>META-INF</targetPath>
<includes><include>context.xml</include></includes>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<version>1.5</version>
<configuration>
<driver>com.mysql.cj.jdbc.Driver</driver>
<url>${turbine_database_url}${turbine_database_name}?useSSL=false</url>
<username>db_user</username>
<password>db_password</password>
<onError>continue</onError>
<autocommit>true</autocommit>
<orderFile>descending</orderFile>
</configuration>
<executions>
<execution>
<id>create-db</id>
<phase>pre-integration-test</phase>
<goals><goal>execute</goal></goals>
<configuration>
<skip>false</skip>
<orderFile>ascending</orderFile>
<fileset>
<basedir>${project.build.directory}/generated-sql/torque/mysql</basedir>
<includes><include>*.sql</include></includes>
</fileset>
</configuration>
</execution>
<execution>
<id>create-data</id>
<phase>pre-integration-test</phase>
<goals><goal>execute</goal></goals>
<configuration>
<skip>false</skip>
<orderFile>ascending</orderFile>
<srcFiles>
<srcFile>${project.basedir}/src/main/data/sample-mysql-data/_application-data.sql</srcFile>
<srcFile>${project.basedir}/src/main/data/sample-mysql-data/_turbine-security-data.sql</srcFile>
</srcFiles>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-maven-plugin</artifactId>
<version>12.0.22</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<httpConnector>
<port>8081</port>
<idleTimeout>60000</idleTimeout>
</httpConnector>
<webApp>
<jettyEnvXml>${project.build.directory}/${project.build.finalName}/WEB-INF/jetty-ee10-env.xml</jettyEnvXml>
</webApp>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jakarta-web</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.torque</groupId>
<artifactId>torque-runtime</artifactId>
<version>${torque.version}</version>
</dependency>
<dependency>
<groupId>org.apache.turbine</groupId>
<artifactId>turbine</artifactId>
<version>${turbine.core}</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-yaafi</artifactId>
<version>${fulcrum.yaafi}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-intake</artifactId>
<version>${fulcrum.intake}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-cache</artifactId>
<version>${fulcrum.cache}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-security-memory</artifactId>
<version>${fulcrum.security}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-security-torque</artifactId>
<version>${fulcrum.security}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-security-api</artifactId>
<version>${fulcrum.security}</version>
</dependency>
<dependency>
<groupId>org.apache.fulcrum</groupId>
<artifactId>fulcrum-json-jackson2</artifactId>
<version>${fulcrum.json}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compile.source>17</maven.compile.source>
<maven.compile.target>17</maven.compile.target>
<maven.surefire.version>3.5.3</maven.surefire.version>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<log4j2.version>2.25.1</log4j2.version>
<mysql.version>8.3.0</mysql.version>
<fulcrum.intake>4.0.0</fulcrum.intake>
<fulcrum.parser>4.0.0</fulcrum.parser>
<fulcrum.security>4.0.0</fulcrum.security>
<fulcrum.yaafi>2.0.1</fulcrum.yaafi>
<fulcrum.json>2.0.1</fulcrum.json>
<fulcrum.cache>2.0.1</fulcrum.cache>
<torque.version>7.0</torque.version>
<turbine.core>7.0</turbine.core>
<skipTests>true</skipTests>
<turbine_database_url>jdbc:mysql://localhost:3306/</turbine_database_url>
<turbine_database_name>turbine</turbine_database_name>
</properties>
<profiles>
<profile>
<id>mysql</id>
<activation><activeByDefault>true</activeByDefault></activation>
<properties>
<driverClassName>com.mysql.cj.jdbc.Driver</driverClassName>
<turbine_database_url>jdbc:mysql://localhost:3306/</turbine_database_url>
</properties>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
2.3 web.xml 全文
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<servlet>
<description>My Turbine Web Application</description>
<servlet-name>Turbine</servlet-name>
<servlet-class>org.apache.turbine.Turbine</servlet-class>
<init-param>
<param-name>applicationRoot</param-name>
<param-value>webContext</param-value>
</init-param>
<init-param>
<param-name>properties</param-name>
<param-value>WEB-INF/conf/TurbineResources.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Turbine</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
<cookie-config><max-age>3600</max-age></cookie-config>
</session-config>
<welcome-file-list>
<welcome-file>app</welcome-file>
</welcome-file-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>templates</web-resource-name>
<url-pattern>/templates/*</url-pattern>
</web-resource-collection>
<web-resource-collection>
<web-resource-name>logs</web-resource-name>
<url-pattern>/logs/*</url-pattern>
</web-resource-collection>
<auth-constraint />
</security-constraint>
</web-app>
2.4 TurbineResources.properties 关键配置
实测工程中最核心的 8 项(完整解析见《Apache Turbine 配置参数详解》):
pipeline.default.descriptor = WEB-INF/conf/turbine-classic-pipeline.xml
module.packages=com.example.modules,com.example.flux.modules,org.apache.turbine.modules
template.homepage=Index.vm
template.login=Login.vm
template.error=Error.vm
action.login=LoginUserIntake
action.logout=LogoutUser
services.TemplateService.classname=org.apache.turbine.services.template.TurbineTemplateService
三、运行与验证
3.1 初始化数据库并启动
mysql -u root -e "CREATE DATABASE turbine CHARACTER SET utf8mb4;"
mysql -u root -e "CREATE USER 'db_user'@'localhost' IDENTIFIED BY 'db_password';"
mysql -u root -e "GRANT ALL PRIVILEGES ON turbine.* TO 'db_user'@'localhost';"
cd /root/turbine-demo
mvn integration-test
MAVEN_OPTS='-Xms256m -Xmx512m' nohup mvn jetty:run > /root/jetty.log 2>&1 &
启动就绪日志:
[INFO] Started ServerConnector@39124055{HTTP/1.1, (http/1.1)}{0.0.0.0:8081}
[INFO] Started oejs.Server@377d66f{STARTING}[12.0.22,sto=0] @56049ms
3.2 验证默认页与登录
curl -s http://localhost:8081/app | grep -oE '<title>[^<]*</title>'
<title>Sample Apache Turbine Application</title>
演示账号 admin / q1w2e3r4(种子数据改法见安装指南):
curl -s -c /tmp/ck.txt -d 'username=admin&password=q1w2e3r4' http://localhost:8081/app/action/LoginUser -o /dev/null -w '%{http_code}\n'
curl -s -b /tmp/ck.txt http://localhost:8081/app/template/Index.vm | grep -c Logout
实测依次返回 200 与 1(登录成功后导航栏出现 Flux 管理菜单与 Logout 链接)。
四、自建 Screen 与 Template 页面
4.1 Screen 类
新建 src/main/java/com/example/modules/screens/HelloTurbine.java(包名必须在 module.packages 列出的包下):
package com.example.modules.screens;
import java.util.Date;
import org.apache.turbine.modules.screens.VelocityScreen;
import org.apache.turbine.pipeline.PipelineData;
import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;
public class HelloTurbine extends VelocityScreen
{
@Override
protected void doBuildTemplate(PipelineData data, Context context) throws Exception
{
RunData rundata = data.getRunData();
context.put("visitor", rundata.getUser().getName());
context.put("now", new Date().toString());
}
}
注意:Turbine 7.0 的回调签名是 PipelineData(6.0 引入),不是老版本的 RunData;经 data.getRunData() 取回 RunData 后再取当前用户。
4.2 Velocity 模板
新建 src/main/webapp/templates/app/screens/HelloTurbine.vm(与 Screen 同名,模板服务按名字匹配):
<h2>Hello Turbine!</h2>
<p>当前登录用户:$visitor</p>
<p>服务器时间:$now</p>
4.3 编译并验证
mvn compile
MAVEN_OPTS='-Xms256m -Xmx512m' nohup mvn jetty:run > /root/jetty.log 2>&1 &
curl -s http://localhost:8081/app/template/HelloTurbine.vm | grep -E 'Hello Turbine|anon|服务器时间'
实测回显:
<h2>Hello Turbine!</h2>
<p>当前登录用户:anon</p>
<p>服务器时间:Mon Oct 12 12:18:15 GMT 2026</p>
未登录时 Turbine 自动以匿名用户 anon 渲染;登录后访问则显示登录名。
五、核心概念实操
5.1 Pipeline 与 Valve
Pipeline 是 Turbine 处理请求的责任链。实测工程的 WEB-INF/conf/turbine-classic-pipeline.xml 按序声明了 10 个 Valve:SetEncoding → DetermineAction → DetermineTarget → SessionTimeout → Login → SessionValidation → ACLCreation → ExecutePage → CleanUp → DetermineRedirectRequested。一个请求进入 /app/* 后依次穿过这些 Valve:先确定要执行的 Action,再确定目标模板,校验会话与登录,构建 ACL,最后由 ExecutePageValve 渲染页面。自定义横切逻辑(如审计、限流)实现 org.apache.turbine.pipeline.Valve 接口后插入该文件即可。
5.2 Screen / Action / Navigation
- Screen 负责生成页面主体(第四章的 HelloTurbine 即最简 Screen),渲染顺序为 Screen 输出嵌入 Layout(
Default.vm),Layout 中的导航片段来自templates/app/navigations/Menu.vm。 - Action 处理业务动作、不直接输出页面。实测工程
modules/actions/LoginUser.java继承org.apache.turbine.modules.actions.LoginUser,在doPerform中取username参数、拒绝匿名登录、调用安全服务认证后写入会话;表单提交的eventSubmit_doPerform按钮值触发事件方法(action.eventsubmit.needsvalue=false时无需特定值)。 - URL 规律:
/app/template/<名字>.vm走 Screen+模板渲染,/app/action/<动作名>触发 Action 后重定向回模板。
5.3 Intake 表单校验
Intake 把表单校验规则集中到 WEB-INF/conf/intake.xml。实测 Login 组定义:
<group name="Login" key="login">
<field name="Username" key="u" type="String">
<rule name="required" value="true">username_required_msg</rule>
<rule name="minLength" value="4">username_min_length_msg</rule>
</field>
<field name="Password" key="p" type="String">
<rule name="required" value="true">password_msg</rule>
</field>
</group>
模板用 $loginGroup.Username.Key 渲染输入框,实测页面上生成的控件名为 login_0u(组键_序号_字段键)。用 curl 携带这些参数提交即可完成 Intake 校验流程的登录:
curl -s -c /tmp/ik.txt -d 'login_0u=admin&login_0p=q1w2e3r4&eventSubmit_doPerform=1' \
http://localhost:8081/app/action/LoginUserIntake/template/Login.vm -o /dev/null -w '%{http_code}\n'
实测回显:
200
5.4 Fulcrum Security 用户体系
Turbine 7.0 的用户/角色/权限由 Fulcrum Security 组件提供,数据落库为 TURBINE_USER、TURBINE_ROLE、TURBINE_GROUP、TURBINE_PERMISSION 及三张关联表(实测建表清单见第三章)。要点:
- 用户模型:
TurbineUserImpl,经services.SecurityService.wrapper.class包装为应用用户对象。 - 认证:
security.getAuthenticatedUser(username, password),匿名用户固定为anon(种子数据中存在)。 - 授权:登录后 ACL 由 DefaultACLCreationValve 构建,Flux 管理界面提供 user/role/group/permission 增删改查(实测以 admin 登录后导航栏出现 User List / User Roles / Add User 菜单)。
- 密码:演示数据明文存储,生产启用
componentConfiguration.xml中<authenticator>的加密算法。
六、JVM 堆验证
以 256–512MB 堆启动后,用 JDK 自带工具核对:
jps -v | grep -v Jps
实测回显:
7746 Launcher -Xms256m -Xmx512m -Dclassworlds.conf=/opt/maven/bin/m2.conf -Dmaven.home=/opt/maven -Dlibrary.jansi.path=/opt/maven/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/root/turbine-demo
ps -ef | grep '[j]ava' | grep -oE '\-Xm[sx][0-9a-z]+'
-Xms256m
-Xmx512m
部署到独立 Tomcat 时改为在 /opt/tomcat/bin/setenv.sh 写 CATALINA_OPTS="-Xms256m -Xmx512m",验证目标进程名变为 Bootstrap。
七、常见问题
**Q1:**自建 Screen 访问报错或显示默认页?
三查:包名是否在 module.packages 中;类名与模板名是否一致;mvn compile 后是否重启了 mvn jetty:run(jetty:run 使用 target/classes 中的类)。
**Q2:**覆写 doBuildTemplate 编译报错 "method does not override"?
Turbine 7.0 签名为 doBuildTemplate(PipelineData, Context),老教程中的 doBuildTemplate(RunData, Context) 已不适用(实测踩坑)。
**Q3:**模板中文乱码?
确认 input.encoding=UTF-8、services.VelocityService.default_encoding=UTF-8 与模板文件本身均为 UTF-8(实测工程默认即 UTF-8,无需修改)。
**Q4:**登录后菜单没出现 Flux 管理入口?
种子数据中 admin 需绑定角色与权限(TURBINE_USER_GROUP_ROLE 关联表);确认执行了 mvn integration-test 完整灌数,且用 admin 而非 user/anon 登录。
**Q5:**mvn integration-test 报数据库连接失败?
检查 MariaDB 已启动、turbine 库已创建、db_user 授权与 pom/sql-maven-plugin、jetty-ee10-env.xml 中的账号一致。
八、参考链接
- Turbine 7.0 官方文档:https://turbine.apache.org/turbine/turbine-7-0/
- Turbine 7.0 快速上手:https://turbine.apache.org/turbine/turbine-7-0/getting-started.html
- Turbine 7.0 Howto 文档:https://turbine.apache.org/turbine/turbine-7-0/howto/index.html
- Turbine 7.0 API 文档:https://turbine.apache.org/turbine/turbine-7-0/apidocs/index.html
- Maven Central 上的 turbine 7.0:https://central.sonatype.com/artifact/org.apache.turbine/turbine/7.0
- 官方代码仓库(turbine-core):https://github.com/apache/turbine-core
- 官方 archetype 仓库:https://github.com/apache/turbine-archetypes
- Fulcrum 组件列表:https://turbine.apache.org/fulcrum/