├── LICENSE ├── README.en-US.md ├── README.md ├── README.zh-CN.md ├── doc └── image │ ├── create.gif │ ├── other.gif │ └── string.gif ├── pom.xml ├── rdm-common ├── pom.xml └── src │ ├── main │ └── java │ │ └── xyz │ │ └── hashdog │ │ └── rdm │ │ └── common │ │ ├── Constant.java │ │ ├── pool │ │ ├── DaemonThreadFactory.java │ │ └── ThreadPool.java │ │ ├── tuple │ │ └── Tuple2.java │ │ └── util │ │ ├── DataUtil.java │ │ ├── EncodeUtil.java │ │ ├── FileUtil.java │ │ ├── GzipUtil.java │ │ └── TUtil.java │ └── test │ └── java │ └── xyz │ └── hashdog │ └── rdm │ └── common │ └── util │ ├── DataUtilTest.java │ └── EncodeUtilTest.java ├── rdm-redis-imp ├── pom.xml └── src │ ├── main │ ├── java │ │ └── xyz │ │ │ └── hashdog │ │ │ └── rdm │ │ │ └── redis │ │ │ └── imp │ │ │ ├── Constant.java │ │ │ ├── RedisContext.java │ │ │ ├── RedisFactory.java │ │ │ ├── client │ │ │ ├── DefaultRedisClientCreator.java │ │ │ ├── JedisPoolClient.java │ │ │ └── RedisClientCreator.java │ │ │ └── console │ │ │ ├── ReaderParseEnum.java │ │ │ ├── ReaderParser.java │ │ │ ├── RedisConsole.java │ │ │ └── SocketAcquirer.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── xyz.hashdog.rdm.redis.RedisFactory │ └── test │ └── java │ └── xyz │ └── hashdog │ └── rdm │ └── redis │ └── imp │ ├── RedisContextTest.java │ ├── client │ └── JedisPoolClientTest.java │ └── console │ └── RedisConsoleTest.java ├── rdm-redis ├── pom.xml └── src │ └── main │ └── java │ └── xyz │ └── hashdog │ └── rdm │ └── redis │ ├── Message.java │ ├── RedisConfig.java │ ├── RedisContext.java │ ├── RedisFactory.java │ ├── RedisFactorySingleton.java │ ├── client │ ├── RedisClient.java │ └── RedisConsole.java │ └── exceptions │ └── RedisException.java └── rdm-ui ├── pom.xml └── src ├── main ├── java │ └── xyz │ │ └── hashdog │ │ └── rdm │ │ └── ui │ │ ├── Main.java │ │ ├── common │ │ ├── Applications.java │ │ ├── CacheConfigSingleton.java │ │ ├── Config.java │ │ ├── Constant.java │ │ ├── RedisDataTypeEnum.java │ │ └── ValueTypeEnum.java │ │ ├── controller │ │ ├── AppendController.java │ │ ├── BaseController.java │ │ ├── BaseKeyController.java │ │ ├── BaseWindowController.java │ │ ├── ByteArrayController.java │ │ ├── ConsoleController.java │ │ ├── HashTypeController.java │ │ ├── KeyTabController.java │ │ ├── ListTypeController.java │ │ ├── MainController.java │ │ ├── NewConnectionController.java │ │ ├── NewGroupController.java │ │ ├── NewKeyController.java │ │ ├── ServerConnectionsController.java │ │ ├── ServerTabController.java │ │ ├── SetTypeController.java │ │ ├── StringTypeController.java │ │ └── ZsetTypeController.java │ │ ├── entity │ │ ├── ConnectionServerNode.java │ │ ├── DBNode.java │ │ ├── HashTypeTable.java │ │ ├── ListTypeTable.java │ │ ├── PassParameter.java │ │ ├── SetTypeTable.java │ │ └── ZsetTypeTable.java │ │ ├── exceptions │ │ └── GeneralException.java │ │ ├── handler │ │ ├── BinaryConvertHandler.java │ │ ├── HexConvertHandler.java │ │ ├── ImageBase64ConvertHandler.java │ │ ├── ImageConvertHandler.java │ │ ├── NewKeyHandler.java │ │ ├── TextConvertHandler.java │ │ ├── TextGzipConvertHandler.java │ │ ├── TextJsonConvertHandler.java │ │ └── ValueConvertHandler.java │ │ └── util │ │ └── GuiUtil.java └── resources │ ├── css │ ├── global.css │ └── no-border.css │ ├── fxml │ ├── AppendView.fxml │ ├── ByteArrayView.fxml │ ├── ConsoleView.fxml │ ├── HashTypeView.fxml │ ├── KeyTabView.fxml │ ├── ListTypeView.fxml │ ├── MainView.fxml │ ├── NewConnectionView.fxml │ ├── NewGroupView.fxml │ ├── NewKeyView.fxml │ ├── ServerConnectionsView.fxml │ ├── ServerTabView.fxml │ ├── SetTypeView.fxml │ ├── StringTypeView.fxml │ └── ZsetTypeView.fxml │ ├── i18n │ ├── messages.properties │ ├── messages_en_US.properties │ ├── messages_ja_JP.properties │ └── messages_zh_CN.properties │ └── icon │ ├── connection.png │ ├── console.png │ ├── export.png │ ├── group.png │ ├── into.png │ ├── key.png │ ├── new_key.png │ ├── redis128.png │ ├── redis256.ico │ ├── redis256.png │ ├── redis32.ico │ └── search.png └── test └── java └── xyz └── hashdog └── rdm └── ui └── common └── RedisFactorySingletonTest.java /README.en-US.md: -------------------------------------------------------------------------------- 1 | # RedisDesktopManagerFX 2 | 3 | - This is a Jedis based, using JavaFX development of Redis GUI tool, and provides Linux and Windows packages, has a built-in jre environment, decompression can be run. 4 | - If you already have JDK1.8 or more installed on your computer (which requires the JavaFX package), you can also start by running the jar package directly. 5 | - [Download address](https://github.com/tanhuang2016/RedisDesktopManagerFX/releases) 6 | 7 | ## Language 8 | English | [中文](README.zh-CN.md) 9 | 10 | ## Feature 11 | 12 | 1. Multiple redis connections can be managed simultaneously 13 | 2. Only single-node redis can be connected 14 | 3. The string list hash set zset type is supported 15 | 4. Support console mode to use command interaction 16 | 5. string Character encoding Optional 17 | 6. The string type supports image file display 18 | 19 | ## TODO 20 | 21 | 1. Preferences support i18n switching 22 | 2. Preferences support font switching 23 | 3. Add loading animation 24 | 4. Binary data of string type can be imported or exported 25 | 5. Backup and restore keys 26 | 6. Support for clusters and Sentinels 27 | 7. Support for SSH and SSL 28 | 29 | ## DevBuild 30 | 31 | - The development environment is JDK1.8, built on Maven3.8.8 32 | - Developed using Eclipase or Intellij IdeaDeveloped using Eclipase or Intellij Idea 33 | - This project relies only on [Jedis4.3.0](https://github.com/redis/jedis) 34 | - Use [JavaPackager](https://github.com/fvarrui/JavaPackager) plug-in to Packaging(which can be packaged Windows, Linux, Mac installation package) 35 | ### Structure 36 | 37 | ```text 38 | RedisDesktopManagerFX 39 | ├─rdm-common Common modules, defining common tools, thread pools, etc 40 | ├─rdm-redis Redis service module, which constrains the interface required by the ui 41 | ├─rdm-redis-imp Redis service implementation module, the implementation of specific client interface 42 | └─rdm-ui Gui module for the entire tool 43 | └─src 44 | ├─main 45 | │ ├─java 46 | │ │ └─xyz 47 | │ │ └─hashdog 48 | │ │ ─rdm 49 | │ │ └─ui 50 | │ │ ├─common Common encapsulation 51 | │ │ ├─controller View's controller 52 | │ │ ├─entity Bean 53 | │ │ ├─exceptions Ui exceptions 54 | │ │ ├─handler Package processor 55 | │ │ └─util Ui-related tools 56 | │ └─resources 57 | │ ├─css Ui-related style sheets 58 | │ ├─fxml View corresponding fxml file 59 | │ ├─i18n internationalization 60 | │ └─icon icon 61 | ``` 62 | 63 | ## Show 64 | 65 | ![create.gif](doc%2Fimage%2Fcreate.gif) 66 | ![string.gif](doc%2Fimage%2Fstring.gif) 67 | ![other.gif](doc%2Fimage%2Fother.gif) 68 | 69 | ## Supported by 70 | 71 | [Jetbrains](https://www.jetbrains.com/) 72 | ![https://www.jetbrains.com/](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_gl=1*389d01*_ga*MTcyNjk5OTM4MC4xNjg3NzkwNTEy*_ga_9J976DJZ68*MTcwNDk2NjczNi4xMC4xLjE3MDQ5NjY4OTUuMC4wLjA.&_ga=2.205748112.2037968390.1704966739-1726999380.1687790512) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RedisDesktopManagerFX 2 | 3 | - 这是一个基于Jedis,使用JavaFX开发的Redis GUI工具,并提供了Linux和Windows的包,已内置jre环境,解压即可运行。 4 | - 如果您的电脑已经安装了JDK1.8以上(需要JavaFX包),也可以直接运行jar包启动。 5 | - [下载地址](https://github.com/tanhuang2016/RedisDesktopManagerFX/releases) 6 | 7 | ## 语言 8 | [English](README.en-US.md) | 中文 9 | 10 | ## 特性 11 | 12 | 1. 可同时管理多个redis连接 13 | 2. 只支持单机redis的连接 14 | 3. 支持string list hash set zset类型 15 | 4. 支持控制台方式使用命令交互 16 | 5. string类型支持字符编码可选 17 | 6. string类型支持图片文件的展示 18 | 19 | ## 规划 20 | 21 | 1. 首选项支持国际化的切换 22 | 2. 首选项支持字体的切换 23 | 3. 添加loading动画 24 | 4. string类型的二进制数据支持导入导出 25 | 5. 对key的备份与还原 26 | 6. 对集群和哨兵的支持 27 | 7. 对SSH、SSL的支持 28 | 29 | ## 开发环境 30 | 31 | - 开发环境为JDK1.8,基于Maven3.8.8构建 32 | - 使用Eclipase或Intellij Idea开发 33 | - 本项目只依赖的[Jedis4.3.0](https://github.com/redis/jedis) 34 | - 使用[JavaPackager](https://github.com/fvarrui/JavaPackager)插件进行打包操作(可打包windows、Linux、Mac安装包) 35 | ### 项目结构 36 | 37 | ```text 38 | RedisDesktopManagerFX 39 | ├─rdm-common 公共模块,定义通用工具,线程池等 40 | ├─rdm-redis redis服务模块,对ui需要的接口进行约束 41 | ├─rdm-redis-imp redis 服务实现模块,具体客户端接口的实现 42 | └─rdm-ui 整个工具的gui模块 43 | └─src 44 | ├─main 45 | │ ├─java 46 | │ │ └─xyz 47 | │ │ └─hashdog 48 | │ │ ─rdm 49 | │ │ └─ui 50 | │ │ ├─common 公共封装 51 | │ │ ├─controller view的控制层 52 | │ │ ├─entity 实体类bean 53 | │ │ ├─exceptions ui异常 54 | │ │ ├─handler 封装处理器 55 | │ │ └─util ui相关的工具 56 | │ └─resources 57 | │ ├─css ui相关样式表 58 | │ ├─fxml 视图对应的fxml文件 59 | │ ├─i18n 国际化 60 | │ └─icon 图标 61 | ``` 62 | 63 | ## 展示 64 | 65 | ![create.gif](doc%2Fimage%2Fcreate.gif) 66 | ![string.gif](doc%2Fimage%2Fstring.gif) 67 | ![other.gif](doc%2Fimage%2Fother.gif) 68 | 69 | ## 感谢支持 70 | 71 | [Jetbrains](https://www.jetbrains.com/) 72 | ![https://www.jetbrains.com/](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_gl=1*389d01*_ga*MTcyNjk5OTM4MC4xNjg3NzkwNTEy*_ga_9J976DJZ68*MTcwNDk2NjczNi4xMC4xLjE3MDQ5NjY4OTUuMC4wLjA.&_ga=2.205748112.2037968390.1704966739-1726999380.1687790512) -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # RedisDesktopManagerFX 2 | 3 | - 这是一个基于Jedis,使用JavaFX开发的Redis GUI工具,并提供了Linux和Windows的包,已内置jre环境,解压即可运行。 4 | - 如果您的电脑已经安装了JDK1.8以上(需要JavaFX包),也可以直接运行jar包启动。 5 | - [下载地址](https://github.com/tanhuang2016/RedisDesktopManagerFX/releases) 6 | 7 | ## 语言 8 | [English](README.en-US.md) | 中文 9 | 10 | ## 特性 11 | 12 | 1. 可同时管理多个redis连接 13 | 2. 只支持单机redis的连接 14 | 3. 支持string list hash set zset类型 15 | 4. 支持控制台方式使用命令交互 16 | 5. string类型支持字符编码可选 17 | 6. string类型支持图片文件的展示 18 | 19 | ## 规划 20 | 21 | 1. 首选项支持国际化的切换 22 | 2. 首选项支持字体的切换 23 | 3. 添加loading动画 24 | 4. string类型的二进制数据支持导入导出 25 | 5. 对key的备份与还原 26 | 6. 对集群和哨兵的支持 27 | 7. 对SSH、SSL的支持 28 | 29 | ## 开发环境 30 | 31 | - 开发环境为JDK1.8,基于Maven3.8.8构建 32 | - 使用Eclipase或Intellij Idea开发 33 | - 本项目只依赖的[Jedis4.3.0](https://github.com/redis/jedis) 34 | - 使用[JavaPackager](https://github.com/fvarrui/JavaPackager)插件进行打包操作(可打包windows、Linux、Mac安装包) 35 | ### 项目结构 36 | 37 | ```text 38 | RedisDesktopManagerFX 39 | ├─rdm-common 公共模块,定义通用工具,线程池等 40 | ├─rdm-redis redis服务模块,对ui需要的接口进行约束 41 | ├─rdm-redis-imp redis 服务实现模块,具体客户端接口的实现 42 | └─rdm-ui 整个工具的gui模块 43 | └─src 44 | ├─main 45 | │ ├─java 46 | │ │ └─xyz 47 | │ │ └─hashdog 48 | │ │ ─rdm 49 | │ │ └─ui 50 | │ │ ├─common 公共封装 51 | │ │ ├─controller view的控制层 52 | │ │ ├─entity 实体类bean 53 | │ │ ├─exceptions ui异常 54 | │ │ ├─handler 封装处理器 55 | │ │ └─util ui相关的工具 56 | │ └─resources 57 | │ ├─css ui相关样式表 58 | │ ├─fxml 视图对应的fxml文件 59 | │ ├─i18n 国际化 60 | │ └─icon 图标 61 | ``` 62 | 63 | ## 展示 64 | 65 | ![create.gif](doc%2Fimage%2Fcreate.gif) 66 | ![string.gif](doc%2Fimage%2Fstring.gif) 67 | ![other.gif](doc%2Fimage%2Fother.gif) 68 | 69 | ## 感谢支持 70 | 71 | [Jetbrains](https://www.jetbrains.com/) 72 | ![https://www.jetbrains.com/](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_gl=1*389d01*_ga*MTcyNjk5OTM4MC4xNjg3NzkwNTEy*_ga_9J976DJZ68*MTcwNDk2NjczNi4xMC4xLjE3MDQ5NjY4OTUuMC4wLjA.&_ga=2.205748112.2037968390.1704966739-1726999380.1687790512) -------------------------------------------------------------------------------- /doc/image/create.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhuang2016/RedisDesktopManagerFX/0c78d2bf492b6f04be8fa8ce603ddf90277d6eb1/doc/image/create.gif -------------------------------------------------------------------------------- /doc/image/other.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhuang2016/RedisDesktopManagerFX/0c78d2bf492b6f04be8fa8ce603ddf90277d6eb1/doc/image/other.gif -------------------------------------------------------------------------------- /doc/image/string.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhuang2016/RedisDesktopManagerFX/0c78d2bf492b6f04be8fa8ce603ddf90277d6eb1/doc/image/string.gif -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | xyz.hashdog.rdm 7 | RedisDesktopManagerFX 8 | pom 9 | 1.0.0 10 | 11 | 12 | 13 | UTF-8 14 | 1.8 15 | 1.8 16 | 1.0.0 17 | 18 | 4.12 19 | 20 | 21 | 22 | 23 | 24 | rdm-ui 25 | rdm-common 26 | rdm-redis 27 | rdm-redis-imp 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /rdm-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | xyz.hashdog.rdm 8 | RedisDesktopManagerFX 9 | 1.0.0 10 | 11 | 12 | 13 | xyz.hashdog.rdm 14 | rdm-common 15 | 16 | 17 | 8 18 | 8 19 | UTF-8 20 | 21 | 22 | 23 | junit 24 | junit 25 | ${junit.version} 26 | test 27 | 28 | 29 | com.google.code.gson 30 | gson 31 | 2.8.9 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/Constant.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common; 2 | 3 | 4 | import java.nio.charset.Charset; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.Locale; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * @author th 12 | * @version 1.0.0 13 | * @since 2023/7/18 13:12 14 | */ 15 | public class Constant { 16 | /** 17 | * 字符集常量,这里设置了Local依然不会有区别,所以直接搞为常量吧 18 | */ 19 | public static final List CHARSETS = Collections.unmodifiableList(Charset.availableCharsets().values().stream().map(e -> e.displayName(Locale.ENGLISH)).collect(Collectors.toList())); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/pool/DaemonThreadFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

