第09章-开发扩展与定制

第九章:开发扩展与定制

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

  1. 打开项目:File → Open → 选择 geoserver-cloud 目录
  2. 等待 Maven 导入完成
  3. 配置 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 代码审查清单

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 的开发和扩展:

  1. 项目结构:了解了源码目录组织和模块依赖。

  2. 开发环境:搭建了开发环境并配置了 IDE。

  3. 编码规范:掌握了代码风格、提交规范和测试要求。

  4. 创建扩展:学习了开发自定义输出格式、认证提供者和 WPS 处理过程。

  5. 贡献代码:了解了向开源项目贡献代码的流程。

  6. 集成测试:掌握了编写和运行集成测试的方法。

在下一章中,我们将学习 GeoServer Cloud 的最佳实践和实际案例分析。

9.11 思考题

  1. 在开发自定义扩展时,如何确保与 GeoServer Cloud 的版本兼容性?

  2. Spring Boot Auto-configuration 机制如何帮助简化扩展的集成?

  3. 如何测试一个新的 WPS 处理过程?需要考虑哪些测试场景?

  4. 贡献代码时,如何编写高质量的 Pull Request 描述?

  5. 如果需要修改 GeoServer 核心功能,应该如何处理与上游的关系?

posted @ 2025-11-29 13:41  我才是银古  阅读(2)  评论(0)    收藏  举报