Springboot项目启动前,使用GUI做初始化配置页面并将Log4j2的日志实时显示在GUI上

news2024/11/17 22:15:35

Springboot项目启动前,使用GUI做初始化配置页面并将Log4j2的日志实时显示在GUI上

效果预览

Mac Os效果图

image-20231219104412561

Windows 10 效果图

image-20231219104640971

需求分析

做这样的一个功能并不适用于所有系统,主要用于交付给用户的产品,这样方便客户自行维护。传统的服务一般都是集群部署在Linux,有专业的运维工程师去维护,对于局域网内单机部署在Windows的情况,此功能特别实用。客户并不具备用专业的技术去部署去配置,所以用一套傻瓜式的安装、配置、启动等显得尤为重要。

代码实现

主面板

/**
 * 主面板
 * @author YoungJ
 */
@Slf4j
public class MainPanel {

    public static JPanel mainPanel;
    public static EpilepsyDbConfig epilepsyDbConfig = readEpilepsyDbConfigFromProperties();
    public static GdDbConfig gdDbConfig = readGdDbConfigFromProperties();
    public static SysConfig sysConfig = readSysConfigFromProperties();

    /**
     * 左侧配置面板宽度
     */
    public static final int CONFIG_PANEL_WIDTH = 300;
    /**
     * 日志面板宽度
     */
    public static final int LOG_PANEL_WIDTH = 850;
    /**
     * 系统数据库面板起点坐标
     */
    public static final int[] EPILEPSDB_PANEL_START_COOR = {5, 0};
    /**
     * 系统数据库面板高度
     */
    public static final int EPILEPSDB_PANEL_HEIGHT = 190;
    /**
     * 光电数据库面板起点坐标
     */
    public static final int[] GDDB_PANEL_START_COOR = {5, 200};
    /**
     * 光电数据库面板高度
     */
    public static final int GDDB_PANEL_HEIGHT = 150;
    /**
     * 证书面板起点坐标
     */
    public static final int[] CERT_PANEL_START_COOR = {5, 360};
    /**
     * 证书面板高度
     */
    public static final int CERT_PANEL_HEIGHT = 160;
    /**
     * 服务面板起点坐标
     */
    public static final int[] SERVER_PANEL_START_COOR = {5, 530};
    /**
     * 服务面板高度
     */
    public static final int SERVER_PANEL_HEIGHT = 140;
    /**
     * 日志面板起点坐标
     */
    public static final int[] LOG_PANEL_START_COOR = {310, 0};
    /**
     * 日志面板高度
     */
    public static final int LOG_PANEL_HEIGHT = 670;

    public MainPanel() {
        mainPanel = new JPanel();
        mainPanel.setLayout(null);
        EpilepsysDbPanel.buildJpanel(mainPanel);
        GdDbPanel.buildJpanel(mainPanel);
        CertPanel.buildJpanel(mainPanel);
        ServerPanel.buildJpanel(mainPanel);
        LogPanel.buildJpanel(mainPanel);
        buildFrame(mainPanel);
    }

    protected static JButton buildJButton(String name, int x, int y, int width, int height) {
        JButton button = new JButton(name);
        button.setBounds(x, y, width, height);
        return button;
    }

    protected static JRadioButton buildJRadioButton(String name, int x, int y, int width, int height) {
        JRadioButton button = new JRadioButton(name);
        button.setBounds(x, y, width, height);
        return button;
    }

    // 文本框
    protected static JTextField buildJTextField(String value, String name, int columns, int x, int y, int width, int height) {
        JTextField jtf = new JTextField(columns);
        jtf.setText(value);
        jtf.setName(name);
        jtf.setBounds(x, y, width, height);
        return jtf;
    }

    // 密码框
    protected static JPasswordField buildJPasswordField(String value, String name, int columns, int x, int y, int width, int height) {
        JPasswordField jtf = new JPasswordField(columns);
        jtf.setText(value);
        jtf.setName(name);
        jtf.setBounds(x, y, width, height);
        return jtf;
    }

    protected static JLabel buildJLabel(String name, int x, int y, int width, int height) {
        JLabel label = new JLabel(name);
        label.setBounds(x, y, width, height);

        return label;
    }

    protected static JLabel buildJBorder(String name, int x, int y, int width, int height) {
        JLabel label = new JLabel();
        label.setBounds(x, y, width, height);
        label.setBorder(BorderFactory.createTitledBorder(name));
        return label;
    }