Title:DaemonThreadFactory.java

3 | *

Description: 守护线程池工厂

4 | *

Copyright:研发部 Copyright(c)2022

5 | *

Date:2022-07-02

6 | * 7 | * @author th 8 | * @version 1.0.0 9 | * @version 1.0 10 | */ 11 | package xyz.hashdog.rdm.common.pool; 12 | 13 | 14 | import java.util.concurrent.ThreadFactory; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | /** 18 | * 守护线程池工厂 19 | */ 20 | public class DaemonThreadFactory implements ThreadFactory { 21 | 22 | final ThreadGroup group; 23 | final AtomicInteger threadNumber = new AtomicInteger(1); 24 | final String namePrefix; 25 | 26 | DaemonThreadFactory(String name) { 27 | this(name, 1); 28 | } 29 | 30 | DaemonThreadFactory(String name, int firstThreadNum) { 31 | threadNumber.set(firstThreadNum); 32 | SecurityManager s = System.getSecurityManager(); 33 | group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); 34 | namePrefix = name + "-"; 35 | } 36 | 37 | @Override 38 | public Thread newThread(Runnable r) { 39 | Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); 40 | if (!t.isDaemon()) { 41 | t.setDaemon(true); 42 | } 43 | if (t.getPriority() != Thread.NORM_PRIORITY) { 44 | t.setPriority(Thread.NORM_PRIORITY); 45 | } 46 | return t; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/pool/ThreadPool.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

