diff --git a/.claude/project-rules.md b/.claude/project-rules.md new file mode 100644 index 000000000..c0b800085 --- /dev/null +++ b/.claude/project-rules.md @@ -0,0 +1,184 @@ +# FIT Framework 项目规则 + +## 文件权限管理规则 + +### 规则 1: 新增和修改文件的所有权 + +**重要:** 所有新增或修改的文件必须设置正确的所有者权限,确保用户可以自主修改。 + +**原则:** 不要硬编码用户名和用户组,而是从项目中已有文件动态获取权限信息。 + +**动态获取权限的方法:** + +```bash +# 方法1: 从项目根目录的 pom.xml 获取所有者 +REF_FILE="pom.xml" # 或其他稳定存在的文件 +OWNER=$(ls -l $REF_FILE | awk '{print $3}') +GROUP=$(ls -l $REF_FILE | awk '{print $4}') + +# 方法2: 获取当前工作目录的所有者 +OWNER=$(ls -ld . | awk '{print $3}') +GROUP=$(ls -ld . | awk '{print $4}') + +# 方法3: 使用 stat 命令(更可靠) +OWNER=$(stat -f "%Su" $REF_FILE) # macOS +GROUP=$(stat -f "%Sg" $REF_FILE) # macOS +# 或 +OWNER=$(stat -c "%U" $REF_FILE) # Linux +GROUP=$(stat -c "%G" $REF_FILE) # Linux +``` + +**实施方法:** + +```bash +# 1. 先获取参考文件的所有者 +OWNER_GROUP=$(ls -l pom.xml | awk '{print $3":"$4}') + +# 2. 应用到新文件 +sudo chown $OWNER_GROUP + +# 或者分步执行 +OWNER=$(ls -l pom.xml | awk '{print $3}') +GROUP=$(ls -l pom.xml | awk '{print $4}') +sudo chown $OWNER:$GROUP +``` + +**检查清单:** +- [ ] 使用 Write 工具创建新文件后,立即从已有文件获取权限并设置 +- [ ] 使用 Edit 工具修改文件时,确认文件所有者与项目其他文件一致 +- [ ] 批量创建文件后,统一修改所有权 +- [ ] 创建目录后,递归修改目录及其内容的所有权 + +**完整示例:** + +```bash +# ❌ 错误做法:硬编码用户信息 +Write(file_path, content) +Bash("sudo chown jiyujie:staff " + file_path) # 在其他人电脑上会失败 + +# ✅ 正确做法:动态获取权限 +Write(file_path, content) +# 从项目根目录的 pom.xml 获取所有者信息 +Bash("OWNER_GROUP=$(ls -l pom.xml | awk '{print $3\":\"$4}') && sudo chown $OWNER_GROUP " + file_path) + +# ✅ 更简洁的做法:批量处理 +Write(file1, content1) +Write(file2, content2) +Write(file3, content3) +Bash("OWNER_GROUP=$(ls -l pom.xml | awk '{print $3\":\"$4}') && sudo chown $OWNER_GROUP file1 file2 file3") +``` + +## Pull Request 提交规范 + +### 规则 2: PR 提交必须遵循项目规范 + +本项目的 PR 规范定义在 `.github/PULL_REQUEST_TEMPLATE.md` 文件中。 + +**强制要求:** +1. 创建 PR 前,必须先阅读 `.github/PULL_REQUEST_TEMPLATE.md` +2. PR 描述必须完整填写模板中的所有必填项 +3. 不需要每次让用户提醒查看 PR 模板 + +**PR 模板位置:** +``` +.github/PULL_REQUEST_TEMPLATE.md +``` + +**必填项清单:** + +1. **相关问题 / Related Issue** + - [ ] Issue 链接或说明这是微小修改 + +2. **变更类型 / Type of Change** + - [ ] 选择适当的变更类型(Bug修复/新功能/破坏性变更/文档/重构/性能优化/依赖升级/功能增强/代码清理) + +3. **变更目的 / Purpose of the Change** + - [ ] 详细描述变更的目的和必要性 + +4. **主要变更 / Brief Changelog** + - [ ] 列出主要的变更内容 + +5. **验证变更 / Verifying this Change** + - [ ] 测试步骤 + - [ ] 测试覆盖情况 + +6. **贡献者检查清单 / Contributor Checklist** + - [ ] 基本要求 + - [ ] 代码质量 + - [ ] 测试要求 + - [ ] 文档和兼容性 + +**自动化流程:** + +当用户要求创建 PR 时: +1. 自动读取 `.github/PULL_REQUEST_TEMPLATE.md` +2. 根据当前变更内容填写 PR 模板 +3. 生成完整的 PR 描述 +4. 提供推送和创建 PR 的命令 + +**示例工作流:** + +```bash +# 1. 自动读取 PR 模板 +Read(".github/PULL_REQUEST_TEMPLATE.md") + +# 2. 分析当前变更 +Bash("git diff --stat") +Bash("git log -1") + +# 3. 生成 PR 描述(根据模板) +# ... 填写各个部分 + +# 4. 推送并创建 PR +Bash("git push -u origin ") +Bash("gh pr create --base --title --body <description>") +``` + +## 通用最佳实践 + +### 文件操作 +- 创建文件后立即检查并修改权限 +- 使用 `ls -l` 验证文件所有者 +- 批量操作后统一修改权限 + +### Git 操作 +- 提交前检查 `.github/` 目录中的规范 +- 遵循项目的 commit message 格式 +- PR 描述要完整、清晰 + +### 文档更新 +- 修改代码时同步更新相关文档 +- 确保 README 的准确性 +- 添加必要的使用示例 + +## 项目特定信息 + +**项目名称**: FIT Framework +**主分支**: main +**开发分支**: 3.5.x +**仓库**: https://github.com/ModelEngine-Group/fit-framework + +**常用命令:** + +```bash +# 编译项目 +mvn clean install + +# 运行测试 +mvn test + +# 启动应用 +./fit start + +# 检查文件权限 +ls -l <file> + +# 动态修改文件权限(从 pom.xml 获取所有者) +OWNER_GROUP=$(ls -l pom.xml | awk '{print $3":"$4}') +sudo chown $OWNER_GROUP <file> +``` + +--- + +**最后更新**: 2025-11-08 +**Claude Code 版本**: 最新版 diff --git a/framework/fit/java/fit-launcher/README.md b/framework/fit/java/fit-launcher/README.md index e1e4bfdae..a3bd5ba5a 100644 --- a/framework/fit/java/fit-launcher/README.md +++ b/framework/fit/java/fit-launcher/README.md @@ -1,18 +1,341 @@ -<div style="text-align: center;"><span style="font-size: 40px"><b>常规FIT应用启动程序</b></span></div> +# FIT Discrete Launcher(FIT 离散启动器) -[TOC] +## 模块概述 -# 说明 +`fit-discrete-launcher` 是 FIT 框架的应用启动入口模块,提供了完整的应用程序启动、类加载和进程管理能力。该模块支持 FIT 应用以离散(独立进程)模式运行,是 FIT 框架"聚散部署"能力的核心实现之一。 -在常规FIT应用中,以目录展开形式被安装在OS中,通过在环境变量中配置`FIT_HOME`来指定应用程序所在根目录。 +**主要职责:** +- 应用程序的启动引导 +- 自定义类加载器体系的初始化 +- 跨平台启动脚本的提供 +- 应用程序的生命周期管理 -应用程序可通过FIT提供的启动脚本,通过相应命令启动应用程序。 +## 核心特性 -# 运行时目录结构 +### 1. 离散启动能力 -![avatar](https://cloudmodelingapi.tools.huawei.com/cloudmodelingdrawiosvr/d/2Fay) +支持 FIT 应用以独立进程的方式启动和运行: +- 每个应用拥有独立的 JVM 进程 +- 完全隔离的运行环境 +- 支持多应用并行部署 -# ClassLoader模型 +### 2. 自定义类加载体系 -![avatar](https://cloudmodelingapi.tools.huawei.com/cloudmodelingdrawiosvr/d/2FfL) +实现了分层的类加载器架构: +- **SharedClassLoader**: 加载共享类库 +- **FrameworkClassLoader**: 加载框架核心类 +- 支持插件化的类隔离机制 +### 3. 跨平台启动脚本 + +提供基于 Node.js 的跨平台启动脚本: +- Unix/Linux/macOS: `fit` 脚本 +- Windows: `fit.cmd` 批处理文件 +- 统一的命令行接口 + +## 构建和安装 + +### 编译模块 + +在 `framework/fit/java` 目录下执行: + +```bash +mvn clean install +``` + +编译完成后,会生成: +- `fit-discrete-launcher-{version}.jar`: 启动器 JAR 文件 +- `build/bin/`: 启动脚本目录 + +### 构建产物 + +``` +build/ +├── fit-discrete-launcher-3.5.5-SNAPSHOT.jar +└── bin/ + ├── fit # Unix/Linux/macOS 启动脚本 + ├── fit.js # Node.js 核心脚本 + └── fit.cmd # Windows 启动脚本 +``` + +## 启动脚本使用说明 + +### 前置要求 + +- **Node.js**: 12.0+ (推荐 16.0+) +- **Java**: 17+ + +### Unix/Linux/macOS 使用方法 + +#### 初始化新项目 + +使用 `init` 命令可以快速创建一个新的 FIT 项目(脚手架功能): + +```bash +./fit init <project-name> [options] +``` + +**选项:** +- `--group-id=<id>`: Maven Group ID(默认:com.example) +- `--artifact-id=<id>`: Maven Artifact ID(默认:项目名称) +- `--package=<name>`: Java 包名(默认:groupId.artifactId) + +**示例:** + +```bash +# 交互式创建项目(会提示输入信息) +./fit init my-app + +# 使用命令行参数创建项目 +./fit init my-app --group-id=com.mycompany --artifact-id=my-app --package=com.mycompany.myapp +``` + +创建的项目包含: +- 标准的 Maven 项目结构 +- FIT 框架依赖配置 +- 示例启动类(Application.java) +- 示例控制器(HelloController.java) +- 示例领域模型(Message.java) +- README.md 和 .gitignore 文件 + +创建后即可进入项目目录,编译并运行: + +```bash +cd my-app +mvn clean install +./fit start +``` + +访问 http://localhost:8080/hello 测试应用。 + +#### 启动应用 + +```bash +./fit start [Java参数] [程序参数] +``` + +**示例:** +```bash +# 基本启动 +./fit start + +# 指定 JVM 内存参数 +./fit start -Xmx1g -Xms512m + +# 传递应用参数 +./fit start -Xmx1g myapp config.yaml +``` + +#### Debug 模式启动 + +```bash +./fit debug [Java参数] [程序参数] +``` + +Debug 模式会自动添加以下 JVM 参数: +``` +-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 +``` + +启动后可以使用 IDE(如 IntelliJ IDEA)连接到端口 5005 进行远程调试。 + +#### 查看版本 + +```bash +./fit version +``` + +#### 查看帮助 + +```bash +./fit help +``` + +### Windows 使用方法 + +使用 `fit.cmd` 命令: + +```cmd +# 初始化新项目 +fit.cmd init my-app +fit.cmd init my-app --group-id=com.mycompany --artifact-id=my-app + +# 启动应用 +fit.cmd start + +# Debug 模式启动 +fit.cmd debug + +# 查看版本 +fit.cmd version + +# 查看帮助 +fit.cmd help + +# 传递参数 +fit.cmd start -Xmx1g -Xms512m myapp config.yaml +``` + +### 参数传递机制 + +启动脚本会自动区分 Java 参数和程序参数: + +- **Java 参数**: 以 `-` 开头的参数(如 `-Xmx512m`、`-Dproperty=value`) +- **程序参数**: 不以 `-` 开头的参数 + +**完整示例:** +```bash +./fit start -Xmx1g -Dplugin.path=/custom/path myapp config.yaml +``` + +实际执行的 Java 命令: +```bash +java -Xmx1g -Dplugin.path=/custom/path \ + -Dsun.io.useCanonCaches=true \ + -Djdk.tls.client.enableSessionTicketExtension=false \ + -Dplugin.fit.dynamic.plugin.directory=/current/working/directory \ + -jar fit-discrete-launcher-3.5.5-SNAPSHOT.jar \ + myapp config.yaml +``` + +### 环境变量和系统属性 + +启动脚本会自动设置以下 Java 系统属性: + +| 系统属性 | 默认值 | 说明 | +|---------|--------|------| +| `sun.io.useCanonCaches` | `true` | 启用文件路径缓存,提升性能 | +| `jdk.tls.client.enableSessionTicketExtension` | `false` | 禁用 TLS 会话票证扩展 | +| `plugin.fit.dynamic.plugin.directory` | 当前工作目录 | 动态插件加载目录 | + +## 技术实现 + +### 启动脚本实现(Node.js) + +#### 为什么使用 Node.js? + +相比传统的 Bash 脚本,Node.js 实现具有以下优势: + +1. **跨平台兼容性**: 可以在 Windows、Linux、macOS 上无缝运行 +2. **无需特殊处理**: 避免了 Shell 脚本在不同系统上的兼容性问题(如 macOS 上的 `readlink -f`) +3. **更好的可维护性**: JavaScript 语法更容易理解和维护 +4. **统一的开发体验**: 与现代开发工具保持一致 + +#### 脚本工作原理 + +1. 解析命令行参数 +2. 区分 Java 参数和程序参数 +3. 在脚本所在目录的上级目录查找 `fit-discrete-launcher-*.jar` 文件 +4. 构造完整的 Java 命令 +5. 使用 `child_process.spawn` 启动 Java 进程 +6. 继承标准输入/输出/错误流 +7. 正确处理进程信号(SIGINT、SIGTERM) + +### 类加载器实现 + +#### SharedClassLoader + +加载共享类库,位于 `lib/shared/` 目录: +- 基础工具类 +- 日志框架 +- 其他共享依赖 + +#### FrameworkClassLoader + +加载 FIT 框架核心类,位于 `lib/framework/` 目录: +- FIT IoC 容器 +- FIT 插件系统 +- FIT 运行时 + +## 故障排除 + +### 找不到 JAR 文件 + +**错误信息:** +``` +No fit-discrete-launcher-[version].jar file found. +``` + +**解决方法:** +1. 确保已经编译了项目:`mvn clean install` +2. 检查 `bin/..` 目录下是否存在 `fit-discrete-launcher-*.jar` 文件 +3. 确保脚本在正确的位置执行 + +### Node.js 未安装 + +**错误信息:** +``` +bash: node: command not found +``` +或 +``` +'node' 不是内部或外部命令... +``` + +**解决方法:** +从 [Node.js 官网](https://nodejs.org/) 下载并安装 Node.js。 + +### Java 未安装或版本不正确 + +**错误信息:** +``` +Failed to start Java process: spawn java ENOENT +``` + +**解决方法:** +1. 安装 Java 17 或更高版本 +2. 确保 `java` 命令在系统 PATH 中 + +验证 Java 安装: +```bash +java -version +``` + +### 类加载错误 + +**错误信息:** +``` +ClassNotFoundException: ... +``` + +**可能原因:** +1. 缺少必要的依赖库 +2. 类加载器配置错误 +3. JAR 文件损坏 + +**解决方法:** +1. 检查 `lib/` 目录下的依赖是否完整 +2. 重新编译和打包应用 +3. 查看详细的错误日志 + +## 与其他启动方式的对比 + +FIT 框架支持多种启动方式: + +| 启动方式 | 适用场景 | 类加载方式 | 进程模型 | +|---------|---------|-----------|---------| +| **Discrete Launcher** | 生产环境、独立部署 | 自定义类加载器 | 独立进程 | +| IDE 直接启动 | 开发调试 | IDE 类加载器 | IDE 进程 | +| Spring Boot 集成 | Spring Boot 应用 | Spring Boot 类加载器 | Spring Boot 进程 | +| Aggregated Launcher | 聚合部署 | 共享类加载器 | 多应用单进程 | + +## 相关文档 + +- [FIT 快速入门指南](../../docs/framework/fit/java/quick-start-guide/) +- [FIT 用户指导手册](../../docs/framework/fit/java/user-guide-book/) +- [启动程序设计](./设计文档链接) +- [类加载架构设计](./设计文档链接) + +## 兼容性 + +- **Node.js**: 12.0.0+ (推荐 16.0.0+) +- **操作系统**: Windows 7+, macOS 10.12+, Linux (any modern distribution) +- **Java**: 17+ + +## 贡献 + +如果你发现任何问题或有改进建议,请提交 Issue 或 Pull Request。 + +## 许可证 + +与 FIT 框架保持一致。 diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/fit b/framework/fit/java/fit-launcher/src/main/resources/bin/fit old mode 100644 new mode 100755 index 094fbc271..1be1b52e4 --- a/framework/fit/java/fit-launcher/src/main/resources/bin/fit +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/fit @@ -1,74 +1,3 @@ -#!/bin/bash - -# 获取脚本所在的路径 -CURRENT_DIR=$(pwd) -CMD_PATH=$(dirname $(readlink -f $0)) - -# 定义版本号 -VERSION="3.5.5-SNAPSHOT" - -function start { - local DEBUG_ARGS="" - if [ "$1" == "debug" ]; then - DEBUG_ARGS=" -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" - fi - - # 初始化变量 - JAVA_ARGS="" - PROGRAM_ARGS="" - - # 迭代所有的参数,跳过第一个参数("start"或"debug") - shift - for arg in "$@"; do - # 检查参数是否以-开头 - if [[ $arg == -* ]]; then - JAVA_ARGS="$JAVA_ARGS $arg" - else - PROGRAM_ARGS="$PROGRAM_ARGS $arg" - fi - done - - cd $CMD_PATH/.. - # 查找符合 fit-discrete-launcher-[version].jar 格式的文件 - JAR_FILE=$(ls | grep "fit-discrete-launcher-.*.jar" | head -n 1) - if [ -z "$JAR_FILE" ]; then - echo "No fit-discrete-launcher-[version].jar file found." - else - # 构造并运行 Java 命令 - echo "Running command: java${DEBUG_ARGS}${JAVA_ARGS} -D\"sun.io.useCanonCaches=true\" -D\"plugin.fit.dynamic.plugin.directory=${CURRENT_DIR}\" -jar ${JAR_FILE}${PROGRAM_ARGS}" - java${DEBUG_ARGS}${JAVA_ARGS} -D"sun.io.useCanonCaches=true" -D"jdk.tls.client.enableSessionTicketExtension=false" -D"plugin.fit.dynamic.plugin.directory=${CURRENT_DIR}" -jar ${JAR_FILE}${PROGRAM_ARGS} - fi -} - -function version { - echo "Version: $VERSION" -} - -function help { - echo "Usage: fit <command> [arguments]" - echo "Commands:" - echo " start Start the application" - echo " debug Start the application in debug mode" - echo " version Display the version number" - echo " help Display this help message" -} - -# 根据输入的参数执行相应的函数 -case "$1" in - start) - start "$@" - ;; - debug) - start "$@" - ;; - version) - version - ;; - help) - help - ;; - *) - echo "Unknown command: $1" - echo "Run 'fit help' for usage." - exit 1 -esac +#!/usr/bin/env node +// 这个文件会直接执行 fit.js +require('./fit.js'); diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/fit.bat b/framework/fit/java/fit-launcher/src/main/resources/bin/fit.bat deleted file mode 100644 index 4f0648189..000000000 --- a/framework/fit/java/fit-launcher/src/main/resources/bin/fit.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -bash %~dp0fit %* diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/fit.cmd b/framework/fit/java/fit-launcher/src/main/resources/bin/fit.cmd new file mode 100644 index 000000000..3af017509 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/fit.cmd @@ -0,0 +1,2 @@ +@echo off +node "%~dp0fit.js" %* diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/fit.js b/framework/fit/java/fit-launcher/src/main/resources/bin/fit.js new file mode 100755 index 000000000..96484df37 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/fit.js @@ -0,0 +1,539 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { spawn } = require('child_process'); +const readline = require('readline'); + +// 定义版本号 +const VERSION = '3.5.5-SNAPSHOT'; + +// 获取脚本所在的路径 +const scriptDir = __dirname; +const currentDir = process.cwd(); +const templatesDir = path.join(scriptDir, 'templates'); + +/** + * 查找符合 fit-discrete-launcher-[version].jar 格式的文件 + */ +function findJarFile(directory) { + try { + const files = fs.readdirSync(directory); + const jarFile = files.find(file => + file.startsWith('fit-discrete-launcher-') && file.endsWith('.jar') + ); + return jarFile || null; + } catch (err) { + console.error(`Error reading directory ${directory}:`, err.message); + return null; + } +} + +/** + * 读取模板文件 + */ +function readTemplate(templateType, fileName) { + const templatePath = path.join(templatesDir, templateType, fileName); + try { + return fs.readFileSync(templatePath, 'utf-8'); + } catch (err) { + console.error(`Error reading template ${templatePath}:`, err.message); + throw err; + } +} + +/** + * 替换模板变量 + */ +function replaceTemplateVars(template, vars) { + let result = template; + for (const [key, value] of Object.entries(vars)) { + const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g'); + result = result.replace(regex, value); + } + return result; +} + +/** + * 启动应用 + */ +function start(args) { + const isDebugMode = args.length > 0 && args[0] === 'debug'; + + // 初始化参数数组 + const javaArgs = []; + const programArgs = []; + + // 如果是 debug 模式,添加 debug 参数 + if (isDebugMode) { + javaArgs.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'); + } + + // 跳过第一个参数(start 或 debug) + const remainingArgs = args.slice(1); + + // 分离 Java 参数和程序参数 + for (const arg of remainingArgs) { + if (arg.startsWith('-')) { + javaArgs.push(arg); + } else { + programArgs.push(arg); + } + } + + // 切换到脚本所在目录的上级目录 + const jarDir = path.join(scriptDir, '..'); + + // 查找 JAR 文件 + const jarFile = findJarFile(jarDir); + + if (!jarFile) { + console.error('No fit-discrete-launcher-[version].jar file found.'); + process.exit(1); + } + + // 构造 Java 命令参数 + const javaCommand = 'java'; + const commandArgs = [ + ...javaArgs, + `-Dsun.io.useCanonCaches=true`, + `-Djdk.tls.client.enableSessionTicketExtension=false`, + `-Dplugin.fit.dynamic.plugin.directory=${currentDir}`, + '-jar', + path.join(jarDir, jarFile), + ...programArgs + ]; + + // 打印运行命令 + console.log(`Running command: ${javaCommand} ${commandArgs.join(' ')}`); + + // 执行 Java 命令 + const javaProcess = spawn(javaCommand, commandArgs, { + stdio: 'inherit', + cwd: jarDir + }); + + // 处理进程退出 + javaProcess.on('exit', (code) => { + process.exit(code || 0); + }); + + // 处理错误 + javaProcess.on('error', (err) => { + console.error('Failed to start Java process:', err.message); + process.exit(1); + }); + + // 处理 SIGINT 和 SIGTERM 信号 + process.on('SIGINT', () => { + javaProcess.kill('SIGINT'); + }); + + process.on('SIGTERM', () => { + javaProcess.kill('SIGTERM'); + }); +} + +/** + * 显示版本号 + */ +function version() { + console.log(`Version: ${VERSION}`); +} + +/** + * 初始化项目 + */ +async function init(args) { + let projectName = args.length > 1 ? args[1] : null; + + // 解析命令行参数 + let projectType = null; + let groupId = null; + let artifactId = null; + let packageName = null; + let serviceName = null; + let serviceGroupId = null; + let serviceArtifactId = null; + let serviceVersion = null; + let servicePackage = null; + + for (let i = 2; i < args.length; i++) { + const arg = args[i]; + if (arg.startsWith('--type=')) { + projectType = arg.substring('--type='.length); + } else if (arg.startsWith('--group-id=')) { + groupId = arg.substring('--group-id='.length); + } else if (arg.startsWith('--artifact-id=')) { + artifactId = arg.substring('--artifact-id='.length); + } else if (arg.startsWith('--package=')) { + packageName = arg.substring('--package='.length); + } else if (arg.startsWith('--service=')) { + serviceName = arg.substring('--service='.length); + } else if (arg.startsWith('--service-group-id=')) { + serviceGroupId = arg.substring('--service-group-id='.length); + } else if (arg.startsWith('--service-artifact-id=')) { + serviceArtifactId = arg.substring('--service-artifact-id='.length); + } else if (arg.startsWith('--service-version=')) { + serviceVersion = arg.substring('--service-version='.length); + } else if (arg.startsWith('--service-package=')) { + servicePackage = arg.substring('--service-package='.length); + } + } + + // 如果在非交互式环境(如 CI/CD)且缺少必需参数,则报错 + const isInteractive = process.stdin.isTTY; + + if (!isInteractive) { + if (!projectName) { + console.error('Error: Project name is required'); + console.error('Usage: fit init <project-name> --type=<service|plugin> [options]'); + process.exit(1); + } + if (!projectType || (projectType !== 'service' && projectType !== 'plugin')) { + console.error('Error: --type must be either "service" or "plugin"'); + process.exit(1); + } + if (projectType === 'plugin' && (!serviceGroupId || !serviceArtifactId)) { + console.error('Error: Plugin type requires --service-group-id and --service-artifact-id'); + process.exit(1); + } + } + + // 交互式引导 + if (isInteractive) { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + const question = (prompt) => new Promise((resolve) => { + rl.question(prompt, resolve); + }); + + try { + console.log('Welcome to FIT Project Generator!\n'); + + // 项目名称 + if (!projectName) { + projectName = await question('Project name: '); + if (!projectName) { + console.error('Error: Project name is required'); + rl.close(); + process.exit(1); + } + } + + // 检查目录是否已存在 + const projectDir = path.join(currentDir, projectName); + if (fs.existsSync(projectDir)) { + console.error(`Error: Directory '${projectName}' already exists`); + rl.close(); + process.exit(1); + } + + // 项目类型 + if (!projectType) { + console.log('\nProject type:'); + console.log(' 1) service - Service interface (SPI)'); + console.log(' 2) plugin - Plugin implementation'); + const typeChoice = await question('Select type (1 or 2, default: 1): '); + projectType = typeChoice === '2' ? 'plugin' : 'service'; + } + + // 验证项目类型 + if (projectType !== 'service' && projectType !== 'plugin') { + console.error('Error: type must be either "service" or "plugin"'); + rl.close(); + process.exit(1); + } + + console.log(`\nCreating FIT ${projectType === 'service' ? 'Service' : 'Plugin'}: ${projectName}\n`); + + // 通用参数 + const defaultGroupId = 'com.example'; + const inputGroupId = await question(`Group ID (default: ${defaultGroupId}): `); + groupId = inputGroupId.trim() || defaultGroupId; + + const defaultArtifactId = projectName; + const inputArtifactId = await question(`Artifact ID (default: ${defaultArtifactId}): `); + artifactId = inputArtifactId.trim() || defaultArtifactId; + + const defaultPackage = `${groupId}.${artifactId.replace(/-/g, '.')}`; + const inputPackage = await question(`Package name (default: ${defaultPackage}): `); + packageName = inputPackage.trim() || defaultPackage; + + const defaultServiceName = 'Demo'; + const inputServiceName = await question(`Service name (default: ${defaultServiceName}): `); + serviceName = inputServiceName.trim() || defaultServiceName; + + // Plugin 特有参数 + if (projectType === 'plugin') { + console.log('\nPlugin depends on a service interface:'); + + if (!serviceGroupId) { + const inputServiceGroupId = await question('Service Group ID: '); + serviceGroupId = inputServiceGroupId.trim(); + if (!serviceGroupId) { + console.error('Error: Service Group ID is required for plugin'); + rl.close(); + process.exit(1); + } + } + + if (!serviceArtifactId) { + const inputServiceArtifactId = await question('Service Artifact ID: '); + serviceArtifactId = inputServiceArtifactId.trim(); + if (!serviceArtifactId) { + console.error('Error: Service Artifact ID is required for plugin'); + rl.close(); + process.exit(1); + } + } + + const defaultServiceVersion = '1.0-SNAPSHOT'; + const inputServiceVersion = await question(`Service Version (default: ${defaultServiceVersion}): `); + serviceVersion = inputServiceVersion.trim() || defaultServiceVersion; + + const defaultServicePackage = `${serviceGroupId}.${serviceArtifactId.replace(/-/g, '.')}`; + const inputServicePackage = await question(`Service Package (default: ${defaultServicePackage}): `); + servicePackage = inputServicePackage.trim() || defaultServicePackage; + } + + rl.close(); + } catch (err) { + rl.close(); + console.error('Error during interactive input:', err.message); + process.exit(1); + } + } else { + // 非交互式模式,检查目录 + const projectDir = path.join(currentDir, projectName); + if (fs.existsSync(projectDir)) { + console.error(`Error: Directory '${projectName}' already exists`); + process.exit(1); + } + console.log(`Creating FIT ${projectType === 'service' ? 'Service' : 'Plugin'}: ${projectName}\n`); + } + + // 设置默认值(如果用户没有提供) + groupId = groupId || 'com.example'; + artifactId = artifactId || projectName; + packageName = packageName || `${groupId}.${artifactId.replace(/-/g, '.')}`; + serviceName = serviceName || 'Demo'; + serviceVersion = serviceVersion || '1.0-SNAPSHOT'; + + // Plugin 的 service package 默认值 + if (projectType === 'plugin' && !servicePackage) { + servicePackage = `${serviceGroupId}.${serviceArtifactId.replace(/-/g, '.')}`; + } + + const projectDir = path.join(currentDir, projectName); + + try { + console.log(`\nGenerating ${projectType} project structure...`); + + // 准备模板变量 + const serviceId = serviceName.toLowerCase(); + const packagePath = packageName.replace(/\./g, '/'); + const year = new Date().getFullYear(); + + const templateVars = { + GROUP_ID: groupId, + ARTIFACT_ID: artifactId, + PACKAGE: packageName, + PACKAGE_PATH: packagePath, + SERVICE_NAME: serviceName, + SERVICE_ID: serviceId, + FIT_VERSION: VERSION, + YEAR: year.toString(), + PROJECT_NAME: projectName, + SERVICE_GROUP_ID: serviceGroupId || '', + SERVICE_ARTIFACT_ID: serviceArtifactId || '', + SERVICE_VERSION: serviceVersion || '', + SERVICE_PACKAGE: servicePackage || '' + }; + + if (projectType === 'service') { + // 生成 Service 项目 + const srcDir = path.join(projectDir, 'src', 'main', 'java'); + const packageDir = path.join(srcDir, ...packageName.split('.')); + + fs.mkdirSync(packageDir, { recursive: true }); + + // 生成 pom.xml + const pomTemplate = readTemplate('service', 'pom.xml.tpl'); + const pomContent = replaceTemplateVars(pomTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, 'pom.xml'), pomContent); + console.log(' ✓ Created pom.xml'); + + // 生成服务接口 + const serviceTemplate = readTemplate('service', 'Service.java.tpl'); + const serviceContent = replaceTemplateVars(serviceTemplate, templateVars); + fs.writeFileSync(path.join(packageDir, `${serviceName}.java`), serviceContent); + console.log(` ✓ Created ${serviceName}.java`); + + // 生成 README.md + const readmeTemplate = readTemplate('service', 'README.md.tpl'); + const readmeContent = replaceTemplateVars(readmeTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, 'README.md'), readmeContent); + console.log(' ✓ Created README.md'); + + // 生成 .gitignore + const gitignoreTemplate = readTemplate('plugin', '.gitignore.tpl'); + const gitignoreContent = replaceTemplateVars(gitignoreTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent); + console.log(' ✓ Created .gitignore'); + + } else if (projectType === 'plugin') { + // 生成 Plugin 项目 + const srcDir = path.join(projectDir, 'src', 'main', 'java'); + const resourcesDir = path.join(projectDir, 'src', 'main', 'resources'); + const packageDir = path.join(srcDir, ...packageName.split('.')); + + fs.mkdirSync(packageDir, { recursive: true }); + fs.mkdirSync(resourcesDir, { recursive: true }); + + // 生成 pom.xml + const pomTemplate = readTemplate('plugin', 'pom.xml.tpl'); + const pomContent = replaceTemplateVars(pomTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, 'pom.xml'), pomContent); + console.log(' ✓ Created pom.xml'); + + // 生成服务实现 + const serviceImplTemplate = readTemplate('plugin', 'ServiceImpl.java.tpl'); + const serviceImplContent = replaceTemplateVars(serviceImplTemplate, templateVars); + fs.writeFileSync(path.join(packageDir, `Default${serviceName}.java`), serviceImplContent); + console.log(` ✓ Created Default${serviceName}.java`); + + // 生成 application.yml + const ymlTemplate = readTemplate('plugin', 'application.yml.tpl'); + const ymlContent = replaceTemplateVars(ymlTemplate, templateVars); + fs.writeFileSync(path.join(resourcesDir, 'application.yml'), ymlContent); + console.log(' ✓ Created application.yml'); + + // 生成 README.md + const readmeTemplate = readTemplate('plugin', 'README.md.tpl'); + const readmeContent = replaceTemplateVars(readmeTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, 'README.md'), readmeContent); + console.log(' ✓ Created README.md'); + + // 生成 .gitignore + const gitignoreTemplate = readTemplate('plugin', '.gitignore.tpl'); + const gitignoreContent = replaceTemplateVars(gitignoreTemplate, templateVars); + fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent); + console.log(' ✓ Created .gitignore'); + } + + // 显示成功信息 + console.log(`\n✨ FIT ${projectType === 'service' ? 'Service' : 'Plugin'} created successfully!\n`); + + if (projectType === 'service') { + console.log('Service Structure:'); + console.log(` ${projectName}/`); + console.log(` ├── pom.xml # Maven 配置`); + console.log(` ├── README.md # 服务文档`); + console.log(` └── src/main/java/${packagePath}/`); + console.log(` └── ${serviceName}.java # 服务接口(SPI)`); + console.log('\nNext steps:'); + console.log(` 1. cd ${projectName}`); + console.log(' 2. mvn clean install'); + console.log(' 3. Create plugin implementations that depend on this service'); + } else { + console.log('Plugin Structure:'); + console.log(` ${projectName}/`); + console.log(` ├── pom.xml # Maven 配置`); + console.log(` ├── README.md # 插件文档`); + console.log(` └── src/main/`); + console.log(` ├── java/${packagePath}/`); + console.log(` │ └── Default${serviceName}.java # 服务实现`); + console.log(` └── resources/`); + console.log(` └── application.yml # FIT 配置`); + console.log('\nNext steps:'); + console.log(` 1. cd ${projectName}`); + console.log(' 2. mvn clean install'); + console.log(' 3. Deploy the plugin JAR to your FIT application'); + } + + } catch (err) { + console.error('Error during initialization:', err.message); + process.exit(1); + } +} + +/** + * 显示帮助信息 + */ +function help() { + console.log('Usage: fit <command> [arguments]'); + console.log('\nCommands:'); + console.log(' init [name] Initialize a new FIT service or plugin project'); + console.log(' start Start the application'); + console.log(' debug Start the application in debug mode'); + console.log(' version Display the version number'); + console.log(' help Display this help message'); + console.log('\nInit Usage:'); + console.log(' fit init # Interactive mode (recommended)'); + console.log(' fit init <name> # Interactive mode with project name'); + console.log(' fit init <name> [options] # Non-interactive mode with options'); + console.log('\nInit Options:'); + console.log(' --type=<service|plugin> Project type'); + console.log(' --group-id=<id> Maven Group ID'); + console.log(' --artifact-id=<id> Maven Artifact ID'); + console.log(' --package=<name> Java package name'); + console.log(' --service=<name> Service name'); + console.log('\nPlugin-specific options:'); + console.log(' --service-group-id=<id> Service Group ID'); + console.log(' --service-artifact-id=<id> Service Artifact ID'); + console.log(' --service-version=<version> Service Version'); + console.log(' --service-package=<package> Service Package'); + console.log('\nExamples:'); + console.log(' # Interactive mode (easiest)'); + console.log(' fit init'); + console.log(' fit init my-project'); + console.log('\n # Non-interactive mode - Create a service (SPI)'); + console.log(' fit init weather-service --type=service --service=Weather'); + console.log('\n # Non-interactive mode - Create a plugin implementation'); + console.log(' fit init default-weather --type=plugin --service=Weather \\'); + console.log(' --service-group-id=com.example --service-artifact-id=weather-service'); +} + +/** + * 主函数 + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error('Unknown command: (no command provided)'); + console.error("Run 'fit help' for usage."); + process.exit(1); + } + + const command = args[0]; + + switch (command) { + case 'init': + await init(args); + break; + case 'start': + start(args); + break; + case 'debug': + start(args); + break; + case 'version': + version(); + break; + case 'help': + help(); + break; + default: + console.error(`Unknown command: ${command}`); + console.error("Run 'fit help' for usage."); + process.exit(1); + } +} + +// 执行主函数 +main(); diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/.gitignore.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/.gitignore.tpl new file mode 100644 index 000000000..d4fea541c --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/.gitignore.tpl @@ -0,0 +1,30 @@ +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# IDE +.idea/ +*.iml +.vscode/ +.eclipse/ +.settings/ +.classpath +.project + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Build +build/ +dist/ diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/README.md.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/README.md.tpl new file mode 100644 index 000000000..54fa2d0b3 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/README.md.tpl @@ -0,0 +1,73 @@ +# {{PROJECT_NAME}} + +A FIT Framework Plugin (Service Implementation). + +## Project Information + +- **Group ID**: {{GROUP_ID}} +- **Artifact ID**: {{ARTIFACT_ID}} +- **Package**: {{PACKAGE}} +- **Service Dependency**: {{SERVICE_GROUP_ID}}:{{SERVICE_ARTIFACT_ID}} +- **FIT Version**: {{FIT_VERSION}} + +## Build + +```bash +mvn clean install +``` + +This will: +1. Build the plugin JAR file +2. Package the plugin with all dependencies + +## Plugin Structure + +``` +{{PROJECT_NAME}}/ +├── src/main/java/ +│ └── {{PACKAGE_PATH}}/ +│ └── Default{{SERVICE_NAME}}.java # 服务实现(使用 @Component 和 @Fitable) +└── src/main/resources/ + └── application.yml # FIT 配置文件 +``` + +## Key Annotations + +- `@Component` - 标记为 FIT 组件,自动注册到 IoC 容器 +- `@Fitable(id = "default-{{SERVICE_ID}}")` - 标记为可远程调用的实现 + +## Usage + +### 1. 部署插件 + +将构建好的插件 JAR 包部署到 FIT 应用的插件目录: + +```bash +cp target/{{ARTIFACT_ID}}-1.0-SNAPSHOT.jar /path/to/fit-app/plugins/ +``` + +### 2. 在应用中使用 + +首先确保 service 项目已安装到本地仓库或远程仓库。 + +然后在应用中注入并使用服务: + +```java +@Component +public class MyController { + + @FitBean + private {{SERVICE_NAME}} {{SERVICE_ID}}; + + public void usePlugin() { + String result = {{SERVICE_ID}}.execute(); + System.out.println(result); + } +} +``` + +## Documentation + +For more information about FIT Framework plugins, visit: +- [FIT Plugin Development Guide](https://github.com/ModelEngine-Group/fit-framework/tree/main/docs) +- [FIT Quick Start Guide](https://github.com/ModelEngine-Group/fit-framework/tree/main/docs) diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/ServiceImpl.java.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/ServiceImpl.java.tpl new file mode 100644 index 000000000..d10cd051e --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/ServiceImpl.java.tpl @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) {{YEAR}} FIT Framework Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package {{PACKAGE}}; + +import {{SERVICE_PACKAGE}}.{{SERVICE_NAME}}; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Fitable; + +/** + * {{SERVICE_NAME}} 的默认实现 + */ +@Component +public class Default{{SERVICE_NAME}} implements {{SERVICE_NAME}} { + @Override + @Fitable(id = "default-{{SERVICE_ID}}") + public String execute() { + return "{{SERVICE_NAME}} plugin is working successfully!"; + } +} diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/application.yml.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/application.yml.tpl new file mode 100644 index 000000000..496250e97 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/application.yml.tpl @@ -0,0 +1,4 @@ +fit: + beans: + packages: + - '{{PACKAGE}}' diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/pom.xml.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/pom.xml.tpl new file mode 100644 index 000000000..fc18f57de --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/plugin/pom.xml.tpl @@ -0,0 +1,75 @@ +<?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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>{{GROUP_ID}}</groupId> + <artifactId>{{ARTIFACT_ID}}</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <java.version>17</java.version> + + <!-- FIT version --> + <fit.version>{{FIT_VERSION}}</fit.version> + + <!-- Maven plugin versions --> + <maven.compiler.version>3.14.0</maven.compiler.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.fitframework</groupId> + <artifactId>fit-api</artifactId> + <version>${fit.version}</version> + </dependency> + <dependency> + <groupId>org.fitframework</groupId> + <artifactId>fit-util</artifactId> + <version>${fit.version}</version> + </dependency> + <dependency> + <groupId>{{SERVICE_GROUP_ID}}</groupId> + <artifactId>{{SERVICE_ARTIFACT_ID}}</artifactId> + <version>{{SERVICE_VERSION}}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.version}</version> + <configuration> + <source>${java.version}</source> + <target>${java.version}</target> + <encoding>${project.build.sourceEncoding}</encoding> + <compilerArgs> + <arg>-parameters</arg> + </compilerArgs> + </configuration> + </plugin> + <plugin> + <groupId>org.fitframework</groupId> + <artifactId>fit-build-maven-plugin</artifactId> + <version>${fit.version}</version> + <executions> + <execution> + <id>build-plugin</id> + <goals> + <goal>build-plugin</goal> + </goals> + </execution> + <execution> + <id>package-plugin</id> + <goals> + <goal>package-plugin</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/README.md.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/README.md.tpl new file mode 100644 index 000000000..7047ca0a0 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/README.md.tpl @@ -0,0 +1,72 @@ +# {{PROJECT_NAME}} + +A FIT Framework Service (SPI). + +## Project Information + +- **Group ID**: {{GROUP_ID}} +- **Artifact ID**: {{ARTIFACT_ID}} +- **Package**: {{PACKAGE}} +- **FIT Version**: {{FIT_VERSION}} + +## Build + +```bash +mvn clean install +``` + +This will: +1. Build the service JAR file +2. Make it available for plugin implementations + +## Service Structure + +``` +{{PROJECT_NAME}}/ +└── src/main/java/ + └── {{PACKAGE_PATH}}/ + └── {{SERVICE_NAME}}.java # 服务接口(使用 @Genericable) +``` + +## Key Annotations + +- `@Genericable(id = "{{SERVICE_NAME}}")` - 标记服务接口,支持远程和本地调用 + +## Usage + +### 1. 安装到本地仓库 + +```bash +mvn clean install +``` + +### 2. 创建插件实现 + +其他项目可以依赖此服务并创建插件实现: + +```xml +<dependency> + <groupId>{{GROUP_ID}}</groupId> + <artifactId>{{ARTIFACT_ID}}</artifactId> + <version>1.0-SNAPSHOT</version> +</dependency> +``` + +然后实现 {{SERVICE_NAME}} 接口: + +```java +@Component +public class Default{{SERVICE_NAME}} implements {{SERVICE_NAME}} { + @Override + @Fitable(id = "default-{{SERVICE_ID}}") + public String execute() { + return "Implementation result"; + } +} +``` + +## Documentation + +For more information about FIT Framework services, visit: +- [FIT Service Development Guide](https://github.com/ModelEngine-Group/fit-framework/tree/main/docs) +- [FIT Quick Start Guide](https://github.com/ModelEngine-Group/fit-framework/tree/main/docs) diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/Service.java.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/Service.java.tpl new file mode 100644 index 000000000..d87efb9c4 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/Service.java.tpl @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) {{YEAR}} FIT Framework Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package {{PACKAGE}}; + +import modelengine.fitframework.annotation.Genericable; + +/** + * {{SERVICE_NAME}} 服务接口(SPI) + */ +public interface {{SERVICE_NAME}} { + /** + * 执行服务操作 + * + * @return 服务执行结果 + */ + @Genericable(id = "{{SERVICE_NAME}}") + String execute(); +} diff --git a/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/pom.xml.tpl b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/pom.xml.tpl new file mode 100644 index 000000000..f3bdea388 --- /dev/null +++ b/framework/fit/java/fit-launcher/src/main/resources/bin/templates/service/pom.xml.tpl @@ -0,0 +1,64 @@ +<?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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>{{GROUP_ID}}</groupId> + <artifactId>{{ARTIFACT_ID}}</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <java.version>17</java.version> + + <!-- FIT version --> + <fit.version>{{FIT_VERSION}}</fit.version> + + <!-- Maven plugin versions --> + <maven.compiler.version>3.14.0</maven.compiler.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.fitframework</groupId> + <artifactId>fit-api</artifactId> + <version>${fit.version}</version> + </dependency> + <dependency> + <groupId>org.fitframework</groupId> + <artifactId>fit-util</artifactId> + <version>${fit.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.version}</version> + <configuration> + <source>${java.version}</source> + <target>${java.version}</target> + <encoding>${project.build.sourceEncoding}</encoding> + <compilerArgs> + <arg>-parameters</arg> + </compilerArgs> + </configuration> + </plugin> + <plugin> + <groupId>org.fitframework</groupId> + <artifactId>fit-build-maven-plugin</artifactId> + <version>${fit.version}</version> + <executions> + <execution> + <id>build-service</id> + <goals> + <goal>build-service</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project>