    private static void buildFrame(JComponent component) {
        JFrame frame = new JFrame("癫痫数据管理系统");
        component.setBounds(0, 0, 1055, 730);
        frame.add(component);
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().setLayout(null);
        frame.getContentPane().add(BorderLayout.CENTER, component);
        // 设置窗口最小尺寸
        frame.setMinimumSize(new Dimension(1065, 730));
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                int result = JOptionPane.showConfirmDialog(frame, "关闭窗口服务将停止运行,确定要关闭吗?", "关闭确认", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_OPTION) {
                    // 如果用户点击是,执行关闭操作
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    // 关闭时同时停止 Spring Boot
                    ServerPanel.taskStop();
                    saveConfigToFile();
                }
            }
        });
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
//        frame.setResizable(false);
    }

    private static EpilepsyDbConfig readEpilepsyDbConfigFromProperties() {
        EpilepsyDbConfig config = new EpilepsyDbConfig();
        Properties properties = loadProperties();

        config.setDatabase(properties.getProperty("epilepsysys.db.database"));
        config.setUrl(properties.getProperty("epilepsysys.db.url"));
        config.setPort(Integer.parseInt(properties.getProperty("epilepsysys.db.port")));
        config.setUsername(properties.getProperty("epilepsysys.db.username"));
        config.setPassword(properties.getProperty("epilepsysys.db.password"));

        return config;
    }

    private static GdDbConfig readGdDbConfigFromProperties() {
        GdDbConfig config = new GdDbConfig();
        Properties properties = loadProperties();

        config.setDatabase(properties.getProperty("gd.db.database"));
        config.setUrl(properties.getProperty("gd.db.url"));
        config.setUsername(properties.getProperty("gd.db.username"));
        config.setPassword(properties.getProperty("gd.db.password"));

        return config;
    }

    private static SysConfig readSysConfigFromProperties() {
        SysConfig config = new SysConfig();
        Properties properties = loadProperties();
        config.setCertPath(properties.getProperty("spring.config.import"));
        config.setAutoStart(properties.getProperty("sys.auto.start"));
        config.setZipName(properties.getProperty("sys.license.name"));
        return config;
    }

    private static Properties loadProperties() {
        Properties properties = new Properties();
        String filePath = System.getProperty("user.dir") + File.separator + "config/application.properties";
        try (InputStream input = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)));) {
            properties.load(input);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }

    public static void saveConfigToFile() {
        try {
            EpilepsysDbPanel.updateConfigFromGui();
            GdDbPanel.updateConfigFromGui();
            String filePath = System.getProperty("user.dir") + File.separator + "config/application.properties";
            Properties properties = new Properties();

            properties.setProperty("epilepsysys.db.database", epilepsyDbConfig.getDatabase());
            properties.setProperty("epilepsysys.db.url", epilepsyDbConfig.getUrl());
            properties.setProperty("epilepsysys.db.port", String.valueOf(epilepsyDbConfig.getPort()));
            properties.setProperty("epilepsysys.db.username", epilepsyDbConfig.getUsername());
            properties.setProperty("epilepsysys.db.password", epilepsyDbConfig.getPassword());

            properties.setProperty("gd.db.database", gdDbConfig.getDatabase());
            properties.setProperty("gd.db.url", gdDbConfig.getUrl());
            properties.setProperty("gd.db.username", gdDbConfig.getUsername());
            properties.setProperty("gd.db.password", gdDbConfig.getPassword());

            properties.setProperty("sys.auto.start", sysConfig.getAutoStart() == null ? "false" : sysConfig.getAutoStart());
            properties.setProperty("spring.config.import", sysConfig.getCertPath() == null ? "" : sysConfig.getCertPath());
            properties.setProperty("sys.license.name", sysConfig.getZipName() == null ? "" : sysConfig.getZipName());

            try (OutputStream output = Files.newOutputStream(Paths.get(filePath))) {
                properties.store(output, "Updated configuration");
            }
        } catch (IOException e) {
            Toolkit.getDefaultToolkit().beep();
        }
    }

    public static void disableConfigFields() {
        EpilepsysDbPanel.disableConfigFields();
        GdDbPanel.disableConfigFields();
        CertPanel.disableConfigFields();
        ServerPanel.disableConfigFields();
    }

    public static void enableConfigFields() {
        EpilepsysDbPanel.enableConfigFields();
        GdDbPanel.enableConfigFields();
        CertPanel.enableConfigFields();
        ServerPanel.enableConfigFields();
    }
}

系统数据库配置面板

/**
 * 系统数据库配置面板
 * @author YoungJ
 */
@Slf4j
public class EpilepsysDbPanel {

    private static JTextField dbNameTextField;
    private static JTextField urlTextField;
    private static JTextField portTextField;
    private static JTextField usernameTextField;
    private static JPasswordField passwordTextField;
    private static final int y0 = MainPanel.EPILEPSDB_PANEL_START_COOR[1];
    private static final int x0 = MainPanel.EPILEPSDB_PANEL_START_COOR[0];

    private static EpilepsyDbConfig epilepsyDbConfig = MainPanel.epilepsyDbConfig;

