Browse Source
* commit '9962c24492dc5bec1854a54f58e91a5eae0d9fac': (118 commits) REPORT-71839 设计器启动时启动服务器,仍连曾断开的服务器,会有远程更新提醒 REPORT-67316 决策报表-客户模板是绝对布局固定大小的,复制客户模板里的tab到一个新建的绝对布局固定大小frm,保存关闭再打开新建模板,tab下的报表块尺寸变了 REPORT-71824 frm打开报表块编辑页面,切换工作目录无法打开当前frm REPORT-73808 海外版alphafine功能问题 KERNEL-11501 去除classhelper REPORT-74100【冒烟】远程BI环境,13个jar全部提示缺失 BI 集成的 JAR 或者之前的环境, 这里需要进行判断是否需要进行检查。 REPORT-73970 手动修改参数面板宽度未生效 1、REPORT-66771改动导致,在dolayout的时候修改了组件宽度 2、改为,在form宽度修改事件中修改组件宽度 REPORT-73996-新建frm预览空白 KERNEL-11531 修改下设计,ColoneCollector中触发clone() REPORT-73802 【设计器环境监测】windows下没有finedb权限,首次启动设计器能起来,第二次就启动失败了 KERNEL-11531 数据链接越权漏洞调用com.fr.invoke.ClassHelper中遍历搜索对象存在空间时间效率问题导致宕机 单独写一个 DefaultLoginKeys REPORT-72595 FR源码中存在加密密钥硬编码,建议放到配置文件中 REPORT-73833【设计器环境检测】本地目录,只有finedb异常,自动弹窗没弹 需要对使用后埋点。之前是使用的 Stream. 埋点后 Stream 流就不能用了。 这边换成 Collection 适配一下。 REPORT-73811 图表条件属性汇总字段值,汇总方式下拉内容显示了两遍 REPORT-72819 弹窗显示不全 REPORT-73833【设计器环境检测】本地目录,只有finedb异常,自动弹窗没弹 1-这里容易吞异常,修改一下。 2-标记为 Careful REPORT-73488 使用取色器配置颜色后,最近使用的颜色列表不会被更新 KERNEL-11517 实现主题色时ClassHelper中遍历搜索对象存在性能问题 REPORT-72851 去除无用导入 ...fix-lag
superman
2 years ago
161 changed files with 6229 additions and 664 deletions
@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.base.function; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* 可抛出异常的 Runnable |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public interface ThrowableRunnable<T extends Exception> { |
||||||
|
|
||||||
|
void run() throws T; |
||||||
|
|
||||||
|
static <T extends Exception> Runnable toRunnable(ThrowableRunnable<T> runnable) { |
||||||
|
|
||||||
|
return () -> { |
||||||
|
try { |
||||||
|
runnable.run(); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public interface NotificationAction { |
||||||
|
|
||||||
|
/** |
||||||
|
* 行为名 |
||||||
|
* |
||||||
|
* @return 名称 |
||||||
|
*/ |
||||||
|
String name(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 行为动作 |
||||||
|
* |
||||||
|
* @param args 参数 |
||||||
|
*/ |
||||||
|
void run(Object... args); |
||||||
|
} |
@ -0,0 +1,391 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
import com.fr.base.function.ThrowableRunnable; |
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.components.page.PageControlModel; |
||||||
|
import com.fr.design.components.page.PageControlPanel; |
||||||
|
import com.fr.design.dialog.link.MessageWithLink; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.VerticalFlowLayout; |
||||||
|
import com.fr.design.utils.LinkStrUtils; |
||||||
|
import com.fr.env.detect.base.EnvDetectorConfig; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JScrollPane; |
||||||
|
import javax.swing.ScrollPaneConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Desktop; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.net.URI; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.function.Function; |
||||||
|
import java.util.function.Supplier; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 右下角的提醒 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=388333688">异常提醒</a> |
||||||
|
* 相关使用方式见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=415212013">提醒组件</a> |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class NotificationDialog extends JDialog { |
||||||
|
|
||||||
|
/** |
||||||
|
* 通知框的内部高度 |
||||||
|
*/ |
||||||
|
private static final int CONTENT_INNER_HEIGHT = 60; |
||||||
|
/** |
||||||
|
* 通知框如果出现滚动条后的内部宽度 |
||||||
|
*/ |
||||||
|
private static final int CONTENT_SCROLL_WIDTH = 280; |
||||||
|
|
||||||
|
private static final int CONTENT_WIDTH = 300; |
||||||
|
private static final int CONTENT_HEIGHT = 100; |
||||||
|
/** |
||||||
|
* 通知框的外部宽高 |
||||||
|
*/ |
||||||
|
private static final Dimension CONTENT_SIZE = new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT); |
||||||
|
private static final Dimension BUTTON_DIMENSION = new Dimension(68, 20); |
||||||
|
|
||||||
|
/** |
||||||
|
* 标记 LABEL, 没有作用 |
||||||
|
*/ |
||||||
|
private static final UILabel SIGN_LABEL = new UILabel("#"); |
||||||
|
/** |
||||||
|
* 确认一个 LABEL 的宽高 |
||||||
|
*/ |
||||||
|
private static final Dimension SIGN_LABEL_DIMENSION = SIGN_LABEL.getPreferredSize(); |
||||||
|
|
||||||
|
private NotificationDialogProperties properties; |
||||||
|
|
||||||
|
/* 数据 model */ |
||||||
|
|
||||||
|
private List<NotificationModel> notificationModels; |
||||||
|
private PageControlModel pageControlModel; |
||||||
|
|
||||||
|
private JPanel body; |
||||||
|
private JPanel headerPanel; |
||||||
|
private JPanel contentPanel; |
||||||
|
private JPanel tailPanel; |
||||||
|
|
||||||
|
public NotificationDialog(NotificationDialogProperties properties, List<NotificationModel> notificationModels) { |
||||||
|
|
||||||
|
super(properties.getOwner()); |
||||||
|
setTitle(properties.getTitle()); |
||||||
|
this.properties = properties; |
||||||
|
|
||||||
|
this.notificationModels = notificationModels; |
||||||
|
this.pageControlModel = new PageControlModel(0, this.notificationModels.size()); |
||||||
|
|
||||||
|
initComponents(); |
||||||
|
} |
||||||
|
|
||||||
|
public void initComponents() { |
||||||
|
|
||||||
|
//UI 配置
|
||||||
|
configProperties(); |
||||||
|
|
||||||
|
this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
body.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||||
|
|
||||||
|
//首行
|
||||||
|
layoutHeaderPanel(); |
||||||
|
|
||||||
|
//消息内容
|
||||||
|
layoutContentPanel(); |
||||||
|
|
||||||
|
//查看详情
|
||||||
|
layoutTailPanel(); |
||||||
|
|
||||||
|
add(body); |
||||||
|
|
||||||
|
Dimension dimension = body.getPreferredSize(); |
||||||
|
setSize(dimension.width, dimension.height); |
||||||
|
|
||||||
|
Container parent = getParent(); |
||||||
|
setLocation((parent.getWidth() - dimension.width - 30 + parent.getX()), |
||||||
|
parent.getY() + parent.getHeight() - dimension.height - 30); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void open() { |
||||||
|
|
||||||
|
setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private void configProperties() { |
||||||
|
|
||||||
|
setModal(properties.isModal()); |
||||||
|
setFocusable(false); |
||||||
|
setAutoRequestFocus(false); |
||||||
|
setResizable(false); |
||||||
|
} |
||||||
|
|
||||||
|
protected JPanel createHeaderPanel() { |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 内容 |
||||||
|
* |
||||||
|
* @return 内容面板 |
||||||
|
*/ |
||||||
|
protected JPanel createContentPanel() { |
||||||
|
|
||||||
|
JPanel contentPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
contentPanel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10)); |
||||||
|
contentPanel.setName("contentPanel"); |
||||||
|
|
||||||
|
NotificationModel model = getCurrentModel(); |
||||||
|
|
||||||
|
UILabel icon = new UILabel(getIconForType(model.getType())); |
||||||
|
icon.setPreferredSize(new Dimension(16, 16)); |
||||||
|
JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 10, 8)); |
||||||
|
iconPanel.add(icon, BorderLayout.NORTH); |
||||||
|
|
||||||
|
contentPanel.add(iconPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); |
||||||
|
|
||||||
|
NotificationMessage[] messages = model.getMessages(); |
||||||
|
List<? extends JComponent> messageComponents = Arrays.stream(messages) |
||||||
|
.map((messageModel) -> { |
||||||
|
if (messageModel.getType() == NotificationMessage.Type.LINK) { |
||||||
|
NotificationMessage.LinkMessage linkMessage = (NotificationMessage.LinkMessage) messageModel; |
||||||
|
return new MessageWithLink(linkMessage.format(), ThrowableRunnable.toRunnable(() -> { |
||||||
|
Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); |
||||||
|
})); |
||||||
|
} |
||||||
|
return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format())); |
||||||
|
}) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
// 当高度 大于 60 时,就会出现滚动条。
|
||||||
|
// 当出现滚动条时,需要将内部的宽度限制为 280, 否则会展示不出来
|
||||||
|
Function<Double, Integer> calStandardWidth = height -> height > CONTENT_INNER_HEIGHT ? CONTENT_SCROLL_WIDTH : CONTENT_WIDTH; |
||||||
|
|
||||||
|
int widthUnit = messageComponents.stream() |
||||||
|
.map((component) -> { |
||||||
|
Dimension preferredSize = component.getPreferredSize(); |
||||||
|
double width = preferredSize.getWidth(); |
||||||
|
double widthFactor = Math.ceil(width / CONTENT_WIDTH); |
||||||
|
// 这里的高度是没有限制宽度的,如果限制宽度,高度会变更,所以这里需要加上宽度的影响
|
||||||
|
return preferredSize.getHeight() + widthFactor * SIGN_LABEL_DIMENSION.getHeight(); |
||||||
|
}) |
||||||
|
.reduce(Double::sum) |
||||||
|
.map(calStandardWidth) |
||||||
|
.orElse(CONTENT_WIDTH); |
||||||
|
|
||||||
|
messageComponents = messageComponents.stream() |
||||||
|
.peek((component) -> { |
||||||
|
Dimension preferredSize = component.getPreferredSize(); |
||||||
|
double componentWidth = preferredSize.getWidth(); |
||||||
|
double componentHeight = preferredSize.getHeight(); |
||||||
|
double heightFactor = Math.ceil(componentHeight / SIGN_LABEL_DIMENSION.getHeight()); |
||||||
|
double widthFactor = Math.ceil(componentWidth / widthUnit); |
||||||
|
int realHeight = (int)Math.ceil(heightFactor + widthFactor - 1) * (int)(Math.ceil(SIGN_LABEL_DIMENSION.getHeight())); |
||||||
|
component.setPreferredSize(new Dimension(widthUnit, realHeight)); |
||||||
|
}) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
// 竖向排列
|
||||||
|
JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); |
||||||
|
messageComponents.forEach(messageSummaryPanel::add); |
||||||
|
|
||||||
|
JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
||||||
|
jScrollPane.setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
|
||||||
|
centerPanel.add(jScrollPane, BorderLayout.CENTER); |
||||||
|
centerPanel.setPreferredSize(CONTENT_SIZE); |
||||||
|
|
||||||
|
contentPanel.add(centerPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
return contentPanel; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 行动 |
||||||
|
* |
||||||
|
* UI布局 |
||||||
|
* /翻页/不再提醒/提醒行为/我知道了 |
||||||
|
* |
||||||
|
* @return 行动面板 |
||||||
|
*/ |
||||||
|
protected JPanel createTailPanel() { |
||||||
|
|
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
tailPanel.setName("tailPanel"); |
||||||
|
|
||||||
|
// 翻页按钮效果
|
||||||
|
PageControlPanel pageControlPanel = new PageControlPanel(pageControlModel); |
||||||
|
|
||||||
|
pageControlPanel.actions(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
pageControlModel = pageControlPanel.performPrevious(); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
}, new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
pageControlModel = pageControlPanel.performNext(); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
tailPanel.add(pageControlPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
// 行为效果
|
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||||
|
|
||||||
|
{ |
||||||
|
actionsPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
actionsPanel.setName("actionsPanel"); |
||||||
|
|
||||||
|
UILabel notReminder = new UILabel(); |
||||||
|
notReminder.setText(Toolkit.i18nText("Fine-Design_Basic_Not_Reminder")); |
||||||
|
notReminder.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
// 配置处理
|
||||||
|
EnvDetectorConfig.getInstance().setEnabled(false); |
||||||
|
// 点击事件
|
||||||
|
destroy(); |
||||||
|
} |
||||||
|
}); |
||||||
|
Color color = new Color(65, 155, 249); |
||||||
|
notReminder.setForeground(color); |
||||||
|
actionsPanel.add(notReminder, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel buttonPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||||
|
buttonPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
|
||||||
|
// real-action
|
||||||
|
NotificationModel currentModel = getCurrentModel(); |
||||||
|
NotificationAction action = currentModel.getAction(); |
||||||
|
if (action != null) { |
||||||
|
UIButton actionButton = new UIButton(action.name()); |
||||||
|
actionButton.setPreferredSize(BUTTON_DIMENSION); |
||||||
|
actionButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
action.run(); |
||||||
|
} |
||||||
|
}); |
||||||
|
buttonPanel.add(actionButton, BorderLayout.WEST); |
||||||
|
} |
||||||
|
|
||||||
|
UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); |
||||||
|
knowButton.setPreferredSize(BUTTON_DIMENSION); |
||||||
|
knowButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (pageControlModel.isLast()) { |
||||||
|
destroy(); |
||||||
|
return; |
||||||
|
} |
||||||
|
pageControlModel = pageControlPanel.performNext(); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
}); |
||||||
|
buttonPanel.add(knowButton, BorderLayout.EAST); |
||||||
|
|
||||||
|
actionsPanel.add(buttonPanel, BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
|
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void refresh() { |
||||||
|
|
||||||
|
layoutContentPanel(); |
||||||
|
|
||||||
|
layoutTailPanel(); |
||||||
|
|
||||||
|
this.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
private void layoutHeaderPanel() { |
||||||
|
|
||||||
|
this.headerPanel = layoutPanel(this.headerPanel, this::createHeaderPanel, BorderLayout.NORTH); |
||||||
|
} |
||||||
|
|
||||||
|
private void layoutTailPanel() { |
||||||
|
|
||||||
|
this.tailPanel = layoutPanel(this.tailPanel, this::createTailPanel, BorderLayout.SOUTH); |
||||||
|
} |
||||||
|
|
||||||
|
private void layoutContentPanel() { |
||||||
|
|
||||||
|
this.contentPanel = layoutPanel(this.contentPanel, this::createContentPanel, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel layoutPanel(JPanel oldPanel, Supplier<JPanel> supplier, Object constraints){ |
||||||
|
|
||||||
|
if (oldPanel != null) { |
||||||
|
this.body.remove(oldPanel); |
||||||
|
} |
||||||
|
JPanel newPanel = supplier.get(); |
||||||
|
if (newPanel != null) { |
||||||
|
this.body.add(newPanel, constraints); |
||||||
|
} |
||||||
|
return newPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private NotificationModel getCurrentModel() { |
||||||
|
|
||||||
|
int index = pageControlModel.getIndex(); |
||||||
|
return notificationModels.get(index); |
||||||
|
} |
||||||
|
|
||||||
|
protected Icon getIconForType(NotificationType type) { |
||||||
|
|
||||||
|
String iconPath; |
||||||
|
switch (type) { |
||||||
|
case ERROR: |
||||||
|
iconPath = "/com/fr/design/standard/reminder/reminder_error.svg"; |
||||||
|
break; |
||||||
|
case INFO: |
||||||
|
iconPath = "/com/fr/design/standard/reminder/reminder_success.svg"; |
||||||
|
break; |
||||||
|
case WARNING: |
||||||
|
iconPath = "/com/fr/design/standard/reminder/reminder_warning.svg"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return null; |
||||||
|
} |
||||||
|
return IconUtils.readIcon(iconPath); |
||||||
|
} |
||||||
|
|
||||||
|
private void destroy() { |
||||||
|
|
||||||
|
setVisible(false); |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dispose() { |
||||||
|
|
||||||
|
super.dispose(); |
||||||
|
// todo
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
import java.awt.Frame; |
||||||
|
|
||||||
|
/** |
||||||
|
* 通知会话的属性 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class NotificationDialogProperties { |
||||||
|
|
||||||
|
private Frame owner; |
||||||
|
|
||||||
|
private String title; |
||||||
|
|
||||||
|
private boolean modal; |
||||||
|
|
||||||
|
public NotificationDialogProperties(Frame owner, String title) { |
||||||
|
this.owner = owner; |
||||||
|
this.title = title; |
||||||
|
this.modal = false; |
||||||
|
} |
||||||
|
|
||||||
|
public void setModal(boolean modal) { |
||||||
|
this.modal = modal; |
||||||
|
} |
||||||
|
|
||||||
|
public Frame getOwner() { |
||||||
|
return owner; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTitle() { |
||||||
|
return title; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isModal() { |
||||||
|
return modal; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
import com.fr.design.utils.LinkStrUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public interface NotificationMessage { |
||||||
|
|
||||||
|
/** |
||||||
|
* 格式化 |
||||||
|
* |
||||||
|
* @return 通知信息 |
||||||
|
*/ |
||||||
|
String format(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 类型 |
||||||
|
* |
||||||
|
* @return 类型 |
||||||
|
*/ |
||||||
|
Type getType(); |
||||||
|
|
||||||
|
enum Type { |
||||||
|
|
||||||
|
/** |
||||||
|
* 简单型 |
||||||
|
*/ |
||||||
|
SIMPLE, |
||||||
|
|
||||||
|
/** |
||||||
|
* 链接 |
||||||
|
*/ |
||||||
|
LINK |
||||||
|
} |
||||||
|
|
||||||
|
class SimpleMessage implements NotificationMessage { |
||||||
|
|
||||||
|
private String text; |
||||||
|
|
||||||
|
public SimpleMessage(String text) { |
||||||
|
this.text = text; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String format() { |
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return Type.SIMPLE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class LinkMessage implements NotificationMessage { |
||||||
|
|
||||||
|
private String text; |
||||||
|
|
||||||
|
private String link; |
||||||
|
|
||||||
|
public LinkMessage(String text, String link) { |
||||||
|
|
||||||
|
this.text = text; |
||||||
|
this.link = link; |
||||||
|
} |
||||||
|
|
||||||
|
public String getText() { |
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLink() { |
||||||
|
return link; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String format() { |
||||||
|
|
||||||
|
return LinkStrUtils.generateHtmlTag(text); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
|
||||||
|
return Type.LINK; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class NotificationModel { |
||||||
|
|
||||||
|
private final NotificationType type; |
||||||
|
private final NotificationMessage[] messages; |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private final NotificationAction action; |
||||||
|
|
||||||
|
public NotificationModel(NotificationType type, @Nullable NotificationAction action, List<NotificationMessage> messages) { |
||||||
|
this(type, action, messages.toArray(new NotificationMessage[0])); |
||||||
|
} |
||||||
|
|
||||||
|
public NotificationModel(NotificationType type, @Nullable NotificationAction action, NotificationMessage... messages) { |
||||||
|
|
||||||
|
this.type = type; |
||||||
|
this.messages = messages; |
||||||
|
this.action = action; |
||||||
|
} |
||||||
|
|
||||||
|
public NotificationType getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public NotificationMessage[] getMessages() { |
||||||
|
return messages == null ? new NotificationMessage[0] : messages; |
||||||
|
} |
||||||
|
|
||||||
|
public @Nullable NotificationAction getAction() { |
||||||
|
|
||||||
|
return action; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
/** |
||||||
|
* 提醒类型 |
||||||
|
* 决定图标种类 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public enum NotificationType { |
||||||
|
|
||||||
|
INFO, |
||||||
|
|
||||||
|
ERROR, |
||||||
|
|
||||||
|
WARNING |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
package com.fr.design.components.page; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class PageControlModel { |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前索引 |
||||||
|
* |
||||||
|
* = (页数-1) |
||||||
|
*/ |
||||||
|
private int index; |
||||||
|
|
||||||
|
/** |
||||||
|
* 总页数 |
||||||
|
*/ |
||||||
|
private int summary; |
||||||
|
|
||||||
|
public PageControlModel(int index, int summary) { |
||||||
|
this.index = index; |
||||||
|
this.summary = summary; |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel() { |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel previous() { |
||||||
|
|
||||||
|
this.index--; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel next() { |
||||||
|
|
||||||
|
this.index++; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 页数 |
||||||
|
* index+1 |
||||||
|
* |
||||||
|
* @return 页数 |
||||||
|
*/ |
||||||
|
public int getNumber() { |
||||||
|
return index + 1; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isFirst() { |
||||||
|
return getNumber() == 1; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isLast() { |
||||||
|
return getNumber() == getSummary(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
public void setIndex(int index) { |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
public int getSummary() { |
||||||
|
return summary; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSummary(int summary) { |
||||||
|
this.summary = summary; |
||||||
|
} |
||||||
|
|
||||||
|
public String toContent() { |
||||||
|
|
||||||
|
return getNumber() + "/" + this.summary; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
package com.fr.design.components.page; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.util.function.Function; |
||||||
|
|
||||||
|
/** |
||||||
|
* 翻页组件 |
||||||
|
* 相关使用方式见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=415212013">翻页组件</a> |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class PageControlPanel extends JPanel { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 8140501834691131305L; |
||||||
|
|
||||||
|
private static final Dimension PAGE_CONTROL_BUTTON_DIMENSION = new Dimension(20, 20); |
||||||
|
|
||||||
|
private UIButton previous; |
||||||
|
private Runnable previousAction; |
||||||
|
|
||||||
|
private UILabel content; |
||||||
|
private PageControlModel model; |
||||||
|
|
||||||
|
private UIButton next; |
||||||
|
private Runnable nextAction; |
||||||
|
|
||||||
|
public PageControlPanel(PageControlModel model) { |
||||||
|
|
||||||
|
this.model = model; |
||||||
|
setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
setLayout(new BorderLayout(6, 0)); |
||||||
|
|
||||||
|
this.previous = new UIButton(); |
||||||
|
previous.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); |
||||||
|
previous.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_left.svg")); |
||||||
|
previous.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_left.svg")); |
||||||
|
previous.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
performAction(previousAction); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
this.add(previous, BorderLayout.WEST); |
||||||
|
|
||||||
|
this.content = new UILabel(model.toContent()); |
||||||
|
this.add(content, BorderLayout.CENTER); |
||||||
|
|
||||||
|
next = new UIButton(); |
||||||
|
next.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); |
||||||
|
next.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_right.svg")); |
||||||
|
next.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_right.svg")); |
||||||
|
next.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
performAction(nextAction); |
||||||
|
} |
||||||
|
}); |
||||||
|
this.add(next, BorderLayout.EAST); |
||||||
|
|
||||||
|
refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel performPrevious() { |
||||||
|
|
||||||
|
update(PageControlModel::previous); |
||||||
|
return this.model; |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel performNext() { |
||||||
|
|
||||||
|
update(PageControlModel::next); |
||||||
|
return this.model; |
||||||
|
} |
||||||
|
|
||||||
|
public PageControlModel getModel() { |
||||||
|
|
||||||
|
return this.model; |
||||||
|
} |
||||||
|
|
||||||
|
public void update(PageControlModel model) { |
||||||
|
|
||||||
|
this.model.setIndex(model.getIndex()); |
||||||
|
this.model.setSummary(model.getSummary()); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
public void update(Function<PageControlModel, PageControlModel> updateAction) { |
||||||
|
|
||||||
|
PageControlModel newModel = updateAction.apply(this.model); |
||||||
|
update(newModel); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
public void refresh() { |
||||||
|
|
||||||
|
this.content.setText(this.model.toContent()); |
||||||
|
this.content.repaint(); |
||||||
|
|
||||||
|
this.previous.setEnabled(true); |
||||||
|
this.next.setEnabled(true); |
||||||
|
if (model.getNumber() == 1) { |
||||||
|
// 禁用上一个
|
||||||
|
disableButton(this.previous); |
||||||
|
// 禁用next
|
||||||
|
if (model.getNumber() == model.getSummary()) { |
||||||
|
disableButton(this.next); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 禁用next
|
||||||
|
if (model.getNumber() == model.getSummary()) { |
||||||
|
disableButton(this.next); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void enable(UIButton button) { |
||||||
|
|
||||||
|
button.setEnabled(true); |
||||||
|
} |
||||||
|
|
||||||
|
private void disableButton(UIButton button) { |
||||||
|
|
||||||
|
button.setEnabled(false); |
||||||
|
} |
||||||
|
|
||||||
|
private void performAction(Runnable action) { |
||||||
|
|
||||||
|
if (action != null) { |
||||||
|
action.run(); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void actions(Runnable previousAction, Runnable nextAction) { |
||||||
|
|
||||||
|
this.previousAction = previousAction; |
||||||
|
this.nextAction = nextAction; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,184 @@ |
|||||||
|
package com.fr.design.components.table; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.utils.ColorUtils; |
||||||
|
import com.fr.third.org.apache.commons.lang3.ArrayUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JSeparator; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.GridLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* 表头 |
||||||
|
* 内容 |
||||||
|
* |
||||||
|
* 适用于需要一个表格的 Panel |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class TablePanel extends JPanel { |
||||||
|
|
||||||
|
private static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); |
||||||
|
|
||||||
|
private static final Color DEFAULT_ODD_ROW_COLOR = new Color(245, 245, 247); |
||||||
|
|
||||||
|
private static final Color DEFAULT_EVEN_ROW_COLOR = Color.WHITE; |
||||||
|
|
||||||
|
private JPanel headerPanel; |
||||||
|
|
||||||
|
private JPanel[] headerItemPanels; |
||||||
|
|
||||||
|
private JPanel contentPanel; |
||||||
|
|
||||||
|
private JPanel[][] cellPanels; |
||||||
|
|
||||||
|
public TablePanel(int row, int column) { |
||||||
|
|
||||||
|
setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
|
||||||
|
/* header 部分 */ |
||||||
|
|
||||||
|
this.headerPanel = new JPanel(); |
||||||
|
headerPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); |
||||||
|
headerPanel.setName("header-panel"); |
||||||
|
headerPanel.setPreferredSize(new Dimension(640, 24)); |
||||||
|
|
||||||
|
// border
|
||||||
|
headerPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); |
||||||
|
syncHeaderColor(headerPanel); |
||||||
|
|
||||||
|
headerItemPanels = new JPanel[column]; |
||||||
|
for (int i = 0; i < column; i++) { |
||||||
|
JPanel headerItemWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
syncHeaderColor(headerItemWrapper); |
||||||
|
headerItemWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); |
||||||
|
|
||||||
|
JPanel headerItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
headerItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||||
|
headerItemPanels[i] = headerItemPanel; |
||||||
|
|
||||||
|
UILabel label = new UILabel(); |
||||||
|
syncHeaderColor(label); |
||||||
|
|
||||||
|
headerItemPanel.add(new UILabel(), BorderLayout.CENTER); |
||||||
|
|
||||||
|
headerItemWrapper.add(headerItemPanel, BorderLayout.WEST); |
||||||
|
if (i != column - 1) { |
||||||
|
JSeparator separator = new JSeparator(JSeparator.VERTICAL); |
||||||
|
separator.setBackground(new Color(218, 218, 221)); |
||||||
|
headerItemWrapper.add(separator, BorderLayout.EAST); |
||||||
|
} |
||||||
|
headerPanel.add(headerItemWrapper); |
||||||
|
} |
||||||
|
|
||||||
|
/* content 部分 */ |
||||||
|
|
||||||
|
contentPanel = new JPanel(); |
||||||
|
|
||||||
|
contentPanel.setLayout(new GridLayout(row, 1)); |
||||||
|
contentPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); |
||||||
|
|
||||||
|
cellPanels = new JPanel[row][column]; |
||||||
|
for (int i = 0; i < row; i++) { |
||||||
|
|
||||||
|
JPanel rowPanel = new JPanel(); |
||||||
|
// 获取行号
|
||||||
|
Color rowColor = getRowColorByRowNumber(i + 1); |
||||||
|
rowPanel.setBackground(rowColor); |
||||||
|
rowPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); |
||||||
|
rowPanel.setName("row-" + i); |
||||||
|
rowPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
rowPanel.setPreferredSize(new Dimension(640, 24)); |
||||||
|
|
||||||
|
for (int j = 0; j < column; j++) { |
||||||
|
|
||||||
|
JPanel rowItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
rowItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||||
|
rowItemPanel.setName("rowItemPanel-"+ i + "-" + j); |
||||||
|
final UILabel empty = new UILabel(); |
||||||
|
empty.setPreferredSize(new Dimension(210, 24)); |
||||||
|
rowItemPanel.setBackground(rowPanel.getBackground()); |
||||||
|
rowItemPanel.add(empty, BorderLayout.CENTER); |
||||||
|
|
||||||
|
rowPanel.add(rowItemPanel); |
||||||
|
cellPanels[i][j] = rowItemPanel; |
||||||
|
} |
||||||
|
|
||||||
|
contentPanel.add(rowPanel); |
||||||
|
} |
||||||
|
|
||||||
|
add(headerPanel, BorderLayout.NORTH); |
||||||
|
add(contentPanel, BorderLayout.SOUTH); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取行的颜色 |
||||||
|
* |
||||||
|
* @param row 行号 |
||||||
|
* @return 颜色 |
||||||
|
*/ |
||||||
|
private Color getRowColorByRowNumber(int row) { |
||||||
|
|
||||||
|
Color rowColor; |
||||||
|
if (row % 2 != 0) { |
||||||
|
rowColor = DEFAULT_EVEN_ROW_COLOR; |
||||||
|
} else { |
||||||
|
rowColor = DEFAULT_ODD_ROW_COLOR; |
||||||
|
} |
||||||
|
return rowColor; |
||||||
|
} |
||||||
|
|
||||||
|
public void updateHeaders(String[] headers) { |
||||||
|
|
||||||
|
for (int i = 0; i < headers.length; i++) { |
||||||
|
String header = headers[i]; |
||||||
|
UILabel headerContent = new UILabel(header); |
||||||
|
JPanel headerItemPanel = headerItemPanels[i]; |
||||||
|
if (ArrayUtils.getLength(headerItemPanel.getComponents()) == 1) { |
||||||
|
headerItemPanel.remove(0); |
||||||
|
} |
||||||
|
headerItemPanel.add(headerContent); |
||||||
|
syncHeaderColor(headerItemPanel); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void updateCell(int row, int column, Component component) { |
||||||
|
|
||||||
|
int x = row - 1; |
||||||
|
int y = column - 1; |
||||||
|
|
||||||
|
syncCellColor(row, component); |
||||||
|
|
||||||
|
JPanel cellPanel = this.cellPanels[x][y]; |
||||||
|
if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { |
||||||
|
cellPanel.remove(0); |
||||||
|
} |
||||||
|
cellPanel.add(component); |
||||||
|
} |
||||||
|
|
||||||
|
public void updateCell(int row, int column, String value) { |
||||||
|
|
||||||
|
UILabel cellContent = new UILabel(value); |
||||||
|
syncCellColor(row, cellContent); |
||||||
|
this.updateCell(row, column, cellContent); |
||||||
|
} |
||||||
|
|
||||||
|
private void syncHeaderColor(Component component) { |
||||||
|
|
||||||
|
ColorUtils.syncBackground(component, DEFAULT_HEADER_COLOR); |
||||||
|
} |
||||||
|
|
||||||
|
private void syncCellColor(int row, Component component) { |
||||||
|
|
||||||
|
Color rowColor = getRowColorByRowNumber(row); |
||||||
|
ColorUtils.syncBackground(component, rowColor); |
||||||
|
component.setBackground(rowColor); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.design.constants; |
||||||
|
|
||||||
|
import java.awt.Color; |
||||||
|
|
||||||
|
/** |
||||||
|
* 见 <a href="https://www.figma.com/file/G2f40rv7cY8zpDDYbt6pY2/%E8%AE%BE%E8%AE%A1%E5%99%A811.0%E8%A7%84%E8%8C%83%E6%95%B4%E7%90%86">设计器规范</a> |
||||||
|
* 将相关的逻辑抽象过来 |
||||||
|
* 如果后面更改的话, 可以统一修改 |
||||||
|
* 如果换版本,可以换成 v2 这种类推 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public interface DesignerColor { |
||||||
|
|
||||||
|
interface Button { |
||||||
|
|
||||||
|
interface Primary { |
||||||
|
|
||||||
|
Color PRESSED = new Color(29, 122, 220); |
||||||
|
Color HOVER = new Color(84, 165, 249); |
||||||
|
Color NORMAL = new Color(65, 155, 249); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.fr.design.login.config; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Lanlan |
||||||
|
* @version 10.0 |
||||||
|
* Created by Lanlan on 2022/6/20 |
||||||
|
*/ |
||||||
|
public class DefaultLoginKeys { |
||||||
|
|
||||||
|
private static final String FILENAME = "com/fr/design/config/default"; |
||||||
|
|
||||||
|
private static final DefaultLoginKeys INSTANCE = new DefaultLoginKeys(); |
||||||
|
|
||||||
|
public static DefaultLoginKeys getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private final Map<String, String> keys = new HashMap<>(); |
||||||
|
|
||||||
|
private DefaultLoginKeys() { |
||||||
|
Properties properties = load(); |
||||||
|
for (Map.Entry<Object, Object> entry : properties.entrySet()) { |
||||||
|
String name = entry.getKey().toString(); |
||||||
|
keys.put(name, entry.getValue().toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String getKey(String name) { |
||||||
|
return keys.get(name); |
||||||
|
} |
||||||
|
|
||||||
|
private Properties load() { |
||||||
|
Properties properties = new Properties(); |
||||||
|
try (InputStream inputStream = DefaultLoginKeys.class.getClassLoader().getResourceAsStream(FILENAME)) { |
||||||
|
properties.load(inputStream); |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
return properties; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.base.CloneCollector; |
||||||
|
import com.fr.base.Formula; |
||||||
|
import com.fr.data.impl.NameDatabaseConnection; |
||||||
|
import com.fr.data.impl.NameTableData; |
||||||
|
import com.fr.report.cell.cellattr.core.group.DSColumn; |
||||||
|
import com.fr.stable.FCloneable; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class AuthorityTargetObjectCollector extends CloneCollector { |
||||||
|
List<Object> targetObject = new ArrayList<>(); |
||||||
|
|
||||||
|
public AuthorityTargetObjectCollector(FCloneable rootObject) { |
||||||
|
super(rootObject); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void collect(Object object) { |
||||||
|
if (object instanceof DSColumn || object instanceof Formula |
||||||
|
|| object instanceof NameDatabaseConnection || object instanceof NameTableData) { |
||||||
|
targetObject.add(object); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public List<Object> collectTargetObject() { |
||||||
|
CloneCollector.setCollector(this); |
||||||
|
this.targetObject = new ArrayList<>(); |
||||||
|
this.collect(); |
||||||
|
CloneCollector.clearCollector(); |
||||||
|
return targetObject; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,144 +0,0 @@ |
|||||||
package com.fr.design.mainframe.theme.edit.ui; |
|
||||||
|
|
||||||
import com.fr.base.theme.FineColorManager; |
|
||||||
import com.fr.design.layout.FRGUIPaneFactory; |
|
||||||
|
|
||||||
import javax.swing.JPanel; |
|
||||||
import java.awt.Color; |
|
||||||
import java.awt.Dimension; |
|
||||||
import java.awt.Graphics; |
|
||||||
import java.awt.event.MouseEvent; |
|
||||||
import java.awt.event.MouseListener; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Starryi |
|
||||||
* @version 1.0 |
|
||||||
* Created by Starryi on 2021/8/7 |
|
||||||
*/ |
|
||||||
public class ColorListExtendedPane extends JPanel implements MouseListener { |
|
||||||
public static final int DEFAULT_COLOR_COUNT = 8; |
|
||||||
public static final int DEFAULT_EXTENDED_COUNT = 5; |
|
||||||
public static final int DEFAULT_COLOR_SIZE = 16; |
|
||||||
public static final int DEFAULT_COLOR_GAP = 3; |
|
||||||
|
|
||||||
public static final ExtendedColorComputer DEFAULT_EXTENDED_COMPUTER = new ExtendedColorComputer() { |
|
||||||
@Override |
|
||||||
public Color computeExtendedColor(Color color, int index, int count) { |
|
||||||
return FineColorManager.computeExtendedColor(color, index, count); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
private final boolean selectable; |
|
||||||
private final int colorCount; |
|
||||||
private final int extendedCount; |
|
||||||
private final int boxSize; |
|
||||||
private final int boxGap; |
|
||||||
|
|
||||||
private final List<Color> colorList = new ArrayList<>(); |
|
||||||
private ExtendedColorComputer extendedColorComputer = DEFAULT_EXTENDED_COMPUTER; |
|
||||||
|
|
||||||
private int selectedColorIndex = -1; |
|
||||||
private int selectedExtendedIndex = -1; |
|
||||||
|
|
||||||
public ColorListExtendedPane() { |
|
||||||
this(false, DEFAULT_COLOR_COUNT, DEFAULT_EXTENDED_COUNT, DEFAULT_COLOR_SIZE, DEFAULT_COLOR_GAP); |
|
||||||
} |
|
||||||
|
|
||||||
public ColorListExtendedPane(boolean selectable, int colorCount, int extendedCount, int boxSize, int boxGap) { |
|
||||||
setLayout(FRGUIPaneFactory.createBorderLayout()); |
|
||||||
this.selectable = selectable; |
|
||||||
this.colorCount = Math.max(1, colorCount); |
|
||||||
this.extendedCount = extendedCount; |
|
||||||
this.boxSize = boxSize; |
|
||||||
this.boxGap = boxGap; |
|
||||||
|
|
||||||
for (int i = 0; i < colorCount; i++) { |
|
||||||
colorList.add(Color.WHITE); |
|
||||||
} |
|
||||||
|
|
||||||
int width = colorCount * boxSize + (colorCount - 1) * boxGap; |
|
||||||
int height = extendedCount * boxSize + (extendedCount - 1) * boxGap; |
|
||||||
setPreferredSize(new Dimension(width, height)); |
|
||||||
} |
|
||||||
|
|
||||||
public void populate(List<Color> colors) { |
|
||||||
if (colors.size() > 0) { |
|
||||||
colorList.clear(); |
|
||||||
colorList.addAll(colors); |
|
||||||
repaint(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public List<Color> update() { |
|
||||||
return new ArrayList<>(colorList); |
|
||||||
} |
|
||||||
|
|
||||||
public void setExtendedColorComputer(ExtendedColorComputer extendedColorComputer) { |
|
||||||
this.extendedColorComputer = extendedColorComputer; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void mouseClicked(MouseEvent e) { |
|
||||||
int x = e.getX(); |
|
||||||
int y = e.getY(); |
|
||||||
|
|
||||||
int colorIndex = x / (boxSize + boxGap); |
|
||||||
int colorX = colorIndex * (boxSize + boxGap); |
|
||||||
|
|
||||||
int extendedIndex = y / boxSize; |
|
||||||
int extendedY = extendedIndex * boxSize; |
|
||||||
|
|
||||||
if (colorX <= x && x <= colorX + boxSize && extendedY <= y && y <= extendedY + boxSize) { |
|
||||||
selectedColorIndex = colorIndex; |
|
||||||
selectedExtendedIndex = extendedIndex; |
|
||||||
} else { |
|
||||||
selectedColorIndex = -1; |
|
||||||
selectedExtendedIndex = -1; |
|
||||||
} |
|
||||||
repaint(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void mousePressed(MouseEvent e) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void mouseReleased(MouseEvent e) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void mouseEntered(MouseEvent e) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void mouseExited(MouseEvent e) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public interface ExtendedColorComputer { |
|
||||||
Color computeExtendedColor(Color color, int index, int count); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void paint(Graphics g) { |
|
||||||
super.paint(g); |
|
||||||
Color oldColor = g.getColor(); |
|
||||||
|
|
||||||
for (int i = 0; i < colorCount; i++) { |
|
||||||
int x = i * (boxSize + boxGap); |
|
||||||
for (int j = 0; j < extendedCount; j++) { |
|
||||||
Color color = extendedColorComputer.computeExtendedColor(colorList.get(i), j, extendedCount); |
|
||||||
g.setColor(color); |
|
||||||
int y = j * (boxSize + boxGap); |
|
||||||
g.fillRect(x, y, boxSize, boxSize); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
g.setColor(oldColor); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.design.record.analyzer.Interceptor; |
||||||
|
|
||||||
|
import com.fr.design.mod.ContentObjectManager; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bind.annotation.AllArguments; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bind.annotation.Origin; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bind.annotation.RuntimeType; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bind.annotation.SuperCall; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* 收集 |
||||||
|
* |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/6/17 |
||||||
|
*/ |
||||||
|
public class CollectInterceptor { |
||||||
|
|
||||||
|
@RuntimeType |
||||||
|
public static Object intercept(@Origin Method method, |
||||||
|
@SuperCall Callable<?> callable, |
||||||
|
@AllArguments Object[] args) throws Exception { |
||||||
|
Object result = callable.call(); |
||||||
|
ContentObjectManager.getInstance().collect(result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.mod.ContentObjectManager; |
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/6/17 |
||||||
|
*/ |
||||||
|
public class CollectAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception { |
||||||
|
ContentObjectManager.getInstance().collect(result); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,30 @@ |
|||||||
|
package com.fr.design.utils; |
||||||
|
|
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/29 |
||||||
|
**/ |
||||||
|
public class ColorUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* 递归的同步颜色 |
||||||
|
* |
||||||
|
* @param color 颜色 |
||||||
|
*/ |
||||||
|
public static void syncBackground(Component component, Color color) { |
||||||
|
|
||||||
|
component.setBackground(color); |
||||||
|
|
||||||
|
if (component instanceof Container) { |
||||||
|
Container container = (Container) component; |
||||||
|
Component[] components = container.getComponents(); |
||||||
|
if (components != null) { |
||||||
|
Arrays.stream(components).forEach((e) -> syncBackground(e, color)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package com.fr.design.utils; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Font; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class LinkStrUtils { |
||||||
|
|
||||||
|
public static final UILabel LABEL = new UILabel(); |
||||||
|
|
||||||
|
public static UILabel generateLabel(String html, JComponent templateLabel) { |
||||||
|
|
||||||
|
String style = generateStyle(templateLabel.getBackground(), templateLabel.getFont(), templateLabel.getForeground()); |
||||||
|
String fullHtml = generateHtmlTag(style, html); |
||||||
|
return new UILabel(fullHtml); |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateHtmlTag(String html) { |
||||||
|
|
||||||
|
String defaultStyle = generateDefaultStyle(); |
||||||
|
return generateHtmlTag(defaultStyle, html); |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateHtmlTag(String style, String html) { |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(style)) { |
||||||
|
throw new NullPointerException("style"); |
||||||
|
} |
||||||
|
if (StringUtils.isEmpty(html)) { |
||||||
|
throw new NullPointerException("html"); |
||||||
|
} |
||||||
|
return "<html><body style=\"" + style + "\">" + html + "</body></html>"; |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateLinkTag(String link, String text) { |
||||||
|
|
||||||
|
return "<a href=\"" + link + "\">" + text + "</a>"; |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { |
||||||
|
|
||||||
|
// 构建相同风格样式
|
||||||
|
StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); |
||||||
|
|
||||||
|
style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); |
||||||
|
style.append("font-size:").append(font.getSize()).append("pt;"); |
||||||
|
style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); |
||||||
|
style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); |
||||||
|
|
||||||
|
return style.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateDefaultStyle() { |
||||||
|
|
||||||
|
return generateStyle(LABEL.getBackground(), LABEL.getFont(), LABEL.getForeground()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.fr.env; |
||||||
|
|
||||||
|
import com.fr.env.detect.EnvDetectorCenter; |
||||||
|
import com.fr.module.Activator; |
||||||
|
|
||||||
|
/** |
||||||
|
* 设计器环境准备 |
||||||
|
* 更多的是一些钩子,需要在环境启动、切换时进行处理 |
||||||
|
* 使用监听 {@link com.fr.workspace.WorkspaceEvent} 只能满足 |
||||||
|
* before -> stop -> start -> after |
||||||
|
* 现在支持 => |
||||||
|
* before -> stop -> prepare -> start -> after |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/29 |
||||||
|
**/ |
||||||
|
public class EnvPrepare extends Activator { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
EnvDetectorCenter.getInstance().init(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() { |
||||||
|
EnvDetectorCenter.getInstance().destroy(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,226 @@ |
|||||||
|
package com.fr.env.detect; |
||||||
|
|
||||||
|
import com.fr.common.util.Collections; |
||||||
|
import com.fr.design.components.notification.NotificationDialog; |
||||||
|
import com.fr.design.components.notification.NotificationDialogProperties; |
||||||
|
import com.fr.design.components.notification.NotificationModel; |
||||||
|
import com.fr.design.constants.DesignerLaunchStatus; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.env.detect.base.DetectorBridge; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.base.EnvDetectorConfig; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorStatus; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.EventDispatcher; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.core.UUID; |
||||||
|
import com.fr.task.Once; |
||||||
|
import com.fr.update.delay.DelayHelper; |
||||||
|
import com.fr.workspace.Workspace; |
||||||
|
import com.fr.workspace.WorkspaceEvent; |
||||||
|
|
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.concurrent.atomic.AtomicReference; |
||||||
|
import java.util.function.Supplier; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* 环境检测中心 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public class EnvDetectorCenter { |
||||||
|
|
||||||
|
private final Listener<Null> START_UP_COMPLETE_LISTENER = new Listener<Null>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
// startup 的监听,执行一次即可
|
||||||
|
EventDispatcher.stopListen(this); |
||||||
|
FineLoggerFactory.getLogger().debug("startup complete, start detecting env"); |
||||||
|
stop(); |
||||||
|
} |
||||||
|
}; |
||||||
|
private final Listener<Workspace> AFTER_SWITCH_LISTENER = new Listener<Workspace>() { |
||||||
|
@Override |
||||||
|
public void on(Event event, Workspace param) { |
||||||
|
FineLoggerFactory.getLogger().debug("switch env complete, start detecting env"); |
||||||
|
stop(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private final Once launchOnce = new Once(() -> { |
||||||
|
|
||||||
|
// 添加启动完成监听
|
||||||
|
EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, START_UP_COMPLETE_LISTENER); |
||||||
|
// 切换完成后的监听
|
||||||
|
EventDispatcher.listen(WorkspaceEvent.AfterSwitch, AFTER_SWITCH_LISTENER); |
||||||
|
}); |
||||||
|
|
||||||
|
private final AtomicReference<DetectorProcess> PROCESS = new AtomicReference<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前还有什么动作未执行 |
||||||
|
* 如果切换环境,这里的动作是要被清空的 |
||||||
|
* 从而防止切换环境后, 上一个动作的环境延续过来 |
||||||
|
*/ |
||||||
|
private final Set<String> pendingActions = new HashSet<>(); |
||||||
|
|
||||||
|
public static EnvDetectorCenter getInstance() { |
||||||
|
return EnvDetectorCenterHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class EnvDetectorCenterHolder { |
||||||
|
private static final EnvDetectorCenter INSTANCE = new EnvDetectorCenter(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化 |
||||||
|
*/ |
||||||
|
public void init() { |
||||||
|
|
||||||
|
// 如果已经启动了,则不再启动
|
||||||
|
if (PROCESS.get() != null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
start(); |
||||||
|
// 默认是启动
|
||||||
|
PROCESS.set(DetectorProcess.DESIGN_LAUNCH); |
||||||
|
|
||||||
|
launchOnce.run(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 销毁,一般用在模块关闭中 |
||||||
|
*/ |
||||||
|
public void destroy() { |
||||||
|
|
||||||
|
// 清空
|
||||||
|
pendingActions.clear(); |
||||||
|
|
||||||
|
// 重置内容
|
||||||
|
DetectorBridge.getInstance().reset(); |
||||||
|
// 关闭逻辑
|
||||||
|
DetectorBridge.getInstance().stop(); |
||||||
|
|
||||||
|
PROCESS.set(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动 |
||||||
|
*/ |
||||||
|
public void start() { |
||||||
|
|
||||||
|
DetectorBridge.getInstance().start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭 |
||||||
|
*/ |
||||||
|
public void stop() { |
||||||
|
|
||||||
|
// 结束
|
||||||
|
DetectorBridge.getInstance().stop(); |
||||||
|
|
||||||
|
// id值
|
||||||
|
String uuid = UUID.randomUUID().toString(); |
||||||
|
// 确认当前的 action 是否有效
|
||||||
|
Supplier<Boolean> validAction = () -> pendingActions.contains(uuid); |
||||||
|
|
||||||
|
// 30s后执行
|
||||||
|
Runnable detectorAction = () -> { |
||||||
|
|
||||||
|
// 如果当前没开启,则直接返回
|
||||||
|
if (!EnvDetectorConfig.getInstance().isEnabled()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 如果当前不包含这个 id, 就不执行
|
||||||
|
if (!validAction.get()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Stream<DetectorResult> resultStream = DetectorBridge.getInstance().detect().stream(); |
||||||
|
|
||||||
|
// 展示效果
|
||||||
|
NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); |
||||||
|
List<NotificationModel> notificationModels = resultStream |
||||||
|
.filter(Objects::nonNull) |
||||||
|
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||||
|
.map(DetectorUtil::convert2Notification) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
if (Collections.isEmpty(notificationModels)) { |
||||||
|
FineLoggerFactory.getLogger().debug("detector not found any exception"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
UIUtil.invokeLaterIfNeeded(() -> { |
||||||
|
|
||||||
|
// 如果当前不包含这个 id, 就不执行
|
||||||
|
if (!validAction.get()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
NotificationDialog dialog = new NotificationDialog(properties, notificationModels); |
||||||
|
dialog.open(); |
||||||
|
|
||||||
|
// 执行完移除
|
||||||
|
pendingActions.remove(uuid); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
// 添加
|
||||||
|
pendingActions.add(uuid); |
||||||
|
|
||||||
|
|
||||||
|
DelayHelper.delayCall(EnvDetectorCenter.class.getName(), detectorAction, 30, TimeUnit.SECONDS); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用预期外的错误进行展示 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public List<DetectorResult> terminate(Throwable throwable) { |
||||||
|
|
||||||
|
Stream<DetectorResult> resultStream = DetectorBridge.getInstance().detect(throwable).stream(); |
||||||
|
return resultStream |
||||||
|
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 预期外的终止 |
||||||
|
* |
||||||
|
* @return 检测结果 |
||||||
|
*/ |
||||||
|
public List<DetectorResult> terminateUnexpectedly() { |
||||||
|
|
||||||
|
Stream<DetectorResult> resultStream = DetectorBridge.getInstance().detect().stream(); |
||||||
|
return resultStream |
||||||
|
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private enum DetectorProcess { |
||||||
|
|
||||||
|
/** |
||||||
|
* 设计器启动 |
||||||
|
*/ |
||||||
|
DESIGN_LAUNCH, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public abstract class AbstractExceptionDetector implements ExceptionDetector { |
||||||
|
|
||||||
|
private DetectorType type; |
||||||
|
|
||||||
|
public AbstractExceptionDetector(DetectorType type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorType type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.thowable.ThrowableConverter; |
||||||
|
import com.fr.env.detect.thowable.ThrowableStore; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public abstract class CatchExceptionDetector extends AbstractExceptionDetector { |
||||||
|
|
||||||
|
private ThrowableStore throwableStore; |
||||||
|
|
||||||
|
private ThrowableConverter throwableConverter; |
||||||
|
|
||||||
|
public CatchExceptionDetector(DetectorType type, ThrowableConverter throwableConverter) { |
||||||
|
super(type); |
||||||
|
this.throwableStore = ThrowableStore.getInstance(); |
||||||
|
this.throwableConverter = throwableConverter; |
||||||
|
} |
||||||
|
|
||||||
|
public CatchExceptionDetector(DetectorType type, ThrowableStore throwableStore, ThrowableConverter throwableConverter) { |
||||||
|
super(type); |
||||||
|
this.throwableStore = throwableStore; |
||||||
|
this.throwableConverter = throwableConverter; |
||||||
|
} |
||||||
|
|
||||||
|
public ThrowableStore getThrowableStore() { |
||||||
|
return throwableStore; |
||||||
|
} |
||||||
|
|
||||||
|
public ThrowableConverter getThrowableConverter() { |
||||||
|
return throwableConverter; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DetectorResult detect() { |
||||||
|
|
||||||
|
List<Throwable> throwableList = throwableStore.getAll(); |
||||||
|
for (Throwable throwable : throwableList) { |
||||||
|
if (throwableConverter.accept(throwable)) { |
||||||
|
|
||||||
|
DetectorResult result = throwableConverter.convert(throwable); |
||||||
|
if (result == null) { |
||||||
|
result = DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.common.annotations.Careful; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.impl.DetectorChain; |
||||||
|
import com.fr.env.detect.impl.FineDbDirtyDetector; |
||||||
|
import com.fr.env.detect.impl.FineDbLockedDetector; |
||||||
|
import com.fr.env.detect.impl.FineDbPermissionDetector; |
||||||
|
import com.fr.env.detect.impl.JarConflictDetector; |
||||||
|
import com.fr.env.detect.impl.JarInconsistentDetector; |
||||||
|
import com.fr.env.detect.impl.JarLackDetector; |
||||||
|
import com.fr.env.detect.thowable.ThrowableLogAppender; |
||||||
|
import com.fr.env.detect.thowable.ThrowableStore; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.value.NotNullLazyValue; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测器桥接逻辑 |
||||||
|
* [detect-core] - bridge - ui |
||||||
|
* 标记为谨慎类。目前埋点依赖于包名、方法名、方法特征,不能随意更改 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
@Careful |
||||||
|
public class DetectorBridge { |
||||||
|
|
||||||
|
public static DetectorBridge getInstance() { |
||||||
|
|
||||||
|
return DetectorBridgeHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class DetectorBridgeHolder { |
||||||
|
|
||||||
|
private static final DetectorBridge INSTANCE = new DetectorBridge(); |
||||||
|
} |
||||||
|
|
||||||
|
private final NotNullLazyValue<DetectorManager> detectorManager = new NotNullLazyValue<DetectorManager>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected @NotNull DetectorManager compute() { |
||||||
|
|
||||||
|
DetectorManager manager = new DetectorManager(); |
||||||
|
|
||||||
|
// 按照顺序构造检测链
|
||||||
|
manager.register(DetectorChain.construct( |
||||||
|
new FineDbLockedDetector(), |
||||||
|
new FineDbPermissionDetector(), |
||||||
|
new FineDbDirtyDetector())); |
||||||
|
|
||||||
|
manager.register(new JarInconsistentDetector()); |
||||||
|
manager.register(new JarLackDetector()); |
||||||
|
manager.register(new JarConflictDetector()); |
||||||
|
return manager; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private final AtomicBoolean hasStarted = new AtomicBoolean(false); |
||||||
|
|
||||||
|
public void start() { |
||||||
|
|
||||||
|
if (!hasStarted.get() && EnvDetectorConfig.getInstance().isEnabled()) { |
||||||
|
// 开始注册异常处理
|
||||||
|
ThrowableLogAppender.getInstance().enable(); |
||||||
|
hasStarted.set(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void stop() { |
||||||
|
|
||||||
|
if (hasStarted.compareAndSet(true, false)) { |
||||||
|
// 关闭异常处理
|
||||||
|
ThrowableLogAppender.getInstance().disable(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
|
||||||
|
ThrowableStore.getInstance().reset(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 针对某一项进行检测 |
||||||
|
* 主要用于手动检测时 |
||||||
|
* |
||||||
|
* @param type 检测类型 |
||||||
|
* @return 检测结果 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public DetectorResult detect(DetectorType type) { |
||||||
|
|
||||||
|
try { |
||||||
|
return detectorManager.getValue().detect(type); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error("detect failed", e); |
||||||
|
// 返回未知错误信息
|
||||||
|
return DetectorResult.unknown(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 针对某一项 \ 某一个异常进行检测 |
||||||
|
* |
||||||
|
* @param type 类型 |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 结果 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public DetectorResult detect(DetectorType type, Throwable throwable) { |
||||||
|
|
||||||
|
ThrowableStore.getInstance().add(throwable); |
||||||
|
|
||||||
|
DetectorResult result = detect(type); |
||||||
|
|
||||||
|
ThrowableStore.getInstance().reset(); |
||||||
|
|
||||||
|
return result; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常检测 |
||||||
|
* 对异常统一检测 |
||||||
|
* |
||||||
|
* @return 能够检测出的异常情况 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public Collection<DetectorResult> detect() { |
||||||
|
|
||||||
|
Stream<DetectorResult> results = detectorManager.getValue().detect(); |
||||||
|
return results.collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常检测 |
||||||
|
* 当遇到异常时,且异常难以处理,直接导致服务器启动失败时,调用 |
||||||
|
* 将异常添加进来,统一检测 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 检测结果 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public Collection<DetectorResult> detect(Throwable throwable) { |
||||||
|
|
||||||
|
ThrowableStore.getInstance().add(throwable); |
||||||
|
Collection<DetectorResult> result = detect(); |
||||||
|
ThrowableStore.getInstance().reset(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/25 |
||||||
|
**/ |
||||||
|
public class DetectorConstants { |
||||||
|
|
||||||
|
public static final String JAR_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4700.html?source=3"; |
||||||
|
|
||||||
|
public static final String FINE_DB_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4701.html?source=3"; |
||||||
|
|
||||||
|
public static final String SEPARATOR = "、"; |
||||||
|
public static final String BR_TAG = "<br/>"; |
||||||
|
|
||||||
|
public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; |
||||||
|
|
||||||
|
public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.impl.DetectorChain; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Optional; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测器中心 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
class DetectorManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测链的列表 |
||||||
|
*/ |
||||||
|
private List<DetectorChain> chains = new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 单纯的检测器的列表 |
||||||
|
*/ |
||||||
|
private List<ExceptionDetector> detectors = new ArrayList<>(); |
||||||
|
|
||||||
|
public void register(DetectorChain chain) { |
||||||
|
|
||||||
|
if (chain != null) { |
||||||
|
chains.add(chain); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void register(ExceptionDetector detector) { |
||||||
|
|
||||||
|
if (detector != null) { |
||||||
|
detectors.add(detector); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Stream<DetectorResult> detect() { |
||||||
|
|
||||||
|
Stream<DetectorResult> results = detectors.stream() |
||||||
|
.map(ExceptionDetector::detect) |
||||||
|
.filter(Objects::nonNull); |
||||||
|
|
||||||
|
Stream<DetectorResult> chainResults = chains.stream() |
||||||
|
.map(DetectorChain::detect) |
||||||
|
.filter(Objects::nonNull); |
||||||
|
|
||||||
|
List<DetectorResult> resultList = Stream.concat(results, chainResults) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
resultList.forEach(DetectorResult::log); |
||||||
|
|
||||||
|
return resultList.stream(); |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResult detect(DetectorType type) { |
||||||
|
|
||||||
|
Stream<ExceptionDetector> chainDetectors = chains.stream() |
||||||
|
.map(DetectorChain::getDetectors) |
||||||
|
.flatMap(Collection::stream); |
||||||
|
|
||||||
|
Stream<ExceptionDetector> allDetectors = Stream.concat(detectors.stream(), chainDetectors); |
||||||
|
|
||||||
|
Optional<DetectorResult> result = allDetectors |
||||||
|
.filter((detector -> type == detector.type())) |
||||||
|
.findFirst() |
||||||
|
.map(ExceptionDetector::detect); |
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
result.ifPresent(DetectorResult::log); |
||||||
|
|
||||||
|
return result.orElse(DetectorResult.normal(type)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,166 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.base.function.ThrowableRunnable; |
||||||
|
import com.fr.common.util.Collections; |
||||||
|
import com.fr.design.components.notification.NotificationAction; |
||||||
|
import com.fr.design.components.notification.NotificationMessage; |
||||||
|
import com.fr.design.components.notification.NotificationModel; |
||||||
|
import com.fr.design.components.notification.NotificationType; |
||||||
|
import com.fr.design.dialog.link.MessageWithLink; |
||||||
|
import com.fr.design.utils.LinkStrUtils; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.ExceptionTips; |
||||||
|
import com.fr.env.detect.bean.Message; |
||||||
|
import com.fr.env.detect.bean.SolutionAction; |
||||||
|
import com.fr.general.build.BuildInfo; |
||||||
|
import com.fr.third.org.apache.commons.lang3.StringUtils; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import java.awt.Desktop; |
||||||
|
import java.net.URI; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Optional; |
||||||
|
import java.util.function.Function; |
||||||
|
|
||||||
|
import static com.fr.env.detect.base.DetectorConstants.BR_TAG; |
||||||
|
import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; |
||||||
|
import static com.fr.env.detect.base.DetectorConstants.SEPARATOR; |
||||||
|
import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/25 |
||||||
|
**/ |
||||||
|
public class DetectorUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否是设计器的 jar |
||||||
|
* |
||||||
|
* @param info 信息 |
||||||
|
* @return 是/否 |
||||||
|
*/ |
||||||
|
public static boolean isDesignerJar(BuildInfo info) { |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return StringUtils.contains(info.getJar(), "fine-report-designer"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将结果转化为提醒的数据 |
||||||
|
* |
||||||
|
* @param result 结果 |
||||||
|
* @return 数据 |
||||||
|
*/ |
||||||
|
public static NotificationModel convert2Notification(DetectorResult result) { |
||||||
|
|
||||||
|
List<NotificationMessage> messages = new ArrayList<>(); |
||||||
|
|
||||||
|
Function<Message, Optional<NotificationMessage>> convert2NotificationMsg = message -> { |
||||||
|
|
||||||
|
NotificationMessage notificationMessage = null; |
||||||
|
if (message != null) { |
||||||
|
Message.Type type = message.getType(); |
||||||
|
switch (type) { |
||||||
|
case SIMPLE: |
||||||
|
notificationMessage = (new NotificationMessage.SimpleMessage(message.get())); |
||||||
|
break; |
||||||
|
case LINK: |
||||||
|
Message.Link linkMsg = (Message.Link) message; |
||||||
|
notificationMessage = new NotificationMessage.LinkMessage(linkMsg.getText(), linkMsg.getLink()); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return Optional.ofNullable(notificationMessage); |
||||||
|
}; |
||||||
|
|
||||||
|
ExceptionTips tips = result.getTips(); |
||||||
|
if (tips != null) { |
||||||
|
convert2NotificationMsg |
||||||
|
.apply(tips.getMessage()) |
||||||
|
.ifPresent(messages::add); |
||||||
|
} |
||||||
|
|
||||||
|
ExceptionSolution solution = result.getSolution(); |
||||||
|
if (solution != null) { |
||||||
|
convert2NotificationMsg.apply(solution.getMessage()) |
||||||
|
.ifPresent(messages::add); |
||||||
|
} |
||||||
|
|
||||||
|
NotificationAction notificationAction = null; |
||||||
|
if (solution != null) { |
||||||
|
SolutionAction solutionAction = solution.getAction(); |
||||||
|
if (solutionAction != null) { |
||||||
|
notificationAction = new NotificationAction() { |
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return solutionAction.name(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(Object... args) { |
||||||
|
solutionAction.run(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return new NotificationModel(NotificationType.WARNING, notificationAction, messages); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将信息转化为展示的组件 |
||||||
|
* |
||||||
|
* @param message 信息 |
||||||
|
* @return 组件 |
||||||
|
*/ |
||||||
|
public static JComponent convert2TextComponent(@NotNull Message message, JComponent template) { |
||||||
|
|
||||||
|
if (message.getType() == Message.Type.LINK) { |
||||||
|
Message.Link linkMsg = (Message.Link) message; |
||||||
|
return new MessageWithLink(linkMsg.getText(), ThrowableRunnable.toRunnable(() -> { |
||||||
|
Desktop.getDesktop().browse(URI.create(linkMsg.getLink())); |
||||||
|
})); |
||||||
|
} |
||||||
|
return LinkStrUtils.generateLabel(message.get(), template); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将 lib 转化成合适的格式 |
||||||
|
* %FR_HOME%/lib |
||||||
|
* %FR_HOME%/webapps/webroot/WEB-INF/lib |
||||||
|
* |
||||||
|
* @param libMap jar 路径, key为前缀 |
||||||
|
* @return 信息 |
||||||
|
*/ |
||||||
|
public static String concatLibFormatMsg(Map<String, List<String>> libMap) { |
||||||
|
|
||||||
|
String webLibPath = WEB_LIB_PATH; |
||||||
|
String homeLibPath = FR_HOME_LIB; |
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
|
||||||
|
List<String> homeLibs = libMap.get(homeLibPath); |
||||||
|
if (!Collections.isEmpty(homeLibs)) { |
||||||
|
sb.append(homeLibPath); |
||||||
|
sb.append(StringUtils.join(homeLibs, SEPARATOR)); |
||||||
|
} |
||||||
|
|
||||||
|
List<String> webLibs = libMap.get(webLibPath); |
||||||
|
if (!Collections.isEmpty(webLibs)) { |
||||||
|
if (sb.length() != 0) { |
||||||
|
sb.append(BR_TAG); |
||||||
|
} |
||||||
|
sb.append(webLibPath); |
||||||
|
sb.append(StringUtils.join(webLibs, SEPARATOR)); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLable; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class EnvDetectorConfig implements XMLable { |
||||||
|
|
||||||
|
public static final String XML_TAG = "EnvDetectorConfig"; |
||||||
|
|
||||||
|
private static final long serialVersionUID = -8170289826729582122L; |
||||||
|
|
||||||
|
private static final EnvDetectorConfig INSTANCE = new EnvDetectorConfig(); |
||||||
|
|
||||||
|
public static EnvDetectorConfig getInstance() { |
||||||
|
|
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean enabled = true; |
||||||
|
|
||||||
|
public boolean isEnabled() { |
||||||
|
return enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
this.enabled = enabled; |
||||||
|
save(); |
||||||
|
} |
||||||
|
|
||||||
|
private void save() { |
||||||
|
|
||||||
|
DesignerEnvManager.getEnvManager(false).saveXMLFile(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object clone() throws CloneNotSupportedException { |
||||||
|
return super.clone(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readXML(XMLableReader reader) { |
||||||
|
if (reader.isAttr()) { |
||||||
|
this.setEnabled(reader.getAttrAsBoolean("isEnabled", true)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeXML(XMLPrintWriter writer) { |
||||||
|
writer.startTAG(XML_TAG); |
||||||
|
writer.attr("isEnabled", this.isEnabled()); |
||||||
|
writer.end(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public interface ExceptionDetector { |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测类型 |
||||||
|
* |
||||||
|
* @return TYPE |
||||||
|
*/ |
||||||
|
DetectorType type(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测结果 |
||||||
|
* |
||||||
|
* @return 结果 |
||||||
|
*/ |
||||||
|
DetectorResult detect(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package com.fr.env.detect.base; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.thowable.ThrowableConverter; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
class ThrowableBridge { |
||||||
|
|
||||||
|
private List<ThrowableConverter> throwableConverters = new ArrayList<>(); |
||||||
|
|
||||||
|
public void register(ThrowableConverter throwableConverter) { |
||||||
|
|
||||||
|
if (throwableConverter != null) { |
||||||
|
throwableConverters.add(throwableConverter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将 throwable 转化成检测的结果 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 结果 |
||||||
|
*/ |
||||||
|
public Optional<DetectorResult> convert(Throwable throwable) { |
||||||
|
|
||||||
|
return throwableConverters |
||||||
|
.stream() |
||||||
|
.filter((e) -> e.accept(throwable)) |
||||||
|
.map((e) -> e.convert(throwable)) |
||||||
|
.filter(Objects::nonNull) |
||||||
|
.findFirst(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
/** |
||||||
|
* 设计器环境检测 见 {@see https://kms.fineres.com/pages/viewpage.action?pageId=388333688}
|
||||||
|
*/ |
||||||
|
package com.fr.env.detect.base; |
@ -0,0 +1,147 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测结果 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class DetectorResult { |
||||||
|
|
||||||
|
private DetectorType type; |
||||||
|
|
||||||
|
private DetectorStatus status; |
||||||
|
|
||||||
|
private ExceptionTips tips; |
||||||
|
|
||||||
|
private ExceptionSolution solution; |
||||||
|
|
||||||
|
private ExceptionLog log; |
||||||
|
|
||||||
|
private DetectorResult(DetectorType type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
private DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { |
||||||
|
|
||||||
|
this.type = type; |
||||||
|
this.tips = tips; |
||||||
|
this.solution = solution; |
||||||
|
this.log = log; |
||||||
|
} |
||||||
|
|
||||||
|
public static DetectorResult unknown(DetectorType type) { |
||||||
|
DetectorResult result = new DetectorResult(type); |
||||||
|
result.status = DetectorStatus.UNKNOWN; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public static DetectorResult normal(DetectorType type) { |
||||||
|
|
||||||
|
DetectorResult result = new DetectorResult(type); |
||||||
|
result.status = DetectorStatus.NORMAL; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public static DetectorResult exception(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { |
||||||
|
|
||||||
|
DetectorResult result = new DetectorResult(type, tips, solution, log); |
||||||
|
result.status = DetectorStatus.EXCEPTION; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorStatus getStatus() { |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorType getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public ExceptionTips getTips() { |
||||||
|
return tips; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public ExceptionSolution getSolution() { |
||||||
|
return solution; |
||||||
|
} |
||||||
|
|
||||||
|
public void log() { |
||||||
|
|
||||||
|
if (log != null) { |
||||||
|
log.log(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static DetectorResultBuilder builder() { |
||||||
|
|
||||||
|
return new DetectorResultBuilder() |
||||||
|
.withStatus(DetectorStatus.EXCEPTION); |
||||||
|
} |
||||||
|
|
||||||
|
public static final class DetectorResultBuilder { |
||||||
|
|
||||||
|
private DetectorType type; |
||||||
|
private DetectorStatus status; |
||||||
|
private ExceptionTips tips; |
||||||
|
private ExceptionSolution solution; |
||||||
|
private ExceptionLog log; |
||||||
|
|
||||||
|
private DetectorResultBuilder() { |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withType(DetectorType type) { |
||||||
|
this.type = type; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withStatus(DetectorStatus status) { |
||||||
|
this.status = status; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withTips(ExceptionTips tips) { |
||||||
|
this.tips = tips; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withTips(String message) { |
||||||
|
|
||||||
|
Message.Simple simple = new Message.Simple(message); |
||||||
|
this.tips = new ExceptionTips(simple); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withSolution(ExceptionSolution solution) { |
||||||
|
this.solution = solution; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withSolution(String content, String link) { |
||||||
|
|
||||||
|
Message.Link message = new Message.Link(content, link); |
||||||
|
this.solution = new ExceptionSolution(message, null); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withLog(String log, Object... args) { |
||||||
|
|
||||||
|
this.log = ExceptionLog.create(log, args); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResultBuilder withLog(ExceptionLog log) { |
||||||
|
this.log = log; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResult build() { |
||||||
|
DetectorResult detectorResult = new DetectorResult(type, tips, solution, log); |
||||||
|
detectorResult.status = this.status; |
||||||
|
return detectorResult; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public enum DetectorStatus { |
||||||
|
|
||||||
|
/** |
||||||
|
* 正常 |
||||||
|
*/ |
||||||
|
NORMAL, |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常 |
||||||
|
*/ |
||||||
|
EXCEPTION, |
||||||
|
|
||||||
|
/** |
||||||
|
* 未知 |
||||||
|
*/ |
||||||
|
UNKNOWN, |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测的原生数据 |
||||||
|
* 其实这里可以继续拆分到不同的逻辑下面的, 比如实际生成 DetectorResult 的地方 {@link com.fr.env.detect.base.ExceptionDetector}。 |
||||||
|
* 不过目前没有必要。先这样处理。 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public enum DetectorType { |
||||||
|
|
||||||
|
/** |
||||||
|
* 缺少 JAR |
||||||
|
*/ |
||||||
|
LACK_OF_JAR(Kind.JAR, WorkType.LOCAL, |
||||||
|
"Fine_Design_Basic_Jar_Lacked_Desc", |
||||||
|
"Fine_Design_Basic_Jar_Lack", |
||||||
|
"Fine_Design_Basic_Jar_Solution", |
||||||
|
"Fine_Design_Basic_Log_Jar_Lack"), |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* JAR 包版本不一致 |
||||||
|
*/ |
||||||
|
JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE, |
||||||
|
"Fine_Design_Basic_Jar_InConsistent_Desc", |
||||||
|
"Fine_Design_Basic_Jar_InConsistent", |
||||||
|
"Fine_Design_Basic_Jar_Solution", |
||||||
|
"Fine_Design_Basic_Log_Jar_InConsistent"), |
||||||
|
|
||||||
|
/** |
||||||
|
* JAR 包冲突 |
||||||
|
*/ |
||||||
|
JAR_CONFLICT(Kind.JAR, WorkType.REMOTE, |
||||||
|
"Fine_Design_Basic_Jar_Problem_Desc", |
||||||
|
"Fine_Design_Basic_Jar_Problem", |
||||||
|
"Fine_Design_Basic_Jar_Solution", |
||||||
|
"Fine_Design_Basic_Log_Jar_Problem"), |
||||||
|
|
||||||
|
/** |
||||||
|
* FineDB 权限问题 |
||||||
|
*/ |
||||||
|
FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL, |
||||||
|
"Fine_Design_Basic_FineDB_Permission_Occupied_Desc", |
||||||
|
"Fine_Design_Basic_FineDB_Permission_Occupied", |
||||||
|
"Fine_Design_Basic_FineDB_Solution", |
||||||
|
"Fine_Design_Basic_Log_FineDB_Permission_Occupied"), |
||||||
|
|
||||||
|
/** |
||||||
|
* FineDB 锁 |
||||||
|
*/ |
||||||
|
FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL, |
||||||
|
"Fine_Design_Basic_FineDB_Locked_Desc", |
||||||
|
"Fine_Design_Basic_FineDB_Locked", |
||||||
|
"Fine_Design_Basic_FineDB_Solution", |
||||||
|
"Fine_Design_Basic_Log_FineDB_Locked"), |
||||||
|
|
||||||
|
/** |
||||||
|
* FineDB 脏数据 |
||||||
|
*/ |
||||||
|
FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE, |
||||||
|
"Fine_Design_Basic_FineDB_Dirty_Data_Desc", |
||||||
|
"Fine_Design_Basic_FineDB_Dirty_Data", |
||||||
|
"Fine_Design_Basic_FineDB_Dirty_Solution", |
||||||
|
"Fine_Design_Basic_Log_FineDB_Dirty_Data") |
||||||
|
; |
||||||
|
|
||||||
|
private final Kind kind; |
||||||
|
|
||||||
|
private final WorkType workType; |
||||||
|
|
||||||
|
private final String descLocale; |
||||||
|
|
||||||
|
private final String tipsLocale; |
||||||
|
|
||||||
|
private final String solutionLocale; |
||||||
|
|
||||||
|
private final String logLocale; |
||||||
|
|
||||||
|
DetectorType(Kind kind, WorkType workType, String descLocale, String tipsLocale, String solutionLocale, String logLocale) { |
||||||
|
|
||||||
|
this.kind = kind; |
||||||
|
this.workType = workType; |
||||||
|
this.descLocale = descLocale; |
||||||
|
this.tipsLocale = tipsLocale; |
||||||
|
this.solutionLocale = solutionLocale; |
||||||
|
this.logLocale = logLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDescription() { |
||||||
|
return Toolkit.i18nText(descLocale); |
||||||
|
} |
||||||
|
|
||||||
|
public String getDescLocale() { |
||||||
|
return descLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public Kind getKind() { |
||||||
|
return kind; |
||||||
|
} |
||||||
|
|
||||||
|
public WorkType getWorkType() { |
||||||
|
return workType; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTipsLocale() { |
||||||
|
return tipsLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSolutionLocale() { |
||||||
|
return solutionLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLogLocale() { |
||||||
|
return logLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public enum Kind { |
||||||
|
|
||||||
|
/** |
||||||
|
* JAR 类型 |
||||||
|
*/ |
||||||
|
JAR("Fine_Design_Basic_Jar_Kind_Desc"), |
||||||
|
|
||||||
|
/** |
||||||
|
* FineDB 类型 |
||||||
|
*/ |
||||||
|
FINE_DB("Fine_Design_Basic_FineDB_Kind_Desc"); |
||||||
|
|
||||||
|
private final String locale; |
||||||
|
|
||||||
|
Kind(String locale) { |
||||||
|
this.locale = locale; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDescription() { |
||||||
|
return Toolkit.i18nText(this.locale); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public enum WorkType { |
||||||
|
|
||||||
|
/** |
||||||
|
* 本地 |
||||||
|
*/ |
||||||
|
LOCAL, |
||||||
|
/** |
||||||
|
* 远程 |
||||||
|
*/ |
||||||
|
REMOTE, |
||||||
|
/** |
||||||
|
* 全部 |
||||||
|
*/ |
||||||
|
SIMPLE |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class ExceptionLog { |
||||||
|
|
||||||
|
private final String template; |
||||||
|
|
||||||
|
private final Object[] args; |
||||||
|
|
||||||
|
private ExceptionLog(String template, Object... args) { |
||||||
|
this.template = template; |
||||||
|
this.args = args; |
||||||
|
} |
||||||
|
|
||||||
|
public static ExceptionLog create(String template, Object... args) { |
||||||
|
|
||||||
|
return new ExceptionLog(template, args); |
||||||
|
} |
||||||
|
|
||||||
|
public void log() { |
||||||
|
|
||||||
|
FineLoggerFactory.getLogger().error(template, args); |
||||||
|
} |
||||||
|
|
||||||
|
public String getTemplate() { |
||||||
|
return template; |
||||||
|
} |
||||||
|
|
||||||
|
public Object[] getArgs() { |
||||||
|
return args; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class ExceptionSolution { |
||||||
|
|
||||||
|
private Message message; |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private SolutionAction action; |
||||||
|
|
||||||
|
public static ExceptionSolution create(String text, String link, SolutionAction action) { |
||||||
|
|
||||||
|
return new ExceptionSolution(new Message.Link(text, link), action); |
||||||
|
} |
||||||
|
|
||||||
|
public ExceptionSolution(Message message, @Nullable SolutionAction action) { |
||||||
|
this.message = message; |
||||||
|
this.action = action; |
||||||
|
} |
||||||
|
|
||||||
|
public Message getMessage() { |
||||||
|
return message; |
||||||
|
} |
||||||
|
|
||||||
|
public @Nullable SolutionAction getAction() { |
||||||
|
return action; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常提示 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class ExceptionTips { |
||||||
|
|
||||||
|
private Message message; |
||||||
|
|
||||||
|
public static ExceptionTips create(String text) { |
||||||
|
|
||||||
|
return new ExceptionTips(new Message.Simple(text)); |
||||||
|
} |
||||||
|
|
||||||
|
public ExceptionTips(Message message) { |
||||||
|
this.message = message; |
||||||
|
} |
||||||
|
|
||||||
|
public Message getMessage() { |
||||||
|
return message; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public interface Message { |
||||||
|
|
||||||
|
/** |
||||||
|
* 消息类型 |
||||||
|
* |
||||||
|
* @return 类型 |
||||||
|
*/ |
||||||
|
Type getType(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回内容 |
||||||
|
* |
||||||
|
* @return 内容 |
||||||
|
*/ |
||||||
|
String get(); |
||||||
|
|
||||||
|
enum Type { |
||||||
|
|
||||||
|
/** |
||||||
|
* 简单 |
||||||
|
*/ |
||||||
|
SIMPLE, |
||||||
|
|
||||||
|
/** |
||||||
|
* 链接 |
||||||
|
*/ |
||||||
|
LINK |
||||||
|
} |
||||||
|
|
||||||
|
class Simple implements Message { |
||||||
|
|
||||||
|
private String message; |
||||||
|
|
||||||
|
public Simple(String message) { |
||||||
|
this.message = message; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String get() { |
||||||
|
return message; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return Type.SIMPLE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Link implements Message { |
||||||
|
|
||||||
|
private String text; |
||||||
|
|
||||||
|
private String link; |
||||||
|
|
||||||
|
public Link(String text, String link) { |
||||||
|
this.text = text; |
||||||
|
this.link = link; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String get() { |
||||||
|
return getText(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getText() { |
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLink() { |
||||||
|
return link; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return Type.LINK; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.env.detect.bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public interface SolutionAction { |
||||||
|
|
||||||
|
String name(); |
||||||
|
|
||||||
|
void run(); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.env.detect.base.ExceptionDetector; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorStatus; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测链 |
||||||
|
* 责任链模式, 有先后顺序,哪一个先获得,哪一个先终止。 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/06/16 |
||||||
|
**/ |
||||||
|
public class DetectorChain { |
||||||
|
|
||||||
|
private List<ExceptionDetector> detectors = new ArrayList<>(); |
||||||
|
|
||||||
|
public static DetectorChain construct(ExceptionDetector... detectors) { |
||||||
|
|
||||||
|
DetectorChain detectorChain = new DetectorChain(); |
||||||
|
detectorChain.detectors = Arrays.stream(detectors).collect(Collectors.toList()); |
||||||
|
return detectorChain; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public DetectorResult detect() { |
||||||
|
|
||||||
|
for (ExceptionDetector detector : detectors) { |
||||||
|
DetectorResult result = detector.detect(); |
||||||
|
if (result != null && result.getStatus() == DetectorStatus.EXCEPTION) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public List<ExceptionDetector> getDetectors() { |
||||||
|
|
||||||
|
return this.detectors; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.config.ConfigContext; |
||||||
|
import com.fr.config.Configuration; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.AbstractExceptionDetector; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.Message; |
||||||
|
import com.fr.env.detect.bean.SolutionAction; |
||||||
|
import com.fr.io.utils.ResourceIOUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.third.org.hibernate.exception.GenericJDBCException; |
||||||
|
|
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.function.Function; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/25 |
||||||
|
**/ |
||||||
|
public class FineDbDirtyDetector extends AbstractExceptionDetector { |
||||||
|
|
||||||
|
public FineDbDirtyDetector() { |
||||||
|
|
||||||
|
super(DetectorType.FINE_DB_DIRTY); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DetectorResult detect() { |
||||||
|
|
||||||
|
Iterator<String> tableNames = ConfigContext.getConfigNames(); |
||||||
|
while (tableNames.hasNext()) { |
||||||
|
String tableName = tableNames.next(); |
||||||
|
Class<? extends Configuration> configClass = ConfigContext.getConfigClass(tableName); |
||||||
|
Configuration configuration = ConfigContext.getConfigInstance(configClass); |
||||||
|
try { |
||||||
|
|
||||||
|
// 尝试获取每一个值
|
||||||
|
configuration.mirror(); |
||||||
|
} catch (Throwable e) { |
||||||
|
|
||||||
|
Function<Throwable, Boolean> isDirtyExFunction = throwable -> { |
||||||
|
while (throwable != null) { |
||||||
|
if (throwable instanceof GenericJDBCException) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
throwable = throwable.getCause(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
boolean isDirtyEx = isDirtyExFunction.apply(e); |
||||||
|
|
||||||
|
if (isDirtyEx) { |
||||||
|
DetectorType detectorType = DetectorType.FINE_DB_DIRTY; |
||||||
|
DetectorResult.DetectorResultBuilder builder = DetectorResult |
||||||
|
.builder() |
||||||
|
.withType(detectorType); |
||||||
|
|
||||||
|
String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); |
||||||
|
|
||||||
|
String solutionLocale = detectorType.getSolutionLocale(); |
||||||
|
ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(Toolkit.i18nText(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { |
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
boolean success = false; |
||||||
|
try { |
||||||
|
ResourceIOUtils.copy(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME), |
||||||
|
StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_BAK_NAME)); |
||||||
|
success = ResourceIOUtils.delete(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME)); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
// todo 最好的逻辑是,这里应该和 UI 隔离开的
|
||||||
|
if (!success) { |
||||||
|
FineJOptionPane.showMessageDialog(null, |
||||||
|
Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", |
||||||
|
ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Error"), |
||||||
|
JOptionPane.ERROR_MESSAGE); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
builder.withTips(tipsLocale) |
||||||
|
.withSolution(exceptionSolution) |
||||||
|
.withLog(Toolkit.i18nText(detectorType.getLogLocale(), tableName)); |
||||||
|
|
||||||
|
return builder.build(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return DetectorResult.normal(DetectorType.FINE_DB_DIRTY); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.env.detect.base.CatchExceptionDetector; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.impl.converter.FineDbLockedConverter; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class FineDbLockedDetector extends CatchExceptionDetector { |
||||||
|
|
||||||
|
public FineDbLockedDetector() { |
||||||
|
super(DetectorType.FINE_DB_LOCKED, new FineDbLockedConverter()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.env.detect.base.CatchExceptionDetector; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.impl.converter.FineDbPermissionConverter; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class FineDbPermissionDetector extends CatchExceptionDetector { |
||||||
|
|
||||||
|
public FineDbPermissionDetector() { |
||||||
|
super(DetectorType.FINE_DB_PERMISSION, new FineDbPermissionConverter()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.env.detect.base.CatchExceptionDetector; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.impl.converter.ClassConflictConvertor; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class JarConflictDetector extends CatchExceptionDetector { |
||||||
|
|
||||||
|
public JarConflictDetector() { |
||||||
|
super(DetectorType.JAR_CONFLICT, new ClassConflictConvertor()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,156 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.common.util.Collections; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.AbstractExceptionDetector; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.bean.ExceptionLog; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.ExceptionTips; |
||||||
|
import com.fr.env.detect.bean.Message; |
||||||
|
import com.fr.general.build.BuildInfo; |
||||||
|
import com.fr.general.build.BuildInfoManager; |
||||||
|
import com.fr.general.build.BuildInfoOperator; |
||||||
|
import com.fr.general.build.impl.BuildInfoOperatorImpl; |
||||||
|
import com.fr.third.guava.collect.MapDifference; |
||||||
|
import com.fr.third.guava.collect.Maps; |
||||||
|
import com.fr.third.org.apache.commons.lang3.StringUtils; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Optional; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/25 |
||||||
|
**/ |
||||||
|
public class JarInconsistentDetector extends AbstractExceptionDetector { |
||||||
|
|
||||||
|
public static final String SEPARATOR = ","; |
||||||
|
|
||||||
|
public JarInconsistentDetector() { |
||||||
|
|
||||||
|
super(DetectorType.JAR_IN_CONSISTENCE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 本地 |
||||||
|
* 先获取 Designer, 然后对其他的 JAR 信息匹配 |
||||||
|
* <p> |
||||||
|
* 远程 |
||||||
|
* 两边一一对应匹配 |
||||||
|
* |
||||||
|
* @return 结果 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public DetectorResult detect() { |
||||||
|
|
||||||
|
if (WorkContext.getCurrent().isLocal()) { |
||||||
|
return detectLocal(); |
||||||
|
} else { |
||||||
|
return detectLocalAndRemote(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private DetectorResult detectLocalAndRemote() { |
||||||
|
|
||||||
|
// 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包
|
||||||
|
BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); |
||||||
|
List<BuildInfo> buildInfos = buildInfoOperator.getBuildInfos(); |
||||||
|
|
||||||
|
// 远程情况
|
||||||
|
List<BuildInfo> localInfos = BuildInfoManager.getInstance().getInfos(); |
||||||
|
Map<String, String> localMap = groupBy(localInfos); |
||||||
|
|
||||||
|
List<BuildInfo> remoteInfos = buildInfos; |
||||||
|
Map<String, String> remoteMap = groupBy(remoteInfos); |
||||||
|
|
||||||
|
MapDifference<String, String> difference = Maps.difference(localMap, remoteMap); |
||||||
|
// 获取本地远程不一样的部分
|
||||||
|
Map<String, MapDifference.ValueDifference<String>> diffs = difference.entriesDiffering(); |
||||||
|
|
||||||
|
if (diffs.isEmpty()) { |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> inConsistentJars = diffs.keySet(); |
||||||
|
|
||||||
|
String message = StringUtils.join(inConsistentJars, SEPARATOR); |
||||||
|
Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + message); |
||||||
|
|
||||||
|
return DetectorResult.exception(type(), |
||||||
|
new ExceptionTips(tipsMessage), |
||||||
|
new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale(),DetectorConstants.JAR_HELP_LINK) |
||||||
|
, DetectorConstants.JAR_HELP_LINK), null), |
||||||
|
ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private DetectorResult detectLocal() { |
||||||
|
|
||||||
|
// 本地的获取方式
|
||||||
|
BuildInfoOperator operator = new BuildInfoOperatorImpl(); |
||||||
|
List<BuildInfo> buildInfos = operator.getBuildInfos(); |
||||||
|
|
||||||
|
// 获取设计器的 build
|
||||||
|
Optional<String> designerBuild = buildInfos.stream() |
||||||
|
.filter(DetectorUtil::isDesignerJar) |
||||||
|
.map(BuildInfo::getGroupBuild) |
||||||
|
.filter(StringUtils::isNotEmpty) |
||||||
|
.findFirst(); |
||||||
|
|
||||||
|
// 如果 build
|
||||||
|
if (!designerBuild.isPresent()) { |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取所有的不一致的 build
|
||||||
|
List<BuildInfo> inConsistentInfos = buildInfos.stream() |
||||||
|
.filter((e) -> { |
||||||
|
// 不为空,且不相等
|
||||||
|
return StringUtils.isNotEmpty(e.getGroupBuild()) |
||||||
|
&& !StringUtils.equals(designerBuild.get(), e.getGroupBuild()); |
||||||
|
}) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
// 没有直接返回
|
||||||
|
if (Collections.isEmpty(inConsistentInfos)) { |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
|
||||||
|
// 有的话
|
||||||
|
List<String> inConsistentJars = inConsistentInfos.stream() |
||||||
|
.map(BuildInfo::getJar) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
String message = StringUtils.join(inConsistentJars, SEPARATOR); |
||||||
|
String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + message; |
||||||
|
|
||||||
|
return DetectorResult.exception(type(), |
||||||
|
new ExceptionTips(new Message.Simple(tipsMessage)), |
||||||
|
new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), |
||||||
|
ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); |
||||||
|
} |
||||||
|
|
||||||
|
private Map<String, String> groupBy(List<BuildInfo> localInfos) { |
||||||
|
|
||||||
|
Map<String, String> localMap = new HashMap<>(); |
||||||
|
for (BuildInfo localInfo : localInfos) { |
||||||
|
String jar = localInfo.getJar(); |
||||||
|
String groupContent = localInfo.getGroupBuild(); |
||||||
|
// 不一致的 JAR 检测,忽视缺少的情况
|
||||||
|
if (StringUtils.isNotEmpty(groupContent)) { |
||||||
|
localMap.put(jar, groupContent); |
||||||
|
} |
||||||
|
} |
||||||
|
return localMap; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
package com.fr.env.detect.impl; |
||||||
|
|
||||||
|
import com.fr.common.util.Collections; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.AbstractExceptionDetector; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.bean.ExceptionLog; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.ExceptionTips; |
||||||
|
import com.fr.env.detect.bean.Message; |
||||||
|
import com.fr.general.build.BuildInfo; |
||||||
|
import com.fr.general.build.BuildInfoManager; |
||||||
|
import com.fr.general.build.BuildInfoOperator; |
||||||
|
import com.fr.general.build.impl.BuildInfoOperatorImpl; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
import com.fr.third.org.apache.commons.lang3.StringUtils; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.function.Predicate; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; |
||||||
|
import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class JarLackDetector extends AbstractExceptionDetector { |
||||||
|
|
||||||
|
public JarLackDetector() { |
||||||
|
|
||||||
|
super(DetectorType.LACK_OF_JAR); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DetectorResult detect() { |
||||||
|
|
||||||
|
List<BuildInfo> lackInfos; |
||||||
|
|
||||||
|
// 远程
|
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
// 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包
|
||||||
|
BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); |
||||||
|
// 远程情况
|
||||||
|
List<BuildInfo> remoteInfos = buildInfoOperator.getBuildInfos(); |
||||||
|
// 本地情况
|
||||||
|
List<BuildInfo> localInfos = BuildInfoManager.getInstance().getInfos(); |
||||||
|
|
||||||
|
// 说明远程环境并不存在对应的 info.json, 直接忽略
|
||||||
|
if (Collections.isEmpty(remoteInfos)) { |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> remoteSet = remoteInfos.stream() |
||||||
|
.filter(this::isExistInfo) |
||||||
|
.map(BuildInfo::getJar) |
||||||
|
.collect(Collectors.toSet()); |
||||||
|
|
||||||
|
Predicate<BuildInfo> remoteNotContains = (e) -> !remoteSet.contains(e.getJar()); |
||||||
|
|
||||||
|
lackInfos = localInfos.stream() |
||||||
|
.filter(this::isExistInfo) |
||||||
|
// 不是设计器的 JAR
|
||||||
|
.filter((e) -> !DetectorUtil.isDesignerJar(e)) |
||||||
|
.filter(remoteNotContains) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
} else { |
||||||
|
// 本地
|
||||||
|
// 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包
|
||||||
|
BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); |
||||||
|
List<BuildInfo> buildInfos = buildInfoOperator.getBuildInfos(); |
||||||
|
lackInfos = buildInfos.stream() |
||||||
|
.filter(this::isLackInfo) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Collections.isEmpty(lackInfos)) { |
||||||
|
return DetectorResult.normal(type()); |
||||||
|
} |
||||||
|
|
||||||
|
Message tipsMsg = tipsMessage(lackInfos); |
||||||
|
ExceptionLog exceptionLog = exceptionLog(lackInfos); |
||||||
|
|
||||||
|
return DetectorResult.exception(type(), |
||||||
|
new ExceptionTips(tipsMsg), |
||||||
|
new ExceptionSolution( |
||||||
|
new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), |
||||||
|
null), exceptionLog); |
||||||
|
} |
||||||
|
|
||||||
|
private ExceptionLog exceptionLog(List<BuildInfo> lackInfos) { |
||||||
|
|
||||||
|
List<String> jarInfos = lackInfos.stream() |
||||||
|
.map(BuildInfo::getJar) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
String message = StringUtils.join(jarInfos, ","); |
||||||
|
return ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isExistInfo(BuildInfo e) { |
||||||
|
|
||||||
|
return !isLackInfo(e); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isLackInfo(BuildInfo e) { |
||||||
|
|
||||||
|
return StringUtils.isEmpty(e.getGroupBuild()); |
||||||
|
} |
||||||
|
|
||||||
|
private Message tipsMessage(List<BuildInfo> infos) { |
||||||
|
|
||||||
|
Map<String, List<String>> libMap = groupByPath(infos, FR_HOME_LIB, WEB_LIB_PATH); |
||||||
|
|
||||||
|
String content = DetectorUtil.concatLibFormatMsg(libMap); |
||||||
|
DetectorType type = this.type(); |
||||||
|
String header = Toolkit.i18nText(type.getTipsLocale()); |
||||||
|
return new Message.Simple(header + content); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private Map<String, List<String>> groupByPath(List<BuildInfo> infos, String homeLibPath, String webLibPath) { |
||||||
|
|
||||||
|
Map<String, List<String>> libMap = new HashMap<>(); |
||||||
|
for (BuildInfo info : infos) { |
||||||
|
String key; |
||||||
|
if (inHomeLib(info)) { |
||||||
|
key = homeLibPath; |
||||||
|
} else { |
||||||
|
key = webLibPath; |
||||||
|
} |
||||||
|
libMap.compute(key, (keyA, value) -> { |
||||||
|
if (Collections.isEmpty(value)) { |
||||||
|
value = Lists.newArrayList(info.getJar()); |
||||||
|
} else { |
||||||
|
value.add(info.getJar()); |
||||||
|
} |
||||||
|
return value; |
||||||
|
}); |
||||||
|
} |
||||||
|
return libMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在 %FR_HOME%\lib |
||||||
|
* 否则都是在 %FR_HOME%\webapps\webroot\WEB-INF\lib |
||||||
|
* |
||||||
|
* @param info JAR 信息 |
||||||
|
* @return 是否位于 HOME\LIB |
||||||
|
*/ |
||||||
|
private boolean inHomeLib(BuildInfo info) { |
||||||
|
|
||||||
|
return DetectorUtil.isDesignerJar(info); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
package com.fr.env.detect.impl.converter; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.bean.ExceptionLog; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.ExceptionTips; |
||||||
|
import com.fr.env.detect.thowable.ThrowableConverter; |
||||||
|
import com.fr.stable.EncodeConstants; |
||||||
|
import com.fr.stable.resource.ResourceLoader; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.el.MethodNotFoundException; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.net.URL; |
||||||
|
import java.net.URLDecoder; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* 类抛出异常的转换 |
||||||
|
* 原则:可以找不到,但不要误报 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class ClassConflictConvertor implements ThrowableConverter { |
||||||
|
|
||||||
|
public static final String CLASSES = "classes"; |
||||||
|
public static final String SEPARATOR = "、"; |
||||||
|
/** |
||||||
|
* 获取对应的 JAR 包名称 |
||||||
|
*/ |
||||||
|
private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); |
||||||
|
|
||||||
|
private static final String WEB_INF_STRING = "WEB-INF"; |
||||||
|
private static final String JAR_URL_SUFFIX = ".jar!"; |
||||||
|
private static final String JAR_FILE_SUFFIX = ".jar"; |
||||||
|
private static final String FILE_URL_PREFIX = "file:"; |
||||||
|
|
||||||
|
private final Map<Class<?>, ClassNameConverter> throwableMap = new HashMap<>(); |
||||||
|
|
||||||
|
public ClassConflictConvertor() { |
||||||
|
|
||||||
|
// 类异常
|
||||||
|
this.throwableMap.put(ClassNotFoundException.class, Converter.CLASS); |
||||||
|
this.throwableMap.put(NoClassDefFoundError.class, Converter.CLASS); |
||||||
|
this.throwableMap.put(ClassCastException.class, Converter.CLASS); |
||||||
|
this.throwableMap.put(IncompatibleClassChangeError.class, Converter.CLASS); |
||||||
|
|
||||||
|
// 方法异常
|
||||||
|
this.throwableMap.put(MethodNotFoundException.class, Converter.METHOD); |
||||||
|
this.throwableMap.put(NoSuchMethodException.class, Converter.METHOD); |
||||||
|
this.throwableMap.put(NoSuchMethodError.class, Converter.METHOD); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(Throwable throwable) { |
||||||
|
|
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null) { |
||||||
|
if (throwableMap.containsKey(sign.getClass())) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DetectorResult convert(Throwable throwable) { |
||||||
|
|
||||||
|
Iterable<String> classNames = Collections.emptyList(); |
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null) { |
||||||
|
if (throwableMap.containsKey(sign.getClass())) { |
||||||
|
classNames = throwableMap.get(sign.getClass()) |
||||||
|
.converter(throwable); |
||||||
|
} |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, List<String>> libMap = new HashMap<>(); |
||||||
|
libMap.put(DetectorConstants.FR_HOME_LIB, new ArrayList<>()); |
||||||
|
libMap.put(DetectorConstants.WEB_LIB_PATH, new ArrayList<>()); |
||||||
|
|
||||||
|
Set<String> allPath = new HashSet<>(); |
||||||
|
for (String className : classNames) { |
||||||
|
String classFile = convertClass2Path(className); |
||||||
|
try { |
||||||
|
Enumeration<URL> urls = ResourceLoader.getResources(classFile, this.getClass()); |
||||||
|
List<URL> urlList = new ArrayList<>(); |
||||||
|
while (urls.hasMoreElements()) { |
||||||
|
URL url = urls.nextElement(); |
||||||
|
urlList.add(url); |
||||||
|
} |
||||||
|
for (URL url : urlList) { |
||||||
|
String file = url.getFile(); |
||||||
|
String decodeFileStr = URLDecoder.decode(file, EncodeConstants.ENCODING_UTF_8); |
||||||
|
if (decodeFileStr.contains(JAR_URL_SUFFIX)) { |
||||||
|
String jarPath = decodeFileStr.substring(FILE_URL_PREFIX.length(), decodeFileStr.indexOf(JAR_URL_SUFFIX) + JAR_FILE_SUFFIX.length()); |
||||||
|
String jar = new File(jarPath).getName(); |
||||||
|
if (allPath.add(jar)) { |
||||||
|
List<String> libPath; |
||||||
|
if (file.contains(WEB_INF_STRING)) { |
||||||
|
libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); |
||||||
|
} else { |
||||||
|
libPath = libMap.get(DetectorConstants.FR_HOME_LIB); |
||||||
|
} |
||||||
|
libPath.add(jar); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (IOException ignore) { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 如果少于两个,则不需要提示
|
||||||
|
if (allPath.size() < 2) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
String msg = DetectorUtil.concatLibFormatMsg(libMap); |
||||||
|
|
||||||
|
DetectorType type = DetectorType.JAR_CONFLICT; |
||||||
|
return DetectorResult.exception(type, |
||||||
|
ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale()) + msg), |
||||||
|
ExceptionSolution.create(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.JAR_HELP_LINK, null), |
||||||
|
ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()) + msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private String convertClass2Path(String className) { |
||||||
|
|
||||||
|
return "/" + className.replaceAll("\\.", "/") + ".class"; |
||||||
|
} |
||||||
|
|
||||||
|
private interface ClassNameConverter { |
||||||
|
|
||||||
|
/** |
||||||
|
* 将异常解析为类名,可能有多个 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 类名 |
||||||
|
*/ |
||||||
|
Iterable<String> converter(Throwable throwable); |
||||||
|
} |
||||||
|
|
||||||
|
public enum Converter implements ClassNameConverter { |
||||||
|
|
||||||
|
/** |
||||||
|
* 类匹配 |
||||||
|
*/ |
||||||
|
CLASS { |
||||||
|
|
||||||
|
/** |
||||||
|
* 匹配 111.222.333 |
||||||
|
* 至少有一个 111. |
||||||
|
* 至少有一个 333 |
||||||
|
*/ |
||||||
|
private final Pattern CLASS_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))"); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Iterable<String> converter(Throwable throwable) { |
||||||
|
|
||||||
|
String message = throwable.getMessage(); |
||||||
|
Matcher matcher = CLASS_PATTERN.matcher(message); |
||||||
|
List<String> names = new ArrayList<>(); |
||||||
|
while (matcher.find()) { |
||||||
|
String className = matcher.group(); |
||||||
|
names.add(className); |
||||||
|
} |
||||||
|
return names; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 方法匹配 |
||||||
|
*/ |
||||||
|
METHOD { |
||||||
|
|
||||||
|
/** |
||||||
|
* 后面有 .method()xxxx |
||||||
|
*/ |
||||||
|
private final Pattern METHOD_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))(\\.\\w+\\(\\))"); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Iterable<String> converter(Throwable throwable) { |
||||||
|
|
||||||
|
String message = throwable.getMessage(); |
||||||
|
Matcher matcher = METHOD_PATTERN.matcher(message); |
||||||
|
List<String> names = new ArrayList<>(); |
||||||
|
while (matcher.find()) { |
||||||
|
String className = matcher.group(); |
||||||
|
names.add(className); |
||||||
|
} |
||||||
|
return names; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package com.fr.env.detect.impl.converter; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.thowable.ThrowableConverter; |
||||||
|
import com.fr.third.org.apache.commons.lang3.StringUtils; |
||||||
|
import com.fr.third.org.hsqldb.HsqlException; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class FineDbLockedConverter implements ThrowableConverter { |
||||||
|
|
||||||
|
public static final String LOCK_FILE = "lockFile"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(Throwable throwable) { |
||||||
|
|
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null) { |
||||||
|
if (sign.getClass() == HsqlException.class) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测 FineDB 是否锁住 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 结果 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public DetectorResult convert(Throwable throwable) { |
||||||
|
|
||||||
|
// 远程不执行
|
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null && sign.getClass() != HsqlException.class) { |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
|
||||||
|
if (sign == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
DetectorType type = DetectorType.FINE_DB_LOCKED; |
||||||
|
|
||||||
|
String message = sign.getMessage(); |
||||||
|
if (StringUtils.containsIgnoreCase(message, LOCK_FILE)) { |
||||||
|
|
||||||
|
return DetectorResult.builder() |
||||||
|
.withType(type) |
||||||
|
.withTips(Toolkit.i18nText(type.getTipsLocale())) |
||||||
|
.withSolution(Toolkit.i18nText(type.getSolutionLocale(),DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK) |
||||||
|
.withLog(Toolkit.i18nText(type.getLogLocale())) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
package com.fr.env.detect.impl.converter; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.env.detect.base.DetectorConstants; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.env.detect.thowable.ThrowableConverter; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.third.org.apache.commons.io.FileUtils; |
||||||
|
import com.fr.third.org.hsqldb.HsqlException; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.RandomAccessFile; |
||||||
|
import java.util.Collection; |
||||||
|
|
||||||
|
/** |
||||||
|
* HSQL 下的权限检测 |
||||||
|
* <p> |
||||||
|
* created by Harrison on 2022/05/24 |
||||||
|
**/ |
||||||
|
public class FineDbPermissionConverter implements ThrowableConverter { |
||||||
|
|
||||||
|
public static final String EMBED_DB_NAME = "finedb"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(Throwable throwable) { |
||||||
|
|
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null) { |
||||||
|
if (sign.getClass() == HsqlException.class) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DetectorResult convert(Throwable throwable) { |
||||||
|
|
||||||
|
// 远程不执行
|
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
Throwable sign = throwable; |
||||||
|
while (sign != null && sign.getClass() != HsqlException.class) { |
||||||
|
sign = sign.getCause(); |
||||||
|
} |
||||||
|
|
||||||
|
if (sign == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
String fineDbDirectory = WorkContext.getCurrent().getPath() + File.separator + ProjectConstants.EMBED_DB_DIRECTORY + File.separator + EMBED_DB_NAME; |
||||||
|
Collection<File> files = FileUtils.listFiles(new File(fineDbDirectory), null, true); |
||||||
|
Boolean isPermitted = files.stream() |
||||||
|
.map((file -> { |
||||||
|
try { |
||||||
|
// 进行权限判断
|
||||||
|
new RandomAccessFile(file, "rw"); |
||||||
|
return true; |
||||||
|
} catch (FileNotFoundException e) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
})) |
||||||
|
.reduce((a, b) -> a & b) |
||||||
|
.orElse(Boolean.FALSE); |
||||||
|
|
||||||
|
if (!isPermitted) { |
||||||
|
DetectorType type = DetectorType.FINE_DB_PERMISSION; |
||||||
|
return DetectorResult.builder() |
||||||
|
.withType(type) |
||||||
|
.withTips(Toolkit.i18nText(type.getTipsLocale())) |
||||||
|
.withSolution(Toolkit.i18nText(type.getSolutionLocale(), DetectorConstants.FINE_DB_HELP_LINK), |
||||||
|
DetectorConstants.FINE_DB_HELP_LINK) |
||||||
|
.withLog(type.getLogLocale()) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.env.detect.thowable; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public interface ThrowableConverter { |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否支持该异常 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 是/否 |
||||||
|
*/ |
||||||
|
boolean accept(Throwable throwable); |
||||||
|
|
||||||
|
/** |
||||||
|
* 将异常转化为结果 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 转化结果 |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
DetectorResult convert(Throwable throwable); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
package com.fr.env.detect.thowable; |
||||||
|
|
||||||
|
import com.fr.general.FRLogger; |
||||||
|
import com.fr.log.LogHandler; |
||||||
|
import com.fr.third.apache.logging.log4j.Level; |
||||||
|
import com.fr.third.apache.logging.log4j.core.Filter; |
||||||
|
import com.fr.third.apache.logging.log4j.core.Layout; |
||||||
|
import com.fr.third.apache.logging.log4j.core.LogEvent; |
||||||
|
import com.fr.third.apache.logging.log4j.core.appender.AbstractAppender; |
||||||
|
import com.fr.third.apache.logging.log4j.core.config.Property; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class ThrowableLogAppender extends AbstractAppender { |
||||||
|
|
||||||
|
public ThrowableLogAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, Property[] properties) { |
||||||
|
super(name, filter, layout, ignoreExceptions, properties); |
||||||
|
} |
||||||
|
|
||||||
|
public static ThrowableLogAppender getInstance() { |
||||||
|
return ExceptionLogAppenderHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class ExceptionLogAppenderHolder { |
||||||
|
private static final ThrowableLogAppender INSTANCE = new ThrowableLogAppender("exception-detect-appender", null, null, false, null); |
||||||
|
} |
||||||
|
|
||||||
|
private final LogHandler<ThrowableLogAppender> logHandler = toHandler(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void append(LogEvent logEvent) { |
||||||
|
|
||||||
|
try { |
||||||
|
if (logEvent.getLevel() == Level.ERROR) { |
||||||
|
Throwable thrown = logEvent.getThrown(); |
||||||
|
if (thrown != null) { |
||||||
|
ThrowableStore.getInstance().add(thrown); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Throwable ignore) { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private LogHandler<ThrowableLogAppender> toHandler() { |
||||||
|
|
||||||
|
final ThrowableLogAppender appender = this; |
||||||
|
appender.start(); |
||||||
|
LogHandler<ThrowableLogAppender> handler = new LogHandler<ThrowableLogAppender>() { |
||||||
|
@Override |
||||||
|
public ThrowableLogAppender getHandler() { |
||||||
|
return appender; |
||||||
|
} |
||||||
|
}; |
||||||
|
return handler; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void enable() { |
||||||
|
|
||||||
|
// 初始化一下,别出问题
|
||||||
|
logHandler.getHandler().start(); |
||||||
|
FRLogger.getLogger().addExtendLogAppender(logHandler); |
||||||
|
} |
||||||
|
|
||||||
|
public void disable() { |
||||||
|
|
||||||
|
FRLogger.getLogger().removeExtendLogAppender(logHandler); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.env.detect.thowable; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常存储中心 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/13 |
||||||
|
**/ |
||||||
|
public class ThrowableStore { |
||||||
|
|
||||||
|
public static ThrowableStore getInstance() { |
||||||
|
return ThrowableStoreHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class ThrowableStoreHolder { |
||||||
|
private static final ThrowableStore INSTANCE = new ThrowableStore(); |
||||||
|
} |
||||||
|
|
||||||
|
private List<Throwable> exceptions = new ArrayList<>(); |
||||||
|
|
||||||
|
public void add(Throwable throwable) { |
||||||
|
if (throwable != null) { |
||||||
|
exceptions.add(throwable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public List<Throwable> getAll() { |
||||||
|
return exceptions; |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
exceptions.clear(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
package com.fr.env.detect.ui; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.RestartHelper; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.utils.ColorUtils; |
||||||
|
import com.fr.design.utils.DesignUtils; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.ExceptionSolution; |
||||||
|
import com.fr.env.detect.bean.ExceptionTips; |
||||||
|
import com.fr.env.detect.bean.Message; |
||||||
|
import com.fr.exit.DesignerExiter; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JScrollPane; |
||||||
|
import javax.swing.ScrollPaneConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 未知错误框 |
||||||
|
* todo 其实这里可以将多个 error-dialog 抽象在一起的。 时间不够, 简单写写 |
||||||
|
* 见 {@link com.fr.design.dialog.ErrorDialog} |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/29 |
||||||
|
**/ |
||||||
|
public class DetectorErrorDialog extends JDialog implements ActionListener { |
||||||
|
|
||||||
|
public static final float FONT_BOLD_HEIGHT = 16f; |
||||||
|
private UIButton okButton; |
||||||
|
private UIButton restartButton; |
||||||
|
|
||||||
|
public DetectorErrorDialog(Frame parent, List<DetectorResult> results) { |
||||||
|
|
||||||
|
super(parent, true); |
||||||
|
|
||||||
|
// 可能是还没初始化 UI 的时候出现的问题,初始化一下 UI
|
||||||
|
DesignUtils.initLookAndFeel(); |
||||||
|
|
||||||
|
JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
JPanel headerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
headerPane.setLayout(new BorderLayout(15, 0)); |
||||||
|
|
||||||
|
UILabel iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_warning_window.svg")); |
||||||
|
headerPane.add(iconLabel, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); |
||||||
|
{ |
||||||
|
UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); |
||||||
|
Font font = boldHeader.getFont(); |
||||||
|
Font boldFont = font.deriveFont(FONT_BOLD_HEIGHT); |
||||||
|
boldHeader.setFont(boldFont); |
||||||
|
messagePane.add(boldHeader); |
||||||
|
|
||||||
|
UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); |
||||||
|
messagePane.add(description); |
||||||
|
} |
||||||
|
headerPane.add(messagePane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
northPane.add(headerPane); |
||||||
|
|
||||||
|
JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
centerPane.setLayout(new BorderLayout(0, 5)); |
||||||
|
|
||||||
|
UILabel detailDesc = new UILabel(Toolkit.i18nText("Fine-Design_Problem_Detail_Message")); |
||||||
|
centerPane.add(detailDesc, BorderLayout.NORTH); |
||||||
|
|
||||||
|
JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 10)); |
||||||
|
detailPanel.setLayout(new BorderLayout(0, 8)); |
||||||
|
|
||||||
|
for (DetectorResult result : results) { |
||||||
|
JPanel detailItemPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
detailItemPanel.setLayout(new BorderLayout(0, 8)); |
||||||
|
ExceptionTips tips = result.getTips(); |
||||||
|
|
||||||
|
UILabel template = new UILabel(); |
||||||
|
template.setBackground(Color.white); |
||||||
|
|
||||||
|
if (tips != null) { |
||||||
|
Message tipsMsg = tips.getMessage(); |
||||||
|
detailItemPanel.add(DetectorUtil.convert2TextComponent(tipsMsg, template), BorderLayout.NORTH); |
||||||
|
} |
||||||
|
ExceptionSolution solution = result.getSolution(); |
||||||
|
if (solution != null) { |
||||||
|
Message solutionMsg = solution.getMessage(); |
||||||
|
detailItemPanel.add(DetectorUtil.convert2TextComponent(solutionMsg, template), BorderLayout.CENTER); |
||||||
|
} |
||||||
|
detailPanel.add(detailItemPanel, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
JScrollPane detailPanelWrapper = new JScrollPane(detailPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
||||||
|
ColorUtils.syncBackground(detailPanelWrapper, Color.WHITE); |
||||||
|
centerPane.add(detailPanelWrapper, BorderLayout.CENTER); |
||||||
|
|
||||||
|
JPanel southPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
JPanel controlPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); |
||||||
|
okButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Ok")); |
||||||
|
okButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
okEvent(); |
||||||
|
} |
||||||
|
}); |
||||||
|
buttonPane.add(okButton); |
||||||
|
restartButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Restart")); |
||||||
|
restartButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
restartEvent(); |
||||||
|
} |
||||||
|
}); |
||||||
|
buttonPane.add(restartButton); |
||||||
|
controlPane.add(buttonPane, BorderLayout.EAST); |
||||||
|
southPane.add(controlPane); |
||||||
|
|
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Error_Start_Report")); |
||||||
|
this.setResizable(false); |
||||||
|
this.add(northPane, BorderLayout.NORTH); |
||||||
|
this.add(centerPane, BorderLayout.CENTER); |
||||||
|
this.add(southPane, BorderLayout.SOUTH); |
||||||
|
this.setSize(new Dimension(650, 500)); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void okEvent() { |
||||||
|
|
||||||
|
dispose(); |
||||||
|
DesignerExiter.getInstance().execute(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void restartEvent() { |
||||||
|
|
||||||
|
dispose(); |
||||||
|
RestartHelper.restart(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.env.detect.ui; |
||||||
|
|
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
|
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 工具栏里面的行为 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/29 |
||||||
|
**/ |
||||||
|
public class EnvDetectorAction extends UpdateAction { |
||||||
|
|
||||||
|
public EnvDetectorAction() { |
||||||
|
|
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Detect_Toolbar_Title")); |
||||||
|
this.setSmallIcon("com/fr/env/detect/detect_normal.svg"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
|
||||||
|
EnvDetectorDialog dialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||||
|
dialog.setVisible(true); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,569 @@ |
|||||||
|
package com.fr.env.detect.ui; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.components.notification.NotificationDialog; |
||||||
|
import com.fr.design.components.notification.NotificationDialogProperties; |
||||||
|
import com.fr.design.components.notification.NotificationModel; |
||||||
|
import com.fr.design.components.table.TablePanel; |
||||||
|
import com.fr.design.constants.DesignerColor; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ibutton.UIButtonUI; |
||||||
|
import com.fr.design.gui.icheckbox.UICheckBox; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.design.utils.gui.GUIPaintUtils; |
||||||
|
import com.fr.env.detect.base.DetectorBridge; |
||||||
|
import com.fr.env.detect.base.DetectorUtil; |
||||||
|
import com.fr.env.detect.base.EnvDetectorConfig; |
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorStatus; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.ImageIcon; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import javax.swing.plaf.ButtonUI; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Image; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.net.URL; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/05/26 |
||||||
|
**/ |
||||||
|
public class EnvDetectorDialog extends JDialog { |
||||||
|
|
||||||
|
private static final ImageIcon LOADING_ICON = getLoadingIcon(); |
||||||
|
private static final int TIMEOUT = 1000; |
||||||
|
|
||||||
|
private static final Color SUCCESS_COLOR = new Color(22, 193, 83); |
||||||
|
private static final Color DETAIL_FONT_COLOR = new Color(65, 155, 249); |
||||||
|
|
||||||
|
private final JPanel body; |
||||||
|
|
||||||
|
private final JPanel headerPanel; |
||||||
|
private UIButton detectButton; |
||||||
|
private JPanel resultSummaryPane; |
||||||
|
|
||||||
|
private final TablePanel tablePanel; |
||||||
|
|
||||||
|
private final JPanel tailPanel; |
||||||
|
|
||||||
|
/* 数据 model */ |
||||||
|
|
||||||
|
private EnvDetectorModel model; |
||||||
|
|
||||||
|
/* 流程 model */ |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认是第一个要检测 |
||||||
|
*/ |
||||||
|
private int currentDetectIndex = 0; |
||||||
|
|
||||||
|
private EnvDetectorButtonStatus buttonStatus = EnvDetectorButtonStatus.START; |
||||||
|
|
||||||
|
private SwingWorker<Void, Void> detectWorker = null; |
||||||
|
|
||||||
|
/* config model */ |
||||||
|
|
||||||
|
private boolean detectOpen = EnvDetectorConfig.getInstance().isEnabled(); |
||||||
|
|
||||||
|
public EnvDetectorDialog(Frame owner) { |
||||||
|
this(owner, null); |
||||||
|
} |
||||||
|
|
||||||
|
public EnvDetectorDialog(Frame owner, EnvDetectorModel model) { |
||||||
|
super(owner); |
||||||
|
|
||||||
|
configProperties(); |
||||||
|
|
||||||
|
if (model == null) { |
||||||
|
this.model = new EnvDetectorModel(); |
||||||
|
} else { |
||||||
|
this.model = model; |
||||||
|
} |
||||||
|
|
||||||
|
this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
Color backgroundColor = new Color(240, 240, 243, 1); |
||||||
|
this.body.setBackground( backgroundColor); |
||||||
|
|
||||||
|
this.headerPanel = createHeaderPanel(); |
||||||
|
body.add(headerPanel, BorderLayout.NORTH); |
||||||
|
|
||||||
|
this.tablePanel = createTablePanel(); |
||||||
|
body.add(tablePanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
/* tailPanel*/ |
||||||
|
this.tailPanel = createTailPanel(); |
||||||
|
body.add(tailPanel, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
add(body); |
||||||
|
|
||||||
|
Dimension preferredSize = body.getPreferredSize(); |
||||||
|
setSize(preferredSize); |
||||||
|
|
||||||
|
repaint(); |
||||||
|
pack(); |
||||||
|
|
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
/* header */ |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private JPanel createHeaderPanel() { |
||||||
|
|
||||||
|
JPanel headerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 12, 0)); |
||||||
|
this.detectButton = new UIButton(buttonStatus.getDesc()) { |
||||||
|
@Override |
||||||
|
public ButtonUI getUI() { |
||||||
|
|
||||||
|
return new UIButtonUI() { |
||||||
|
@Override |
||||||
|
protected void doExtraPainting(UIButton b, Graphics2D g2d, int w, int h, String selectedRoles) { |
||||||
|
if (isPressed(b) && b.isPressedPainted()) { |
||||||
|
GUIPaintUtils.fillPressed(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), |
||||||
|
DesignerColor.Button.Primary.PRESSED); |
||||||
|
} else if (isRollOver(b)) { |
||||||
|
GUIPaintUtils.fillRollOver(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), |
||||||
|
DesignerColor.Button.Primary.HOVER); |
||||||
|
} else if (b.isNormalPainted()) { |
||||||
|
GUIPaintUtils.fillNormal(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), |
||||||
|
DesignerColor.Button.Primary.NORMAL); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
}; |
||||||
|
detectButton.setForeground(Color.WHITE); |
||||||
|
detectButton.addActionListener(event -> { |
||||||
|
if (buttonStatus.isNotExecuting()) { |
||||||
|
startDetecting(); |
||||||
|
} else { |
||||||
|
stopDetecting(detectButton); |
||||||
|
} |
||||||
|
}); |
||||||
|
detectButton.setPreferredSize(new Dimension(68, 20)); |
||||||
|
detectButton.setBorderPainted(false); |
||||||
|
detectButton.setContentAreaFilled(false); |
||||||
|
headerPanel.add(detectButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
return headerPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void startDetecting() { |
||||||
|
|
||||||
|
// 重新检测的时候需要处理一些逻辑
|
||||||
|
if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { |
||||||
|
reInit(); |
||||||
|
} |
||||||
|
// 执行前
|
||||||
|
buttonStatus = buttonStatus.next(); |
||||||
|
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); |
||||||
|
detectWorker = new SwingWorker<Void, Void>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Void doInBackground() throws Exception { |
||||||
|
List<EnvDetectorItem> items = model.getItems(); |
||||||
|
// 执行刷新
|
||||||
|
for (int i = currentDetectIndex; i < items.size(); i++) { |
||||||
|
|
||||||
|
// 看一下是否关闭了, 有可能已经关闭了。
|
||||||
|
if (buttonStatus.isNotExecuting()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// 刷新一下面板-开始执行啦
|
||||||
|
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); |
||||||
|
|
||||||
|
EnvDetectorItem item = items.get(i); |
||||||
|
DetectorType type = item.getType(); |
||||||
|
|
||||||
|
// 执行检测, UI-当前在检测中
|
||||||
|
DetectorResult result = UIUtil.waitUntil( |
||||||
|
() -> DetectorBridge.getInstance().detect(type), |
||||||
|
TIMEOUT, TimeUnit.MILLISECONDS); |
||||||
|
// 获取结果
|
||||||
|
item.setResult(result); |
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
// 只有还在运行中,才会真正的刷新面板
|
||||||
|
if (buttonStatus.isExecuting()) { |
||||||
|
// 在刷新一下面板
|
||||||
|
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); |
||||||
|
currentDetectIndex++; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
|
||||||
|
try { |
||||||
|
this.get(); |
||||||
|
if (buttonStatus.isExecuting()) { |
||||||
|
// 执行结束
|
||||||
|
buttonStatus = EnvDetectorButtonStatus.A_NEW; |
||||||
|
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error("detect failed", e); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
// 开始执行
|
||||||
|
detectWorker.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
private void reInit() { |
||||||
|
currentDetectIndex = 0; |
||||||
|
for (EnvDetectorItem e : model.getItems()) { |
||||||
|
e.setResult(null); |
||||||
|
} |
||||||
|
// 刷新一下面板-开始执行啦
|
||||||
|
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); |
||||||
|
} |
||||||
|
|
||||||
|
private void stopDetecting(UIButton detectButton) { |
||||||
|
|
||||||
|
buttonStatus = buttonStatus.next(); |
||||||
|
|
||||||
|
// 先停止
|
||||||
|
detectWorker.cancel(false); |
||||||
|
// 更改-UI
|
||||||
|
// 执行中
|
||||||
|
UIUtil.invokeLaterIfNeeded(() -> { |
||||||
|
// 刷新按钮
|
||||||
|
detectButton.setText(buttonStatus.getDesc()); |
||||||
|
// 刷新面板
|
||||||
|
refreshBody(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void updateHeaderPanel() { |
||||||
|
|
||||||
|
// 刷新按钮
|
||||||
|
detectButton.setText(buttonStatus.getDesc()); |
||||||
|
if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { |
||||||
|
this.resultSummaryPane = new JPanel(); |
||||||
|
this.resultSummaryPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||||
|
this.resultSummaryPane.setLayout(new BorderLayout(5, 0)); |
||||||
|
Boolean success = model.getResults() |
||||||
|
.map((e) -> { |
||||||
|
if (e != null && e.getStatus() == DetectorStatus.NORMAL) { |
||||||
|
return Boolean.TRUE; |
||||||
|
} |
||||||
|
return Boolean.FALSE; |
||||||
|
}).reduce((a, b) -> a && b) |
||||||
|
.orElse(Boolean.FALSE); |
||||||
|
|
||||||
|
if (success) { |
||||||
|
resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); |
||||||
|
UILabel successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Success")); |
||||||
|
successLabel.setForeground(SUCCESS_COLOR); |
||||||
|
resultSummaryPane.add(successLabel, BorderLayout.CENTER); |
||||||
|
} else { |
||||||
|
resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); |
||||||
|
UILabel resultLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Error")); |
||||||
|
resultLabel.setForeground(Color.RED); |
||||||
|
resultSummaryPane.add(resultLabel, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
this.headerPanel.add(BorderLayout.CENTER, resultSummaryPane); |
||||||
|
} else { |
||||||
|
if (resultSummaryPane != null) { |
||||||
|
this.headerPanel.remove(resultSummaryPane); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* table */ |
||||||
|
|
||||||
|
|
||||||
|
@NotNull |
||||||
|
private TablePanel createTablePanel() { |
||||||
|
|
||||||
|
TablePanel tablePanel = new TablePanel(18, 3); |
||||||
|
tablePanel.updateHeaders(new String[] { |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Detect_Kind"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Detect_Item"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Detect_Result")}); |
||||||
|
|
||||||
|
updateTable(tablePanel); |
||||||
|
|
||||||
|
return tablePanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void updateTable(TablePanel tablePanel) { |
||||||
|
|
||||||
|
Map<DetectorType.Kind, List<EnvDetectorItem>> itemMap = model.getItemMap(); |
||||||
|
|
||||||
|
// 行号, 这边更新是通过 行/列 。 不是索引
|
||||||
|
int row = 1; |
||||||
|
for (Map.Entry<DetectorType.Kind, List<EnvDetectorItem>> entry : itemMap.entrySet()) { |
||||||
|
|
||||||
|
DetectorType.Kind kind = entry.getKey(); |
||||||
|
List<EnvDetectorItem> items = entry.getValue(); |
||||||
|
for (int i = 0; i < items.size(); i++) { |
||||||
|
if (i == 0) { |
||||||
|
tablePanel.updateCell(row, 1, kind.getDescription()); |
||||||
|
} |
||||||
|
EnvDetectorItem item = items.get(i); |
||||||
|
tablePanel.updateCell(row, 2, new UILabel(item.getDescription())); |
||||||
|
DetectorResult result = item.getResult(); |
||||||
|
|
||||||
|
int detectRow = currentDetectIndex + 1; |
||||||
|
|
||||||
|
if (result == null) { |
||||||
|
// 处于非正在检测状态 或者 索引不等于当前行号的时候
|
||||||
|
UILabel label; |
||||||
|
if (buttonStatus.isExecuting() && detectRow == row) { |
||||||
|
// 正在检测索引
|
||||||
|
label = new UILabel(LOADING_ICON, UILabel.LEADING); |
||||||
|
} else { |
||||||
|
label = new UILabel("-"); |
||||||
|
} |
||||||
|
tablePanel.updateCell(row, 3, label); |
||||||
|
} else { |
||||||
|
Component resultComponent = createResultComponent(result); |
||||||
|
tablePanel.updateCell(row, 3, resultComponent); |
||||||
|
} |
||||||
|
row++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Component createResultComponent(DetectorResult result) { |
||||||
|
|
||||||
|
JPanel statusPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
if (result.getStatus() == DetectorStatus.NORMAL) { |
||||||
|
statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_success.svg")), BorderLayout.WEST); |
||||||
|
statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Normal")), BorderLayout.CENTER); |
||||||
|
} else { |
||||||
|
JPanel infoPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
{ |
||||||
|
infoPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_error.svg")), BorderLayout.WEST); |
||||||
|
infoPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); |
||||||
|
} |
||||||
|
statusPanel.add(infoPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
// 如果结果是检测出的异常,则出现详细信息。
|
||||||
|
if (result.getStatus() == DetectorStatus.EXCEPTION) { |
||||||
|
JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
{ |
||||||
|
detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||||
|
UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); |
||||||
|
detailLabel.setForeground(DETAIL_FONT_COLOR); |
||||||
|
detailLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent mouseEvent) { |
||||||
|
NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); |
||||||
|
Stream<DetectorResult> results = model.getResults(); |
||||||
|
List<NotificationModel> notificationModels = results |
||||||
|
.filter(Objects::nonNull) |
||||||
|
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||||
|
.map(DetectorUtil::convert2Notification) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
NotificationDialog dialog = new NotificationDialog(properties, notificationModels); |
||||||
|
dialog.open(); |
||||||
|
} |
||||||
|
}); |
||||||
|
detailPanel.add(detailLabel, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
statusPanel.add(detailPanel, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
return statusPanel; |
||||||
|
} |
||||||
|
|
||||||
|
/* tail */ |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private JPanel createTailPanel() { |
||||||
|
|
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tailPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); |
||||||
|
|
||||||
|
JPanel configPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
{ |
||||||
|
UICheckBox checkBox = new UICheckBox(); |
||||||
|
checkBox.setSelected(this.detectOpen); |
||||||
|
checkBox.addChangeListener(e -> { |
||||||
|
UICheckBox source = (UICheckBox) e.getSource(); |
||||||
|
EnvDetectorDialog.this.detectOpen = source.isSelected(); |
||||||
|
}); |
||||||
|
configPanel.add(checkBox, BorderLayout.WEST); |
||||||
|
UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Switch")); |
||||||
|
configPanel.add(description, BorderLayout.EAST); |
||||||
|
} |
||||||
|
tailPanel.add(configPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
{ |
||||||
|
UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); |
||||||
|
confirmButton.addActionListener((e) -> { |
||||||
|
setVisible(false); |
||||||
|
dispose(); |
||||||
|
// 配置处理
|
||||||
|
EnvDetectorConfig.getInstance().setEnabled(this.detectOpen); |
||||||
|
}); |
||||||
|
actionsPanel.add(confirmButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.addActionListener((e) -> { |
||||||
|
setVisible(false); |
||||||
|
dispose(); |
||||||
|
}); |
||||||
|
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||||
|
} |
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void refreshHeader() { |
||||||
|
|
||||||
|
updateHeaderPanel(); |
||||||
|
pack(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
private void refreshBody() { |
||||||
|
|
||||||
|
updateTable(this.tablePanel); |
||||||
|
pack(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
private static ImageIcon getLoadingIcon() { |
||||||
|
|
||||||
|
URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); |
||||||
|
if (resource == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
ImageIcon loadingIcon = new ImageIcon(resource); |
||||||
|
int width = 16; |
||||||
|
int height = 16; |
||||||
|
loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); |
||||||
|
return loadingIcon; |
||||||
|
} |
||||||
|
|
||||||
|
private void configProperties() { |
||||||
|
|
||||||
|
setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); |
||||||
|
setModal(false); |
||||||
|
setFocusable(false); |
||||||
|
setAutoRequestFocus(false); |
||||||
|
setResizable(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 按钮的当前状态 |
||||||
|
*/ |
||||||
|
private enum EnvDetectorButtonStatus { |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始 -> 停止 |
||||||
|
*/ |
||||||
|
START("Fine-Design_Basic_Detect_Start") { |
||||||
|
@Override |
||||||
|
public EnvDetectorButtonStatus next() { |
||||||
|
return STOP; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止 -> 继续 |
||||||
|
*/ |
||||||
|
STOP("Fine-Design_Basic_Detect_Stop") { |
||||||
|
@Override |
||||||
|
public EnvDetectorButtonStatus next() { |
||||||
|
return CONTINUE; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 继续 -> 停止 |
||||||
|
*/ |
||||||
|
CONTINUE("Fine-Design_Basic_Detect_Continue") { |
||||||
|
@Override |
||||||
|
public EnvDetectorButtonStatus next() { |
||||||
|
return STOP; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 重新 -> 停止 |
||||||
|
*/ |
||||||
|
A_NEW("Fine-Design_Basic_Detect_A_New") { |
||||||
|
@Override |
||||||
|
public EnvDetectorButtonStatus next() { |
||||||
|
return STOP; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
; |
||||||
|
|
||||||
|
private String descLocale; |
||||||
|
|
||||||
|
EnvDetectorButtonStatus(String descLocale) { |
||||||
|
|
||||||
|
this.descLocale = descLocale; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDesc() { |
||||||
|
|
||||||
|
return Toolkit.i18nText(descLocale); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在执行中 |
||||||
|
* |
||||||
|
* @return 是/否 |
||||||
|
*/ |
||||||
|
public boolean isExecuting() { |
||||||
|
|
||||||
|
return this == EnvDetectorButtonStatus.STOP; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 不在执行中 |
||||||
|
* |
||||||
|
* @return 是/否 |
||||||
|
*/ |
||||||
|
public boolean isNotExecuting() { |
||||||
|
|
||||||
|
return !isExecuting(); |
||||||
|
} |
||||||
|
|
||||||
|
public abstract EnvDetectorButtonStatus next(); |
||||||
|
} |
||||||
|
|
||||||
|
private class EnvDetectorHeaderPanel extends JPanel { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package com.fr.env.detect.ui; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
|
||||||
|
/** |
||||||
|
* 环境检测条目 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public class EnvDetectorItem { |
||||||
|
|
||||||
|
private DetectorType type; |
||||||
|
|
||||||
|
private DetectorResult result; |
||||||
|
|
||||||
|
public EnvDetectorItem(DetectorType detectorType) { |
||||||
|
this.type = detectorType; |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorType getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public String getKind() { |
||||||
|
return this.type.getKind().getDescription(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getDescription() { |
||||||
|
return this.type.getDescription(); |
||||||
|
} |
||||||
|
|
||||||
|
public DetectorResult getResult() { |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public void setResult(DetectorResult result) { |
||||||
|
this.result = result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package com.fr.env.detect.ui; |
||||||
|
|
||||||
|
import com.fr.env.detect.bean.DetectorResult; |
||||||
|
import com.fr.env.detect.bean.DetectorType; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* 环境检测数据格式 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public class EnvDetectorModel { |
||||||
|
|
||||||
|
/** |
||||||
|
* 类型 -> list [{type-result}] |
||||||
|
*/ |
||||||
|
private final Map<DetectorType.Kind, List<EnvDetectorItem>> itemMap = new LinkedHashMap<>(); |
||||||
|
|
||||||
|
public EnvDetectorModel() { |
||||||
|
|
||||||
|
itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList( |
||||||
|
new EnvDetectorItem(DetectorType.LACK_OF_JAR), |
||||||
|
new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), |
||||||
|
new EnvDetectorItem(DetectorType.JAR_CONFLICT))); |
||||||
|
|
||||||
|
itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList( |
||||||
|
new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), |
||||||
|
new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), |
||||||
|
new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); |
||||||
|
} |
||||||
|
|
||||||
|
public void update(DetectorType type, DetectorResult result) { |
||||||
|
|
||||||
|
List<EnvDetectorItem> items = itemMap.get(type.getKind()); |
||||||
|
items.stream() |
||||||
|
.filter((e) -> e.getType() == type) |
||||||
|
.findFirst() |
||||||
|
.ifPresent((e) -> { |
||||||
|
e.setResult(result); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public Stream<DetectorResult> getResults() { |
||||||
|
|
||||||
|
return getItems().stream() |
||||||
|
.map(EnvDetectorItem::getResult); |
||||||
|
} |
||||||
|
|
||||||
|
public List<EnvDetectorItem> getItems() { |
||||||
|
|
||||||
|
return itemMap.values().stream() |
||||||
|
.flatMap(Collection::stream) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
public Map<DetectorType.Kind, List<EnvDetectorItem>> getItemMap() { |
||||||
|
|
||||||
|
return this.itemMap; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue