Browse Source

Merge branch 'release/11.0' of ssh://code.fineres.com:7999/~renekton/design into release/11.0

release/11.0
renekton 3 months ago
parent
commit
c5f5d55b68
  1. 61
      designer-base/src/main/java/com/fr/design/carton/CartonConstants.java
  2. 10
      designer-base/src/main/java/com/fr/design/carton/CartonThreadExecutorPool.java
  3. 147
      designer-base/src/main/java/com/fr/design/carton/CartonUtils.java
  4. 186
      designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java
  5. 4
      designer-base/src/main/java/com/fr/design/carton/FeedbackToolboxDialog.java
  6. 37
      designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java
  7. 174
      designer-base/src/main/java/com/fr/design/carton/latency/DesignerLatencyMetric.java
  8. 65
      designer-base/src/main/java/com/fr/design/carton/latency/LatencyLevel.java
  9. 4
      designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java
  10. 2
      designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java
  11. 15
      designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java
  12. 12
      designer-base/src/main/java/com/fr/design/write/submit/DBManipulationPane.java
  13. 1
      designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
  14. 1
      designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties
  15. 1
      designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties
  16. 1
      designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties
  17. 1
      designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties
  18. 17
      designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCasePaneDelegate.java
  19. 3
      designer-realize/src/main/java/com/fr/grid/selection/CellSelection.java
  20. 4
      designer-realize/src/main/java/com/fr/start/MainDesigner.java

61
designer-base/src/main/java/com/fr/design/carton/CartonConstants.java

@ -0,0 +1,61 @@
package com.fr.design.carton;
import com.fr.stable.ProductConstantsBase;
import com.fr.stable.StableUtils;
import java.text.SimpleDateFormat;
/**
* 卡顿常量类管理
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/07/04
*/
public class CartonConstants {
public static final String TIME = "time";
public static final String APPID = "appId";
public static final String USERID = "userId";
public static final String LOCAL = "local";
public static final String REMOTE = "remote";
public static final String OPERANDS_NUM = "operands";
public static final String DESIGN_METHOD = "designMethod";
public static final String DESIGNER_VERSION = "designerVersion";
public static final String DESIGNER_ID = "designerId";
public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv";
public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv";
/**
* 开启间隔检测后两次检测的相隔时间ms
*/
public static final long CHECK_INTERVAL_MS = 100;
/**
* 最大的事件允许执行时间超过该时间则打印堆栈等相关信息
*/
public static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500;
/**
* UI检测采样频率
*/
public static final int LATENCY_SAMPLING_FREQUENCY = 100;
/**
* 输出日志所在地址
*/
public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log");
/**
* 日期事件格式
*/
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* Designer4Debug类名
*/
public static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug";
}

10
designer-base/src/main/java/com/fr/design/carton/CartonThreadExecutorPool.java

@ -57,18 +57,18 @@ public class CartonThreadExecutorPool extends ThreadPoolExecutor {
private void examineHang() { private void examineHang() {
StackTraceElement[] currentStack = eventThread.getStackTrace(); StackTraceElement[] currentStack = eventThread.getStackTrace();
if (lastReportedStack!=null && EventDispatchThreadHangMonitor.stacksEqual(currentStack, lastReportedStack)) { if (lastReportedStack!=null && CartonUtils.stacksEqual(currentStack, lastReportedStack)) {
return; return;
} }
lastReportedStack = currentStack; lastReportedStack = currentStack;
String stackTrace = EventDispatchThreadHangMonitor.stackTraceToString(currentStack); String stackTrace = CartonUtils.stackTraceToString(currentStack);
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis()));
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber);
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms");
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace);
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG);
EventDispatchThreadHangMonitor.checkForDeadlock(); CartonUtils.checkForDeadlock();
} }
} }
@ -129,7 +129,7 @@ public class CartonThreadExecutorPool extends ThreadPoolExecutor {
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber);
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime)); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime));
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime + "ms"); jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime + "ms");
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG);
} }
concurrentHashMap.remove(currentThreadId); concurrentHashMap.remove(currentThreadId);

147
designer-base/src/main/java/com/fr/design/carton/CartonUtils.java

@ -0,0 +1,147 @@
package com.fr.design.carton;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.SimpleDateFormat;
import static com.fr.design.carton.CartonConstants.EASY_CHECKER_FILE_NAME;
import static com.fr.design.carton.CartonConstants.JOURNAL_FILE_PATH;
import static com.fr.design.carton.CartonConstants.TIMER_CHECKER_FILE_NAME;
/**
* 设计器卡顿业务工具类
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/07/10
*/
public class CartonUtils {
/**
* 堆栈元素输出为文本
*
* @param stackTrace 堆栈元素
* @return 文本
*/
public static String stackTraceToString(StackTraceElement[] stackTrace) {
StringBuilder result = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
String indentation = " ";
result.append("~").append(indentation).append(stackTraceElement);
}
return result.toString();
}
/**
* 命令行显示堆栈信息
*
* @param stackTrace 堆栈元素
* @return 文本
*/
public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) {
StringBuilder result = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
String indentation = " ";
result.append("\r\n").append(indentation).append(stackTraceElement);
}
return result.toString();
}
/**
* 堆栈是否一致
*
* @param a 堆栈A
* @param b 堆栈B
* @return 是否一致
*/
public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) {
if (!ArrayUtils.isSameLength(a, b)) {
return false;
}
for (int i = 0; i < a.length; ++i) {
if (!a[i].equals(b[i])) {
return false;
}
}
return true;
}
/**
* 检查死锁
*/
public static void checkForDeadlock() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.findDeadlockedThreads();
if (threadIds == null) {
return;
}
FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:");
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
for (ThreadInfo info : threadInfos) {
FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(),
info.getThreadState(), info.getLockName(), info.getLockOwnerName(), CartonUtils.stackTraceToStringForConsole(info.getStackTrace()));
}
}
/**
* 输出卡顿日志
*
* @param message 文本信息
* @param flag 类型true时为简单检查false时为定时检查
*/
public static void outPutJournalLog(String message, int flag) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = simpleDateFormat.format(System.currentTimeMillis());
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? EASY_CHECKER_FILE_NAME : TIMER_CHECKER_FILE_NAME;
String[] split = date.split("-");
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]);
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date);
File dirFile = new File(dirPath);
File file = new File(StableUtils.pathJoin(dirPath, filename));
try {
if (!file.exists()) {
if (!dirFile.exists()) {
dirFile.mkdirs();
}
file.createNewFile();
}
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true))) {
String outputMessage = message.replaceAll("~", "\r\n") + "," + "\r\n";
bufferedWriter.write(outputMessage);
}
} catch (IOException e) {
FineLoggerFactory.getLogger().error("output fail", e);
}
}
/**
* 用于判断是不是特定的堆栈
*/
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) {
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative;
}
/**
* 用于判断某个堆栈是否在等待另一个事件
* 取当前堆栈前三层判断是是不是匹配等待堆栈的格式
*/
public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) {
return currentStack != null && currentStack.length >= 3 &&
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true)
&& stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false)
&& stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false);
}
}