    public static void buildJpanel(JPanel panel) {
        String dbName = epilepsyDbConfig.getDatabase();
        String url = epilepsyDbConfig.getUrl();
        String username = epilepsyDbConfig.getUsername();
        Integer port = epilepsyDbConfig.getPort();
        String password = epilepsyDbConfig.getPassword();

        panel.add(MainPanel.buildJBorder("系统数据库配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.EPILEPSDB_PANEL_HEIGHT));

        // 数据库
        panel.add(MainPanel.buildJLabel("数据库:", x0 + 15, y0 + 40, 80, 25));
        dbNameTextField = MainPanel.buildJTextField(dbName, "database", 20, 100, y0 + 40, 165, 25);
        panel.add(dbNameTextField);

        // URL
        panel.add(MainPanel.buildJLabel("URL:", x0 + 15, y0 + 70, 80, 25));
        urlTextField = MainPanel.buildJTextField(url, "url", 20, 100, y0 + 70, 165, 25);
        panel.add(urlTextField);

        // 端口
        panel.add(MainPanel.buildJLabel("端口:", x0 + 15, y0 + 100, 80, 25));
        portTextField = MainPanel.buildJTextField(port + "", "port", 20, 100, y0 + 100, 165, 25);
        panel.add(portTextField);
        // 用户名
        panel.add(MainPanel.buildJLabel("用户名:", x0 + 15, y0 + 130, 80, 25));
        usernameTextField = MainPanel.buildJTextField(username, "username", 20, 100, y0 + 130, 165, 25);
        panel.add(usernameTextField);

        // 密码
        panel.add(MainPanel.buildJLabel("密码:", x0 + 15, y0 + 160, 80, 25));
        passwordTextField = MainPanel.buildJPasswordField(password, "password", 20, 100, y0 + 160, 165, 25);
        panel.add(passwordTextField);
    }

    // 为按钮绑定监听
    private static void addActionListener(JButton saveButton) {
        saveButton.addActionListener(
                e -> updateConfigFromGui());

    }

    public static void updateConfigFromGui() {
        epilepsyDbConfig.setDatabase(dbNameTextField.getText());
        epilepsyDbConfig.setUrl(urlTextField.getText());
        epilepsyDbConfig.setPort(Integer.parseInt(portTextField.getText()));
        epilepsyDbConfig.setUsername(usernameTextField.getText());
        epilepsyDbConfig.setPassword(new String(passwordTextField.getPassword()));
    }

    public static void disableConfigFields() {
        dbNameTextField.setEnabled(false);
        urlTextField.setEnabled(false);
        portTextField.setEnabled(false);
        usernameTextField.setEnabled(false);
        passwordTextField.setEnabled(false);
    }

    public static void enableConfigFields() {
        dbNameTextField.setEnabled(true);
        urlTextField.setEnabled(true);
        portTextField.setEnabled(true);
        usernameTextField.setEnabled(true);
        passwordTextField.setEnabled(true);
    }
}

其他数据库配置面板

/**
 * 光电数据库配置面板
 * @author YoungJ
 */
public class GdDbPanel {
    private static JTextField gdDbNameTextField;
    private static JTextField gdUrlTextField;
    private static JTextField gdUserNameTextField;
    private static JPasswordField gdPasswordTextField;
    private static final int y0 = MainPanel.GDDB_PANEL_START_COOR[1];
    private static final int x0 = MainPanel.GDDB_PANEL_START_COOR[0];

    private static GdDbConfig gdDbConfig = MainPanel.gdDbConfig;

    public static void buildJpanel(JPanel panel) {
        String dbName = gdDbConfig.getDatabase();
        String url = gdDbConfig.getUrl();
        String username = gdDbConfig.getUsername();
        String password = gdDbConfig.getPassword();

        //添加服务器配置区
        panel.add(MainPanel.buildJBorder("光电数据库配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.GDDB_PANEL_HEIGHT));
        //数据库
        panel.add(MainPanel.buildJLabel("数据库:", x0 + 15, y0 + 30, 80, 25));
        gdDbNameTextField = MainPanel.buildJTextField(dbName, "database", 20, 100, y0 + 30, 165, 25);
        panel.add(gdDbNameTextField);
        //URL
        panel.add(MainPanel.buildJLabel("URL:", x0 + 15, y0 + 60, 80, 25));
        gdUrlTextField = MainPanel.buildJTextField(url, "url", 20, 100, y0 + 60, 165, 25);
        panel.add(gdUrlTextField);
        //用户名
        panel.add(MainPanel.buildJLabel("用户名:", x0 + 15, y0 + 90, 80, 25));
        gdUserNameTextField = MainPanel.buildJTextField(username, "username", 20, 100, y0 + 90, 165, 25);
        panel.add(gdUserNameTextField);
        //密码
        panel.add(MainPanel.buildJLabel("密码:", x0 + 15, y0 + 120, 80, 25));
        gdPasswordTextField = MainPanel.buildJPasswordField(password, "password", 20, 100, y0 + 120, 165, 25);
        panel.add(gdPasswordTextField);
    }

    // 为按钮绑定监听
    private static void addActionListener(JButton saveButton) {
        saveButton.addActionListener(e -> {
            if (Objects.equals(saveButton.getText(), "保存")) {
                updateConfigFromGui();
            }
        });

    }

    // save event
    public static void updateConfigFromGui() {
        gdDbConfig.setDatabase(gdDbNameTextField.getText());
        gdDbConfig.setUrl(gdUrlTextField.getText());
        gdDbConfig.setUsername(gdUserNameTextField.getText());
        gdDbConfig.setPassword(new String(gdPasswordTextField.getPassword()));
    }

    public static void disableConfigFields() {
        gdDbNameTextField.setEnabled(false);
        gdUrlTextField.setEnabled(false);
        gdUserNameTextField.setEnabled(false);
        gdPasswordTextField.setEnabled(false);
    }

    public static void enableConfigFields() {
        gdDbNameTextField.setEnabled(true);
        gdUrlTextField.setEnabled(true);
        gdUserNameTextField.setEnabled(true);
        gdPasswordTextField.setEnabled(true);
    }
}

证书配置面板

/**
 * 证书配置面板
 *
 * @author YoungJ
 */
@Slf4j
public class CertPanel {
    private static ImageIcon insertedIcon;
    private static ImageIcon notInsertedIcon;
    private static JLabel statusLabel;
    private static JLabel licenseLabel;
    private static JLabel licenseInfoLable;
    private static JLabel licenseDescLabel;
    private static JButton chooseButton;

    private static JLabel validLabel;
    private static JLabel failLabel;
    private static final int y0 = MainPanel.CERT_PANEL_START_COOR[1];
    private static final int x0 = MainPanel.CERT_PANEL_START_COOR[0];
    private static final String basePath = System.getProperty("user.dir");


    public static void buildJpanel(JPanel panel) {
        // 配置区
        panel.add(MainPanel.buildJBorder("证书配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.CERT_PANEL_HEIGHT));
        insertedIcon = createStatusIcon(true);
        notInsertedIcon = createStatusIcon(false);
        panel.add(MainPanel.buildJLabel("加密锁:", x0 + 15, y0 + 30, 80, 25));
        // 证书按钮
        panel.add(MainPanel.buildJLabel("选择证书:", x0 + 15, y0 + 60, 80, 25));
        chooseButton = MainPanel.buildJButton("选择…", x0 + 100, y0 + 60, 80, 25);
        addActionListener(chooseButton, panel);
        panel.add(chooseButton);
        String certPath = MainPanel.sysConfig.getCertPath();
        if (StringUtils.isNotBlank(certPath)) {
            // 新增状态显示组件
            JPanel licensePanel = new JPanel();
            licensePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));

            licenseLabel = new JLabel(insertedIcon);  // 图标
            licenseLabel.setText("已安装");  // 文本
            licensePanel.add(licenseLabel);

            licensePanel.setBounds(x0 + 220, y0 + 60, 80, 20);
            panel.add(licensePanel);
            if (checkFileExist("license.properties")
                    && checkFileExist("license.lic")
                    && checkFileExist("publicCerts.keystore")) {
                chooseButton.setText("更换…");
                panel.add(licenseDescLabel = MainPanel.buildJLabel("证书详情:", x0 + 15, y0 + 100, 80, 20));
                panel.add(licenseInfoLable = MainPanel.buildJLabel(MainPanel.sysConfig.getZipName(), x0 + 100, y0 + 100, 165, 20));
            }
        } else {
            JPanel licensePanel = new JPanel();
            licensePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));

            licenseLabel = new JLabel(notInsertedIcon);  // 图标
            licenseLabel.setText("未安装");  // 文本
            licensePanel.add(licenseLabel);

            licensePanel.setBounds(x0 + 220, y0 + 60, 80, 20);
            panel.add(licensePanel);
        }

        // 新增状态显示组件
        JPanel statusPanel = new JPanel();
        statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));

        statusLabel = new JLabel(notInsertedIcon);  // 图标
        statusLabel.setText("未检测");  // 文本
        setDongleStatus(DongleManager.isLicenseInserted());
        statusPanel.add(statusLabel);

        statusPanel.setBounds(x0 + 220, y0 + 30, 80, 20);
        panel.add(statusPanel);

        // 初始化 USB 检查定时器,每隔一段时间检查一次状态
        Timer dongleCheckTimer = new Timer(30000, e -> setDongleStatus(DongleManager.isLicenseInserted()));
        dongleCheckTimer.start();
    }

