Newer
Older
CasicTimeGuard / src / main / java / com / casic / swing / ui / TimeGuardNtp.java
package com.casic.swing.ui;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.casic.swing.utils.*;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import okhttp3.Request;

import javax.swing.*;
import java.awt.*;
import java.net.InetAddress;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author a203
 */
public class TimeGuardNtp extends JFrame {
    private JPanel ntpPanel;
    private JLabel currentTimeLabel;
    private JPanel dotPanel;
    private JLabel stateView;
    private JLabel recentlyTimeLabel;
    private JLabel frequencyLabel;
    private JTextField hostTextField;
    private JComboBox<String> localHostBox;
    private JCheckBox autoCheckBox;
    private JButton updateTimeButton;
    private JLabel udpPortLabel;
    private JLabel udpPortTitle;

    private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build());
    private ScheduledFuture future;
    private boolean hasNtp = false;
    private final String serverConfig;

    private boolean isTaskRunning = false;

    public static void main(String[] args) {
        new TimeGuardNtp();
    }

    public TimeGuardNtp() {
        setContentPane(ntpPanel);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setSize(400, 300);
        //居中
        setLocationRelativeTo(null);
        setVisible(true);
        stateView.setText("未同步");
        recentlyTimeLabel.setText("无法确定最近同步时间");
        setStateView(Color.GRAY);

        //检查环境
        new SwingWorker<Boolean, Void>() {

            @Override
            protected Boolean doInBackground() {
                hasNtp = CommandUtil.checkEnv();
                return hasNtp;
            }

            @Override
            protected void done() {
                if (!hasNtp) {
                    JOptionPane.showMessageDialog(ntpPanel, "未当前设备发现可用的NTP配置", "Runtime Error", JOptionPane.ERROR_MESSAGE);
                    //环境不对,关闭窗体
                    dispose();
                }
                super.done();
            }
        }.execute();

        // 获取后台地址的配置
        serverConfig = ConfigHelper.getConfigData();

        // 每秒更新当前时间
        updateCurrentTimePerSecond();

        // 获取同步时间参数
        getParamTimeSync();

        // 获取监听的udp端口
        getParamCmdUdpPort();

        // 获取默认的ntp服务器地址
        getNtpServerAddress();

        //初始化JComboBox
        List<InetAddress> addressList = HttpRequestHelper.localHost();
        for (InetAddress address : addressList) {
            localHostBox.addItem(address.getHostAddress());
        }

        // 绑定自动同步复选框事件
        autoCheckBox.addActionListener(e -> {
            JCheckBox checkBox = (JCheckBox) e.getSource();
            if (checkBox.isSelected()) {
                updateTimeButton.setEnabled(false);
                hostTextField.setEnabled(false);

                String host = hostTextField.getText().trim();
                if (host.isEmpty()) {
                    autoCheckBox.setSelected(false);
                    updateTimeButton.setEnabled(true);
                    hostTextField.setEnabled(true);
                    JOptionPane.showMessageDialog(ntpPanel, "授时中心服务器输入错误,请检查", "Runtime Error", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // 立即执行一次任务
                future = executorService.schedule(this::updateView, 1, TimeUnit.SECONDS);
            } else {
                if (null != future) {
                    future.cancel(true);
                }
                updateTimeButton.setEnabled(true);
                hostTextField.setEnabled(true);
            }
        });

        //按钮点击事件
        updateTimeButton.addActionListener(e -> updateView());
    }

    private void updateView() {
        String host = hostTextField.getText().trim();
        if (host.isEmpty()) {
            autoCheckBox.setSelected(false);
            updateTimeButton.setEnabled(true);
            hostTextField.setEnabled(true);
            JOptionPane.showMessageDialog(ntpPanel, "授时中心服务器输入错误,请检查", "Runtime Error", JOptionPane.ERROR_MESSAGE);
            return;
        }

        if (null != future) {
            future.cancel(false);
        }

        // 重新获取同步周期
        getParamTimeSync();

//        String result = CommandUtil.simulateNtpResult();
        String result = CommandUtil.ntpDate(host);
        LogToFile.save(result);

        // 重新启动定时任务
        if (autoCheckBox.isSelected() == true) {
            future = executorService.schedule(this::updateView, Integer.parseInt(frequencyLabel.getText()) * 60 - 5, TimeUnit.SECONDS);
        }

        // 向后台接口提交ntp同步校时的结果
        JSONObject object = new JSONObject();
        if ("".equals(result)) {
            setStateView(Color.RED);
            stateView.setText("同步失败");
            recentlyTimeLabel.setText("");

            object.put("deltaTime", "");
            object.put("status", 0);
        } else {
            String systemTime = TimeOrDateUtil.timestampToTime();

            String resultString = result.split(":")[3];
            boolean isSuccess = !resultString.contains("no server suitable");
            if (isSuccess) {
                setStateView(Color.GREEN);
                stateView.setText("同步成功");
                recentlyTimeLabel.setText(systemTime);

                String[] dataSplit = resultString.split(" ");
                object.put("deltaTime", dataSplit[6]);
                object.put("status", 1);
            } else {
                setStateView(Color.RED);
                stateView.setText("同步失败");
                recentlyTimeLabel.setText("");

                object.put("deltaTime", "");
                object.put("status", 0);
            }
            //POST提交
            try {
                object.put("deviceIp", localHostBox.getSelectedItem());
                object.put("ntpResult", result);
                object.put("createTime", systemTime);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Request request = new Request.Builder()
                    .url(serverConfig + Constant.SYNCHRONIZE_URL)
                    .post(HttpRequestHelper.createRequestBody(object.toJSONString()))
                    .build();
            HttpRequestHelper.doHttpRequest(request, new IHttpCallback() {
                @Override
                public void onSuccess(String s) {
                    System.out.println("保存同步结果成功:" + object.get("deltaTime"));
                }

                @Override
                public void onFailure(Exception e) {
                    System.out.println("保存同步结果失败:" + e.getMessage());
                }
            });
        }
    }

    private void setStateView(Color color) {
        dotPanel.setPreferredSize(new Dimension(15, 15));
        dotPanel.setBackground(color);
    }

    private void getParamTimeSync() {
        Request request = new Request.Builder().url(serverConfig + Constant.FREQUENCY_URL).build();
        HttpRequestHelper.doHttpRequest(request, new IHttpCallback() {
            @Override
            public void onSuccess(String s) {
                frequencyLabel.setText(StringHelper.parseJson(s));
            }

            @Override
            public void onFailure(Exception e) {

            }
        });
    }

    private void getParamCmdUdpPort() {
        Request request = new Request.Builder().url(serverConfig + Constant.CMD_UDP_PORT).build();
        HttpRequestHelper.doHttpRequest(request, new IHttpCallback() {
            @Override
            public void onSuccess(String s) {
                udpPortLabel.setText(StringHelper.parseJson(s));

                startCmdUdpServer();
            }

            @Override
            public void onFailure(Exception e) {

            }
        });
    }

    private void getNtpServerAddress() {
        Request request = new Request.Builder().url(serverConfig + Constant.NTP_SERV_ADDR).build();
        HttpRequestHelper.doHttpRequest(request, new IHttpCallback() {
            @Override
            public void onSuccess(String s) {
                System.out.println(StringHelper.parseJson(s));
                hostTextField.setText(StringHelper.parseJson(s));
            }

            @Override
            public void onFailure(Exception e) {
                hostTextField.setText("");
            }
        });
    }

    private void updateCurrentTimePerSecond() {
        // 当前时间字体颜色为蓝色
        currentTimeLabel.setForeground(Color.BLUE);

        // 每秒更新
        new Timer(1000, e -> {
            String systemTime = TimeOrDateUtil.timestampToTime();
            currentTimeLabel.setText(systemTime);
        }).start();
    }

    private void startCmdUdpServer() {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();//
            b.group(workerGroup).channel(NioDatagramChannel.class)//设置UDP通道
                    .handler(new SimpleChannelInboundHandler()  {
                        @Override
                        protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
                            try {
                                DatagramPacket datagramPacket = (DatagramPacket) o;
                                String msg = datagramPacket.content().toString(CharsetUtil.UTF_8);

                                if (msg.trim().toUpperCase().equals("SYNC")) {
                                    updateView();
                                }
                            } catch (Exception e) {
                                throw e;
                            }
                        }
                    })//初始化处理器
                    .option(ChannelOption.SO_BROADCAST, true);// 支持广播

            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(Integer.parseInt(udpPortLabel.getText())).sync();

            udpPortTitle.setText("监听端口[已监听]:");

            //让线程进入wait状态,也就是main线程暂时不会执行到finally里面,nettyserver也持续运行,如果监听到关闭事件,可以优雅的关闭通道和nettyserver
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("Exception:" + e.getMessage());
            workerGroup.shutdownGracefully();
        }
    }
}