186
designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java

@ -1,34 +1,26 @@
package com.fr.design.carton; package com.fr.design.carton;
import com.fr.concurrent.FineExecutors; import com.fr.concurrent.FineExecutors;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.ui.util.UIUtil; import com.fr.design.ui.util.UIUtil;
import com.fr.json.JSONObject; import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.ProductConstantsBase;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.AWTEvent; import java.awt.AWTEvent;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.SimpleDateFormat;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.fr.design.carton.CartonConstants.CHECK_INTERVAL_MS;
import static com.fr.design.carton.CartonConstants.DATE_FORMAT;
import static com.fr.design.carton.CartonConstants.LATENCY_SAMPLING_FREQUENCY;
import static com.fr.design.carton.CartonConstants.UNREASONABLE_DISPATCH_DURATION_MS;
/** /**
* 参考自git swinghelper * 参考自git swinghelper
* 用于卡顿检测 * 用于卡顿检测
@ -38,31 +30,16 @@ import java.util.concurrent.TimeUnit;
*/ */
public final class EventDispatchThreadHangMonitor extends EventQueue { public final class EventDispatchThreadHangMonitor extends EventQueue {
/**
* 日期事件格式
*/
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor(); public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor();
/** /**
* 一个timer * 一个timer
*/ */
private Timer timer; private Timer timer;
/**
* 开启间隔检测后两次检测的相隔时间ms
*/
private static final long CHECK_INTERVAL_MS = 100;
/**
* 最大的事件允许执行时间超过该时间则打印堆栈等相关信息
*/
private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500;
/** /**
* 事件唯一编码用于方便日志的查看 * 事件唯一编码用于方便日志的查看
*/ */
private static long hangCount = 0; private static long hangCount = 0;
/**
* 输出日志所在地址
*/
private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log");
/** /**
* 类似于一个开关当该值为默认的false启动时定时任务在窗口开启前都不会对执行的事件进行检查 * 类似于一个开关当该值为默认的false启动时定时任务在窗口开启前都不会对执行的事件进行检查
*/ */
@ -103,43 +80,6 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
return hangCount++; return hangCount++;
} }
/**
* @param a can not be null
* @param b can not be null
* @return
*/
public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) {
if (!ArrayUtils.isSameLength(a, b)) {
return false;
}
for (int i = 0; i < a.length; ++i) {
if (!a[i].equals(b[i])) {
return false;
}
}
return true;
}
/**
* 用于判断是不是特定的堆栈
*/
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) {
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative;
}
/**
* 用于判断某个堆栈是否在等待另一个事件
* 取当前堆栈前三层判断是是不是匹配等待堆栈的格式
*/
public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) {
return currentStack != null && currentStack.length >= 3 &&
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true)
&& stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false)
&& stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false);
}
/** /**
* event事件的包装类 * event事件的包装类
*/ */
@ -153,11 +93,13 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
//事件开始的时间 //事件开始的时间
private final long startDispatchTimeMillis = System.currentTimeMillis(); private final long startDispatchTimeMillis = System.currentTimeMillis();
//事件编号 //事件编号
private long hangNumber; private final long hangNumber;
//构造函数,给当前对象赋一个递增的唯一编号 //构造函数,给当前对象赋一个递增的唯一编号
public DispatchInfo() { public DispatchInfo() {
hangNumber = getHangCount(); hangNumber = getHangCount();
} }
//定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法 //定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法
public void checkForHang() { public void checkForHang() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
@ -168,36 +110,38 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
private void examineHang() { private void examineHang() {
//获取执行线程的当前堆栈 //获取执行线程的当前堆栈
StackTraceElement[] currentStack = eventDispatchThread.getStackTrace(); StackTraceElement[] currentStack = eventDispatchThread.getStackTrace();
if (isWaitingForNextEvent(currentStack)) { if (CartonUtils.isWaitingForNextEvent(currentStack)) {
return; return;
} }
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次 //某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
if (lastReportedStack !=null && stacksEqual(lastReportedStack, currentStack)) { if (lastReportedStack != null && CartonUtils.stacksEqual(lastReportedStack, currentStack)) {
return; return;
} }
String stackTrace = stackTraceToString(currentStack); String stackTrace = CartonUtils.stackTraceToString(currentStack);
lastReportedStack = currentStack; lastReportedStack = currentStack;
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber);
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms");
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace);
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG);
checkForDeadlock(); CartonUtils.checkForDeadlock();
} }
//记录连续运行了多长时间 //记录连续运行了多长时间
private long timeSoFar() { public long timeSoFar() {
return (System.currentTimeMillis() - lastDispatchTimeMillis); return (System.currentTimeMillis() - lastDispatchTimeMillis);
} }
//记录一个事件从被分发到结束的总运行时间 //记录一个事件从被分发到结束的总运行时间
private long totalTime() { public long totalTime() {
return (System.currentTimeMillis() - startDispatchTimeMillis); return (System.currentTimeMillis() - startDispatchTimeMillis);
} }
//事件处理完后的时间判断 //事件处理完后的时间判断
public void dispose() { public void dispose() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
exportCartonLog(true); exportCartonLog(true);
} else if (lastReportedStack != null){ } else if (lastReportedStack != null) {
exportCartonLog(false); exportCartonLog(false);
} }
} }
@ -208,40 +152,15 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
*/ */
private void exportCartonLog(boolean flag) { private void exportCartonLog(boolean flag) {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber);
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormat.format(startDispatchTimeMillis)); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), DATE_FORMAT.format(startDispatchTimeMillis));
if (flag) { if (flag) {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms"); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms");
} else { } else {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms"); jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms");
} }
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG);
}
}
public static void outPutJournalLog(String message, int flag) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = simpleDateFormat.format(System.currentTimeMillis());
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? SwitchForSwingChecker.EASY_CHECKER_FILE_NAME: SwitchForSwingChecker.TIMER_CHECKER_FILE_NAME;
String[] split = date.split("-");
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]);
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date);
File dirFile = new File(dirPath);
File file = new File( StableUtils.pathJoin(dirPath, filename));
try {
if (!file.exists()) {
if (!dirFile.exists()) {
dirFile.mkdirs();
}
file.createNewFile();
}
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true));
String outputMessage = new StringBuilder(message.replaceAll("~", "\r\n")).append(",").append("\r\n").toString();
bufferedWriter.write(outputMessage);
bufferedWriter.close();
} catch (IOException e) {
FineLoggerFactory.getLogger().error("output fail", e);
} }
} }
@ -325,8 +244,7 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
*/ */
@Override @Override
protected void dispatchEvent(AWTEvent event) { protected void dispatchEvent(AWTEvent event) {
//如果两个开关都没开,那就不走重写方法了 if (!useCustomEventQueue()) {
if (!isEasyWitch() && !isTimerWitch()) {
super.dispatchEvent(event); super.dispatchEvent(event);
} else { } else {
try { try {
@ -342,6 +260,18 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
} }
} }
private boolean useCustomEventQueue() {
// 开启性能监控或开启卡顿工具箱,则走自定义的EventQueue
return SwitchForSwingChecker.isLatencyMonitoring() ||
isEasyWitch() || isTimerWitch();
}
private boolean needSampling() {
// UI性能采样逻辑:开启采样并且符合采样频次
return SwitchForSwingChecker.isLatencyMonitoring()
&& (hangCount % LATENCY_SAMPLING_FREQUENCY == 0);
}
/** /**
* Starts tracking a dispatch. * Starts tracking a dispatch.
*/ */
@ -357,6 +287,9 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
private synchronized void postDispatchEvent() { private synchronized void postDispatchEvent() {
synchronized (dispatches) { synchronized (dispatches) {
DispatchInfo justFinishedDispatch = dispatches.removeLast(); DispatchInfo justFinishedDispatch = dispatches.removeLast();
if (needSampling()) {
DesignerLatencyMetric.getInstance().record(justFinishedDispatch.timeSoFar());
}
if (isEasyWitch()) { if (isEasyWitch()) {
justFinishedDispatch.dispose(); justFinishedDispatch.dispose();
} }
@ -370,39 +303,4 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
} }
} }
/**
* 检查死锁
*/
public static void checkForDeadlock() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.findDeadlockedThreads();
if (threadIds == null) {
return;
}
FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:");
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
for (ThreadInfo info : threadInfos) {
FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(),
info.getThreadState(), info.getLockName(), info.getLockOwnerName(), stackTraceToStringForConsole(info.getStackTrace()));
}
}
public static String stackTraceToString(StackTraceElement[] stackTrace) {
StringBuilder result = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
String indentation = " ";
result.append("~").append(indentation).append(stackTraceElement);
}
return result.toString();
}
public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) {
StringBuilder result = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
String indentation = " ";
result.append("\r\n").append(indentation).append(stackTraceElement);
}
return result.toString();
}
} }