    // 为按钮绑定监听
    private static void addActionListener(JButton button, JPanel panel) {
        button.addActionListener(e -> {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setFileFilter(new FileFilter() {
                @Override
                public boolean accept(File f) {
                    return f.getName().toLowerCase().endsWith(".zip") || f.isDirectory();
                }

                @Override
                public String getDescription() {
                    return "ZIP Files (*.zip)";
                }
            });
            int result = fileChooser.showOpenDialog(null);
            if (result == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                extractZip(selectedFile, panel);
            }
        });

    }

    private static void extractZip(File zipFile, JPanel panel) {
        try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile.toPath()))) {
            ZipEntry entry;
            if (licenseDescLabel == null) {
                panel.add(licenseDescLabel = MainPanel.buildJLabel("证书详情", x0 + 15, y0 + 100, 80, 25));
            }
            while ((entry = zipInputStream.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    Path outputPath = Paths.get(basePath, entry.getName());
                    Files.copy(zipInputStream, outputPath, StandardCopyOption.REPLACE_EXISTING);
                }
            }
            if (licenseInfoLable == null) {
                panel.add(licenseInfoLable = MainPanel.buildJLabel(zipFile.getName(), x0 + 100, y0 + 100, 80, 25));
            } else {
                licenseInfoLable.setText(zipFile.getName());
            }
            chooseButton.setText("更换…");
            panel.updateUI();
            MainPanel.sysConfig.setCertPath("file:" + basePath + File.separator + "license.properties");
            MainPanel.sysConfig.setZipName(zipFile.getName());
            setLicenseStatus(true);
            MainPanel.saveConfigToFile();
            JOptionPane.showMessageDialog(null, "安装成功!");
        } catch (IOException e) {
            log.error(e.getMessage());
            JOptionPane.showMessageDialog(null, "安装失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
        }
    }

    private static boolean checkFileExist(String fileName) {
        Path filePath = Paths.get(basePath, fileName);

        if (Files.exists(filePath)) {
            return true;
        } else {
            return false;
        }
    }

    public static void addCertLabel() {
        if (LicenseInfo.VERIFY_SUCCESS) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String notBefore = sdf.format(LicenseInfo.notBefore);
            String notAfter = sdf.format(LicenseInfo.notAfter);
            MainPanel.mainPanel.add(validLabel = MainPanel.buildJLabel("有效期至:     " + notAfter, x0 + 15, y0 + 130, 250, 20));
            validLabel.setToolTipText(notBefore + " 至 " + notAfter);
        } else {
            MainPanel.mainPanel.add(validLabel = MainPanel.buildJLabel("失败原因:     " + String.join(",", LicenseInfo.FAIL_MSG), x0 + 15, y0 + 130, 250, 20));
            validLabel.setToolTipText("失败原因:" + String.join(",", LicenseInfo.FAIL_MSG));
        }
        MainPanel.mainPanel.updateUI();
    }

    public static void removeCertLabel() {
        if (validLabel != null) {
            MainPanel.mainPanel.remove(validLabel);
        }
        if (failLabel != null) {
            MainPanel.mainPanel.remove(failLabel);
        }
        MainPanel.mainPanel.updateUI();
    }

    public static void disableConfigFields() {
        chooseButton.setEnabled(false);
    }

    public static void enableConfigFields() {
        chooseButton.setEnabled(true);
    }

    // 用于设置插入状态
    private static void setDongleStatus(boolean status) {
        if (status) {
            statusLabel.setIcon(insertedIcon);
            statusLabel.setText("已插入");
        } else {
            statusLabel.setIcon(notInsertedIcon);
            statusLabel.setText("未插入");
        }
    }

    // 用于设置证书安装状态
    private static void setLicenseStatus(boolean status) {
        if (status) {
            licenseLabel.setIcon(insertedIcon);
            licenseLabel.setText("已安装");
        } else {
            licenseLabel.setIcon(notInsertedIcon);
            licenseLabel.setText("未安装");
        }
    }

    // 新增方法用于创建状态图标
    private static ImageIcon createStatusIcon(boolean status) {
        int size = 12;  // 调整图标大小
        ImageIcon icon = new ImageIcon(new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB));
        Graphics g = icon.getImage().getGraphics();
        if (status) {
            g.setColor(Color.GREEN);
        } else {
            g.setColor(Color.GRAY);
        }
        g.fillOval(0, 0, size, size);
        g.dispose();
        return icon;
    }

}

服务管理面板

/**
 * 服务面板
 * @author YoungJ
 */
public class ServerPanel {

    private static JButton stopBtn;
    private static JButton startBtn;
    private static JRadioButton yesRadioBtn;
    private static JRadioButton noRadioBtn;
    private static PrintStream consolePrintStream;

    private static JLabel statusLabel;

    // 使用单线程的 ExecutorService
    public static ExecutorService executorService = Executors.newSingleThreadExecutor();

    private static final int y0 = MainPanel.SERVER_PANEL_START_COOR[1];
    private static final int x0 = MainPanel.SERVER_PANEL_START_COOR[0];

    private enum StartupStatus {
        STARTING, STARTED, NOT_STARTED
    }

    private static ImageIcon startingIcon;
    private static ImageIcon startedIcon;
    private static ImageIcon notStartedIcon;

    public static JPanel buildJpanel(JPanel panel) {

        panel.add(MainPanel.buildJBorder("服务管理", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.SERVER_PANEL_HEIGHT));

        // 初始化图标
        startingIcon = createStatusIcon(StartupStatus.STARTING);
        startedIcon = createStatusIcon(StartupStatus.STARTED);
        notStartedIcon = createStatusIcon(StartupStatus.NOT_STARTED);

        // 新增状态显示组件
        JPanel statusPanel = new JPanel();  // 创建容器用于放置图标和文本
        statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));  // 设置容器布局

        statusLabel = new JLabel(notStartedIcon);  // 图标
        statusLabel.setText("未启动");  // 文本
        statusPanel.add(statusLabel);

        statusPanel.setBounds(x0 + 220, y0 + 30, 80, 20);
        panel.add(statusPanel);

        String osName = System.getProperty("os.name");
        String osArch = System.getProperty("os.arch");
        panel.add(MainPanel.buildJLabel(osName + " " + osArch, x0 + 10, y0 + 30, 200, 20));

        panel.add(MainPanel.buildJLabel("自动启动:", x0 + 10, y0 + 60, 80, 20));
        panel.add(yesRadioBtn = MainPanel.buildJRadioButton("是", x0 + 90, y0 + 60, 50, 20));
        panel.add(noRadioBtn = MainPanel.buildJRadioButton("否", x0 + 150, y0 + 60, 50, 20));
        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(yesRadioBtn);
        buttonGroup.add(noRadioBtn);
        if ("true".equals(MainPanel.sysConfig.getAutoStart())) {
            buttonGroup.setSelected(yesRadioBtn.getModel(), true);
            taskStart();
        } else {
            buttonGroup.setSelected(noRadioBtn.getModel(), true);
        }
        addActionListener(yesRadioBtn);
        addActionListener(noRadioBtn);

        // 开始按钮
        startBtn = MainPanel.buildJButton("启动", x0 + 50, y0 + 100, 80, 25);
        addActionListener(startBtn);
        panel.add(startBtn);

        //添加停止上报按钮
        stopBtn = MainPanel.buildJButton("停止", x0 + 150, y0 + 100, 80, 25);
        stopBtn.setEnabled(false);
        addActionListener(stopBtn);
        panel.add(stopBtn);
        return panel;
    }

    // 为按钮绑定监听
    private static void addActionListener(JButton button) {
        button.addActionListener(e -> {
            if (Objects.equals(button.getText(), "启动")) {
                taskStart();
            } else if (Objects.equals(button.getText(), "停止")) {
                taskStop();
            }
        });

    }

    // 为按钮绑定监听
    private static void addActionListener(JRadioButton button) {
        button.addActionListener(e -> {
            if (Objects.equals(button.getText(), "是")) {
                MainPanel.sysConfig.setAutoStart("true");
            } else {
                MainPanel.sysConfig.setAutoStart("false");
            }
        });

    }

    // 开始
    private static void taskStart() {
        SwingUtilities.invokeLater(() -> {
            startBtn.setEnabled(false);
            setStartupStatus(StartupStatus.STARTING);
            MainPanel.disableConfigFields();
            MainPanel.saveConfigToFile();
            // 创建一个新的 PrintStream,用于重定向 System.out
            consolePrintStream = new PrintStream(new LogPanel.LogOutputStream());

            // 将 System.out 重定向到新的 PrintStream
            System.setOut(consolePrintStream);
            System.setErr(consolePrintStream);
            // 使用新线程去启动,否则会阻塞其他线程
            executorService.execute(() -> {
                CertPanel.removeCertLabel();
                try {
                    ManageApplication.startSpringBoot(MainPanel.epilepsyDbConfig, MainPanel.gdDbConfig);
                } catch (Exception e) {
                    MainPanel.enableConfigFields();
                    stopBtn.setEnabled(false);
                    startBtn.setEnabled(true);
                    setStartupStatus(StartupStatus.NOT_STARTED);
                    return;
                }
                stopBtn.setEnabled(true);
                startBtn.setEnabled(false);
                setStartupStatus(StartupStatus.STARTED);
                CertPanel.addCertLabel();
                if (LicenseInfo.VERIFY_SUCCESS) {
                    openBrowser("http://localhost:9999");
                }
            });
        });
    }

    public static void main(String[] args) {
        try {
            System.out.println("Local IP Addresses:");
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                while (inetAddresses.hasMoreElements()) {
                    InetAddress inetAddress = inetAddresses.nextElement();
                    if (!inetAddress.isLoopbackAddress() && !inetAddress.getHostAddress().contains(":")) {
                        System.out.println("  " + networkInterface.getName() + ": " + inetAddress.getHostAddress());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void openBrowser(String url) {
        try {
            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
                Desktop.getDesktop().browse(new URI(url));
            } else {
                System.err.println("Desktop or Browser is not supported on this platform");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 停止服务
    public static void taskStop() {
        MainPanel.enableConfigFields();
        // 开始执行任务
        SwingUtilities.invokeLater(ManageApplication::stopSpringBoot);
        stopBtn.setEnabled(false);
        startBtn.setEnabled(true);
        setStartupStatus(StartupStatus.NOT_STARTED);
    }

    // 新增方法用于设置启动状态
    private static void setStartupStatus(StartupStatus status) {
        switch (status) {
            case STARTING:
                statusLabel.setIcon(startingIcon);
                statusLabel.setText("启动中");
                break;
            case STARTED:
                statusLabel.setIcon(startedIcon);
                statusLabel.setText("已启动");
                break;
            case NOT_STARTED:
                statusLabel.setIcon(notStartedIcon);
                statusLabel.setText("未启动");
                break;
        }
    }

    // 新增方法用于创建状态图标
    private static ImageIcon createStatusIcon(StartupStatus status) {
        int size = 12;  // 调整图标大小
        ImageIcon icon = new ImageIcon(new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB));
        Graphics g = icon.getImage().getGraphics();
        switch (status) {
            case STARTING:
                g.setColor(Color.ORANGE);
                break;
            case STARTED:
                g.setColor(Color.GREEN);
                break;
            case NOT_STARTED:
                g.setColor(Color.GRAY);
                break;
        }
        g.fillOval(0, 0, size, size);
        g.dispose();
        return icon;
    }

    public static void disableConfigFields() {
        yesRadioBtn.setEnabled(false);
        noRadioBtn.setEnabled(false);
    }

    public static void enableConfigFields() {
        yesRadioBtn.setEnabled(true);
        noRadioBtn.setEnabled(true);
    }

}

日志区域面板

/**
 * 日志面板
 *
 * @author YoungJ
 */
public class LogPanel {
    private static final int MAX_LOG_LINES = 1000; // 限制最大日志行数

    public static JTextArea logTextArea;
    private static final int y0 = MainPanel.LOG_PANEL_START_COOR[1];
    private static final int x0 = MainPanel.LOG_PANEL_START_COOR[0];

    private static JButton clearBtn;

    public static void buildJpanel(JPanel panel) {

        panel.add(MainPanel.buildJBorder("服务日志", x0, y0 + 10, MainPanel.LOG_PANEL_WIDTH, MainPanel.LOG_PANEL_HEIGHT));
        panel.add(clearBtn = MainPanel.buildJButton("清空", x0 + 10, y0 + 28, 60, 20));
        addActionListener(clearBtn);

        logTextArea = new JTextArea();
        logTextArea.setEditable(false);
        panel.add(logTextArea);

        JScrollPane logScrollPane = new JScrollPane();
        logScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        logScrollPane.setBounds(x0 + 10, y0 + 50, MainPanel.LOG_PANEL_WIDTH - 10, MainPanel.LOG_PANEL_HEIGHT - 50);
        logScrollPane.getViewport().add(logTextArea);
        panel.add(logScrollPane);

        LogPanelAppender.setTextArea(logTextArea);
    }

    public static void print(String message) {
        SwingUtilities.invokeLater(() -> {
            logTextArea.append(message);
            int lineCount = logTextArea.getLineCount();
            if (lineCount > MAX_LOG_LINES) {
                try {
                    int startOffset = logTextArea.getLineStartOffset(0);
                    int endOffset = logTextArea.getLineEndOffset(lineCount - MAX_LOG_LINES);
                    logTextArea.replaceRange("", startOffset, endOffset);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
    }

    // 自定义 OutputStream,用于重定向输出流到 LogPanel
    static class LogOutputStream extends OutputStream {

        @Override
        public void write(int b) {
        }

        @Override
        public void write(byte[] b, int off, int len) {
            SwingUtilities.invokeLater(() -> print(new String(b, off, len)));
        }
    }

    private static void addActionListener(JButton button) {
        button.addActionListener(e -> {
            if (Objects.equals(button.getText(), "清空")) {
                logTextArea.setText("");
            }
        });

    }

}

将Log4j2的日志输出到GUI上

import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.layout.PatternLayout;

import javax.swing.*;
import java.io.Serializable;

/**
 * 日志Appender,将log4j2的日志输出到GUI
 * @author zhaoyajie
 */
public class LogPanelAppender extends AbstractAppender {

    private static JTextArea textArea;

    // 静态方法用于设置 textArea
    public static void setTextArea(JTextArea logTextArea) {
        textArea = logTextArea;
    }

    private LogPanelAppender(String name, Layout<? extends Serializable> layout, Filter filter, boolean ignoreExceptions) {
        super(name, filter, layout, ignoreExceptions);
    }

    @Override
    public void append(LogEvent event) {
        if (event != null && textArea != null) {
            String message = new String(getLayout().toByteArray(event));
            SwingUtilities.invokeLater(() -> textArea.append(message + "\n"));
        }
    }

    // 需要提供一个工厂方法,Log4j2 才能正确初始化 Appender
    public static Appender createAppender(String name, JTextArea area) {
        textArea = area;
        Layout<? extends Serializable> layout = PatternLayout.createDefaultLayout();
        return new LogPanelAppender(name, layout, null, false);
    }
}

log4j2配置文件增加输出配置

# 添加 LogPanelAppender
appender.logPanel.type = com.xxx.gui.LogPanelAppender
appender.logPanel.name = LogPanelAppender
appender.logPanel.layout.type = PatternLayout
appender.logPanel.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

rootLogger.appenderRefs = stdout,I,E,logPanel

rootLogger.appenderRef.logPanel.ref = LogPanelAppender

Springboot启动类

@SpringBootApplication
@Slf4j
public class ManageApplication {

    private static boolean isSpringBootRunning = false;
    private static ConfigurableApplicationContext context;
    private static MainPanel mainPanel;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> mainPanel = new MainPanel());
    }

    public static void startSpringBoot(EpilepsyDbConfig epilepsyDbConfig, GdDbConfig gdDbConfig) {
        if (!isSpringBootRunning) {
            // 设置Spring Boot启动参数
            SpringApplication app = new SpringApplication(ManageApplication.class);
            Map<String, Object> properties = new HashMap<>();
            properties.put("spring.profiles.active", "prod");
            properties.put("epilepsysys.db.database", epilepsyDbConfig.getDatabase());
            properties.put("epilepsysys.db.url", epilepsyDbConfig.getUrl());
            properties.put("epilepsysys.db.port", String.valueOf(epilepsyDbConfig.getPort()));
            properties.put("epilepsysys.db.username", epilepsyDbConfig.getUsername());
            properties.put("epilepsysys.db.password", epilepsyDbConfig.getPassword());

            properties.put("gd.db.database", gdDbConfig.getDatabase());
            properties.put("gd.db.url", gdDbConfig.getUrl());
            properties.put("gd.db.username", gdDbConfig.getUsername());
            properties.put("gd.db.password", gdDbConfig.getPassword());

            if (StringUtils.isNotBlank(MainPanel.sysConfig.getCertPath())) {
                properties.put("spring.config.import", MainPanel.sysConfig.getCertPath());
            }

            app.setDefaultProperties(properties);
            context = app.run();
            isSpringBootRunning = true;
        }
    }

    public static void stopSpringBoot() {
        if (isSpringBootRunning) {
            SpringApplication.exit(context);
            isSpringBootRunning = false;
        }
    }

}

qrcode_for_gh_905f664473ad_344

关注公众号“呲花是朵花”,查看更多的实用技术分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1322528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scratch:让少儿编程变得简单有趣

在21世纪这个信息化、数字化的时代&#xff0c;编程已经成为一种重要的技能。然而&#xff0c;对于许多孩子来说&#xff0c;编程可能是一种难以理解和掌握的技能。那么&#xff0c;有没有一种方法&#xff0c;可以让孩子们在轻松愉快的环境中学习编程呢&#xff1f;答案是肯定…

2024年电工(初级)证模拟考试题库及电工(初级)理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年电工&#xff08;初级&#xff09;证模拟考试题库及电工&#xff08;初级&#xff09;理论考试试题是由安全生产模拟考试一点通提供&#xff0c;电工&#xff08;初级&#xff09;证模拟考试题库是根据电工&…

简单的git操作指令

简单的git操作指令 1. git clone2. git add .3. git commit4. git push5. git rm6. git status7. git log8. git pull 1. git clone 如果有小伙伴出现报错的情况&#xff0c;爆出错误fatal: unable to access…not resolve host或者Network is unreachable可以看看这篇文章。 …

第三周:Python能力复盘

资料&#xff1a; 《笨办法学Python》阅读地址&#xff1a;https://www.bookstack.cn/read/LearnPython3TheHardWay 《廖雪峰Python教程》阅读地址&#xff1a;http://t.cn/RK0qGu7 《机器学习numpy与pandas基础》&#xff1a;https://zhuanlan.zhihu.com/p/639733816 《matplo…

手把手教你使用Cypress进行端到端测试

一、引言 Cypress是一个流行的端到端测试框架&#xff0c;它提供了一个全面的解决方案&#xff0c;可以测试任何在浏览器中运行的内容。不论你是想为一个小型项目添加测试&#xff0c;还是在大型企业级应用中进行端到端测试&#xff0c;Cypress都是一个不错的选择。本文将会手…

堆与二叉树(上)

本篇主要讲的是一些概念&#xff0c;推论和堆的实现&#xff08;核心在堆的实现这一块&#xff09; 涉及到的一些结论&#xff0c;证明放到最后&#xff0c;可以选择跳过&#xff0c;知识点过多&#xff0c;当复习一用差不多&#xff0c;如果是刚学这一块的&#xff0c;建议打…

python实现贪吃蛇游戏

文章目录 1、项目说明2、项目预览3、开发必备4、贪吃蛇代码实现4.1、窗口和基本参数实现4.2、绘制背景4.3、绘制墙壁4.4、绘制贪吃蛇4.5、绘制食物4.6、实现长度信息显示4.7、定义游戏暂停界面4.8、定义贪吃蛇死亡界面4.9、实现贪吃蛇碰撞效果4.10、实现添加食物功能4.11、实现…

怎么检测DC-DC电源模块稳定性?电源测试系统测试有什么优势?

DC-DC电源模块稳定性测试 稳定性是衡量DC电源模块的重要指标&#xff0c;电源模块的稳定性直接影响着电源产品和设备的工作稳定性。DC-DC电源模块的稳定性&#xff0c;可以通过检测输出电压、输出电流、负载、波形、效率等参数来评估。 1. 静态测试方法 静态测试是通过直流电压…

理解SwiftUI中的matchedGeometryEffect

matchedGeometryEffect SwiftUI 2.0 引入了一个新的修饰符&#xff1a;matchedGeometryEffect 。使用matchedGeometryEffect可以为整个层次结构中的视图创建过渡动画。只需要将它附加到要进行动画处理的两个视图上&#xff0c;并给它们指定相同的标识符。有了 matchedGeometry…

深耕元宇宙领域,强势发力文旅市场

2023年12月14日&#xff0c;“承上启下 智元宇宙&#xff1a;2024元宇宙与人工智能应用场景闭门会——苏州”在苏州泰山路2号百度VR&#xff08;苏州&#xff09;赋能中心成功举办。会议邀请了苏州本地的相关优秀企业代表、科创精英、投资与行业技术代表、公司创始人共计约50余…

龙迅LT6211B,HDMI1.4转LVDS,应用于AR/VR市场

产品描述 LT6211B 是一款用于 VR/ 显示应用的高性能 HDMI1.4 至 LVDS 芯片。 对于 LVDS 输出&#xff0c;LT6211B 可配置为单端口、双端口或四端口。对于2D视频流&#xff0c;同一视频流可以映射到两个单独的面板&#xff0c;对于3D视频格式&#xff0c;左侧数据可以发送到一个…

MySQL表的增删改查(初阶)

CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四个单词的首字母缩写。且增删改查&#xff08;CRUD&#xff0c;create&#xff0c;retrieve&#xff0c;update&#xff0c;delete&#xff09;数据库的核心模块。 1. 新增&#xff08;Create&#xff09; 实…

去面试性能测试工程师必问的问题,

性能测试的三个核心原理是什么&#xff1f; 1.基于协议。性能测试的对象是网络分布式架构的软件&#xff0c;而网络分布式架构的核心是网络协议 2.多线程。人的大脑是单线程的&#xff0c;电脑的cpu是多线程的。性能测试就是利用多线程的技术模拟多用户去负载 3.模拟真实场景。…

(详解版)创建线程的四种方式

文章目录 Java中创建线程的四种方式1. 继承Thread类并重写 run 方法来创建线程2. 实现Runnable接口并实现 run 方法来创建线程。3. 使用Callable接口创建线程4. 使用Executor框架创建线程 Java中创建线程的四种方式 接下来我会详细解释这四种方式创建线程如何实现. 我们如果要…

STM32——串口通信应用篇

一、引言 STM32微控制器是一款功能强大的嵌入式系统芯片&#xff0c;广泛应用于各种领域。其中&#xff0c;串口通信是其重要功能之一&#xff0c;可用于与外部设备进行数据交换和控制。本文将介绍STM32串口通信的基本原理、应用场景以及实现方法。 二、STM32串口通信基本原理 …

linux xxd命令(将文件或标准输入转换为hex(十六进制)和ASCII(美国信息交换标准代码)表示,或者从hex dump(十六进制转储)反向到二进制)

文章目录 Linux xxd命令安装xxd基本使用方法创建hex dump从hex dump恢复到二进制 命令选项疑难技术点解析在脚本中使用xxd从hex dump恢复数据 总结 Linux xxd命令 xxd是一个在Linux和UNIX系统中常用的工具&#xff0c;主要用于将文件或标准输入转换为hex&#xff08;十六进制&…

Java中线程状态的描述

多线程-基础方法的认识 截止目前线程的复习 Thread 类 创建Thread类的方法 继承Thread类,重写run方法实现Runnable接口,重写run方法使用匿名内部类继承Thread类,重写run方法使用匿名内部类实现Runnable接口,重写run方法使用Lambda表达式 run方法中的所有的代码是当前线程对…

[Linux] LVS负载均衡群集——DR模式

一、 DR模式的特点 直接路由&#xff1a; 在LVS_DR模式下&#xff0c;负载均衡器不修改数据包的IP地址&#xff0c;只修改目的MAC地址。这使得数据包可以直接路由到后端实际服务器上&#xff0c;而不需要返回到负载均衡器。 高性能&#xff1a; 由于数据包在传输过程中不需要回…

dubbo--03--- dubbo 支持的9种协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Dubbo框架特性Dubbo 和 Spring Cloud区别 dubbo 支持的9种协议协议类型1、dubbo 协议 (默认)特性配置常见问题 2、rmi 协议3、hessian 协议4、http 协议特性 5、web…

案例073:基于微信小程序的智慧旅游平台开发

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…