Title:public class ThreadPool {.java

3 | *

Description: 守护线程池

4 | *

Copyright:研发部 Copyright(c)2022

5 | *

Date:2022-07-02

6 | * 7 | * @author th 8 | * @version 1.0.0 9 | * @version 1.0 10 | */ 11 | package xyz.hashdog.rdm.common.pool; 12 | 13 | import java.util.concurrent.LinkedBlockingDeque; 14 | import java.util.concurrent.ThreadPoolExecutor; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | /** 18 | * 守护线程池 19 | * 不一定会用到,所以使用双重检查实现单例 20 | * @author th 21 | * @version 1.0.0 22 | */ 23 | public class ThreadPool { 24 | /** 25 | * 当前系统核心数 26 | */ 27 | private static final int NUM = Runtime.getRuntime().availableProcessors(); 28 | /** 29 | * 最多两个现城 30 | */ 31 | private static final int DEFAULT_CORE_SIZE = NUM <= 2 ? 1 : 2; 32 | /** 33 | * 最大队列数 34 | */ 35 | private static final int MAX_QUEUE_SIZE = DEFAULT_CORE_SIZE + 2; 36 | /** 37 | * 线程池 38 | */ 39 | private volatile static ThreadPoolExecutor executor; 40 | 41 | 42 | private ThreadPool() { 43 | } 44 | 45 | ; 46 | 47 | /** 48 | * 获取线程池 49 | * @return 50 | */ 51 | public static ThreadPoolExecutor getInstance() { 52 | if (executor == null) { 53 | synchronized (ThreadPoolExecutor.class) { 54 | if (executor == null) { 55 | executor = new ThreadPoolExecutor(DEFAULT_CORE_SIZE,// 核心线程数 56 | MAX_QUEUE_SIZE, // 最大线程数 57 | 60 * 1000, // 闲置线程存活时间 58 | TimeUnit.MILLISECONDS,// 时间单位 59 | new LinkedBlockingDeque(Integer.MAX_VALUE),// 线程队列 60 | // Executors.defaultThreadFactory()// 线程工厂 61 | new DaemonThreadFactory("task") 62 | ); 63 | } 64 | } 65 | } 66 | return executor; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/tuple/Tuple2.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.tuple; 2 | 3 | /** 4 | * 元组 5 | * @author th 6 | * @version 1.0.0 7 | * @since 2023/8/1 12:43 8 | */ 9 | public class Tuple2 { 10 | 11 | private T1 t1; 12 | private T2 t2; 13 | 14 | public Tuple2(T1 t1, T2 t2) { 15 | this.t1 = t1; 16 | this.t2 = t2; 17 | } 18 | 19 | public T1 getT1() { 20 | return t1; 21 | } 22 | 23 | public T2 getT2() { 24 | return t2; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/util/DataUtil.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | import java.awt.*; 7 | import java.nio.charset.Charset; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Locale; 11 | import java.util.UUID; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * @author th 16 | * @version 1.0.0 17 | * @since 2023/7/20 16:30 18 | */ 19 | public class DataUtil { 20 | 21 | 22 | /** 23 | * 获取uuid 24 | * @return 25 | */ 26 | public static String getUUID() { 27 | return UUID.randomUUID().toString(); 28 | } 29 | 30 | /** 31 | * 判断字符是否为空 32 | * @param str 33 | * @return 34 | */ 35 | public static boolean isBlank(String str) { 36 | return str==null||str.isEmpty(); 37 | } 38 | /** 39 | * 判断字符是否不为空 40 | * @param str 41 | * @return 42 | */ 43 | public static boolean isNotBlank(String str) { 44 | return !isBlank(str); 45 | } 46 | 47 | /** 48 | * 获取系统所有字体 49 | * @param locale 50 | * @return 51 | */ 52 | public static java.util.List getFonts(Locale locale) { 53 | java.util.List fonts = new ArrayList<>(); 54 | // 获取本地图形环境 55 | GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 56 | // 获取所有可用的字体 57 | Font[] allFonts = ge.getAllFonts(); 58 | fonts.addAll(Arrays.asList(allFonts).stream().map(e->e.getFontName(locale)).collect(Collectors.toList())); 59 | return fonts; 60 | } 61 | 62 | /** 63 | * json字符串格式化 64 | * @param value 65 | * @param charset 66 | * @return 67 | */ 68 | public static String formatJson(byte[] value, Charset charset,boolean isFormat) { 69 | String s = new String(value, charset); 70 | // 创建一个 GsonBuilder 来配置 Gson 的格式化选项 71 | GsonBuilder gsonBuilder = new GsonBuilder(); 72 | if(isFormat){ 73 | gsonBuilder.setPrettyPrinting(); // 启用格式化输出 74 | } 75 | Gson gson = gsonBuilder.create(); 76 | // 使用 Gson 格式化 JSON 字符串 77 | String formattedJson = gson.toJson(gson.fromJson(s, Object.class)); // 解析 JSON 字符串后再格式化 78 | return formattedJson; 79 | 80 | } 81 | 82 | /** 83 | * json转byte[] 84 | * @param value 85 | * @param charset 86 | * @param isFormat true是需要格式化 87 | * @return 88 | */ 89 | public static byte[] json2Byte(String value, Charset charset,boolean isFormat) { 90 | return formatJson(value.getBytes(charset),charset,isFormat).getBytes(); 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/util/EncodeUtil.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/7/19 16:59 7 | */ 8 | public class EncodeUtil { 9 | 10 | 11 | public static boolean containsSpecialCharacters( byte[] bytes) { 12 | // 检查字节数组中是否包含特殊字符的字节 13 | for (byte b : bytes) { 14 | if ((b & 0x80) != 0) { 15 | // 非ASCII字符,可能是特殊字符 16 | return true; 17 | } 18 | } 19 | 20 | return false; 21 | } 22 | 23 | private static int byteToUnsignedInt(byte data) { 24 | return data & 0xff; 25 | } 26 | 27 | public static boolean isUTF8(byte[] pBuffer) { 28 | boolean IsUTF8 = true; 29 | boolean IsASCII = true; 30 | int size = pBuffer.length; 31 | int i = 0; 32 | while (i < size) { 33 | int value = byteToUnsignedInt(pBuffer[i]); 34 | if (value < 0x80) { 35 | // (10000000): 值小于 0x80 的为 ASCII 字符 36 | if (i >= size - 1) { 37 | if (IsASCII) { 38 | // 假设纯 ASCII 字符不是 UTF 格式 39 | IsUTF8 = false; 40 | } 41 | break; 42 | } 43 | i++; 44 | } else if (value < 0xC0) { 45 | // (11000000): 值介于 0x80 与 0xC0 之间的为无效 UTF-8 字符 46 | IsASCII = false; 47 | IsUTF8 = false; 48 | break; 49 | } else if (value < 0xE0) { 50 | // (11100000): 此范围内为 2 字节 UTF-8 字符 51 | IsASCII = false; 52 | if (i >= size - 1) { 53 | break; 54 | } 55 | 56 | int value1 = byteToUnsignedInt(pBuffer[i + 1]); 57 | if ((value1 & (0xC0)) != 0x80) { 58 | IsUTF8 = false; 59 | break; 60 | } 61 | 62 | i += 2; 63 | } else if (value < 0xF0) { 64 | IsASCII = false; 65 | // (11110000): 此范围内为 3 字节 UTF-8 字符 66 | if (i >= size - 2) { 67 | break; 68 | } 69 | 70 | int value1 = byteToUnsignedInt(pBuffer[i + 1]); 71 | int value2 = byteToUnsignedInt(pBuffer[i + 2]); 72 | if ((value1 & (0xC0)) != 0x80 || (value2 & (0xC0)) != 0x80) { 73 | IsUTF8 = false; 74 | break; 75 | } 76 | 77 | i += 3; 78 | } else if (value < 0xF8) { 79 | IsASCII = false; 80 | // (11111000): 此范围内为 4 字节 UTF-8 字符 81 | if (i >= size - 3) { 82 | break; 83 | } 84 | 85 | int value1 = byteToUnsignedInt(pBuffer[i + 1]); 86 | int value2 = byteToUnsignedInt(pBuffer[i + 2]); 87 | int value3 = byteToUnsignedInt(pBuffer[i + 3]); 88 | if ((value1 & (0xC0)) != 0x80 89 | || (value2 & (0xC0)) != 0x80 90 | || (value3 & (0xC0)) != 0x80) { 91 | IsUTF8 = false; 92 | break; 93 | } 94 | 95 | i += 3; 96 | } else { 97 | IsUTF8 = false; 98 | IsASCII = false; 99 | break; 100 | } 101 | } 102 | 103 | return IsUTF8; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/util/GzipUtil.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.nio.charset.Charset; 7 | import java.util.zip.GZIPInputStream; 8 | import java.util.zip.GZIPOutputStream; 9 | 10 | /** 11 | * @author th 12 | * @version 1.0.0 13 | * @since 2023/8/9 12:37 14 | */ 15 | public class GzipUtil { 16 | /** 17 | * 使用gzip压缩字符串 18 | */ 19 | public static byte[] compress(String str, Charset charset) { 20 | if (str == null || str.length() == 0) { 21 | return new byte[0]; 22 | } 23 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 24 | GZIPOutputStream gzip = null; 25 | try { 26 | gzip = new GZIPOutputStream(out); 27 | gzip.write(str.getBytes(charset)); 28 | } catch (IOException e) { 29 | e.printStackTrace(); 30 | } finally { 31 | if (gzip != null) { 32 | try { 33 | gzip.close(); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | return out.toByteArray(); 40 | } 41 | 42 | /** 43 | * 使用gzip解压缩 44 | */ 45 | public static String uncompress(byte [] compressed,Charset charset) { 46 | if (compressed == null || compressed.length == 0) { 47 | return ""; 48 | } 49 | 50 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 51 | ByteArrayInputStream in = null; 52 | GZIPInputStream zip = null; 53 | String decompressed = null; 54 | try { 55 | in = new ByteArrayInputStream(compressed); 56 | zip = new GZIPInputStream(in); 57 | byte[] buffer = new byte[1024]; 58 | int offset = -1; 59 | while ((offset = zip.read(buffer)) != -1) { 60 | out.write(buffer, 0, offset); 61 | } 62 | decompressed = out.toString(charset.displayName()); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } finally { 66 | try { 67 | if (zip != null) { 68 | zip.close(); 69 | } 70 | if (in != null) { 71 | in.close(); 72 | } 73 | if (out != null) { 74 | out.close(); 75 | } 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | return decompressed; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /rdm-common/src/main/java/xyz/hashdog/rdm/common/util/TUtil.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.ServiceLoader; 7 | import java.util.function.Consumer; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 泛型/反射相关操作工具 12 | * 13 | * @author th 14 | * @version 1.0.0 15 | * @since 2023/7/18 21:09 16 | */ 17 | public class TUtil { 18 | 19 | /** 20 | * 同类复制属性(只复制是null的属性) 21 | * @param souce 22 | * @param target 23 | * @param 24 | * @throws IllegalAccessException 25 | */ 26 | public static void copyProperties(T souce, T target) { 27 | Class clazz = souce.getClass(); 28 | Field[] fields = clazz.getDeclaredFields(); 29 | for (int i = 0; i < fields.length; i++) { 30 | //得到属性 31 | Field field = fields[i]; 32 | //打开私有访问 33 | field.setAccessible(true); 34 | //如果是null,从源对象复制到模板对象 35 | try { 36 | Object o = field.get(target); 37 | Object o2 = field.get(souce); 38 | if(o==null&&o2!=null){ 39 | field.set(target, field.get(souce)); 40 | } 41 | } catch (IllegalAccessException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | } 46 | 47 | 48 | /** 49 | * 递归策略接口 50 | * @param 51 | * @param 52 | */ 53 | public static interface RecursiveTree2List { 54 | 55 | /** 56 | * 递归 57 | * 58 | * @param h 结果集 59 | * @param t 需要递归迭代的目标 60 | * @param recursiveDeal 61 | * @param 62 | * @param 63 | */ 64 | static void recursive(L h, T t, RecursiveTree2List recursiveDeal) { 65 | //获取子集 66 | List ts = recursiveDeal.subset(t); 67 | if (ts == null || ts.isEmpty()) { 68 | recursiveDeal.noSubset(h, t); 69 | } else { 70 | L newh = recursiveDeal.hasSubset(h, t); 71 | for (T t1 : ts) { 72 | recursive(newh, t1, recursiveDeal); 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * 获取节点子集 79 | * @param t 80 | * @return 81 | */ 82 | List subset(T t) ; 83 | /** 84 | * 没有子集的情况怎么处理 85 | * @param h 86 | * @param t 87 | */ 88 | void noSubset(L h, T t); 89 | /** 90 | * 有子集的情况怎么处理 91 | * @param h 92 | * @param t 93 | * @return 94 | */ 95 | L hasSubset(L h, T t); 96 | } 97 | 98 | 99 | 100 | /** 101 | * 递归策略接口 102 | * 将list转为tree 103 | * 104 | * @param 105 | * @param 106 | */ 107 | public static interface RecursiveList2Tree { 108 | 109 | /** 110 | * @param tree 树结果 111 | * @param list 需要迭代的list 112 | * @param recursiveList2Tree 策略 113 | * @param 114 | * @param 115 | */ 116 | static void recursive(T tree, List list, RecursiveList2Tree recursiveList2Tree) { 117 | 118 | //获取子集 119 | List subs = recursiveList2Tree.findSubs(tree, list); 120 | //整合到tree 121 | List treeList = recursiveList2Tree.toTree(tree, subs); 122 | //过滤掉已经迭代到tree的数据 123 | List newList = recursiveList2Tree.filterList(list, subs); 124 | for (T tree1 : treeList) { 125 | recursive(tree1, newList, recursiveList2Tree); 126 | } 127 | 128 | } 129 | /** 130 | * 获取子节 131 | * 一般根据父节点id,从list匹配子节点 132 | * 133 | * @param tree 父节点 134 | * @param list 需要迭代的集合 135 | * @return 找到的子节点 136 | */ 137 | List findSubs(T tree, List list); 138 | /** 139 | * 转为树 140 | * 141 | * @param tree 父节点 142 | * @param subs 子节点 143 | * @return 转为树的子节点 144 | */ 145 | List toTree(T tree, List subs); 146 | 147 | /** 148 | * 一般已经迭代过的list可以过滤掉,避免重复,具体由子类实现 149 | * 150 | * @param list 原始集合 151 | * @param subs 已经迭代处理过的集合 152 | * @return 过滤后的集合 153 | */ 154 | List filterList(List list, List subs); 155 | } 156 | 157 | 158 | /** 159 | * 反射获取对象的字段 160 | * 161 | * @param obj 162 | * @param fieldName 获取的字段 163 | * @param 164 | * @return 165 | */ 166 | public static T getField(Object obj, String fieldName) { 167 | // 使用反射获取字段的值 168 | try { 169 | // 获取对象的 Class 对象 170 | Class objClass = obj.getClass(); 171 | // 获取字段对象 172 | Field field = objClass.getDeclaredField(fieldName); 173 | // 设置允许访问私有字段 174 | field.setAccessible(true); 175 | // 获取字段的值 176 | Object fieldValue = field.get(obj); 177 | return (T) fieldValue; 178 | } catch (NoSuchFieldException | IllegalAccessException e) { 179 | throw new RuntimeException(e); 180 | } 181 | } 182 | 183 | 184 | /** 185 | * spi获取此类服务 186 | * 187 | * @param clazz 188 | * @param 189 | * @return 190 | */ 191 | public static T spi(Class clazz) { 192 | ServiceLoader load = ServiceLoader.load(clazz); 193 | Iterator iterator = load.iterator(); 194 | 195 | //循环获取所需的对象 196 | while (iterator.hasNext()) { 197 | T next = iterator.next(); 198 | if (next != null) { 199 | return next; 200 | } 201 | } 202 | throw new RuntimeException("no such spi:" + clazz.getName()); 203 | } 204 | 205 | 206 | /** 207 | * 执行方法 208 | * 目前用于统一处理jedis执行命令之后的close操作 209 | * 210 | * @param t 可以是jedis 211 | * @param execCommand 需要执行的具体逻辑 212 | * @param callback 执行逻辑之后的回调,比如关流 213 | * @param jedis 214 | * @param 执行jedis命令之后的返回值 215 | * @return 216 | */ 217 | public static R execut(T t, Function execCommand, Consumer callback) { 218 | try { 219 | return execCommand.apply(t); 220 | } finally { 221 | // callback.accept(t); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /rdm-common/src/test/java/xyz/hashdog/rdm/common/util/DataUtilTest.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | import java.util.Locale; 7 | 8 | /** 9 | * @author th 10 | * @version 1.0.0 11 | * @since 2023/8/6 23:40 12 | */ 13 | public class DataUtilTest { 14 | 15 | @Test 16 | public void getFonts() { 17 | List list= DataUtil.getFonts(Locale.CHINA); 18 | System.out.println(list); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rdm-common/src/test/java/xyz/hashdog/rdm/common/util/EncodeUtilTest.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.common.util; 2 | 3 | import org.junit.Test; 4 | 5 | import java.nio.charset.Charset; 6 | import java.util.List; 7 | import java.util.Locale; 8 | 9 | /** 10 | * @author th 11 | * @version 1.0.0 12 | * @since 2023/7/25 22:56 13 | */ 14 | public class EncodeUtilTest { 15 | 16 | @Test 17 | public void containsSpecialCharactersTest() { 18 | System.out.println(EncodeUtil.containsSpecialCharacters("123".getBytes(Charset.forName("gbk")))); 19 | System.out.println(EncodeUtil.containsSpecialCharacters("123".getBytes())); 20 | System.out.println(EncodeUtil.containsSpecialCharacters("你好".getBytes())); 21 | byte[] gbks = "你好".getBytes(Charset.forName("gbk")); 22 | System.out.println(EncodeUtil.containsSpecialCharacters(gbks)); 23 | } 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /rdm-redis-imp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | xyz.hashdog.rdm 8 | RedisDesktopManagerFX 9 | 1.0.0 10 | 11 | 12 | xyz.hashdog.rdm 13 | rdm-redis-imp 14 | 15 | 16 | 8 17 | 8 18 | UTF-8 19 | 4.3.0 20 | 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | ${junit.version} 28 | test 29 | 30 | 31 | redis.clients 32 | jedis 33 | ${jedis.version} 34 | 35 | 36 | xyz.hashdog.rdm 37 | rdm-redis 38 | ${rdm.version} 39 | 40 | 41 | xyz.hashdog.rdm 42 | rdm-common 43 | ${rdm.version} 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/Constant.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp; 2 | 3 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 4 | import redis.clients.jedis.Jedis; 5 | import redis.clients.jedis.JedisPoolConfig; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/7/18 13:12 11 | */ 12 | public class Constant { 13 | 14 | /** 15 | * jedis通用连接池配置 16 | */ 17 | public static final GenericObjectPoolConfig POOL_CONFIG ; 18 | static { 19 | // 创建Jedis连接池配置对象 20 | JedisPoolConfig poolConfig = new JedisPoolConfig(); 21 | poolConfig.setMaxTotal(10); // 设置连接池中的最大连接数 22 | poolConfig.setMaxIdle(2); // 设置连接池中的最大空闲连接数 23 | poolConfig.setTestOnBorrow(true); // 设置Jedis连接池的测试连接是否有效 24 | POOL_CONFIG=poolConfig; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/RedisContext.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp; 2 | 3 | import redis.clients.jedis.exceptions.JedisException; 4 | import xyz.hashdog.rdm.redis.RedisConfig; 5 | import xyz.hashdog.rdm.redis.client.RedisClient; 6 | import xyz.hashdog.rdm.redis.exceptions.RedisException; 7 | import xyz.hashdog.rdm.redis.imp.client.DefaultRedisClientCreator; 8 | import xyz.hashdog.rdm.redis.imp.client.RedisClientCreator; 9 | 10 | /** 11 | * @author th 12 | * @version 1.0.0 13 | * @since 2023/7/18 10:53 14 | */ 15 | public class RedisContext implements xyz.hashdog.rdm.redis.RedisContext{ 16 | /** 17 | * redis配置 18 | */ 19 | private RedisConfig redisConfig; 20 | /** 21 | * redis客户创建器 22 | */ 23 | private RedisClientCreator redisClientCreator; 24 | 25 | 26 | 27 | 28 | public RedisContext(RedisConfig redisConfig) { 29 | this.redisConfig=redisConfig; 30 | this.redisClientCreator=new DefaultRedisClientCreator(); 31 | } 32 | 33 | /** 34 | * 委派给 RedisClientCreator 进行redis客户端的创建 35 | * @return 36 | */ 37 | @Override 38 | public RedisClient newRedisClient() { 39 | try { 40 | return redisClientCreator.create(redisConfig); 41 | 42 | }catch (JedisException e){ 43 | throw new RedisException(e.getMessage()); 44 | } 45 | } 46 | 47 | @Override 48 | public RedisConfig getRedisConfig() { 49 | return redisConfig; 50 | } 51 | 52 | 53 | 54 | /** 55 | * 获取创建器,可以进行创建多个客户端实例 56 | * @return 57 | */ 58 | public RedisClientCreator getRedisClientCreator() { 59 | return redisClientCreator; 60 | } 61 | 62 | /** 63 | * 设置创建器,可以自定义创建器 64 | * @param redisClientCreator 65 | */ 66 | public void setRedisClientCreator(RedisClientCreator redisClientCreator) { 67 | this.redisClientCreator = redisClientCreator; 68 | } 69 | 70 | @Override 71 | public void close() { 72 | redisClientCreator.close(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/RedisFactory.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp; 2 | 3 | import xyz.hashdog.rdm.redis.RedisConfig; 4 | 5 | /** 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/7/18 10:32 9 | */ 10 | public class RedisFactory implements xyz.hashdog.rdm.redis.RedisFactory { 11 | 12 | @Override 13 | public xyz.hashdog.rdm.redis.RedisContext createRedisContext(RedisConfig redisConfig) { 14 | return new RedisContext(redisConfig); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/client/DefaultRedisClientCreator.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.client; 2 | 3 | import redis.clients.jedis.JedisPool; 4 | import xyz.hashdog.rdm.common.util.DataUtil; 5 | import xyz.hashdog.rdm.redis.RedisConfig; 6 | import xyz.hashdog.rdm.redis.client.RedisClient; 7 | import xyz.hashdog.rdm.redis.imp.Constant; 8 | 9 | /** 10 | * @author th 11 | * @version 1.0.0 12 | * @since 2023/7/18 12:47 13 | */ 14 | public class DefaultRedisClientCreator implements RedisClientCreator{ 15 | 16 | /** 17 | * jedis 18 | */ 19 | private JedisPool pool; 20 | /** 21 | * 根据RedisConfig 判断创建什么类型的redis客户端 22 | * @param redisConfig 23 | * @return 24 | */ 25 | @Override 26 | public RedisClient create(RedisConfig redisConfig) { 27 | if(DataUtil.isNotBlank(redisConfig.getAuth())){ 28 | this.pool=new JedisPool(Constant.POOL_CONFIG, redisConfig.getHost(), redisConfig.getPort(),500,redisConfig.getAuth()); 29 | 30 | }else { 31 | this.pool=new JedisPool(Constant.POOL_CONFIG, redisConfig.getHost(), redisConfig.getPort()); 32 | 33 | } 34 | return new JedisPoolClient(pool); 35 | } 36 | 37 | @Override 38 | public void close() { 39 | if(pool!=null){ 40 | pool.close(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/client/RedisClientCreator.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.client; 2 | 3 | import xyz.hashdog.rdm.redis.RedisConfig; 4 | import xyz.hashdog.rdm.redis.client.RedisClient; 5 | 6 | import java.io.Closeable; 7 | 8 | /** 9 | * 10 | * RedisClinent创建器 11 | * @author th 12 | * @version 1.0.0 13 | * @since 2023/7/18 12:45 14 | */ 15 | public interface RedisClientCreator extends Closeable { 16 | /** 17 | * 创建redis客户端 18 | * @param redisConfig 19 | * @return 20 | */ 21 | RedisClient create(RedisConfig redisConfig); 22 | 23 | @Override 24 | void close(); 25 | } 26 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/console/ReaderParseEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.console; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * redis返回协议类型及其解析策略 9 | * @author th 10 | * @version 1.0.0 11 | * @since 2023/7/18 23:24 12 | */ 13 | public enum ReaderParseEnum { 14 | 15 | /** 16 | * 对于简单字符串(Simple Strings),回复的第一个字节是“+” 17 | */ 18 | SIMPLE_STRINGS('+', (l, r) -> new ArrayList() {{ 19 | add(l.substring(1, l.length())); 20 | }}), 21 | /** 22 | * 对于错误(Errors ),回复的第一个字节是“-” 23 | */ 24 | ERRORS('-', (l, r) -> new ArrayList() {{ 25 | add(l.substring(1, l.length())); 26 | }}), 27 | /** 28 | * 对于整数(Integers ),回复的第一个字节是“:” 29 | */ 30 | INTEGERS(':', (l, r) -> new ArrayList() {{ 31 | add(l.substring(1, l.length())); 32 | }}), 33 | /** 34 | * 对于批量字符串(Bulk Strings),回复的第一个字节是“$” 35 | */ 36 | BULK_STRINGS('$', (l, r) -> { 37 | final String newl = r.readLine(); 38 | return new ArrayList() {{ 39 | add(newl); 40 | }}; 41 | }), 42 | /** 43 | * 对于数组(Arrays ),回复的第一个字节是“*” 44 | */ 45 | ARRAYS('*', (l, r) -> { 46 | List result = new ArrayList<>(); 47 | //返回的次数 48 | int count = Integer.parseInt(l.substring(1, l.length())); 49 | for (int i = 0; i < count; i++) { 50 | String next = r.readLine(); 51 | ReaderParseEnum readerParseEnum = ReaderParseEnum.getByLine(next); 52 | List parse = readerParseEnum.readerParser.parse(next, r); 53 | result.addAll(parse); 54 | } 55 | return result; 56 | }), 57 | ; 58 | 59 | /** 60 | * 根据响应的单行数据,进行判断对应那个枚举 61 | * 62 | * @param line 63 | * @return 64 | */ 65 | public static ReaderParseEnum getByLine(String line) { 66 | char c = line.charAt(0); 67 | for (ReaderParseEnum value : ReaderParseEnum.values()) { 68 | if (value.mark == c) { 69 | return value; 70 | } 71 | } 72 | throw new RuntimeException("no this mark:" + c); 73 | } 74 | 75 | /** 76 | * 返回的协议标记 77 | */ 78 | public char mark; 79 | /** 80 | * 解析器 81 | */ 82 | public ReaderParser readerParser; 83 | 84 | 85 | ReaderParseEnum(char mark, ReaderParser readerParser) { 86 | this.mark = mark; 87 | this.readerParser = readerParser; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/console/ReaderParser.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.console; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/7/18 23:24 11 | */ 12 | public interface ReaderParser { 13 | /** 14 | * 解析 15 | * @param line 16 | * @param reader 17 | * @return 18 | */ 19 | List parse(String line, BufferedReader reader) throws IOException; 20 | } 21 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/console/RedisConsole.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.console; 2 | 3 | import java.io.*; 4 | import java.net.Socket; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 通用RedisConsole实现,不管什么类型的RedisClient,都只要实现不同的SocketAcquirer, 10 | * 就可以获取对应的socket进行交互通信 11 | * 12 | * @author th 13 | * @version 1.0.0 14 | * @since 2023/7/18 21:31 15 | */ 16 | public class RedisConsole implements xyz.hashdog.rdm.redis.client.RedisConsole { 17 | 18 | /** 19 | * socket获取器 20 | */ 21 | private SocketAcquirer socketAcquirer; 22 | 23 | public RedisConsole(SocketAcquirer socketAcquirer) { 24 | this.socketAcquirer = socketAcquirer; 25 | } 26 | 27 | @Override 28 | public List sendCommand(String cmd) { 29 | try { 30 | Socket socket = socketAcquirer.getSocket(); 31 | InputStream inputStream = socket.getInputStream(); 32 | OutputStream outputStream = socket.getOutputStream(); 33 | // 获取输入输出流 34 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 35 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream)); 36 | writer.write(cmd + "\r\n"); 37 | writer.flush(); 38 | List result = parseResult(reader); 39 | return result; 40 | } catch (Exception e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | 45 | /** 46 | * 解析到结果集 47 | * 需要根据RESP解析需要的结果 48 | * 49 | * @param reader 50 | * @return 51 | * @throws IOException 52 | */ 53 | private List parseResult(BufferedReader reader) throws IOException { 54 | String line; 55 | if ((line = reader.readLine()) != null) { 56 | return ReaderParseEnum.getByLine(line).readerParser.parse(line,reader); 57 | } 58 | return new ArrayList<>(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/java/xyz/hashdog/rdm/redis/imp/console/SocketAcquirer.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.console; 2 | 3 | import java.net.Socket; 4 | 5 | /** 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/7/18 21:35 9 | */ 10 | @FunctionalInterface 11 | public interface SocketAcquirer { 12 | /** 13 | * 获取套接字,每次都是拿去当前客户端最新的 14 | * @return 15 | */ 16 | Socket getSocket(); 17 | } 18 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/main/resources/META-INF/services/xyz.hashdog.rdm.redis.RedisFactory: -------------------------------------------------------------------------------- 1 | xyz.hashdog.rdm.redis.imp.RedisFactory -------------------------------------------------------------------------------- /rdm-redis-imp/src/test/java/xyz/hashdog/rdm/redis/imp/RedisContextTest.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import xyz.hashdog.rdm.redis.RedisConfig; 6 | import xyz.hashdog.rdm.redis.RedisContext; 7 | 8 | /** 9 | * @author th 10 | * @version 1.0.0 11 | * @since 2023/7/20 10:21 12 | */ 13 | public class RedisContextTest { 14 | 15 | private RedisContext redisContext; 16 | 17 | @Before 18 | public void before() { 19 | RedisConfig redisConfig = new RedisConfig(); 20 | redisConfig.setHost("localhost"); 21 | redisConfig.setPort(6379); 22 | redisContext = new xyz.hashdog.rdm.redis.imp.RedisContext(redisConfig); 23 | } 24 | 25 | @Test 26 | public void testConnect() { 27 | System.out.println(redisContext.newRedisClient().testConnect()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rdm-redis-imp/src/test/java/xyz/hashdog/rdm/redis/imp/console/RedisConsoleTest.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.imp.console; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import xyz.hashdog.rdm.redis.client.RedisClient; 6 | import xyz.hashdog.rdm.redis.RedisConfig; 7 | import xyz.hashdog.rdm.redis.client.RedisConsole; 8 | import xyz.hashdog.rdm.redis.RedisContext; 9 | import xyz.hashdog.rdm.redis.imp.RedisFactory; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author th 15 | * @version 1.0.0 16 | * @since 2023/7/19 9:26 17 | */ 18 | public class RedisConsoleTest { 19 | private RedisConsole redisConsole; 20 | 21 | @Before 22 | public void before(){ 23 | xyz.hashdog.rdm.redis.RedisFactory redisFactory=new RedisFactory(); 24 | RedisConfig redisConfig =new RedisConfig(); 25 | redisConfig.setHost("localhost"); 26 | redisConfig.setPort(6379); 27 | RedisContext redisContext = redisFactory.createRedisContext(redisConfig); 28 | RedisClient redisClient=redisContext.newRedisClient(); 29 | this.redisConsole=redisClient.getRedisConsole(); 30 | } 31 | 32 | @Test 33 | public void scan(){ 34 | List result=redisConsole.sendCommand("SCAN 0 MATCH A* COUNT 10"); 35 | result.forEach(e-> System.out.println(e)); 36 | } 37 | @Test 38 | public void ping(){ 39 | List result=redisConsole.sendCommand("ping"); 40 | result.forEach(e-> System.out.println(e)); 41 | } 42 | @Test 43 | public void keys(){ 44 | List result=redisConsole.sendCommand("keys *"); 45 | result.forEach(e-> System.out.println(e)); 46 | } 47 | @Test 48 | public void lrange(){ 49 | List result=redisConsole.sendCommand("lrange list 0 99"); 50 | result.forEach(e-> System.out.println(e)); 51 | } 52 | 53 | @Test 54 | public void MONITOR(){ 55 | List result=redisConsole.sendCommand("MONITOR"); 56 | result.forEach(e-> System.out.println(e)); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /rdm-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | xyz.hashdog.rdm 8 | RedisDesktopManagerFX 9 | 1.0.0 10 | 11 | 12 | 13 | xyz.hashdog.rdm 14 | rdm-redis 15 | 16 | 17 | 8 18 | 8 19 | UTF-8 20 | 21 | 22 | 23 | xyz.hashdog.rdm 24 | rdm-common 25 | ${rdm.version} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/Message.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/7/20 10:38 7 | */ 8 | public class Message { 9 | /** 10 | * 是否成功 11 | */ 12 | private boolean success; 13 | /** 14 | * 消息 15 | */ 16 | private String message; 17 | 18 | public Message() { 19 | } 20 | 21 | public Message(boolean success) { 22 | this.success = success; 23 | } 24 | 25 | public boolean isSuccess() { 26 | return success; 27 | } 28 | 29 | public void setSuccess(boolean success) { 30 | this.success = success; 31 | } 32 | 33 | public String getMessage() { 34 | return message; 35 | } 36 | 37 | public void setMessage(String message) { 38 | this.message = message; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Message{" + 44 | "success=" + success + 45 | ", message='" + message + '\'' + 46 | '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/7/18 10:49 7 | */ 8 | public class RedisConfig { 9 | 10 | /** 11 | * 地址 12 | */ 13 | private String host; 14 | /** 15 | * 名称 16 | */ 17 | private String name; 18 | /** 19 | * 端口 20 | */ 21 | private int port; 22 | /** 23 | * 授权 24 | */ 25 | private String auth; 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public String getHost() { 36 | return host; 37 | } 38 | 39 | public void setHost(String host) { 40 | this.host = host; 41 | } 42 | 43 | public int getPort() { 44 | return port; 45 | } 46 | 47 | public void setPort(int port) { 48 | this.port = port; 49 | } 50 | 51 | public String getAuth() { 52 | return auth; 53 | } 54 | 55 | public void setAuth(String auth) { 56 | this.auth = auth; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/RedisContext.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis; 2 | 3 | import xyz.hashdog.rdm.redis.client.RedisClient; 4 | 5 | import java.io.Closeable; 6 | 7 | /** 8 | * redis上下文,提供了对redis操作及相关信息所有的包装 9 | * 多实例,可以由RedisFactory创建 10 | * @author th 11 | * @version 1.0.0 12 | * @since 2023/7/18 10:48 13 | */ 14 | public interface RedisContext extends Closeable { 15 | /** 16 | * redis客户端获取,用于操作redis 17 | * @return 18 | */ 19 | RedisClient newRedisClient(); 20 | 21 | /** 22 | * 获取redis的配置 23 | * @return 24 | */ 25 | RedisConfig getRedisConfig(); 26 | 27 | 28 | @Override 29 | void close(); 30 | } 31 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/RedisFactory.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/7/18 9:49 7 | */ 8 | public interface RedisFactory { 9 | 10 | 11 | /** 12 | * 获取redis上下文 13 | * @param redisConfig 14 | * @return 15 | */ 16 | RedisContext createRedisContext(RedisConfig redisConfig); 17 | } 18 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/RedisFactorySingleton.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis; 2 | 3 | import xyz.hashdog.rdm.common.util.TUtil; 4 | import xyz.hashdog.rdm.redis.RedisFactory; 5 | 6 | import java.util.Iterator; 7 | import java.util.ServiceLoader; 8 | 9 | /** 10 | * 静态内部类实现单例 11 | * RedisFactory只用获取一次,使用spi机制 12 | * @author th 13 | * @version 1.0.0 14 | * @since 2023/7/19 9:59 15 | */ 16 | public class RedisFactorySingleton { 17 | private RedisFactorySingleton() { 18 | } 19 | 20 | /** 21 | * 获取RedisFactory单例 22 | * @return 23 | */ 24 | public static RedisFactory getInstance(){ 25 | return SingletonHolder.instance; 26 | } 27 | private static class SingletonHolder{ 28 | private static final RedisFactory instance ; 29 | static { 30 | instance= TUtil.spi(RedisFactory.class); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/client/RedisConsole.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.client; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | *控制台交互 7 | * @author th 8 | * @version 1.0.0 9 | * @since 2023/7/18 21:16 10 | */ 11 | public interface RedisConsole { 12 | /** 13 | * 发送命令 14 | * todo 需要改为队列形式 15 | * 返回结果集 16 | * @return 17 | */ 18 | List sendCommand(String cmd); 19 | } 20 | -------------------------------------------------------------------------------- /rdm-redis/src/main/java/xyz/hashdog/rdm/redis/exceptions/RedisException.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.redis.exceptions; 2 | 3 | /** 4 | * redis 客户端api操作一切异常统一转化为RedisException 5 | * 目的是为了方便客户端进行统一处理 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/7/20 10:30 9 | */ 10 | public class RedisException extends RuntimeException { 11 | 12 | public RedisException(String message) { 13 | super(message); 14 | } 15 | 16 | public RedisException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | public RedisException(Throwable cause) { 21 | super(cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | xyz.hashdog.rdm 8 | RedisDesktopManagerFX 9 | 1.0.0 10 | 11 | 12 | xyz.hashdog.rdm 13 | rdm-ui 14 | ${rdm.version} 15 | 16 | 17 | 18 | 8 19 | 8 20 | UTF-8 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | ${junit.version} 28 | test 29 | 30 | 31 | xyz.hashdog.rdm 32 | rdm-common 33 | ${rdm.version} 34 | 35 | 36 | xyz.hashdog.rdm 37 | rdm-redis-imp 38 | ${rdm.version} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-assembly-plugin 49 | 2.2 50 | 51 | false 52 | 53 | 54 | xyz.hashdog.rdm.ui.Main 55 | 56 | 57 | 58 | jar-with-dependencies 59 | 60 | 61 | 62 | 63 | make-assembly 64 | package 65 | 66 | assembly 67 | 68 | 69 | 70 | 71 | 72 | 73 | io.github.fvarrui 74 | javapackager 75 | 1.7.3 76 | 77 | 78 | bundling-for-windows 79 | package 80 | 81 | package 82 | 83 | 84 | xyz.hashdog.rdm.ui.Main 85 | true 86 | C:\Program Files\Java\jdk-1.8\jre 87 | false 88 | false 89 | windows 90 | true 91 | 92 | ${name}.l4j.ini 93 | 94 | 95 | src/main/resources/icon/redis256.ico 96 | 97 | 98 | 99 | 100 | 101 | bundling-for-linux 102 | package 103 | 104 | package 105 | 106 | 107 | linux 108 | xyz.hashdog.rdm.ui.Main 109 | true 110 | true 111 | D:\soft\JDK\linux_jdk1.8.0_371\jre 112 | 113 | ${name}.l4j.ini 114 | 115 | 116 | src/main/resources/icon/redis256.png 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/Main.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui; 2 | 3 | import javafx.application.Application; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Scene; 6 | import javafx.scene.control.Alert; 7 | import javafx.scene.layout.AnchorPane; 8 | import javafx.stage.Stage; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import xyz.hashdog.rdm.redis.exceptions.RedisException; 12 | import xyz.hashdog.rdm.ui.common.Applications; 13 | import xyz.hashdog.rdm.ui.controller.MainController; 14 | import xyz.hashdog.rdm.ui.exceptions.GeneralException; 15 | import xyz.hashdog.rdm.ui.util.GuiUtil; 16 | 17 | import java.util.Locale; 18 | import java.util.ResourceBundle; 19 | 20 | public class Main extends Application { 21 | protected static Logger log = LoggerFactory.getLogger(Main.class); 22 | 23 | public static final String BASE_NAME = "i18n.messages"; 24 | 25 | public static Locale DEFAULT_LOCALE = Locale.getDefault(); 26 | public static ResourceBundle RESOURCE_BUNDLE=ResourceBundle.getBundle(BASE_NAME, DEFAULT_LOCALE); 27 | 28 | 29 | 30 | public static void main(String[] args) { 31 | Application.launch(args); 32 | } 33 | 34 | @Override 35 | public void start(Stage stage) { 36 | try { 37 | // 设置默认的未捕获异常处理器 38 | Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { 39 | throwable.printStackTrace(); 40 | log.error("",throwable); 41 | if(throwable instanceof RedisException||throwable instanceof GeneralException){ 42 | // 在此处您可以自定义处理异常的逻辑 43 | GuiUtil.alert(Alert.AlertType.WARNING,throwable.getLocalizedMessage()); 44 | return; 45 | } 46 | Throwable cause = getRootCause(throwable); 47 | // 在此处您可以自定义处理异常的逻辑 48 | GuiUtil.alert(Alert.AlertType.ERROR,cause.getMessage()); 49 | }); 50 | stage.setTitle(Applications.NODE_APP_NAME); 51 | // Parent root = FXMLLoader.load(getClass().getResource("/fxml/MainView.fxml"),RESOURCE_BUNDLE); 52 | // stage.setTitle(RESOURCE_BUNDLE.getString(""Applications.NODE_APP_NAME"") ); 53 | 54 | FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/MainView.fxml"),RESOURCE_BUNDLE); 55 | AnchorPane root = fxmlLoader.load(); 56 | MainController controller = fxmlLoader.getController(); 57 | controller.setCurrentStage(stage); 58 | 59 | Scene scene = new Scene(root); 60 | scene.getStylesheets().add(getClass().getResource("/css/global.css").toExternalForm()); 61 | // Application.setUserAgentStylesheet(); 62 | stage.setScene(scene); 63 | stage.show(); 64 | //先默认打开 65 | controller.openServerLinkWindo(null); 66 | }catch (Exception e){ 67 | e.printStackTrace(); 68 | } 69 | 70 | } 71 | public Throwable getRootCause(Throwable throwable) { 72 | Throwable cause = throwable.getCause(); 73 | if (cause == null) { 74 | return throwable; // This is the root cause 75 | } 76 | return getRootCause(cause); // Continue searching deeper 77 | } 78 | @Override 79 | public void init() throws Exception { 80 | // DEFAULT_LOCALE= new Locale("en", "US"); 81 | DEFAULT_LOCALE=Locale.JAPAN; 82 | // DEFAULT_LOCALE=Locale.US; 83 | RESOURCE_BUNDLE=ResourceBundle.getBundle(BASE_NAME, DEFAULT_LOCALE); 84 | } 85 | 86 | 87 | } -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/Applications.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | import javafx.scene.control.TreeItem; 4 | import javafx.scene.text.Font; 5 | import xyz.hashdog.rdm.common.util.DataUtil; 6 | import xyz.hashdog.rdm.common.util.TUtil; 7 | import xyz.hashdog.rdm.redis.Message; 8 | import xyz.hashdog.rdm.ui.entity.ConnectionServerNode; 9 | import xyz.hashdog.rdm.ui.util.GuiUtil; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * 虚拟化和反虚拟化操作工具 16 | * 封装对数据持久化,及视图数据初始化的相关操作 17 | */ 18 | public class Applications { 19 | 20 | public static final String DEFUALT_VALUE="520"; 21 | 22 | 23 | /** 24 | * 连接 25 | */ 26 | public static final String KEY_CONNECTIONS = "Conections"; 27 | /** 28 | * 树的根节点id 29 | */ 30 | public static final String ROOT_ID = "-1"; 31 | /** 32 | * 应用名称 33 | */ 34 | public static final String NODE_APP_NAME = "RedisDesktopManagerFX"; 35 | /** 36 | * 配置节点 37 | */ 38 | public static final String NODE_APP_CONE = "Config"; 39 | /** 40 | * 数据节点 41 | */ 42 | public static final String NODE_APP_DATA = "Data"; 43 | 44 | 45 | 46 | /** 47 | * 获取系统支持的所有字体 48 | * 49 | * @return 50 | */ 51 | public static List getSystemFontNames() { 52 | return Font.getFontNames(); 53 | } 54 | 55 | /** 56 | * 连接/或分组数据的新增和修改 57 | * 58 | * @param connectionServerNode 59 | * @return 60 | */ 61 | public static Message addOrUpdateConnectionOrGroup(ConnectionServerNode connectionServerNode) { 62 | //id存在则修改,否则是新增 63 | if (DataUtil.isNotBlank(connectionServerNode.getDataId())) { 64 | //修改,需要查久数据,补充父id和时间戳 65 | ConnectionServerNode old = CacheConfigSingleton.CONFIG.getConnectionNodeMap().get(connectionServerNode.getDataId()); 66 | connectionServerNode.setParentDataId(old.getParentDataId()); 67 | connectionServerNode.setTimestampSort(old.getTimestampSort()); 68 | } else { 69 | //新增需要设置id和时间戳字段 70 | connectionServerNode.setDataId(DataUtil.getUUID()); 71 | connectionServerNode.setTimestampSort(System.currentTimeMillis()); 72 | } 73 | //put进缓存,会触发持久化 74 | CacheConfigSingleton.CONFIG.getConnectionNodeMap().put(connectionServerNode.getDataId(), connectionServerNode); 75 | return new Message(true); 76 | } 77 | 78 | /** 79 | * 节点重命名,包括连接和分组 80 | * 81 | * @param groupNode 82 | * @return 83 | */ 84 | public static Message renameConnectionOrGroup(ConnectionServerNode groupNode) { 85 | ConnectionServerNode old = CacheConfigSingleton.CONFIG.getConnectionNodeMap().get(groupNode.getDataId()); 86 | TUtil.copyProperties(old, groupNode); 87 | //map在put的时候需要引用地址变更才会触发监听,所以这里进行了域的复制 88 | CacheConfigSingleton.CONFIG.getConnectionNodeMap().put(old.getDataId(), groupNode); 89 | return new Message(true); 90 | } 91 | 92 | 93 | /** 94 | * 根据id递归删除 95 | * 96 | * @param tree 97 | * @return 98 | */ 99 | public static Message deleteConnectionOrGroup(TreeItem tree) { 100 | List ids = new ArrayList<>(); 101 | TUtil.RecursiveTree2List.recursive(ids, tree, new TUtil.RecursiveTree2List, TreeItem>() { 102 | @Override 103 | public List> subset(TreeItem connectionServerNodeTreeItem) { 104 | return connectionServerNodeTreeItem.getChildren(); 105 | } 106 | 107 | @Override 108 | public void noSubset(List h, TreeItem connectionServerNodeTreeItem) { 109 | h.add(connectionServerNodeTreeItem.getValue().getDataId()); 110 | } 111 | 112 | @Override 113 | public List hasSubset(List h, TreeItem connectionServerNodeTreeItem) { 114 | h.add(connectionServerNodeTreeItem.getValue().getDataId()); 115 | return h; 116 | } 117 | }); 118 | for (String id : ids) { 119 | CacheConfigSingleton.CONFIG.getConnectionNodeMap().remove(id); 120 | } 121 | return new Message(true); 122 | } 123 | 124 | /** 125 | * 初始化连接树 126 | * 从缓存去的连接集合,进行递归组装成树4 127 | * 128 | * @return 129 | */ 130 | public static TreeItem initConnectionTreeView() { 131 | List list = new ArrayList<>(CacheConfigSingleton.CONFIG.getConnectionNodeMap().values()); 132 | // for (ConnectionServerNode connectionServerNode : list) { 133 | // if(connectionServerNode.getParentDataId()==null){ 134 | // connectionServerNode.setParentDataId("-1"); 135 | // } 136 | // } 137 | list.sort((a, b) -> a.getTimestampSort() > b.getTimestampSort() ? 1 : -1); 138 | TreeItem root = new TreeItem<>(); 139 | //得造一个隐形的父节点 140 | ConnectionServerNode node = new ConnectionServerNode(ConnectionServerNode.GROUP); 141 | node.setDataId(Applications.ROOT_ID); 142 | root.setValue(node); 143 | TUtil.RecursiveList2Tree.recursive(root, list, new TUtil.RecursiveList2Tree, ConnectionServerNode>() { 144 | @Override 145 | public List findSubs(TreeItem tree, List list) { 146 | List subs = new ArrayList<>(); 147 | String dataId = tree.getValue().getDataId(); 148 | for (ConnectionServerNode connectionServerNode : list) { 149 | if (connectionServerNode.getParentDataId().equals(dataId)) { 150 | subs.add(connectionServerNode); 151 | } 152 | } 153 | return subs; 154 | } 155 | 156 | @Override 157 | public List> toTree(TreeItem tree, List subs) { 158 | List> trees = new ArrayList<>(); 159 | for (ConnectionServerNode sub : subs) { 160 | if(sub.isConnection()){ 161 | trees.add(new TreeItem<>(sub,GuiUtil.creatConnctionImageView())); 162 | }else { 163 | trees.add(new TreeItem<>(sub, GuiUtil.creatGroupImageView())); 164 | } 165 | } 166 | tree.getChildren().addAll(trees); 167 | return trees; 168 | } 169 | 170 | @Override 171 | public List filterList(List list, List subs) { 172 | //这里未进行过滤,因为会造成元数据的删除,除非对list进行深度克隆 173 | return list; 174 | } 175 | 176 | }); 177 | return root; 178 | } 179 | 180 | 181 | } 182 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/CacheConfigSingleton.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import javafx.collections.FXCollections; 6 | import javafx.collections.MapChangeListener; 7 | import javafx.collections.ObservableMap; 8 | import xyz.hashdog.rdm.common.pool.ThreadPool; 9 | import xyz.hashdog.rdm.common.util.DataUtil; 10 | import xyz.hashdog.rdm.ui.entity.ConnectionServerNode; 11 | 12 | import java.lang.reflect.Type; 13 | import java.util.List; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | import java.util.prefs.Preferences; 16 | 17 | /** 18 | * 首选项配置是肯定需要用到的 19 | * 恶汉单例缓存配置 20 | * 21 | * @author th 22 | * @version 1.0.0 23 | * @since 2023/7/20 16:46 24 | */ 25 | public class CacheConfigSingleton { 26 | 27 | 28 | protected final static Config CONFIG; 29 | private final static Preferences PREFERENCES = Preferences.userRoot().node(Applications.NODE_APP_NAME); 30 | 31 | private CacheConfigSingleton() { 32 | } 33 | 34 | static { 35 | CONFIG = new Config(); 36 | //看源码实际上用的ObservableMapWrapper,进行保证,看起来应该对map的增删应该是线程安全的,无所谓客户端能有多大并发量 37 | CONFIG.setConnectionNodeMap(FXCollections.observableMap(new ConcurrentHashMap<>())); 38 | CacheConfigSingleton.initData(); 39 | CacheConfigSingleton.addListener(); 40 | } 41 | 42 | /** 43 | * 初始化数据 44 | */ 45 | private static void initData() { 46 | Preferences node = PREFERENCES.node(Applications.NODE_APP_DATA); 47 | String connectionJsonStr = node.get(Applications.KEY_CONNECTIONS, null); 48 | if (DataUtil.isBlank(connectionJsonStr)) { 49 | return; 50 | } 51 | // 使用 Gson 将 JSON 字符串转换为 List 52 | Gson gson = new Gson(); 53 | Type type = new TypeToken>() { 54 | }.getType(); 55 | List list = gson.fromJson(connectionJsonStr, type); 56 | for (ConnectionServerNode connectionServerNode : list) { 57 | CONFIG.getConnectionNodeMap().put(connectionServerNode.getDataId(),connectionServerNode); 58 | } 59 | } 60 | 61 | /** 62 | * 监听数据的变动,进行异步保存 63 | */ 64 | private static void addListener() { 65 | ((ObservableMap) CONFIG.getConnectionNodeMap()).addListener((MapChangeListener) change -> { 66 | if (change.wasAdded() || change.wasRemoved()) { 67 | ThreadPool.getInstance().execute(()->{ 68 | Preferences node = PREFERENCES.node(Applications.NODE_APP_DATA); 69 | node.put(Applications.KEY_CONNECTIONS,new Gson().toJson(CONFIG.getConnectionNodeMap().values())); 70 | System.out.println("触发保存"); 71 | }); 72 | } 73 | }); 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/Config.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | import xyz.hashdog.rdm.ui.entity.ConnectionServerNode; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/7/20 16:46 11 | */ 12 | public class Config { 13 | 14 | private Map connectionNodeMap; 15 | 16 | public Map getConnectionNodeMap() { 17 | return connectionNodeMap; 18 | } 19 | 20 | public void setConnectionNodeMap(Map connectionNodeMap) { 21 | this.connectionNodeMap = connectionNodeMap; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/Constant.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/8/13 20:53 7 | */ 8 | public interface Constant { 9 | String CLOSE = "common.tab.close"; 10 | String CLOSE_OTHER = "common.tab.closeOther"; 11 | String CLOSE_LEFT ="common.tab.closeLeft"; 12 | String CLOSE_RIGHT ="common.tab.closeRight"; 13 | String CLOSE_ALL = "common.tab.closeAll"; 14 | String OK = "common.ok"; 15 | String CANCEL = "common.cancel"; 16 | String ALERT_MESSAGE_DEL = "alert.message.del"; 17 | String ALERT_MESSAGE_DELCONNECTION = "alert.message.delConnection"; 18 | String ALERT_MESSAGE_DELGROUP ="alert.message.delGroup" ; 19 | String ALERT_MESSAGE_DELFLUSH = "alert.message.delFlush"; 20 | String ALERT_MESSAGE_DELALL = "alert.message.delAll"; 21 | String TITLE_CONNECTION = "title.connection"; 22 | String TITLE_NEW_KEY ="title.newKey"; 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/RedisDataTypeEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | import xyz.hashdog.rdm.redis.Message; 4 | import xyz.hashdog.rdm.redis.client.RedisClient; 5 | import xyz.hashdog.rdm.ui.entity.PassParameter; 6 | import xyz.hashdog.rdm.ui.exceptions.GeneralException; 7 | import xyz.hashdog.rdm.ui.handler.NewKeyHandler; 8 | 9 | /** 10 | * @author th 11 | * @version 1.0.0 12 | * @since 2023/7/23 0:42 13 | */ 14 | public enum RedisDataTypeEnum { 15 | STRING("String","/fxml/StringTypeView.fxml", PassParameter.STRING,((redisClient, db, key, ttl) -> { 16 | checkDB(redisClient,db); 17 | redisClient.set(key, Applications.DEFUALT_VALUE); 18 | checkTTL(redisClient,ttl,key); 19 | return new Message(true); 20 | })), 21 | HASH("Hash","/fxml/HashTypeView.fxml", PassParameter.HASH,((redisClient, db, key, ttl) -> { 22 | checkDB(redisClient,db); 23 | redisClient.hsetnx(key, Applications.DEFUALT_VALUE,Applications.DEFUALT_VALUE); 24 | checkTTL(redisClient,ttl,key); 25 | return new Message(true); 26 | })), 27 | LIST("List","/fxml/ListTypeView.fxml", PassParameter.LIST,((redisClient, db, key, ttl) -> { 28 | checkDB(redisClient,db); 29 | redisClient.lpush(key, Applications.DEFUALT_VALUE); 30 | checkTTL(redisClient,ttl,key); 31 | return new Message(true); 32 | })), 33 | SET("Set","/fxml/SetTypeView.fxml", PassParameter.SET,((redisClient, db, key, ttl) -> { 34 | checkDB(redisClient,db); 35 | redisClient.sadd(key, Applications.DEFUALT_VALUE); 36 | checkTTL(redisClient,ttl,key); 37 | return new Message(true); 38 | })), 39 | ZSET("Zset","/fxml/ZsetTypeView.fxml", PassParameter.ZSET,((redisClient, db, key, ttl) -> { 40 | checkDB(redisClient,db); 41 | redisClient.zadd(key,0, Applications.DEFUALT_VALUE); 42 | checkTTL(redisClient,ttl,key); 43 | return new Message(true); 44 | })), 45 | ; 46 | 47 | /** 48 | * 检查设置有效期 49 | * @param redisClient 50 | * @param ttl 51 | * @param key 52 | */ 53 | private static void checkTTL(RedisClient redisClient, long ttl, String key) { 54 | if(ttl>0){ 55 | redisClient.expire(key,ttl); 56 | } 57 | } 58 | 59 | 60 | /** 61 | *检查设置db 62 | * @param redisClient 63 | * @param db 64 | */ 65 | private static void checkDB(RedisClient redisClient, int db) { 66 | if(redisClient.getDb()!=db){ 67 | redisClient.select(db); 68 | } 69 | } 70 | 71 | 72 | public String type; 73 | public String fxml; 74 | public int tabType; 75 | public NewKeyHandler newKeyHandler; 76 | RedisDataTypeEnum(String type,String fxml,int tabType,NewKeyHandler newKeyHandler) { 77 | this.type=type; 78 | this.fxml=fxml; 79 | this.tabType=tabType; 80 | this.newKeyHandler=newKeyHandler; 81 | } 82 | 83 | /** 84 | * 根据类型字符串获取 85 | * @param type 86 | * @return 87 | */ 88 | public static RedisDataTypeEnum getByType(String type) { 89 | for (RedisDataTypeEnum value : values()) { 90 | if(value.type.equalsIgnoreCase(type)){ 91 | return value; 92 | } 93 | } 94 | throw new GeneralException("This type is not supported "+type); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/common/ValueTypeEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.common; 2 | 3 | import xyz.hashdog.rdm.ui.handler.*; 4 | 5 | /** 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/7/26 21:22 9 | */ 10 | public enum ValueTypeEnum { 11 | TEXT("Text",new TextConvertHandler()), 12 | JSON("Text(Json)",new TextJsonConvertHandler()), 13 | ZIP("Text(Gzip)",new TextGzipConvertHandler()), 14 | HEX("Hex",new HexConvertHandler()), 15 | BINARY("Binary",new BinaryConvertHandler()), 16 | IMAGE("Image",new ImageConvertHandler()), 17 | IMAGE_BASE64("Image(Base64)",new ImageBase64ConvertHandler()), 18 | ; 19 | 20 | 21 | public String name; 22 | public ValueConvertHandler handler; 23 | 24 | ValueTypeEnum(String name,ValueConvertHandler handler) { 25 | this.name = name; 26 | this.handler=handler; 27 | } 28 | 29 | public static ValueTypeEnum getByName(String newValue) { 30 | for (ValueTypeEnum value : ValueTypeEnum.values()) { 31 | if(value.name.equals(newValue)){ 32 | return value; 33 | } 34 | } 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/AppendController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.fxml.FXML; 4 | import javafx.fxml.Initializable; 5 | import javafx.scene.control.Button; 6 | import javafx.scene.layout.BorderPane; 7 | import javafx.scene.layout.Pane; 8 | 9 | import java.net.URL; 10 | import java.util.ResourceBundle; 11 | 12 | /** 13 | * @author th 14 | * @version 1.0.0 15 | * @since 2023/8/12 22:10 16 | */ 17 | public class AppendController extends BaseWindowController implements Initializable { 18 | @FXML 19 | public BorderPane borderPane; 20 | @FXML 21 | public Button ok; 22 | 23 | @Override 24 | public void initialize(URL location, ResourceBundle resources) { 25 | 26 | } 27 | 28 | 29 | 30 | 31 | /** 32 | * 设置内容 33 | * @param t1 34 | */ 35 | public void setSubContent(Pane t1) { 36 | borderPane.setCenter(t1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.fxml.FXML; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.control.TextField; 6 | import javafx.scene.input.KeyEvent; 7 | import xyz.hashdog.rdm.common.pool.ThreadPool; 8 | import xyz.hashdog.rdm.common.tuple.Tuple2; 9 | import xyz.hashdog.rdm.ui.Main; 10 | import xyz.hashdog.rdm.ui.util.GuiUtil; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * 16 | * 用于父子关系 17 | * 封装通用方法 18 | * @author th 19 | * @version 1.0.0 20 | * @since 2023/7/22 10:43 21 | */ 22 | public abstract class BaseController { 23 | /** 24 | * 父控制器 25 | */ 26 | public T parentController; 27 | 28 | /** 29 | * port只能为正整数 30 | * @param keyEvent 31 | */ 32 | @FXML 33 | public void filterIntegerInput(KeyEvent keyEvent) { 34 | // 获取用户输入的字符 35 | String inputChar = keyEvent.getCharacter(); 36 | // 如果输入字符不是整数,则阻止其显示在TextField中 37 | if (!inputChar.matches("\\d")) { 38 | keyEvent.consume(); 39 | } 40 | } 41 | 42 | /** 43 | * 线程池异步执行 44 | * @param runnable 45 | */ 46 | protected void asynexec(Runnable runnable) { 47 | ThreadPool.getInstance().execute(runnable); 48 | } 49 | 50 | /** 51 | * 只让输入整数 52 | */ 53 | protected void filterIntegerInputListener(boolean flg,TextField... port) { 54 | GuiUtil.filterIntegerInput(flg,port); 55 | } 56 | 57 | public Tuple2 loadFXML(String fxml) { 58 | try { 59 | FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxml), Main.RESOURCE_BUNDLE); 60 | T1 t1 = fxmlLoader.load(); 61 | T2 t2 = fxmlLoader.getController(); 62 | Tuple2 tuple2 =new Tuple2<>(t1,t2); 63 | return tuple2; 64 | } catch (IOException e) { 65 | throw new RuntimeException(e); 66 | } 67 | 68 | } 69 | 70 | 71 | 72 | public void setParentController(T parentController) { 73 | this.parentController = parentController; 74 | } 75 | 76 | 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/BaseKeyController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.beans.property.ObjectProperty; 4 | import javafx.beans.property.SimpleObjectProperty; 5 | import javafx.fxml.FXML; 6 | import javafx.scene.Node; 7 | import xyz.hashdog.rdm.redis.RedisContext; 8 | import xyz.hashdog.rdm.redis.client.RedisClient; 9 | import xyz.hashdog.rdm.ui.entity.PassParameter; 10 | 11 | import java.util.function.Function; 12 | 13 | /** 14 | * @author th 15 | * @version 1.0.0 16 | * @since 2023/7/23 22:30 17 | */ 18 | public class BaseKeyController extends BaseController{ 19 | /** 20 | * 当前控制层操作的tab所用的redis客户端连接 21 | * 此客户端可能是单例,也就是共享的 22 | */ 23 | protected RedisClient redisClient; 24 | /** 25 | * redis上下文,由父类传递绑定 26 | */ 27 | protected RedisContext redisContext; 28 | /** 29 | * 当前db 30 | */ 31 | protected int currentDb; 32 | 33 | /** 34 | * 用于父向子传递db和key 35 | */ 36 | protected ObjectProperty parameter = new SimpleObjectProperty<>(); 37 | 38 | /** 39 | * 根上有绑定userdata,setParameter进行绑定操作 40 | */ 41 | @FXML 42 | public Node root; 43 | 44 | /** 45 | * 执行方法 46 | * 目前用于统一处理jedis执行命令之后的close操作 47 | * 48 | * @param execCommand 需要执行的具体逻辑 49 | * @param 执行jedis命令之后的返回值 50 | * @return 51 | */ 52 | public R exeRedis( Function execCommand) { 53 | if(redisClient.getDb()!=this.currentDb){ 54 | redisClient.select(currentDb); 55 | } 56 | return execCommand.apply(redisClient); 57 | } 58 | 59 | public PassParameter getParameter() { 60 | return parameter.get(); 61 | } 62 | 63 | public ObjectProperty parameterProperty() { 64 | return parameter; 65 | } 66 | 67 | public void setParameter(PassParameter parameter) { 68 | this.redisClient=parameter.getRedisClient(); 69 | this.redisContext=parameter.getRedisContext(); 70 | //数据也需要绑定到根布局上 71 | root.setUserData(this); 72 | this.currentDb=parameter.getDb(); 73 | this.parameter.set(parameter); 74 | } 75 | 76 | public RedisClient getRedisClient() { 77 | return redisClient; 78 | } 79 | 80 | public void setRedisClient(RedisClient redisClient) { 81 | this.redisClient = redisClient; 82 | } 83 | 84 | 85 | public RedisContext getRedisContext() { 86 | return redisContext; 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/BaseWindowController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.fxml.FXML; 5 | import javafx.scene.Scene; 6 | import javafx.scene.layout.AnchorPane; 7 | import javafx.stage.Modality; 8 | import javafx.stage.Stage; 9 | import javafx.stage.StageStyle; 10 | import javafx.stage.Window; 11 | import xyz.hashdog.rdm.common.tuple.Tuple2; 12 | import xyz.hashdog.rdm.ui.util.GuiUtil; 13 | 14 | import java.io.IOException; 15 | 16 | /** 17 | * 用于新开窗口的父子关系 18 | * 只用于对非redis操作的窗口 19 | * 如果需要对redis操作,需要用BaseKeyController 20 | * @author th 21 | * @version 1.0.0 22 | * @since 2023/7/20 17:35 23 | */ 24 | public abstract class BaseWindowController extends BaseController { 25 | 26 | /** 27 | * 模式,默认是NONE 28 | */ 29 | protected int model; 30 | protected static final int NONE = 1; 31 | protected static final int ADD = 2; 32 | protected static final int UPDATE = 3; 33 | protected static final int RENAME = 4; 34 | /** 35 | * 当前Stage 36 | */ 37 | public Stage currentStage; 38 | 39 | 40 | @FXML 41 | public void cancel(ActionEvent actionEvent) { 42 | currentStage.close(); 43 | } 44 | 45 | /** 46 | * 子窗口模态框 47 | * 每次都是打开最新的 48 | * 49 | * @param title 窗口标题 50 | * @param fxml fxml路径 51 | * @param parent 父窗口 52 | * @param model 模式 53 | * @throws IOException 54 | */ 55 | protected T loadSubWindow(String title, String fxml, Window parent, int model) throws IOException { 56 | Stage newConnctionWindowStage = new Stage(); 57 | newConnctionWindowStage.initModality(Modality.WINDOW_MODAL); 58 | //去掉最小化和最大化 59 | newConnctionWindowStage.initStyle(StageStyle.DECORATED); 60 | //禁用掉最大最小化 61 | newConnctionWindowStage.setMaximized(false); 62 | newConnctionWindowStage.setTitle(title); 63 | Tuple2 tuple2 = loadFXML(fxml); 64 | AnchorPane borderPane = tuple2.getT1(); 65 | T controller = tuple2.getT2(); 66 | controller.setParentController(this); 67 | controller.setCurrentStage(newConnctionWindowStage); 68 | Scene scene = new Scene(borderPane); 69 | newConnctionWindowStage.initOwner(parent); 70 | newConnctionWindowStage.setScene(scene); 71 | newConnctionWindowStage.show(); 72 | controller.setModel(model); 73 | return controller; 74 | } 75 | 76 | 77 | 78 | /** 79 | * 子窗口模态框 80 | * 每次都是打开最新的 81 | * 82 | * @param title 窗口标题 83 | * @param fxml fxml路径 84 | * @param parent 父窗口 85 | * @throws IOException 86 | */ 87 | public T loadSubWindow(String title, String fxml, Window parent) throws IOException { 88 | return loadSubWindow(title, fxml, parent,NONE); 89 | } 90 | 91 | 92 | 93 | 94 | public void setModel(int model) { 95 | this.model = model; 96 | } 97 | 98 | public void setCurrentStage(Stage currentStage) { 99 | this.currentStage = currentStage; 100 | this.currentStage.getIcons().add(GuiUtil.ICON_REDIS); 101 | } 102 | 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/ConsoleController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.application.Platform; 4 | import javafx.event.ActionEvent; 5 | import javafx.fxml.FXML; 6 | import javafx.fxml.Initializable; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.control.TextArea; 9 | import javafx.scene.control.TextField; 10 | import xyz.hashdog.rdm.common.pool.ThreadPool; 11 | 12 | import java.net.URL; 13 | import java.util.List; 14 | import java.util.ResourceBundle; 15 | 16 | public class ConsoleController extends BaseKeyController implements Initializable { 17 | 18 | @FXML 19 | public TextArea textArea; 20 | @FXML 21 | public TextField textField; 22 | @FXML 23 | public Label label; 24 | 25 | 26 | @FXML 27 | public void addToTextAreaAction(ActionEvent actionEvent) { 28 | String inputText = textField.getText(); 29 | if("clear".equalsIgnoreCase(inputText)){ 30 | textArea.clear(); 31 | textField.clear(); 32 | return; 33 | } 34 | if (!inputText.isEmpty()) { 35 | textArea.appendText( "\n"+"> "+inputText ); 36 | textField.clear(); 37 | ThreadPool.getInstance().execute(()->{ 38 | List strings = redisClient.getRedisConsole().sendCommand(inputText); 39 | Platform.runLater(()->{ 40 | if(inputText.trim().startsWith("select")&&!strings.isEmpty()&& "ok".equalsIgnoreCase(strings.get(0))){ 41 | this.currentDb=Integer.parseInt(inputText.replace("select","").trim()); 42 | label.setText(redisContext.getRedisConfig().getName()+":"+this.currentDb+">"); 43 | } 44 | for (String string : strings) { 45 | textArea.appendText( "\n"+""+string ); 46 | } 47 | }); 48 | }); 49 | } 50 | } 51 | 52 | @Override 53 | public void initialize(URL location, ResourceBundle resources) { 54 | textArea.setPrefRowCount(10); 55 | 56 | // 监听TextArea的textProperty,每当有新内容添加时滚动到底部 57 | textArea.textProperty().addListener((observable, oldValue, newValue) -> { 58 | // 将光标定位到文本末尾,这会自动将滚动条滚动到最下面 59 | // textArea.positionCaret(textArea.getText().length()); 60 | // textArea.setScrollTop(-1d); 61 | }); 62 | super.parameter.addListener((observable, oldValue, newValue) -> { 63 | label.setText(redisContext.getRedisConfig().getName()+":"+this.currentDb+">"); 64 | textArea.appendText( "\n"+redisContext.getRedisConfig().getName()+" 连接成功" ); 65 | if(currentDb!=0){ 66 | ThreadPool.getInstance().execute(()->this.redisClient.select(currentDb)); 67 | } 68 | }); 69 | } 70 | } 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/KeyTabController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.application.Platform; 4 | import javafx.event.ActionEvent; 5 | import javafx.fxml.FXML; 6 | import javafx.fxml.Initializable; 7 | import javafx.scene.control.Alert; 8 | import javafx.scene.control.Label; 9 | import javafx.scene.control.TextField; 10 | import javafx.scene.layout.AnchorPane; 11 | import javafx.scene.layout.BorderPane; 12 | import xyz.hashdog.rdm.common.pool.ThreadPool; 13 | import xyz.hashdog.rdm.common.tuple.Tuple2; 14 | import xyz.hashdog.rdm.ui.Main; 15 | import xyz.hashdog.rdm.ui.common.Constant; 16 | import xyz.hashdog.rdm.ui.common.RedisDataTypeEnum; 17 | import xyz.hashdog.rdm.ui.entity.PassParameter; 18 | import xyz.hashdog.rdm.ui.util.GuiUtil; 19 | 20 | import java.net.URL; 21 | import java.util.Arrays; 22 | import java.util.ResourceBundle; 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.Future; 25 | 26 | public class KeyTabController extends BaseKeyController implements Initializable { 27 | 28 | 29 | @FXML 30 | public TextField key; 31 | @FXML 32 | public TextField ttl; 33 | @FXML 34 | public Label keyType; 35 | @FXML 36 | public BorderPane borderPane; 37 | 38 | 39 | 40 | private long currentTtl; 41 | 42 | 43 | /** 44 | * 子类型控制层 45 | */ 46 | private BaseKeyController subTypeController; 47 | 48 | 49 | @Override 50 | public void initialize(URL location, ResourceBundle resources) { 51 | initListener(); 52 | 53 | } 54 | 55 | 56 | /** 57 | * 初始化监听 58 | */ 59 | private void initListener() { 60 | userDataPropertyListener(); 61 | filterIntegerInputListener(true,this.ttl); 62 | } 63 | 64 | 65 | 66 | /** 67 | * 父层传送的数据监听 68 | * 监听到key的传递 69 | */ 70 | private void userDataPropertyListener() { 71 | super.parameter.addListener((observable, oldValue, newValue) -> { 72 | initInfo(); 73 | }); 74 | } 75 | 76 | /** 77 | * 重新加载 78 | * todo 需要 先把其他key类型的命令写完再做 79 | */ 80 | private void reloadInfo() { 81 | ThreadPool.getInstance().execute(() -> { 82 | String text=null; 83 | loadData(); 84 | // todo 现在只刷新了基本信息,具体类型的信息刷新还没做,需要调用具体类型的子控制层 85 | //subTypeController.刷新 86 | 87 | // String fileTypeByStream = FileUtil.getFileTypeByStream(currentValue); 88 | // //不是可识别的文件类型,都默认采用16进制展示 89 | // if(fileTypeByStream==null){ 90 | // boolean isUtf8 = EncodeUtil.isUTF8(bytes); 91 | // //是utf8编码或则非特殊字符,直接转utf8字符串 92 | // if(isUtf8||!EncodeUtil.containsSpecialCharacters(bytes)){ 93 | // text= new String(bytes); 94 | // } 95 | // } 96 | // if(text==null){ 97 | // text = FileUtil.byte2HexString(bytes); 98 | // type=ValueTypeEnum.HEX; 99 | // } else { 100 | // type = ValueTypeEnum.TEXT; 101 | // } 102 | // String finalText = text; 103 | // Platform.runLater(() -> { 104 | // this.ttl.setText(String.valueOf(ttl)); 105 | // this.value.setText(finalText); 106 | // this.size.setText(String.format(SIZE, bytes.length)); 107 | // this.typeChoiceBox.setValue(type.name); 108 | // }); 109 | }); 110 | 111 | } 112 | 113 | /** 114 | * 加载数据 115 | */ 116 | private void loadData() { 117 | 118 | long ttl = this.exeRedis(j -> j.ttl(this.getParameter().getKey())); 119 | this.currentTtl=ttl; 120 | Platform.runLater(() -> { 121 | this.key.setText(this.getParameter().getKey()); 122 | this.ttl.setText(String.valueOf(currentTtl)); 123 | this.keyType.setText(this.getParameter().getKeyType()); 124 | }); 125 | 126 | } 127 | 128 | /** 129 | * 初始化数据展示 130 | */ 131 | private void initInfo() { 132 | Future submit = ThreadPool.getInstance().submit(() -> { 133 | //加载通用数据 134 | loadData(); 135 | }, true); 136 | 137 | 138 | try { 139 | if(submit.get()){ 140 | RedisDataTypeEnum te = RedisDataTypeEnum.getByType(this.parameter.get().getKeyType()); 141 | Tuple2 tuple2 = loadFXML(te.fxml); 142 | AnchorPane anchorPane = tuple2.getT1(); 143 | this.subTypeController = tuple2.getT2(); 144 | this.subTypeController.setParentController(this); 145 | PassParameter passParameter = new PassParameter(te.tabType); 146 | passParameter.setDb(this.currentDb); 147 | passParameter.setKey(this.parameter.get().getKey()); 148 | passParameter.setKeyType(this.parameter.get().getKeyType()); 149 | passParameter.setRedisClient(redisClient); 150 | passParameter.setRedisContext(redisContext); 151 | this.subTypeController.setParameter(passParameter); 152 | borderPane.setCenter(anchorPane); 153 | } 154 | } catch (InterruptedException e) { 155 | throw new RuntimeException(e); 156 | } catch (ExecutionException e) { 157 | throw new RuntimeException(e); 158 | } 159 | 160 | 161 | } 162 | 163 | /** 164 | * key重命名 165 | * 166 | * @param actionEvent 167 | */ 168 | @FXML 169 | public void rename(ActionEvent actionEvent) { 170 | if (GuiUtil.requiredTextField(this.key)) { 171 | return; 172 | } 173 | asynexec(() -> { 174 | this.exeRedis(j -> j.rename(this.getParameter().getKey(), this.key.getText())); 175 | this.getParameter().setKey(this.key.getText()); 176 | Platform.runLater(() -> { 177 | GuiUtil.alert(Alert.AlertType.INFORMATION, "重命名成功"); 178 | }); 179 | }); 180 | 181 | } 182 | 183 | 184 | /** 185 | * 设置有效期 186 | * 187 | * @param actionEvent 188 | */ 189 | @FXML 190 | public void editTTL(ActionEvent actionEvent) { 191 | if (GuiUtil.requiredTextField(this.ttl)) { 192 | return; 193 | } 194 | int ttl = Integer.parseInt(this.ttl.getText()); 195 | if (ttl <= -1) { 196 | if (GuiUtil.alert(Alert.AlertType.CONFIRMATION, "设为负数将永久保存?")) { 197 | asynexec(()->{ 198 | this.exeRedis(j -> j.persist(this.getParameter().getKey())); 199 | Platform.runLater(()->{ 200 | GuiUtil.alert(Alert.AlertType.INFORMATION,"设置成功"); 201 | }); 202 | }); 203 | } 204 | return; 205 | } 206 | 207 | asynexec(()->{ 208 | this.exeRedis(j -> j.expire(this.getParameter().getKey(),ttl)); 209 | Platform.runLater(()->{ 210 | GuiUtil.alert(Alert.AlertType.INFORMATION,"设置成功"); 211 | }); 212 | }); 213 | } 214 | 215 | /** 216 | * 删除键 217 | * 切需要关闭当前tab 218 | * @param actionEvent 219 | */ 220 | @FXML 221 | public void delete(ActionEvent actionEvent) { 222 | if (GuiUtil.alert(Alert.AlertType.CONFIRMATION, Main.RESOURCE_BUNDLE.getString(Constant.ALERT_MESSAGE_DEL))) { 223 | exeRedis(j -> j.del(parameter.get().getKey())); 224 | if(super.parentController.delKey(parameter)){ 225 | super.parentController.removeTabByKeys(Arrays.asList(parameter.get().getKey())); 226 | } 227 | } 228 | } 229 | 230 | 231 | 232 | /** 233 | * 刷新数据 234 | * @param actionEvent 235 | */ 236 | @FXML 237 | public void refresh(ActionEvent actionEvent) { 238 | reloadInfo(); 239 | } 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | } 250 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/MainController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.fxml.FXML; 5 | import javafx.scene.Scene; 6 | import javafx.scene.control.ContextMenu; 7 | import javafx.scene.control.Tab; 8 | import javafx.scene.control.TabPane; 9 | import javafx.scene.layout.AnchorPane; 10 | import javafx.stage.Modality; 11 | import javafx.stage.Stage; 12 | import xyz.hashdog.rdm.common.pool.ThreadPool; 13 | import xyz.hashdog.rdm.common.tuple.Tuple2; 14 | import xyz.hashdog.rdm.redis.RedisContext; 15 | import xyz.hashdog.rdm.ui.Main; 16 | import xyz.hashdog.rdm.ui.common.Constant; 17 | import xyz.hashdog.rdm.ui.entity.PassParameter; 18 | import xyz.hashdog.rdm.ui.util.GuiUtil; 19 | 20 | import java.io.IOException; 21 | 22 | /** 23 | * 主控制层 24 | */ 25 | public class MainController extends BaseWindowController { 26 | @FXML 27 | public AnchorPane root; 28 | /** 29 | * tab页容器 30 | */ 31 | @FXML 32 | public TabPane serverTabPane; 33 | /** 34 | * 服务连接的Stage 35 | */ 36 | private Stage serverConnectionsWindowStage; 37 | 38 | @FXML 39 | public void initialize() { 40 | } 41 | 42 | @FXML 43 | public void openServerLinkWindo(ActionEvent actionEvent) { 44 | if(this.serverConnectionsWindowStage!=null){ 45 | if(this.serverConnectionsWindowStage.isShowing()){ 46 | //已经设置为WINDOW_MODAL模式,子窗口未关闭是不能操作的,所以子窗口显示在最上方的操作已经没有意义 47 | // serverConnectionsWindowStage.setAlwaysOnTop(true); 48 | // serverConnectionsWindowStage.setAlwaysOnTop(false); 49 | 50 | }else { 51 | serverConnectionsWindowStage.show(); 52 | } 53 | }else{ 54 | this.serverConnectionsWindowStage=new Stage(); 55 | serverConnectionsWindowStage.initModality(Modality.WINDOW_MODAL); 56 | this.serverConnectionsWindowStage.setTitle(Main.RESOURCE_BUNDLE.getString(Constant.TITLE_CONNECTION)); 57 | 58 | Tuple2 tuple2 = loadFXML("/fxml/ServerConnectionsView.fxml"); 59 | AnchorPane borderPane =tuple2.getT1(); 60 | ServerConnectionsController controller = tuple2.getT2(); 61 | Scene scene = new Scene(borderPane); 62 | this.serverConnectionsWindowStage.initOwner(root.getScene().getWindow()); 63 | this.serverConnectionsWindowStage.setScene(scene); 64 | this.serverConnectionsWindowStage.show(); 65 | controller.setParentController(this); 66 | controller.setCurrentStage(serverConnectionsWindowStage); 67 | 68 | 69 | } 70 | 71 | } 72 | 73 | /** 74 | * 创建新的tab页 75 | * 76 | * @param redisContext redis上下文 77 | * @param name 服务名称 78 | */ 79 | public void newRedisTab(RedisContext redisContext, String name) throws IOException { 80 | Tuple2 tuple2 = loadFXML("/fxml/ServerTabView.fxml"); 81 | AnchorPane borderPane = tuple2.getT1(); 82 | ServerTabController controller = tuple2.getT2(); 83 | controller.setParentController(this); 84 | PassParameter passParameter = new PassParameter(PassParameter.REDIS); 85 | passParameter.setRedisContext(redisContext); 86 | passParameter.setRedisClient(redisContext.newRedisClient()); 87 | controller.setParameter(passParameter); 88 | Tab tab = new Tab(name); 89 | tab.setContent(borderPane); 90 | this.serverTabPane.getTabs().add(tab); 91 | this.serverTabPane.getSelectionModel().select(tab); 92 | tab.setGraphic(GuiUtil.creatConnctionImageView()); 93 | 94 | if(passParameter.getTabType()== PassParameter.REDIS){ 95 | // 监听Tab被关闭事件,但是remove是无法监听的 96 | tab.setOnClosed(event2 -> { 97 | ThreadPool.getInstance().execute(()->controller.getRedisContext().close()); 98 | }); 99 | } 100 | ContextMenu cm= GuiUtil.newTabContextMenu(tab); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/NewConnectionController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.fxml.FXML; 5 | import javafx.fxml.Initializable; 6 | import javafx.scene.control.Alert; 7 | import javafx.scene.control.Button; 8 | import javafx.scene.control.PasswordField; 9 | import javafx.scene.control.TextField; 10 | import javafx.scene.layout.AnchorPane; 11 | import xyz.hashdog.rdm.redis.Message; 12 | import xyz.hashdog.rdm.redis.RedisConfig; 13 | import xyz.hashdog.rdm.redis.RedisContext; 14 | import xyz.hashdog.rdm.redis.RedisFactorySingleton; 15 | import xyz.hashdog.rdm.ui.common.Applications; 16 | import xyz.hashdog.rdm.ui.entity.ConnectionServerNode; 17 | import xyz.hashdog.rdm.ui.util.GuiUtil; 18 | 19 | import java.net.URL; 20 | import java.util.ResourceBundle; 21 | 22 | /** 23 | * 新建连接的控制层 24 | * @author th 25 | * @version 1.0.0 26 | * @since 2023/7/19 21:45 27 | */ 28 | public class NewConnectionController extends BaseWindowController implements Initializable { 29 | /** 30 | * 当前根节点 31 | */ 32 | public AnchorPane root; 33 | 34 | /** 35 | * 测试连接按钮 36 | */ 37 | @FXML 38 | public Button testConnectButton; 39 | /** 40 | * 连接名称 41 | */ 42 | @FXML 43 | public TextField name; 44 | /** 45 | * 地址/域 46 | */ 47 | @FXML 48 | public TextField host; 49 | /** 50 | * 端口 51 | */ 52 | @FXML 53 | public TextField port; 54 | /** 55 | * 密码 56 | */ 57 | @FXML 58 | public PasswordField auth; 59 | /** 60 | * 连接id,保存的时候会有 61 | */ 62 | @FXML 63 | public TextField dataId; 64 | 65 | 66 | 67 | 68 | @FXML 69 | @Override 70 | public void initialize(URL location, ResourceBundle resources) { 71 | initListener(); 72 | 73 | } 74 | 75 | /** 76 | * 初始化监听 77 | */ 78 | private void initListener() { 79 | filterIntegerInputListener(false,this.port); 80 | } 81 | 82 | 83 | 84 | @FXML 85 | public void testConnect(ActionEvent actionEvent) { 86 | if(GuiUtil.requiredTextField(host, port)){ 87 | return; 88 | } 89 | String hostStr = host.getText(); 90 | String portStr = port.getText(); 91 | String authStr = auth.getText(); 92 | RedisConfig redisConfig = new RedisConfig(); 93 | redisConfig.setHost(hostStr); 94 | redisConfig.setPort(Integer.parseInt(portStr)); 95 | redisConfig.setAuth(authStr); 96 | RedisContext redisContext = RedisFactorySingleton.getInstance().createRedisContext(redisConfig); 97 | Message message = redisContext.newRedisClient().testConnect(); 98 | if (message.isSuccess()) { 99 | GuiUtil.alert(Alert.AlertType.INFORMATION, "连接成功"); 100 | } else { 101 | GuiUtil.alert(Alert.AlertType.WARNING, message.getMessage()); 102 | } 103 | } 104 | 105 | /** 106 | * 确定之后将新增的节点持久化 107 | * 再对父窗口视图进行更新 108 | * @param actionEvent 109 | */ 110 | @FXML 111 | public void ok(ActionEvent actionEvent) { 112 | if(GuiUtil.requiredTextField(name,host, port)){ 113 | return; 114 | } 115 | ConnectionServerNode connectionServerNode =new ConnectionServerNode(ConnectionServerNode.SERVER); 116 | connectionServerNode.setName(name.getText()); 117 | connectionServerNode.setHost(host.getText()); 118 | connectionServerNode.setPort(Integer.parseInt(port.getText())); 119 | connectionServerNode.setAuth(auth.getText()); 120 | Message message=null; 121 | switch (this.model){ 122 | case ADD: 123 | //父窗口树节点新增,切选中新增节点 124 | connectionServerNode.setParentDataId(super.parentController.getSelectedDataId()); 125 | //更新或修改保存 126 | message=Applications.addOrUpdateConnectionOrGroup(connectionServerNode); 127 | //父窗口树节点新增,切选中新增节点 128 | parentController.AddConnectionOrGourpNodeAndSelect(connectionServerNode); 129 | break; 130 | case UPDATE: 131 | connectionServerNode.setDataId(dataId.getText()); 132 | //更新或修改保存 133 | message=Applications.addOrUpdateConnectionOrGroup(connectionServerNode); 134 | //更新 135 | parentController.updateNodeInfo(connectionServerNode); 136 | break; 137 | default: 138 | break; 139 | 140 | } 141 | if(message.isSuccess()){ 142 | currentStage.close(); 143 | } 144 | 145 | } 146 | 147 | 148 | 149 | 150 | /** 151 | * 填充编辑数据 152 | * @param selectedNode 153 | */ 154 | public void editInfo(ConnectionServerNode selectedNode) { 155 | name.setText(selectedNode.getName()); 156 | host.setText(selectedNode.getHost()); 157 | port.setText(String.valueOf(selectedNode.getPort())); 158 | auth.setText(selectedNode.getAuth()); 159 | dataId.setText(selectedNode.getDataId()); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/NewGroupController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.fxml.FXML; 5 | import javafx.scene.control.TextField; 6 | import javafx.scene.layout.AnchorPane; 7 | import xyz.hashdog.rdm.redis.Message; 8 | import xyz.hashdog.rdm.ui.common.Applications; 9 | import xyz.hashdog.rdm.ui.entity.ConnectionServerNode; 10 | import xyz.hashdog.rdm.ui.util.GuiUtil; 11 | 12 | /** 13 | * 新建分组/分组和连接重命名通用控制层 14 | * @author th 15 | * @version 1.0.0 16 | * @since 2023/7/20 23:36 17 | */ 18 | public class NewGroupController extends BaseWindowController { 19 | /** 20 | * 根 21 | */ 22 | @FXML 23 | public AnchorPane root; 24 | /** 25 | * 名称 26 | */ 27 | @FXML 28 | public TextField name; 29 | /** 30 | * id 31 | */ 32 | @FXML 33 | public TextField dataId; 34 | 35 | 36 | 37 | /** 38 | * 新增/修改的确定 39 | * @param actionEvent 40 | */ 41 | @FXML 42 | public void ok(ActionEvent actionEvent) { 43 | if(GuiUtil.requiredTextField(name)){ 44 | return; 45 | } 46 | ConnectionServerNode groupNode =new ConnectionServerNode(ConnectionServerNode.GROUP); 47 | groupNode.setName(name.getText()); 48 | Message message=null; 49 | switch (this.model){ 50 | case ADD: 51 | //父窗口树节点新增,切选中新增节点 52 | groupNode.setParentDataId(super.parentController.getSelectedDataId()); 53 | message= Applications.addOrUpdateConnectionOrGroup(groupNode); 54 | parentController.AddConnectionOrGourpNodeAndSelect(groupNode); 55 | break; 56 | case UPDATE: 57 | groupNode.setDataId(dataId.getText()); 58 | message= Applications.addOrUpdateConnectionOrGroup(groupNode); 59 | //分组修改只会修改名称,直接更新节点名称就行 60 | parentController.updateNodeName(groupNode.getName()); 61 | break; 62 | case RENAME: 63 | groupNode.setDataId(dataId.getText()); 64 | message= Applications.renameConnectionOrGroup(groupNode); 65 | //分组修改只会修改名称,直接更新节点名称就行 66 | parentController.updateNodeName(groupNode.getName()); 67 | break; 68 | default: 69 | break; 70 | 71 | } 72 | if(message.isSuccess()){ 73 | currentStage.close(); 74 | } 75 | 76 | } 77 | 78 | /** 79 | * 填充编辑数据 80 | * @param selectedNode 81 | */ 82 | public void editInfo(ConnectionServerNode selectedNode) { 83 | name.setText(selectedNode.getName()); 84 | dataId.setText(selectedNode.getDataId()); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/NewKeyController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.fxml.FXML; 5 | import javafx.fxml.Initializable; 6 | import javafx.scene.control.TextField; 7 | import javafx.stage.Stage; 8 | import xyz.hashdog.rdm.redis.Message; 9 | import xyz.hashdog.rdm.ui.common.RedisDataTypeEnum; 10 | import xyz.hashdog.rdm.ui.handler.NewKeyHandler; 11 | import xyz.hashdog.rdm.ui.util.GuiUtil; 12 | 13 | import java.net.URL; 14 | import java.util.ResourceBundle; 15 | 16 | /** 17 | * @author th 18 | * @version 1.0.0 19 | * @since 2023/8/11 22:56 20 | */ 21 | public class NewKeyController extends BaseKeyController implements Initializable { 22 | 23 | /** 24 | * 当前Stage 25 | */ 26 | public Stage currentStage; 27 | 28 | @FXML 29 | public TextField key; 30 | 31 | @FXML 32 | public TextField ttl; 33 | 34 | @Override 35 | public void initialize(URL location, ResourceBundle resources) { 36 | 37 | } 38 | 39 | @FXML 40 | public void ok(ActionEvent actionEvent) { 41 | if(GuiUtil.requiredTextField(key, ttl)){ 42 | return; 43 | } 44 | String keyType = this.parameter.get().getKeyType(); 45 | RedisDataTypeEnum byType = RedisDataTypeEnum.getByType(keyType); 46 | NewKeyHandler newKeyHandler = byType.newKeyHandler; 47 | Message message = newKeyHandler.newKey(this.redisClient, this.currentDb, key.getText(), Long.parseLong(ttl.getText())); 48 | if(message.isSuccess()){ 49 | //将新增的key,添加到参数 50 | this.parameter.get().setKey(key.getText()); 51 | //调父层,增加列表 52 | this.parentController.addKeyAndSelect(this.parameter); 53 | cancel(null); 54 | } 55 | 56 | 57 | } 58 | 59 | 60 | @FXML 61 | public void cancel(ActionEvent actionEvent) { 62 | currentStage.close(); 63 | } 64 | 65 | 66 | 67 | public void setCurrentStage(Stage currentStage) { 68 | this.currentStage = currentStage; 69 | this.currentStage.getIcons().add(GuiUtil.ICON_REDIS); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/controller/StringTypeController.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.controller; 2 | 3 | import javafx.application.Platform; 4 | import javafx.event.ActionEvent; 5 | import javafx.fxml.FXML; 6 | import javafx.fxml.Initializable; 7 | import javafx.scene.control.Alert; 8 | import javafx.scene.layout.AnchorPane; 9 | import javafx.scene.layout.BorderPane; 10 | import xyz.hashdog.rdm.common.pool.ThreadPool; 11 | import xyz.hashdog.rdm.common.tuple.Tuple2; 12 | import xyz.hashdog.rdm.ui.util.GuiUtil; 13 | 14 | import java.net.URL; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.ResourceBundle; 17 | 18 | /** 19 | * @author th 20 | * @version 1.0.0 21 | * @since 2023/7/31 12:08 22 | */ 23 | public class StringTypeController extends BaseKeyController implements Initializable { 24 | 25 | 26 | public BorderPane borderPane; 27 | private ByteArrayController byteArrayController; 28 | /** 29 | * 当前value的二进制 30 | */ 31 | private byte[] currentValue; 32 | 33 | @Override 34 | public void initialize(URL location, ResourceBundle resources) { 35 | initListener(); 36 | } 37 | 38 | /** 39 | * 初始化监听 40 | */ 41 | private void initListener() { 42 | userDataPropertyListener(); 43 | } 44 | 45 | /** 46 | * 父层传送的数据监听 47 | * 监听到key的传递 48 | */ 49 | private void userDataPropertyListener() { 50 | super.parameter.addListener((observable, oldValue, newValue) -> { 51 | initInfo(); 52 | }); 53 | } 54 | 55 | private void initInfo() { 56 | ThreadPool.getInstance().execute(() -> { 57 | byte[] bytes = this.exeRedis(j -> j.get(this.getParameter().getKey().getBytes(StandardCharsets.UTF_8))); 58 | this.currentValue = bytes; 59 | Platform.runLater(()->{ 60 | Tuple2 tuple2 = loadFXML("/fxml/ByteArrayView.fxml"); 61 | AnchorPane anchorPane = tuple2.getT1(); 62 | this.byteArrayController = tuple2.getT2(); 63 | this.byteArrayController.setParentController(this); 64 | this.byteArrayController.setByteArray(this.currentValue); 65 | borderPane.setCenter(anchorPane); 66 | }); 67 | }); 68 | 69 | 70 | } 71 | 72 | 73 | /** 74 | * 保存值 75 | * @param actionEvent 76 | */ 77 | @FXML 78 | public void save(ActionEvent actionEvent) { 79 | byte[] byteArray = byteArrayController.getByteArray(); 80 | asynexec(() -> { 81 | exeRedis(j -> j.set(this.getParameter().getKey().getBytes(), byteArray)); 82 | Platform.runLater(() -> { 83 | byteArrayController.setByteArray(byteArray); 84 | GuiUtil.alert(Alert.AlertType.INFORMATION, "保存成功"); 85 | }); 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/ConnectionServerNode.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | import xyz.hashdog.rdm.ui.common.Applications; 4 | 5 | /** 6 | * 连接实体类,分组和连接共用1个实体 7 | * type,dataId,parentDataId,timestampSort这几个字段在编辑是不可修改的 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/7/20 16:21 11 | */ 12 | public class ConnectionServerNode { 13 | public static final int SERVER = 2; 14 | public static final int GROUP = 1; 15 | /** 16 | * 类型,分组为1,连接为2 17 | */ 18 | private int type; 19 | /** 20 | * 定位id 21 | */ 22 | private String dataId; 23 | /** 24 | * 父节点id 25 | */ 26 | private String parentDataId; 27 | /** 28 | * 名称 29 | */ 30 | private String name; 31 | /** 32 | * 地址 33 | */ 34 | private String host; 35 | /** 36 | * 端口 37 | */ 38 | private int port; 39 | /** 40 | * 密码 41 | */ 42 | private String auth; 43 | /** 44 | * 时间戳排序 45 | */ 46 | private long timestampSort; 47 | 48 | public ConnectionServerNode() { 49 | } 50 | 51 | public long getTimestampSort() { 52 | return timestampSort; 53 | } 54 | 55 | public void setTimestampSort(long timestampSort) { 56 | this.timestampSort = timestampSort; 57 | } 58 | 59 | public ConnectionServerNode(int type) { 60 | this.type=type; 61 | } 62 | 63 | public String getHost() { 64 | return host; 65 | } 66 | 67 | public void setHost(String host) { 68 | this.host = host; 69 | } 70 | 71 | public int getPort() { 72 | return port; 73 | } 74 | 75 | public void setPort(int port) { 76 | this.port = port; 77 | } 78 | 79 | public String getAuth() { 80 | return auth; 81 | } 82 | 83 | public void setAuth(String auth) { 84 | this.auth = auth; 85 | } 86 | 87 | public int getType() { 88 | return type; 89 | } 90 | 91 | public void setType(int type) { 92 | this.type = type; 93 | } 94 | 95 | public String getDataId() { 96 | return dataId; 97 | } 98 | 99 | public void setDataId(String dataId) { 100 | this.dataId = dataId; 101 | } 102 | 103 | public String getParentDataId() { 104 | return parentDataId; 105 | } 106 | 107 | public void setParentDataId(String parentDataId) { 108 | this.parentDataId = parentDataId; 109 | } 110 | 111 | public String getName() { 112 | return name; 113 | } 114 | 115 | public void setName(String name) { 116 | this.name = name; 117 | } 118 | 119 | /** 120 | * 是否是连接 121 | * @return 122 | */ 123 | public boolean isConnection() { 124 | return type==2; 125 | } 126 | 127 | /** 128 | * 是否是跟 129 | * @return 130 | */ 131 | public boolean isRoot() { 132 | return dataId.equals(Applications.ROOT_ID) ; 133 | } 134 | 135 | @Override 136 | public String toString() { 137 | return name.toString(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/DBNode.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | import javafx.beans.property.SimpleObjectProperty; 4 | 5 | /** 6 | * 7 | * 库单选框实体 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/7/22 15:27 11 | */ 12 | public class DBNode { 13 | /** 14 | * 名称 15 | */ 16 | private SimpleObjectProperty name; 17 | /** 18 | * 库号 19 | */ 20 | private int db; 21 | 22 | public DBNode(String name, int db) { 23 | this.db = db; 24 | this.name = new SimpleObjectProperty<>(name); 25 | } 26 | 27 | 28 | public int getDb() { 29 | return db; 30 | } 31 | 32 | public void setDb(int db) { 33 | this.db = db; 34 | } 35 | 36 | public String getName() { 37 | return name.get(); 38 | } 39 | 40 | public SimpleObjectProperty nameProperty() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name.set(name); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return name.get(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/HashTypeTable.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/8/3 22:35 9 | */ 10 | public class HashTypeTable { 11 | 12 | private byte[] keyBytes; 13 | private String key; 14 | private byte[] bytes; 15 | private String value; 16 | 17 | public HashTypeTable(byte[] keyBytes,byte[] bytes) { 18 | this.keyBytes=keyBytes; 19 | this.key = new String(keyBytes); 20 | this.bytes = bytes; 21 | this.value = new String(bytes); 22 | } 23 | // 获取所有属性名称 24 | public static String[] getProperties() { 25 | return new String[]{"#row", "key","value"}; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) { 31 | return true; 32 | } 33 | if (o == null || getClass() != o.getClass()) { 34 | return false; 35 | } 36 | HashTypeTable that = (HashTypeTable) o; 37 | return Arrays.equals(keyBytes, that.keyBytes); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Arrays.hashCode(keyBytes); 43 | } 44 | 45 | public byte[] getBytes() { 46 | return bytes; 47 | } 48 | 49 | public void setBytes(byte[] bytes) { 50 | this.bytes = bytes; 51 | this.value= new String(bytes); 52 | } 53 | 54 | public String getValue() { 55 | return value; 56 | } 57 | 58 | public void setValue(String value) { 59 | this.value = value; 60 | } 61 | 62 | public byte[] getKeyBytes() { 63 | return keyBytes; 64 | } 65 | 66 | public void setKeyBytes(byte[] keyBytes) { 67 | this.keyBytes = keyBytes; 68 | this.key= new String(keyBytes); 69 | } 70 | 71 | public String getKey() { 72 | return key; 73 | } 74 | 75 | public void setKey(String key) { 76 | this.key = key; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/ListTypeTable.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/8/3 22:35 7 | */ 8 | public class ListTypeTable { 9 | 10 | private byte[] bytes; 11 | private String value; 12 | 13 | public ListTypeTable( byte[] bytes) { 14 | this.bytes = bytes; 15 | this.value = new String(bytes); 16 | } 17 | // 获取所有属性名称 18 | public static String[] getProperties() { 19 | return new String[]{"#row", "value"}; 20 | } 21 | 22 | 23 | public byte[] getBytes() { 24 | return bytes; 25 | } 26 | 27 | public void setBytes(byte[] bytes) { 28 | this.bytes = bytes; 29 | this.value= new String(bytes); 30 | } 31 | 32 | public String getValue() { 33 | return value; 34 | } 35 | 36 | public void setValue(String value) { 37 | this.value = value; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/PassParameter.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | import javafx.beans.property.SimpleStringProperty; 4 | import javafx.beans.property.StringProperty; 5 | import xyz.hashdog.rdm.redis.RedisContext; 6 | import xyz.hashdog.rdm.redis.client.RedisClient; 7 | 8 | /** 9 | * 参数传递 10 | * 父向子传递 11 | * @author th 12 | * @version 1.0.0 13 | * @since 2023/7/23 22:28 14 | */ 15 | public class PassParameter { 16 | public static final int NONE = 1; 17 | public static final int REDIS = 2; 18 | public final static int CONSOLE=3; 19 | public final static int STRING=4; 20 | public final static int HASH=5; 21 | public final static int LIST=6; 22 | public static final int SET = 7; 23 | public static final int ZSET = 8; 24 | 25 | private int tabType; 26 | private int db; 27 | private StringProperty key=new SimpleStringProperty(); 28 | private String keyType; 29 | private RedisContext redisContext; 30 | private RedisClient redisClient; 31 | 32 | 33 | public PassParameter(int tabType) { 34 | this.tabType = tabType; 35 | } 36 | 37 | 38 | public RedisContext getRedisContext() { 39 | return redisContext; 40 | } 41 | 42 | public void setRedisContext(RedisContext redisContext) { 43 | this.redisContext = redisContext; 44 | } 45 | 46 | public RedisClient getRedisClient() { 47 | return redisClient; 48 | } 49 | 50 | public void setRedisClient(RedisClient redisClient) { 51 | this.redisClient = redisClient; 52 | } 53 | 54 | public int getDb() { 55 | return db; 56 | } 57 | 58 | public void setDb(int db) { 59 | this.db = db; 60 | } 61 | 62 | public int getTabType() { 63 | return tabType; 64 | } 65 | 66 | public void setTabType(int tabType) { 67 | this.tabType = tabType; 68 | } 69 | 70 | public String getKey() { 71 | return key.get(); 72 | } 73 | 74 | public StringProperty keyProperty() { 75 | return key; 76 | } 77 | 78 | public void setKey(String key) { 79 | this.key.set(key); 80 | } 81 | 82 | public String getKeyType() { 83 | return keyType; 84 | } 85 | 86 | public void setKeyType(String keyType) { 87 | this.keyType = keyType; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/SetTypeTable.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/8/3 22:35 7 | */ 8 | public class SetTypeTable { 9 | 10 | private byte[] bytes; 11 | private String value; 12 | 13 | public SetTypeTable(byte[] bytes) { 14 | this.bytes = bytes; 15 | this.value = new String(bytes); 16 | } 17 | // 获取所有属性名称 18 | public static String[] getProperties() { 19 | return new String[]{"#row", "value"}; 20 | } 21 | 22 | 23 | public byte[] getBytes() { 24 | return bytes; 25 | } 26 | 27 | public void setBytes(byte[] bytes) { 28 | this.bytes = bytes; 29 | this.value= new String(bytes); 30 | } 31 | 32 | public String getValue() { 33 | return value; 34 | } 35 | 36 | public void setValue(String value) { 37 | this.value = value; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/entity/ZsetTypeTable.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.entity; 2 | 3 | /** 4 | * @author th 5 | * @version 1.0.0 6 | * @since 2023/8/3 22:35 7 | */ 8 | public class ZsetTypeTable { 9 | 10 | 11 | private double score; 12 | private byte[] bytes; 13 | private String value; 14 | 15 | public ZsetTypeTable(double score, byte[] bytes) { 16 | this.score=score; 17 | 18 | this.bytes = bytes; 19 | this.value = new String(bytes); 20 | } 21 | // 获取所有属性名称 22 | public static String[] getProperties() { 23 | return new String[]{"#row", "score","value"}; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | public double getScore() { 35 | return score; 36 | } 37 | 38 | public void setScore(double score) { 39 | this.score = score; 40 | } 41 | 42 | public byte[] getBytes() { 43 | return bytes; 44 | } 45 | 46 | public void setBytes(byte[] bytes) { 47 | this.bytes = bytes; 48 | this.value= new String(bytes); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/exceptions/GeneralException.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.exceptions; 2 | 3 | /** 4 | * 5 | * 一般异常,需要自定义message的异常 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/7/20 10:30 9 | */ 10 | public class GeneralException extends RuntimeException { 11 | 12 | public GeneralException(String message) { 13 | super(message); 14 | } 15 | 16 | public GeneralException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | public GeneralException(Throwable cause) { 21 | super(cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/BinaryConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import xyz.hashdog.rdm.common.util.FileUtil; 4 | 5 | import java.nio.charset.Charset; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/8 22:48 11 | */ 12 | public class BinaryConvertHandler implements ValueConvertHandler{ 13 | 14 | @Override 15 | public byte[] text2Byte(String text, Charset charset) { 16 | return FileUtil.binaryStringToByteArray(text); 17 | } 18 | 19 | @Override 20 | public String byte2Text(byte[] bytes, Charset charset) { 21 | return FileUtil.byteArrayToBinaryString(bytes); 22 | 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/HexConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import xyz.hashdog.rdm.common.util.FileUtil; 4 | 5 | import java.nio.charset.Charset; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/8 22:48 11 | */ 12 | public class HexConvertHandler implements ValueConvertHandler{ 13 | 14 | @Override 15 | public byte[] text2Byte(String text, Charset charset) { 16 | return FileUtil.hexStringToByteArray(text); 17 | } 18 | 19 | @Override 20 | public String byte2Text(byte[] bytes, Charset charset) { 21 | return FileUtil.byte2HexString(bytes); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/ImageBase64ConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import javafx.geometry.Insets; 4 | import javafx.scene.Parent; 5 | import javafx.scene.image.Image; 6 | import javafx.scene.image.ImageView; 7 | import javafx.scene.layout.StackPane; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.nio.charset.Charset; 11 | import java.util.Base64; 12 | 13 | /** 14 | * 属于二进制类型 15 | * @author th 16 | * @version 1.0.0 17 | * @since 2023/8/8 22:48 18 | */ 19 | public class ImageBase64ConvertHandler implements ValueConvertHandler{ 20 | 21 | @Override 22 | public byte[] text2Byte(String text, Charset charset) { 23 | return text.getBytes(); 24 | } 25 | 26 | @Override 27 | public String byte2Text(byte[] bytes, Charset charset) { 28 | String base64Str = new String(bytes); 29 | return base64Str; 30 | // return Base64.getEncoder().encodeToString(bytes); 31 | } 32 | 33 | @Override 34 | public Parent view(byte[] base64Bytes, Charset charset) { 35 | byte[] dataBytes = Base64.getDecoder().decode(new String(base64Bytes)); 36 | // 创建StackPane并将ImageView添加到其中 37 | StackPane stackPane = new StackPane(); 38 | stackPane.setPadding(new Insets(10)); 39 | stackPane.setPrefHeight(500); 40 | stackPane.setPrefWidth(500); 41 | ImageView imageView = new ImageView(new Image(new ByteArrayInputStream(dataBytes))); 42 | stackPane.getChildren().add(imageView); 43 | // 设置ImageView居中对齐 44 | StackPane.setAlignment(imageView, javafx.geometry.Pos.CENTER);; 45 | return stackPane; 46 | } 47 | 48 | @Override 49 | public boolean isView() { 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/ImageConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import javafx.geometry.Insets; 4 | import javafx.scene.Parent; 5 | import javafx.scene.image.Image; 6 | import javafx.scene.image.ImageView; 7 | import javafx.scene.layout.StackPane; 8 | import xyz.hashdog.rdm.common.util.FileUtil; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.nio.charset.Charset; 12 | 13 | /** 14 | * 属于二进制类型 15 | * @author th 16 | * @version 1.0.0 17 | * @since 2023/8/8 22:48 18 | */ 19 | public class ImageConvertHandler implements ValueConvertHandler{ 20 | 21 | @Override 22 | public byte[] text2Byte(String text, Charset charset) { 23 | return FileUtil.binaryStringToByteArray(text); 24 | } 25 | 26 | @Override 27 | public String byte2Text(byte[] bytes, Charset charset) { 28 | return FileUtil.byteArrayToBinaryString(bytes); 29 | } 30 | 31 | @Override 32 | public Parent view(byte[] bytes, Charset charset) { 33 | // 创建StackPane并将ImageView添加到其中 34 | StackPane stackPane = new StackPane(); 35 | stackPane.setPadding(new Insets(10)); 36 | stackPane.setPrefHeight(500); 37 | stackPane.setPrefWidth(500); 38 | ImageView imageView = new ImageView(new Image(new ByteArrayInputStream(bytes))); 39 | stackPane.getChildren().add(imageView); 40 | // 设置ImageView居中对齐 41 | StackPane.setAlignment(imageView, javafx.geometry.Pos.CENTER);; 42 | 43 | return stackPane; 44 | } 45 | 46 | @Override 47 | public boolean isView() { 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/NewKeyHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import xyz.hashdog.rdm.redis.Message; 4 | import xyz.hashdog.rdm.redis.client.RedisClient; 5 | 6 | /** 7 | * 新增key的处理 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/12 13:28 11 | */ 12 | public interface NewKeyHandler { 13 | 14 | /** 15 | * 新增key 16 | * @param redisClient 客户端啊 17 | * @param db 数据库号 18 | * @param key key名称 19 | * @param ttl 有效期 20 | * @return 21 | */ 22 | Message newKey(RedisClient redisClient,int db,String key,long ttl); 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/TextConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import java.nio.charset.Charset; 4 | 5 | /** 6 | * @author th 7 | * @version 1.0.0 8 | * @since 2023/8/8 22:48 9 | */ 10 | public class TextConvertHandler implements ValueConvertHandler{ 11 | 12 | @Override 13 | public byte[] text2Byte(String text, Charset charset) { 14 | return text.getBytes(charset); 15 | } 16 | 17 | @Override 18 | public String byte2Text(byte[] bytes, Charset charset) { 19 | return new String(bytes,charset); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/TextGzipConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import xyz.hashdog.rdm.common.util.GzipUtil; 4 | 5 | import java.nio.charset.Charset; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/8 22:48 11 | */ 12 | public class TextGzipConvertHandler implements ValueConvertHandler{ 13 | 14 | @Override 15 | public byte[] text2Byte(String text, Charset charset) { 16 | return GzipUtil.compress(text,charset); 17 | } 18 | 19 | @Override 20 | public String byte2Text(byte[] bytes, Charset charset) { 21 | return GzipUtil.uncompress(bytes,charset); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/TextJsonConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import xyz.hashdog.rdm.common.util.DataUtil; 4 | 5 | import java.nio.charset.Charset; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/8 22:48 11 | */ 12 | public class TextJsonConvertHandler implements ValueConvertHandler{ 13 | 14 | @Override 15 | public byte[] text2Byte(String text, Charset charset) { 16 | return DataUtil.json2Byte(text,charset,false); 17 | } 18 | 19 | @Override 20 | public String byte2Text(byte[] bytes, Charset charset) { 21 | return DataUtil.formatJson(bytes,charset,true); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /rdm-ui/src/main/java/xyz/hashdog/rdm/ui/handler/ValueConvertHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.hashdog.rdm.ui.handler; 2 | 3 | import javafx.scene.Parent; 4 | 5 | import java.nio.charset.Charset; 6 | 7 | /** 8 | * @author th 9 | * @version 1.0.0 10 | * @since 2023/8/8 21:48 11 | */ 12 | public interface ValueConvertHandler { 13 | 14 | /** 15 | * 文本转二进制 16 | * @param text 17 | * @param charset 18 | * @return 19 | */ 20 | byte[] text2Byte(String text, Charset charset); 21 | 22 | /** 23 | * 二进制转文本 24 | * @param bytes 25 | * @param charset 26 | * @return 27 | */ 28 | String byte2Text(byte[] bytes, Charset charset); 29 | 30 | /** 31 | * 查看窗口的控件 32 | * @param bytes 33 | * @param charset 34 | * @return 35 | */ 36 | default Parent view(byte[] bytes, Charset charset){ 37 | return null; 38 | } 39 | 40 | /** 41 | * 是否查看控件 42 | * @return 43 | */ 44 | default boolean isView(){ 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rdm-ui/src/main/resources/css/global.css: -------------------------------------------------------------------------------- 1 | .root{ 2 | /*-fx-font-family: "Segoe UI";*/ 3 | /*-fx-font-family: "Sitka Small";*/ 4 | /*-fx-font-family: "Arial";*/ 5 | } 6 | 7 | .split-pane > .split-pane-divider { 8 | /*-fx-padding: 0.083333em; !* 设置分隔条的宽度为1像素 *!*/ 9 | /*这将让中间的分隔线也没了*/ 10 | /*-fx-background-color: transparent; */ 11 | } 12 | 13 | .split-pane .split-pane { 14 | /*只让四周边框没了*/ 15 | -fx-background-color: transparent; 16 | } 17 | 18 | 19 | .split-pane-divider { 20 | /*设置分隔线宽度*/ 21 | -fx-padding: 1; 22 | -fx-background-insets: 0; 23 | /*-fx-background-color: red;*/ 24 | /*-fx-min-width: 2px;*/ 25 | /*-fx-max-width: 2px;*/ 26 | } 27 | -------------------------------------------------------------------------------- /rdm-ui/src/main/resources/css/no-border.css: -------------------------------------------------------------------------------- 1 | /* no_border.css */ 2 | /* 定义去掉边框的样式 */ 3 | /* 4 | .no-border-text-area { 5 | -fx-background-color: transparent; !* 背景透明 *! 6 | -fx-control-inner-background: transparent; !* 设置控件内部背景透明 *! 7 | -fx-background-insets: 0; !* 边框的内边距设为0,去掉边框 *! 8 | -fx-text-fill: white; 9 | } 10 | 11 | .no-border-text-area { 12 | -fx-background-color: rgba(0,0,0,0); 13 | } 14 | 15 | .no-border-text-area .scroll-pane { 16 | -fx-background-color: transparent; 17 | } 18 | 19 | .no-border-text-area .scroll-pane .viewport{ 20 | -fx-background-color: transparent; 21 | } 22 | 23 | 24 | .no-border-text-area .scroll-pane .content{ 25 | -fx-background-color: transparent; 26 | } 27 | 28 | .no-border-text-area .scroll-pane .viewport { 29 | -fx-background-color: transparent; !* 设置viewport的背景透明 *! 30 | } 31 | 32 | .no-border-text-area .scroll-pane .scroll-bar:vertical, 33 | .no-border-text-area .scroll-pane .scroll-bar:horizontal { 34 | -fx-opacity: 0; !* 设置滚动条透明 *! 35 | -fx-pref-width: 0; !* 设置滚动条宽度为0 *! 36 | !*-fx-pref-height: 0; !* 设置滚动条高度为0 *!*! 37 | } 38 | */ 39 | 40 | 41 | /*输入框取消边框加透明*/ 42 | .no-border-text-field { 43 | -fx-background-color: transparent; /* 背景透明 */ 44 | -fx-background-insets: 0; /* 边框的内边距设为0,去掉边框 */ 45 | -fx-padding: 0; /* 去掉内部填充 */ 46 | -fx-border-width: 0; /* 去掉边框宽度 */ 47 | 48 | } -------------------------------------------------------------------------------- /rdm-ui/src/main/resources/fxml/AppendView.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |