第九章:开发扩展与定制
9.1 引言
GeoServer Cloud 基于 GeoServer 构建,继承了其强大的扩展能力。对于有特定需求的组织,了解如何开发自定义扩展、贡献代码以及定制系统行为是非常重要的。本章将介绍 GeoServer Cloud 的项目结构、开发环境搭建、编码规范以及如何创建自定义扩展。
无论您是想为项目贡献代码,还是需要为您的组织开发专有功能,本章都将为您提供必要的知识和实践指导。
9.2 项目结构概览
9.2.1 源码目录结构
geoserver-cloud/
├── src/ # 主要源代码
│ ├── apps/ # 微服务应用
│ │ ├── base-images/ # Docker 基础镜像
│ │ ├── infrastructure/ # 基础设施服务
│ │ │ ├── config/ # 配置服务
│ │ │ ├── discovery/ # 服务发现
│ │ │ └── gateway/ # API 网关
│ │ └── geoserver/ # GeoServer 服务
│ │ ├── wms/ # WMS 服务
│ │ ├── wfs/ # WFS 服务
│ │ ├── wcs/ # WCS 服务
│ │ ├── wps/ # WPS 服务
│ │ ├── gwc/ # GeoWebCache 服务
│ │ ├── restconfig/ # REST API 服务
│ │ └── webui/ # Web UI 服务
│ │
│ ├── catalog/ # Catalog 相关模块
│ │ ├── backends/ # 目录后端实现
│ │ │ ├── common/ # 通用目录组件
│ │ │ ├── datadir/ # 数据目录后端
│ │ │ ├── jdbcconfig/ # JDBC 后端(已弃用)
│ │ │ └── pgconfig/ # PostgreSQL 后端
│ │ ├── cache/ # 目录缓存
│ │ ├── events/ # 事件模型
│ │ ├── event-bus/ # 事件总线集成
│ │ ├── jackson-bindings/ # JSON 序列化
│ │ └── plugin/ # 核心目录插件
│ │
│ ├── extensions/ # 扩展模块
│ │ ├── security/ # 安全扩展
│ │ ├── input-formats/ # 输入格式扩展
│ │ ├── output-formats/ # 输出格式扩展
│ │ └── ...
│ │
│ ├── gwc/ # GeoWebCache 模块
│ ├── library/ # 公共库
│ ├── starters/ # Spring Boot Starters
│ └── integration-tests/ # 集成测试
│
├── compose/ # Docker Compose 开发环境
├── config/ # 配置仓库(子模块)
├── docs/ # 文档
└── docker-build/ # Docker 构建文件
9.2.2 模块依赖关系
┌──────────────────────────┐
│ 应用层 (apps) │
│ wms / wfs / webui ... │
└────────────┬─────────────┘
│
┌────────────┴─────────────┐
│ Starters (启动器) │
│ webmvc / security ... │
└────────────┬─────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Extensions │ │ Catalog │ │ GWC │
│ 安全/格式扩展 │ │ 目录/配置管理 │ │ 瓦片缓存 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌────────────┴─────────────┐
│ Library (库) │
│ 公共工具 / JNDI 支持 │
└────────────────────────────┘
9.3 开发环境搭建
9.3.1 系统要求
| 工具 | 版本 | 说明 |
|---|---|---|
| JDK | 21+ | 推荐 Eclipse Temurin |
| Maven | 3.6.3+ | 项目构建 |
| Docker | 20.10+ | 容器运行时 |
| Docker Compose | 2.0+ | 开发环境编排 |
| Git | 2.0+ | 版本控制 |
| IDE | - | IntelliJ IDEA 或 Eclipse |
9.3.2 克隆仓库
# 克隆仓库(包含子模块)
git clone --recurse-submodules https://github.com/geoserver/geoserver-cloud.git
cd geoserver-cloud
# 如果已克隆但未初始化子模块
git submodule update --init --recursive
9.3.3 构建项目
# 完整构建(编译、测试、打包)
make
# 仅安装(跳过测试)
make install
# 仅运行测试
make test
# 清理构建
make clean
# 构建 Docker 镜像
make build-image
# 构建指定模块
./mvnw -f src/apps/geoserver/wms/pom.xml clean install
9.3.4 IDE 配置
IntelliJ IDEA
- 打开项目:File → Open → 选择
geoserver-cloud目录 - 等待 Maven 导入完成
- 配置 JDK:File → Project Structure → Project → SDK → 21
代码格式化
项目使用 Google Java Format(AOSP 变体,4空格缩进):
# 安装 google-java-format 插件
# IntelliJ: File → Settings → Plugins → 搜索 "google-java-format"
# 或使用命令行格式化
./mvnw spotless:apply
9.3.5 运行开发环境
# 启动基础服务
cd compose
./pgconfig up -d
# 或启动带本地端口暴露的版本(用于 IDE 调试)
./pgconfig -f localports.yml up -d
9.3.6 IDE 运行单个服务
在 IDE 中运行 WMS 服务:
配置运行参数
Main Class: org.geoserver.cloud.wms.app.WmsApplication
VM Options: -Dspring.profiles.active=local,pgconfig
Environment Variables:
GEOSERVER_DATA_DIR=/path/to/compose/catalog-datadir/ # datadir 后端
本地开发端口
| 服务 | 端口 |
|---|---|
| wfs-service | 9101 |
| wms-service | 9102 |
| wcs-service | 9103 |
| wps-service | 9104 |
| restconfig-v1 | 9105 |
| web-ui | 9106 |
9.4 编码规范
9.4.1 代码风格
GeoServer Cloud 遵循 Google Java Style Guide 的 AOSP 变体:
// 4 空格缩进
public class Example {
public void method() {
if (condition) {
doSomething();
}
}
}
// 行宽限制:100 字符
// 使用 @Nullable/@NonNull 注解
// Lombok 注解简化代码
9.4.2 版权头
每个源文件必须包含版权头:
/* (c) 2024 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.cloud.example;
9.4.3 提交规范
# 提交消息格式
<type>(<scope>): <subject>
<body>
<footer>
# 示例
feat(wms): add support for custom output formats
Add the ability to register custom WMS output formats through
Spring auto-configuration.
Closes #123
类型(type):
feat: 新功能fix: Bug 修复docs: 文档更新style: 代码格式refactor: 重构test: 测试chore: 构建/工具
9.4.4 测试要求
- 每个新功能必须包含测试
- 修复 Bug 必须添加回归测试
- 保持测试覆盖率不下降
@SpringBootTest
@AutoConfigureMockMvc
class WmsControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetCapabilities() throws Exception {
mockMvc.perform(get("/wms?service=WMS&request=GetCapabilities"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_XML));
}
}
9.5 创建自定义扩展
9.5.1 扩展类型
GeoServer Cloud 支持多种扩展类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| 输入格式 | 新的数据源支持 | 自定义数据库驱动 |
| 输出格式 | 新的输出格式 | 自定义导出格式 |
| 样式扩展 | 新的样式语法 | 自定义样式语言 |
| 安全扩展 | 认证/授权 | 自定义认证提供者 |
| 处理过程 | WPS 处理 | 自定义分析算法 |
| REST 端点 | 管理 API | 自定义配置接口 |
9.5.2 扩展开发步骤
1. 创建 Maven 模块
<!-- pom.xml -->
<project>
<parent>
<groupId>org.geoserver.cloud</groupId>
<artifactId>gs-cloud-extensions</artifactId>
<version>2.28.1.0</version>
</parent>
<artifactId>gs-cloud-extension-myformat</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.geoserver.cloud</groupId>
<artifactId>gs-cloud-starter-extensions</artifactId>
</dependency>
<!-- 其他依赖 -->
</dependencies>
</project>
2. 实现扩展功能
// 自定义输出格式示例
@Component
public class MyCustomOutputFormat extends WFSGetFeatureOutputFormat {
public static final String MIME_TYPE = "application/x-myformat";
public MyCustomOutputFormat() {
super(MIME_TYPE);
}
@Override
public String getMimeType(Object value, Operation operation) {
return MIME_TYPE;
}
@Override
protected void write(
FeatureCollectionResponse featureCollection,
OutputStream output,
Operation operation) throws IOException {
// 实现格式化输出
}
}
3. 创建自动配置
@Configuration
@ConditionalOnClass(GeoServer.class)
@AutoConfigureAfter(GeoServerAutoConfiguration.class)
public class MyFormatAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyCustomOutputFormat myCustomOutputFormat() {
return new MyCustomOutputFormat();
}
}
4. 注册自动配置
创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
org.geoserver.cloud.extension.myformat.MyFormatAutoConfiguration
9.5.3 集成到服务
在需要使用扩展的服务中添加依赖:
<!-- wfs-service pom.xml -->
<dependency>
<groupId>org.geoserver.cloud</groupId>
<artifactId>gs-cloud-extension-myformat</artifactId>
</dependency>
9.5.4 创建 Spring Boot Starter
为扩展创建 Starter 方便使用:
// MyFormatStarter 模块
@Configuration
@EnableAutoConfiguration
@Import(MyFormatAutoConfiguration.class)
public class MyFormatStarterConfiguration {
// 可以添加条件配置
}
9.6 自定义认证提供者
9.6.1 实现 AuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 自定义认证逻辑
if (validateCredentials(username, password)) {
List<GrantedAuthority> authorities = loadAuthorities(username);
return new UsernamePasswordAuthenticationToken(
username, password, authorities);
}
throw new BadCredentialsException("Authentication failed");
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication);
}
private boolean validateCredentials(String username, String password) {
// 实现验证逻辑
return true;
}
private List<GrantedAuthority> loadAuthorities(String username) {
// 加载用户权限
return List.of(new SimpleGrantedAuthority("ROLE_USER"));
}
}
9.6.2 配置认证链
@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {
@Autowired
private CustomAuthenticationProvider customAuthProvider;
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authBuilder.authenticationProvider(customAuthProvider);
return authBuilder.build();
}
}
9.7 自定义 WPS 处理过程
9.7.1 创建处理过程
@DescribeProcess(
title = "Custom Buffer",
description = "Create buffer with custom parameters"
)
public class CustomBufferProcess implements GeoServerProcess {
@DescribeResult(description = "Buffered geometry")
public Geometry execute(
@DescribeParameter(name = "geometry", description = "Input geometry")
Geometry geometry,
@DescribeParameter(name = "distance", description = "Buffer distance")
Double distance,
@DescribeParameter(name = "segments", description = "Number of segments", min = 0)
Integer segments) {
if (segments == null) {
segments = 8;
}
return geometry.buffer(distance, segments);
}
}
9.7.2 注册处理过程
@Configuration
public class CustomProcessConfiguration {
@Bean
public CustomBufferProcess customBufferProcess() {
return new CustomBufferProcess();
}
}
9.8 贡献代码指南
9.8.1 贡献流程
1. Fork 仓库到个人账户
2. 创建功能分支
3. 编写代码和测试
4. 提交 Pull Request
5. 等待代码审查
6. 根据反馈修改
7. 合并到主分支
9.8.2 Pull Request 指南
# 1. Fork 并克隆
git clone https://github.com/YOUR_USERNAME/geoserver-cloud.git
cd geoserver-cloud
# 2. 添加上游仓库
git remote add upstream https://github.com/geoserver/geoserver-cloud.git
# 3. 创建功能分支
git checkout -b feature/my-feature main
# 4. 开发和提交
git add .
git commit -m "feat: add new feature"
# 5. 推送到个人仓库
git push origin feature/my-feature
# 6. 创建 Pull Request(通过 GitHub 网页)
9.8.3 代码审查清单
- 代码符合编码规范
- 包含适当的测试
- 文档已更新
- 提交消息清晰
- 无合并冲突
- CI 检查通过
9.9 集成测试
9.9.1 运行集成测试
# 运行所有集成测试
./mvnw -f src/integration-tests/pom.xml verify
# 运行特定测试
./mvnw -f src/integration-tests/pom.xml \
-Dtest=WmsIntegrationTest verify
9.9.2 编写集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({"test", "pgconfig"})
@Testcontainers
class WmsIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@Autowired
private TestRestTemplate restTemplate;
@Test
void testGetCapabilities() {
ResponseEntity<String> response = restTemplate.getForEntity(
"/wms?service=WMS&request=GetCapabilities",
String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).contains("WMS_Capabilities");
}
}
9.10 本章小结
本章介绍了 GeoServer Cloud 的开发和扩展:
-
项目结构:了解了源码目录组织和模块依赖。
-
开发环境:搭建了开发环境并配置了 IDE。
-
编码规范:掌握了代码风格、提交规范和测试要求。
-
创建扩展:学习了开发自定义输出格式、认证提供者和 WPS 处理过程。
-
贡献代码:了解了向开源项目贡献代码的流程。
-
集成测试:掌握了编写和运行集成测试的方法。
在下一章中,我们将学习 GeoServer Cloud 的最佳实践和实际案例分析。
9.11 思考题
-
在开发自定义扩展时,如何确保与 GeoServer Cloud 的版本兼容性?
-
Spring Boot Auto-configuration 机制如何帮助简化扩展的集成?
-
如何测试一个新的 WPS 处理过程?需要考虑哪些测试场景?
-
贡献代码时,如何编写高质量的 Pull Request 描述?
-
如果需要修改 GeoServer 核心功能,应该如何处理与上游的关系?