4
designer-base/src/main/java/com/fr/design/carton/FeedbackToolboxDialog.java

@ -49,6 +49,8 @@ import java.text.ParseException;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static com.fr.design.carton.CartonConstants.JOURNAL_FILE_PATH;
public class FeedbackToolboxDialog extends JDialog { public class FeedbackToolboxDialog extends JDialog {
private UIDatePicker uiDatePicker; private UIDatePicker uiDatePicker;
@ -147,7 +149,7 @@ public class FeedbackToolboxDialog extends JDialog {
//selectDate 2002-03-09例子 //selectDate 2002-03-09例子
String[] split = selectDate.split("-"); String[] split = selectDate.split("-");
int month = Integer.parseInt(split[1]); int month = Integer.parseInt(split[1]);
String sourceFilePath = StableUtils.pathJoin(SwitchForSwingChecker.JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate); String sourceFilePath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate);
File sourceFile = new File(sourceFilePath); File sourceFile = new File(sourceFilePath);
if (sourceFile.exists()) { if (sourceFile.exists()) {
exportCartonLog(sourceFile, path, sourceFilePath); exportCartonLog(sourceFile, path, sourceFilePath);

37
designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java

@ -7,7 +7,6 @@ import com.fr.json.JSON;
import com.fr.json.JSONArray; import com.fr.json.JSONArray;
import com.fr.json.JSONObject; import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory; import com.fr.log.FineLoggerFactory;
import com.fr.stable.ProductConstantsBase;
import com.fr.stable.StableUtils; import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils; import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter; import com.fr.stable.xml.XMLPrintWriter;
@ -29,11 +28,13 @@ import java.util.Map;
import java.util.Date; import java.util.Date;
import java.util.Calendar; import java.util.Calendar;
import static com.fr.design.carton.CartonConstants.DEBUG_MAIN_CLASS_NAME;
import static com.fr.design.carton.CartonConstants.EASY_CHECKER_FILE_NAME;
import static com.fr.design.carton.CartonConstants.JOURNAL_FILE_PATH;
import static com.fr.design.carton.CartonConstants.TIMER_CHECKER_FILE_NAME;
public class SwitchForSwingChecker implements XMLReadable, XMLWriter { public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
/**
* Designer4Debug类名
*/
private static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug";
/** /**
* XML标签 * XML标签
*/ */
@ -46,18 +47,16 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
* 简单记录事件执行时间的开关 * 简单记录事件执行时间的开关
*/ */
private static boolean easyChecker = false; private static boolean easyChecker = false;
/**
* UI性能监控埋点的开关
*/
private static boolean latencyMonitor = true;
/** /**
* 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 * 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测
*/ */
public static final int TIMER_CHECK_FLAG = 0; public static final int TIMER_CHECK_FLAG = 0;
public static final int EASY_CHECK_FLAG = 1; public static final int EASY_CHECK_FLAG = 1;
/**
* 日志存储地址
*/
public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log");
public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv";
public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv";
public static boolean isCheckerTimerSwitch() { public static boolean isCheckerTimerSwitch() {
return checkerTimerSwitch; return checkerTimerSwitch;
} }
@ -66,6 +65,13 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
return easyChecker; return easyChecker;
} }
/**
* 是否开启UI性能检测
*/
public static boolean isLatencyMonitoring() {
return latencyMonitor;
}
public static volatile SwitchForSwingChecker switchForSwingChecker = new SwitchForSwingChecker(); public static volatile SwitchForSwingChecker switchForSwingChecker = new SwitchForSwingChecker();
public static SwitchForSwingChecker getInstance() { public static SwitchForSwingChecker getInstance() {
@ -137,7 +143,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
} }
/** /**
*处理文件 * 处理文件
* 一共四种情况 * 一共四种情况
* 两个文件都不存在 * 两个文件都不存在
* 文件一存在文件二不存在 * 文件一存在文件二不存在
@ -224,7 +230,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
/** /**
* /埋点方法上传卡顿信息入口 * /埋点方法上传卡顿信息入口
date为 2022-09-08的格式 * date为 2022-09-08的格式
*/ */
public static List<CartonUploadMessage> uploadJournalLog(Date dateTime) { public static List<CartonUploadMessage> uploadJournalLog(Date dateTime) {
List<CartonUploadMessage> res = new ArrayList<>(); List<CartonUploadMessage> res = new ArrayList<>();
@ -245,9 +251,8 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
/** /**
* 初始化监控任务主要是替换EventQueue以及SwingWorker执行任务的线程池 * 初始化监控任务主要是替换EventQueue以及SwingWorker执行任务的线程池
*
*/ */
public static void initThreadMonitoring () { public static void initThreadMonitoring() {
String mainClass = System.getProperty("sun.java.command"); String mainClass = System.getProperty("sun.java.command");
//判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用 //判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用
if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) { if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) {
@ -293,6 +298,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
if (reader.isAttr()) { if (reader.isAttr()) {
checkerTimerSwitch = reader.getAttrAsBoolean("checkerTimerSwitch", false); checkerTimerSwitch = reader.getAttrAsBoolean("checkerTimerSwitch", false);
easyChecker = reader.getAttrAsBoolean("easyChecker", false); easyChecker = reader.getAttrAsBoolean("easyChecker", false);
latencyMonitor = reader.getAttrAsBoolean("latencyMonitor", true);
} }
try { try {
initSwitchChecker(); initSwitchChecker();
@ -306,6 +312,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
writer.startTAG(XML_TAG); writer.startTAG(XML_TAG);
writer.attr("checkerTimerSwitch", checkerTimerSwitch); writer.attr("checkerTimerSwitch", checkerTimerSwitch);
writer.attr("easyChecker", easyChecker); writer.attr("easyChecker", easyChecker);
writer.attr("latencyMonitor", latencyMonitor);
writer.end(); writer.end();
} }

174
designer-base/src/main/java/com/fr/design/carton/latency/DesignerLatencyMetric.java

@ -0,0 +1,174 @@
package com.fr.design.carton.latency;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.config.MarketConfig;
import com.fr.design.DesignerEnvManager;
import com.fr.design.carton.SwitchForSwingChecker;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.general.CloudCenter;
import com.fr.general.GeneralUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static com.fr.design.carton.CartonConstants.APPID;
import static com.fr.design.carton.CartonConstants.DESIGNER_ID;
import static com.fr.design.carton.CartonConstants.DESIGNER_VERSION;
import static com.fr.design.carton.CartonConstants.DESIGN_METHOD;
import static com.fr.design.carton.CartonConstants.LOCAL;
import static com.fr.design.carton.CartonConstants.OPERANDS_NUM;
import static com.fr.design.carton.CartonConstants.REMOTE;
import static com.fr.design.carton.CartonConstants.TIME;
import static com.fr.design.carton.CartonConstants.USERID;
/**
* 设计器延迟时间记录Metric
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/07/01
*/
public class DesignerLatencyMetric {
private String latencyUrl;
private ExecutorService executorService;
private ScheduledExecutorService scheduler;
private static final Map<LatencyLevel, AtomicInteger> LATENCY_CONTAINER = new ConcurrentHashMap<>();
private static final String DEFAULT_MONITOR_URL = "https://cloud.fanruan.com/api/monitor/";
private static final String LATENCY_TABLE_SUFFIX = "record_of_designer_latency/single";
private final static class InstanceHolder {
static final DesignerLatencyMetric INSTANCE = new DesignerLatencyMetric();
}
/**
* 获取单例
*/
public static DesignerLatencyMetric getInstance() {
return DesignerLatencyMetric.InstanceHolder.INSTANCE;
}
private DesignerLatencyMetric() {
}
/**
* 启动
*/
public void start() {
if (SwitchForSwingChecker.isLatencyMonitoring()) {
// 初始化容器
initializeContainer();
// 启动异步性能记录线程池
executorService = Executors.newFixedThreadPool(8);
// 启动定时埋点
this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("LatencyMetricWorker"));
this.scheduler.scheduleWithFixedDelay(this::collectAndSubmit, 60, 60, TimeUnit.MINUTES);
// 注册设计器工作目录切换事件监听
EventDispatcher.listen(WorkspaceEvent.BeforeSwitch, new Listener<Workspace>() {
@Override
public void on(Event event, Workspace param) {
collectAndSubmit();
}
});
FineLoggerFactory.getLogger().info("[Latency] designer latency metric started.");
}
}
/**
* 关闭
*/
public void stop() {
if (SwitchForSwingChecker.isLatencyMonitoring()) {
if (this.executorService != null) {
this.executorService.shutdown();
}
if (this.scheduler != null) {
this.scheduler.shutdown();
}
collectAndSubmit();
FineLoggerFactory.getLogger().info("[Latency] designer latency metric stopped.");
}
}
private String getLatencyUrl() {
if (StringUtils.isEmpty(latencyUrl)) {
String monitorEntry = CloudCenter.getInstance().acquireUrlByKind("cloud.monitor.api.entrypoint");
latencyUrl = (StringUtils.isNotEmpty(monitorEntry) ? monitorEntry : DEFAULT_MONITOR_URL)
+ LATENCY_TABLE_SUFFIX;
}
return latencyUrl;
}
private void initializeContainer() {
for (LatencyLevel level : LatencyLevel.values()) {
LATENCY_CONTAINER.put(level, new AtomicInteger());
}
}
private void resetContainer() {
LATENCY_CONTAINER.values().forEach(count -> count.set(0));
}
/**
* 记录性能信息
*/
public void record(long cost) {
executorService.submit(() -> {
try {
LatencyLevel level = LatencyLevel.measure(cost);
LATENCY_CONTAINER.computeIfAbsent(level, k -> new AtomicInteger()).incrementAndGet();
} catch (Throwable ignore) {
// 记录失败不影响业务
}
});
}
/**
* 汇总并提交性能监控埋点
*/
public void collectAndSubmit() {
Map<String, Object> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", collect());
try {
HttpToolbox.post(getLatencyUrl(), para);
FineLoggerFactory.getLogger().debug("[Latency] submit latency log to cloud.");
} catch (Throwable t) {
FineLoggerFactory.getLogger().debug(t,"[Latency] failed to submit latency log to cloud.");
}
resetContainer();
}
private JSONObject collect() {
JSONObject info = new JSONObject();
info.put(TIME, System.currentTimeMillis());
info.put(APPID, MarketConfig.getInstance().getCloudOperationMaintenanceId());
info.put(USERID, MarketConfig.getInstance().getBbsUid());
info.put(DESIGNER_ID, DesignerEnvManager.getEnvManager().getUUID());
info.put(DESIGNER_VERSION, GeneralUtils.getVersion());
info.put(DESIGN_METHOD, WorkContext.getCurrent().isLocal() ? LOCAL : REMOTE);
info.put(OPERANDS_NUM, LATENCY_CONTAINER.values().stream().mapToInt(AtomicInteger::get).sum());
for (Map.Entry<LatencyLevel, AtomicInteger> entry : LATENCY_CONTAINER.entrySet()) {
info.put(entry.getKey().getMark(), entry.getValue().get());
}
return info;
}
}

65
designer-base/src/main/java/com/fr/design/carton/latency/LatencyLevel.java

@ -0,0 +1,65 @@
package com.fr.design.carton.latency;
/**
* 卡顿等级
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/07/01
*/
public enum LatencyLevel {
// 非常流畅
FLASH(0, 50, "waitNum1"),
// 流畅
SMOOTH(50, 100, "waitNum2"),
// 轻微卡顿
SLIGHT(100, 200, "waitNum3"),
// 中等卡顿
MILD(200, 500, "waitNum4"),
// 明显卡顿
NOTICEABLE(500, 1000, "waitNum5"),
// 严重卡顿
SERVE(1000, 2000, "waitNum6"),
// 非常严重卡顿
EXTREME(2000, 3000, "waitNum7"),
// 极度卡顿
CRITICAL(3000, Long.MAX_VALUE, "waitNum8");
final long start;
final long end;
final String mark;
LatencyLevel(long start, long end, String mark) {
this.start = start;
this.end = end;
this.mark = mark;
}
public long getStart() {
return start;
}
public long getEnd() {
return end;
}
public String getMark() {
return mark;
}
/**
* 评估当前卡顿等级
*
* @param cost UI-EventQueue响应耗时
* @return 卡顿等级
*/
public static LatencyLevel measure(long cost) {
for (LatencyLevel level : LatencyLevel.values()) {
if (cost >= level.getStart() && cost < level.getEnd()) {
return level;
}
}
return CRITICAL;
}
}

4
designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java

@ -421,7 +421,9 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi
dialog.setVisible(true); dialog.setVisible(true);
DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refresh(); DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refresh();
LocateAction.gotoEditingTemplateLeaf(targetFile); SwingUtilities.invokeLater(() -> {
LocateAction.gotoEditingTemplateLeaf(targetFile);
});
} }
} }

2
designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java

@ -11,6 +11,7 @@ import com.fr.design.ExtraDesignClassManager;
import com.fr.design.actions.core.ActionFactory; import com.fr.design.actions.core.ActionFactory;
import com.fr.design.base.mode.DesignModeContext; import com.fr.design.base.mode.DesignModeContext;
import com.fr.design.base.mode.DesignerMode; import com.fr.design.base.mode.DesignerMode;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.constants.UIConstants; import com.fr.design.constants.UIConstants;
import com.fr.design.data.DesignTableDataManager; import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.datapane.TableDataTreePane; import com.fr.design.data.datapane.TableDataTreePane;
@ -1161,6 +1162,7 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta
}); });
DesignerEnvManager.getEnvManager().saveXMLFile(); DesignerEnvManager.getEnvManager().saveXMLFile();
DesignerLatencyMetric.getInstance().stop();
} }
/** /**

15
designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java

@ -12,6 +12,7 @@ import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.ActionLabel; import com.fr.design.gui.ilable.ActionLabel;
import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.i18n.DesignSizeI18nManager;
import com.fr.design.i18n.Toolkit; import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper; import com.fr.design.layout.TableLayoutHelper;
@ -69,11 +70,10 @@ public class UpdateMainDialog extends UIDialog {
public static final Dimension DEFAULT = new Dimension(660, 620); public static final Dimension DEFAULT = new Dimension(660, 620);
private static final Dimension PROGRESSBAR = new Dimension(120, 15); private static final Dimension PROGRESSBAR = new Dimension(120, 15);
private static final Dimension UPDATE_BUTTON = new Dimension(80, 24);
private static final int UPDATE_PANE_ROW_SIZE = 30; private static final int UPDATE_PANE_ROW_SIZE = 30;
private static final int UPDATE_CONTENT_PANE_ROW_SIZE = 10; private static final int UPDATE_CONTENT_PANE_ROW_SIZE = 10;
private static final int UPDATE_CONTENT_PANE_COLUMN_SIZE = 10; private static final int UPDATE_CONTENT_PANE_COLUMN_SIZE = 10;
private static final int UPDATE_CONTENT_PANE_LABEL_COLUMN_SIZE = 100; private static final int UPDATE_CONTENT_PANE_LABEL_COLUMN_SIZE = 115;
private static final int SEARCH_PANE_ROW_SIZE = 50; private static final int SEARCH_PANE_ROW_SIZE = 50;
private static final int SEARCH_PANE_TEXT_COLUMN = 130; private static final int SEARCH_PANE_TEXT_COLUMN = 130;
private static final int SEARCH_PANE_COLUMN_GAP = 3; private static final int SEARCH_PANE_COLUMN_GAP = 3;
@ -187,12 +187,15 @@ public class UpdateMainDialog extends UIDialog {
JPanel jarUpdateContentPane = new JPanel(); JPanel jarUpdateContentPane = new JPanel();
jarUpdateContentPane.setLayout(new BorderLayout()); jarUpdateContentPane.setLayout(new BorderLayout());
jarUpdateContentPane.setBorder(BorderFactory.createLineBorder(new Color(UPDATE_CONTENT_PANE_BORDER_COLOR))); jarUpdateContentPane.setBorder(BorderFactory.createLineBorder(new Color(UPDATE_CONTENT_PANE_BORDER_COLOR)));
UILabel jarVersionLabel = new UILabel(Toolkit.i18nText("Fine-Design_Updater_JAR_Version"));
UILabel latestJarLabel = new UILabel(Toolkit.i18nText("Fine-Design_Updater_Latest_JAR"));
jarVersionLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Updater_JAR_Version"));
latestJarLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Updater_Latest_JAR"));
JPanel jarUpdateContentPane2 = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{ JPanel jarUpdateContentPane2 = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{
new Component[]{new UILabel(), new UILabel(), new UILabel()}, new Component[]{new UILabel(), new UILabel(), new UILabel()},
new Component[]{new UILabel(), updateVersionReminderPane, new UILabel()}, new Component[]{new UILabel(), updateVersionReminderPane, new UILabel()},
new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_JAR_Version")), jarCurrentLabel), jarRestoreLabel}, new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, jarVersionLabel, jarCurrentLabel), jarRestoreLabel},
new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Latest_JAR")), loadingLabel), new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, latestJarLabel, loadingLabel),
getNewFeatureActionLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Latest_Feature_Detail"))}, getNewFeatureActionLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Latest_Feature_Detail"))},
new Component[]{new UILabel(), new UILabel(), new UILabel()} new Component[]{new UILabel(), new UILabel(), new UILabel()}
}, rowUpdateSubContentPaneSize, columnUpdateSubContentPaneSize, LayoutConstants.VGAP_LARGE); }, rowUpdateSubContentPaneSize, columnUpdateSubContentPaneSize, LayoutConstants.VGAP_LARGE);
@ -295,7 +298,7 @@ public class UpdateMainDialog extends UIDialog {
loadingLabel = new LoadingLabel(); loadingLabel = new LoadingLabel();
loadingLabel.setText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Checking_Jar_Update")); loadingLabel.setText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Checking_Jar_Update"));
updateButton = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Update")); updateButton = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Update"));
updateButton.setPreferredSize(UPDATE_BUTTON); updateButton.setPreferredSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton"));
updateButton.setEnabled(false); updateButton.setEnabled(false);
double[] rowSize = {TableLayout.PREFERRED}; double[] rowSize = {TableLayout.PREFERRED};

12
designer-base/src/main/java/com/fr/design/write/submit/DBManipulationPane.java

@ -28,6 +28,7 @@ import com.fr.design.gui.icombobox.UIComboBoxRenderer;
import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.ilist.CheckBoxList; import com.fr.design.gui.ilist.CheckBoxList;
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode;
import com.fr.design.i18n.Toolkit;
import com.fr.design.javascript.JavaScriptActionPane; import com.fr.design.javascript.JavaScriptActionPane;
import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.DesignerContext;
@ -197,9 +198,13 @@ public class DBManipulationPane extends BasicBeanPane<DBManipulation> {
checkBoxUpdatePane.setPreferredSize(new Dimension(120, 20)); checkBoxUpdatePane.setPreferredSize(new Dimension(120, 20));
controlBtnPane.add(checkBoxUpdatePane); controlBtnPane.add(checkBoxUpdatePane);
UpdateCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_RWA_NotChange_Unmodified")); String updateCheckBoxName = Toolkit.i18nText("Fine-Design_Basic_RWA_NotChange_Unmodified");
UpdateCheckBox = new UICheckBox(updateCheckBoxName);
UpdateCheckBox.setToolTipText(updateCheckBoxName);
UpdateCheckBox.setPreferredSize(new Dimension(100, 20));
UIButton helpButton = new UIButton(HEIP_ICON); UIButton helpButton = new UIButton(HEIP_ICON);
helpButton.setToolTipText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Base_Help")); helpButton.setToolTipText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Base_Help"));
helpButton.setPreferredSize(new Dimension(20, 20));
helpButton.addActionListener(new ActionListener() { helpButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -291,8 +296,9 @@ public class DBManipulationPane extends BasicBeanPane<DBManipulation> {
cr.setForeground(UIConstants.NORMAL_BACKGROUND); cr.setForeground(UIConstants.NORMAL_BACKGROUND);
JScrollPane jp = new JScrollPane(conditionsTree); JScrollPane jp = new JScrollPane(conditionsTree);
addComponent(conditionPane, jp); addComponent(conditionPane, jp);
String submitCondition = Toolkit.i18nText("Fine-Design_Basic_Set_Submit_Condition");
UIButton addSubmitConditionButton = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Set_Submit_Condition")); UIButton addSubmitConditionButton = new UIButton(submitCondition);
addSubmitConditionButton.setToolTipText(submitCondition);
addSubmitConditionButton.addActionListener(new ActionListener() { addSubmitConditionButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {

1
designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties

@ -29,3 +29,4 @@ com.fr.design.formula.FormulaPane=900*600
com.fr.design.formula.FormulaPaneWhenReserveFormula=1200*600 com.fr.design.formula.FormulaPaneWhenReserveFormula=1200*600
com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=130*20 com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=130*20
com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=140*20 com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=140*20
com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton=100*24

1
designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties

@ -28,3 +28,4 @@ com.fr.design.formula.FormulaPane=900*600
com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600 com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600
com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=100*20 com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=100*20
com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=130*20 com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=130*20
com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton=160*24

1
designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties

@ -28,3 +28,4 @@ com.fr.design.formula.FormulaPane=900*600
com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600 com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600
com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20 com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20
com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20 com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20
com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton=80*24

1
designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties

@ -28,3 +28,4 @@ com.fr.design.formula.FormulaPane=900*600
com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600 com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600
com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20 com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20
com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20 com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20
com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton=80*24

1
designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties

@ -27,3 +27,4 @@ com.fr.design.formula.FormulaPane=900*600
com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600 com.fr.design.formula.FormulaPaneWhenReserveFormula=900*600
com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20 com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane.configLabel=75*20
com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20 com.fr.design.mainframe.mobile.utils.DesignerUtils.configLabel=100*20
com.fr.design.update.ui.dialog.UpdateMainDialog.updateButton=80*24

17
designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCasePaneDelegate.java

@ -6,20 +6,21 @@ import com.fr.design.actions.core.ActionFactory;
import com.fr.design.actions.form.FormECBackgroundAction; import com.fr.design.actions.form.FormECBackgroundAction;
import com.fr.design.actions.form.FormECColumnsAction; import com.fr.design.actions.form.FormECColumnsAction;
import com.fr.design.actions.form.FormECFrozenAction; import com.fr.design.actions.form.FormECFrozenAction;
import com.fr.design.designer.creator.XElementCase; import com.fr.design.base.mode.DesignModeContext;
import com.fr.design.event.TargetModifiedEvent; import com.fr.design.event.TargetModifiedEvent;
import com.fr.design.event.TargetModifiedListener; import com.fr.design.event.TargetModifiedListener;
import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.fit.NewUIModeCellElementPainter; import com.fr.design.fit.NewUIModeCellElementPainter;
import com.fr.design.fit.common.FormDesignerUtil;
import com.fr.design.gui.frpane.HyperlinkGroupPane; import com.fr.design.gui.frpane.HyperlinkGroupPane;
import com.fr.design.mainframe.*; import com.fr.design.mainframe.*;
import com.fr.design.mainframe.cell.QuickEditorRegion; import com.fr.design.mainframe.cell.QuickEditorRegion;
import com.fr.design.mainframe.theme.utils.DefaultThemedTemplateCellElementCase;
import com.fr.design.menu.KeySetUtils; import com.fr.design.menu.KeySetUtils;
import com.fr.design.menu.MenuDef; import com.fr.design.menu.MenuDef;
import com.fr.design.menu.ShortCut; import com.fr.design.menu.ShortCut;
import com.fr.design.menu.ToolBarDef; import com.fr.design.menu.ToolBarDef;
import com.fr.design.present.ConditionAttributesGroupPane; import com.fr.design.present.ConditionAttributesGroupPane;
import com.fr.design.utils.gui.AdjustWorkBookDefaultStyleUtils;
import com.fr.form.fit.common.LightTool; import com.fr.form.fit.common.LightTool;
import com.fr.form.main.Form; import com.fr.form.main.Form;
import com.fr.grid.Grid; import com.fr.grid.Grid;
@ -27,6 +28,7 @@ import com.fr.grid.GridColumn;
import com.fr.grid.GridCorner; import com.fr.grid.GridCorner;
import com.fr.grid.GridRow; import com.fr.grid.GridRow;
import com.fr.page.ReportSettingsProvider; import com.fr.page.ReportSettingsProvider;
import com.fr.report.cell.DefaultTemplateCellElement;
import com.fr.report.elementcase.TemplateElementCase; import com.fr.report.elementcase.TemplateElementCase;
import com.fr.report.worksheet.FormElementCase; import com.fr.report.worksheet.FormElementCase;
import com.fr.design.selection.SelectionEvent; import com.fr.design.selection.SelectionEvent;
@ -34,7 +36,6 @@ import com.fr.design.selection.SelectionListener;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JPanel; import javax.swing.JPanel;
import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
/** /**
@ -69,6 +70,16 @@ public class FormElementCasePaneDelegate extends ElementCasePane<FormElementCase
} }
} }
}); });
// fvs根据主题样式创建默认单元格
if (DesignModeContext.isDuchampMode()) {
sheet.setDefaultCellElementSupplier(() -> {
DefaultTemplateCellElement defaultTemplateCellElement = DefaultThemedTemplateCellElementCase.createInstance();
// fvs调整单元格默认样式
AdjustWorkBookDefaultStyleUtils.adjustCellElement(defaultTemplateCellElement);
return defaultTemplateCellElement;
});
}
} }
private Rectangle getBoundsLineRect(TemplateElementCase elementCase) { private Rectangle getBoundsLineRect(TemplateElementCase elementCase) {

3
designer-realize/src/main/java/com/fr/grid/selection/CellSelection.java

@ -32,6 +32,7 @@ import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.theme.utils.DefaultThemedTemplateCellElementCase; import com.fr.design.mainframe.theme.utils.DefaultThemedTemplateCellElementCase;
import com.fr.design.report.RowColumnPane; import com.fr.design.report.RowColumnPane;
import com.fr.design.selection.QuickEditor; import com.fr.design.selection.QuickEditor;
import com.fr.design.utils.gui.AdjustWorkBookDefaultStyleUtils;
import com.fr.grid.GridUtils; import com.fr.grid.GridUtils;
import com.fr.report.cell.CellElement; import com.fr.report.cell.CellElement;
import com.fr.report.cell.DefaultTemplateCellElement; import com.fr.report.cell.DefaultTemplateCellElement;
@ -562,6 +563,8 @@ public class CellSelection extends Selection {
for (int i = 0; i < removeElementList.size(); i++) { for (int i = 0; i < removeElementList.size(); i++) {
CellElement element = removeElementList.get(i); CellElement element = removeElementList.get(i);
element.setStyle(null); element.setStyle(null);
// fvs调整单元格默认样式
AdjustWorkBookDefaultStyleUtils.adjustCellElement(element);
} }
break; break;

4
designer-realize/src/main/java/com/fr/start/MainDesigner.java

@ -11,6 +11,7 @@ import com.fr.design.actions.server.ServerConfigManagerAction;
import com.fr.design.actions.server.TemplateThemeManagerAction; import com.fr.design.actions.server.TemplateThemeManagerAction;
import com.fr.design.actions.server.WidgetManagerAction; import com.fr.design.actions.server.WidgetManagerAction;
import com.fr.design.base.mode.DesignModeContext; import com.fr.design.base.mode.DesignModeContext;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.carton.SwitchForSwingChecker; import com.fr.design.carton.SwitchForSwingChecker;
import com.fr.design.constants.DesignerLaunchStatus; import com.fr.design.constants.DesignerLaunchStatus;
import com.fr.design.constants.UIConstants; import com.fr.design.constants.UIConstants;
@ -160,8 +161,9 @@ public class MainDesigner extends BaseDesigner {
ServerTray.init(); ServerTray.init();
} }
FineLoggerFactory.getLogger().info("Designer started.Time used {} ms", DesignerStartupContext.getRecorder().getTime(TimeUnit.MILLISECONDS)); FineLoggerFactory.getLogger().info("Designer started.Time used {} ms", DesignerStartupContext.getRecorder().getTime(TimeUnit.MILLISECONDS));
DesignerStartupContext.getRecorder().stop();
SwitchForSwingChecker.initThreadMonitoring(); SwitchForSwingChecker.initThreadMonitoring();
DesignerLatencyMetric.getInstance().start();
} }
/** /**

Loading…
Cancel
Save