Browse Source

Pull request #13293: REPORT-99451 远程设计专项-功能侧优化-模板锁定优化

Merge in DESIGN/design from ~ROGER.CHEN/design:feature/x to feature/x

* commit '1b33fedef80097a538385671d20c6de0a2152eaa':
  修复代码问题
  REPORT-99451 远程设计专项-功能侧优化-模板锁定优化
feature/x
Roger.Chen-陈旺 7 months ago
parent
commit
731adb3be1
  1. 159
      designer-base/src/main/java/com/fr/design/RPCConnectHandlerCenter.java
  2. 56
      designer-base/src/main/java/com/fr/design/lock/TemplateLockInfoReSave.java
  3. 13
      designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java
  4. 22
      designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java
  5. 74
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java
  6. 6
      designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java

159
designer-base/src/main/java/com/fr/design/RPCConnectHandlerCenter.java

@ -0,0 +1,159 @@
package com.fr.design;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.ui.util.UIUtil;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StableUtils;
import com.fr.third.org.apache.http.client.config.RequestConfig;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.client.methods.HttpGet;
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient;
import com.fr.third.org.apache.http.impl.client.HttpClients;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
import com.fr.workspace.base.WorkspaceConstants;
import com.fr.workspace.connect.WorkspaceConnectionInfo;
import com.fr.workspace.engine.channel.WorkspaceChannelFactory;
import com.fr.workspace.engine.exception.WorkspaceConnectionException;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
/**
* RPC连接处理中心
*
* @author Roger
* @since 11.0
* Created on 2023/12/13
*/
public class RPCConnectHandlerCenter {
private static volatile boolean alerting = false;
private static final int TIMEOUT = 5000;
private static Listener<Workspace> listener = new Listener<Workspace>() {
@Override
public void on(Event event, Workspace workspace) {
//暂时先不做重连处理,3次RPC连接失败后提示切换工作目录
showRPCDisconnectDialog();
}
};
/**
* 开启事件监听
*/
public static void startListener() {
if (!WorkContext.getCurrent().isLocal()) {
EventDispatcher.listen(WorkspaceEvent.LostConnect, listener);
}
}
/**
* 弹窗提示连接断开
*/
public static void showRPCDisconnectDialog() {
UIUtil.invokeLaterIfNeeded(RPCConnectHandlerCenter::showDialog);
}
/**
* RPC连接测试
*
* @param info 连接信息
* @return 是否连接成功
*/
public static boolean checkRPCConnect(WorkspaceConnectionInfo info) {
try {
return WorkspaceChannelFactory.testConnection(info);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, e.getMessage());
return false;
}
}
/**
* http连接检测从DesignerSocketIO中移过来的先保留着
*
* @param info 连接信息
* @return 是否连接成功
*/
public static boolean checkHttpConnect(WorkspaceConnectionInfo info) {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(StableUtils.pathJoin(info.getUrl(), WorkspaceConstants.CONTROLLER_PREFIX, WorkspaceConstants.VT));
RequestConfig requestConfig = RequestConfig
.custom()
.setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();
httpGet.setConfig(requestConfig);
try {
CloseableHttpResponse response = httpclient.execute(httpGet);
if (isErrorStatus(response.getStatusLine().getStatusCode())) {
//这边nginx做负载,服务器被kill掉,返回的是502,不会抛错,导致checkRPCConnect通过
//针对500-600的错误码加个判断,其他类型的状态码暂不考虑,如果有遇到再处理,不然怕影响范围大
throw new WorkspaceConnectionException("Response " + response.getStatusLine().toString());
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, e.getMessage());
return false;
}
return true;
}
/**
* 提示连接已经断开如果已经在提示中了就直接返回
*/
private static void showDialog() {
if (alerting) {
return;
}
synchronized (RPCConnectHandlerCenter.class) {
if (alerting) {
return;
}
alerting = true;
try {
FineJOptionPane.showMessageDialog(
DesignerContext.getDesignerFrame(),
Toolkit.i18nText("Fine-Design_Basic_Remote_Disconnected"),
UIManager.getString("OptionPane.messageDialogTitle"),
JOptionPane.ERROR_MESSAGE,
UIManager.getIcon("OptionPane.errorIcon"));
EnvChangeEntrance.getInstance().chooseEnv();
} finally {
alerting = false;
}
}
}
/**
* 错误状态码
* 5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误 这些错误可能是服务器本身的错误而不是请求出错代码 说明
* 500 (服务器内部错误) 服务器遇到错误无法完成请求
* 501 (尚未实施) 服务器不具备完成请求的功能 例如服务器无法识别请求方法时可能会返回此代码
* 502 (错误网关) 服务器作为网关或代理从上游服务器收到无效响应
* 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护) 通常这只是暂时状态
* 504 (网关超时) 服务器作为网关或代理但是没有及时从上游服务器收到请求
* 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本
*
* @param status 错误状态码
* @return 是否是错误状态码
*/
private static boolean isErrorStatus(int status) {
return status >= 500 && status <= 600;
}
/**
* 停止事件监听
*/
public static void stopListener() {
if (!WorkContext.getCurrent().isLocal()) {
EventDispatcher.stopListen(listener);
}
}
}

56
designer-base/src/main/java/com/fr/design/lock/TemplateLockInfoReSave.java

@ -0,0 +1,56 @@
package com.fr.design.lock;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.mainframe.JTemplate;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.log.FineLoggerFactory;
import com.fr.report.lock.LockInfoOperator;
import com.fr.stable.collections.CollectionUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
import java.util.List;
/**
* 更新模板锁信息
*
* @author Roger
* @since 11.0
* Created on 2023/12/11
*/
public class TemplateLockInfoReSave {
private static Listener<Workspace> listener = new Listener<Workspace>() {
@Override
public void on(Event event, Workspace workspace) {
List<JTemplate<?, ?>> templates = HistoryTemplateListCache.getInstance().getHistoryList();
if (CollectionUtils.isEmpty(templates)) {
return;
}
String[] paths = templates.stream().map(JTemplate::getPath).toArray(String[]::new);
String[] lockedPath = WorkContext.getCurrent().get(LockInfoOperator.class).lockTemplates(paths);
FineLoggerFactory.getLogger().warn("template lock failed:{}", String.join(";", lockedPath));
}
};
/**
* 启动监听
*/
public static void startListener() {
if (!WorkContext.getCurrent().isLocal()) {
EventDispatcher.listen(WorkspaceEvent.ConnectSuccess, listener);
}
}
/**
* 停止事件监听
*/
public static void stopListener() {
if (!WorkContext.getCurrent().isLocal()) {
EventDispatcher.stopListen(listener);
}
}
}

13
designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java

@ -92,6 +92,7 @@ import com.fr.plugin.observer.PluginEventListener;
import com.fr.plugin.observer.PluginEventType;
import com.fr.plugin.observer.PluginListenerRegistration;
import com.fr.report.InconsistentLockException;
import com.fr.report.LockedException;
import com.fr.report.UnLockedException;
import com.fr.report.cell.Elem;
import com.fr.report.cell.cellattr.CellImage;
@ -103,6 +104,7 @@ import com.fr.stable.StringUtils;
import com.fr.stable.core.UUID;
import com.fr.widgettheme.designer.WidgetThemeDisplayAction;
import com.fr.workspace.WorkContext;
import com.fr.workspace.base.UserInfo;
import com.fr.workspace.server.lock.TplOperator;
import javax.swing.BorderFactory;
@ -1059,13 +1061,20 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
protected void checkBeforeSave() throws Exception {
// 保存前校验下未解锁
if (WorkContext.getCurrent().get(LockInfoOperator.class).isTplUnLocked(getEditingFILE().getPath())) {
LockInfoOperator lockInfoOperator = WorkContext.getCurrent().get(LockInfoOperator.class);
String path = getEditingFILE().getPath();
if (lockInfoOperator.isTplUnLocked(path)) {
throw new UnLockedException();
}
//睡眠超过90s之后,锁信息会被清掉,之后其他人可能打开模板进行锁定,所以定这里还判断一下模板是否被其他人锁
UserInfo userInfo = lockInfoOperator.getUserInfo(path);
if (userInfo != null) {
throw new LockedException(userInfo);
}
// 过滤掉本地文件
boolean localFile = getEditingFILE() instanceof FileFILE;
boolean inconsistent = !localFile && getEditingFILE().exists()
&& !WorkContext.getCurrent().get(LockInfoOperator.class).isConsistentLock(getEditingFILE().getPath());
&& !lockInfoOperator.consistentCheckAndLockIfNecessary(path);
if (inconsistent) {
throw new InconsistentLockException();
}

22
designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java

@ -4,6 +4,7 @@ import com.fr.common.exception.ThrowableHandler;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.i18n.Toolkit;
import com.fr.design.lock.LockInfoDialog;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.ui.util.UIUtil;
@ -11,7 +12,9 @@ import com.fr.design.utils.TemplateUtils;
import com.fr.file.FileNodeFILE;
import com.fr.file.filetree.FileNode;
import com.fr.general.IOUtils;
import com.fr.report.LockedException;
import com.fr.report.UnLockedException;
import com.fr.workspace.base.UserInfo;
import com.fr.workspace.exception.DiskSpaceFullException;
import com.fr.report.InconsistentLockException;
import java.awt.Frame;
@ -89,6 +92,25 @@ public class SaveFailureHandler implements ThrowableHandler {
}
},
LOCKED {
@Override
public boolean process(Throwable e) {
LockedException exception = null;
if (e.getCause() instanceof LockedException) {
exception = (LockedException) e.getCause();
}
if (e instanceof LockedException) {
exception = (LockedException) e;
}
if (exception != null) {
UserInfo userInfo = exception.getUserInfo();
LockInfoDialog.show(userInfo);
return true;
}
return false;
}
},
Other {
@Override
public boolean process(Throwable e) {

74
designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java

@ -2,15 +2,13 @@ package com.fr.design.mainframe.socketio;
import com.fr.config.RemoteConfigEvent;
import com.fr.design.DesignerEnvManager;
import com.fr.design.EnvChangeEntrance;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.RPCConnectHandlerCenter;
import com.fr.design.env.DesignerWorkspaceInfo;
import com.fr.design.env.DesignerWorkspaceInfoContext;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.i18n.LocaleLinkProvider;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.loghandler.DesignerLogger;
import com.fr.design.mainframe.share.ui.base.MouseClickListener;
import com.fr.design.mainframe.toast.DesignerToastMsgUtil;
@ -21,18 +19,11 @@ import com.fr.event.EventDispatcher;
import com.fr.log.FineLoggerFactory;
import com.fr.serialization.SerializerHelper;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;
import com.fr.third.apache.logging.log4j.core.LogEvent;
import com.fr.third.org.apache.http.client.config.RequestConfig;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.client.methods.HttpGet;
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient;
import com.fr.third.org.apache.http.impl.client.HttpClients;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.base.WorkspaceConstants;
import com.fr.workspace.connect.WorkspaceConnectionInfo;
import com.fr.workspace.engine.exception.WorkspaceConnectionException;
import com.fr.workspace.server.socket.CustomLogEvent;
import com.fr.workspace.server.socket.LogEventConverter;
import io.socket.client.IO;
@ -40,9 +31,7 @@ import io.socket.client.Socket;
import io.socket.emitter.Emitter;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
@ -76,7 +65,6 @@ public class DesignerSocketIO {
private static Status status = Status.Disconnected;
private static Timer disConnectHintTimer = null;
private static long disConnectHintTimerDelay = 3000;
private static final int TIMEOUT = 5000;
//维护一个当前工作环境的websocket连接信息列表
private static ConnectionInfo[] connectionInfos;
//维护一个关于uri列表的计数器
@ -205,10 +193,10 @@ public class DesignerSocketIO {
};
private static void dealWithSocketDisconnect() {
if (checkRPCConnect()) {
if (RPCConnectHandlerCenter.checkRPCConnect(getConnectionInfo())) {
showSocketDisconnectToast();
} else {
showRPCDisconnectDialog();
RPCConnectHandlerCenter.showRPCDisconnectDialog();
}
}
@ -245,62 +233,6 @@ public class DesignerSocketIO {
return jPanel;
}
private static void showRPCDisconnectDialog() {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
FineJOptionPane.showMessageDialog(
DesignerContext.getDesignerFrame(),
Toolkit.i18nText("Fine-Design_Basic_Remote_Disconnected"),
UIManager.getString("OptionPane.messageDialogTitle"),
JOptionPane.ERROR_MESSAGE,
UIManager.getIcon("OptionPane.errorIcon"));
EnvChangeEntrance.getInstance().chooseEnv();
}
});
}
private static boolean checkRPCConnect() {
CloseableHttpClient httpclient = HttpClients.createDefault();
WorkspaceConnectionInfo info = getConnectionInfo();
HttpGet httpGet = new HttpGet(StableUtils.pathJoin(info.getUrl(), WorkspaceConstants.CONTROLLER_PREFIX, WorkspaceConstants.VT));
RequestConfig requestConfig = RequestConfig
.custom()
.setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();
httpGet.setConfig(requestConfig);
try {
CloseableHttpResponse response = httpclient.execute(httpGet);
if (isErrorStatus(response.getStatusLine().getStatusCode())) {
//这边nginx做负载,服务器被kill掉,返回的是502,不会抛错,导致checkRPCConnect通过
//针对500-600的错误码加个判断,其他类型的状态码暂不考虑,如果有遇到再处理,不然怕影响范围大
throw new WorkspaceConnectionException("Response " + response.getStatusLine().toString());
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, e.getMessage());
return false;
}
return true;
}
/**
* 错误状态码
* 5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误 这些错误可能是服务器本身的错误而不是请求出错代码 说明
* 500 (服务器内部错误) 服务器遇到错误无法完成请求
* 501 (尚未实施) 服务器不具备完成请求的功能 例如服务器无法识别请求方法时可能会返回此代码
* 502 (错误网关) 服务器作为网关或代理从上游服务器收到无效响应
* 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护) 通常这只是暂时状态
* 504 (网关超时) 服务器作为网关或代理但是没有及时从上游服务器收到请求
* 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本
*
* @param status 错误状态码
* @return 是否是错误状态码
*/
private static boolean isErrorStatus(int status) {
return status >= 500 && status <= 600;
}
//配置变更监听器
private static final Emitter.Listener modifyConfig = new Emitter.Listener() {
@Override

6
designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java

@ -17,6 +17,7 @@ import com.fr.decision.webservice.v10.plugin.helper.category.impl.PluginResource
import com.fr.decision.webservice.v10.plugin.helper.category.impl.UpmResourceLoader;
import com.fr.design.DesignerEnvManager;
import com.fr.design.ExtraDesignClassManager;
import com.fr.design.RPCConnectHandlerCenter;
import com.fr.design.actions.NewFormAction;
import com.fr.design.actions.core.ActionFactory;
import com.fr.design.actions.insert.cell.BiasCellAction;
@ -51,6 +52,7 @@ import com.fr.design.javascript.EmailPane;
import com.fr.design.javascript.JavaScriptImplPane;
import com.fr.design.javascript.ParameterJavaScriptPane;
import com.fr.design.javascript.ProcessTransitionAdapter;
import com.fr.design.lock.TemplateLockInfoReSave;
import com.fr.design.login.DesignerLoginType;
import com.fr.design.login.guide.DesignerGuideHelper;
import com.fr.design.login.message.DesignerMessageHelper;
@ -214,6 +216,8 @@ public class DesignerActivator extends Activator implements Prepare {
AlphaFineHelper.switchConfig4Locale();
RecoverManager.register(new RecoverForDesigner());
WidgetThemeListenerStarter.start();
TemplateLockInfoReSave.startListener();
RPCConnectHandlerCenter.startListener();
}, DesignerStartupPool.common());
CompletableFuture<Void> resourcePrepare = CompletableFuture.runAsync(() -> {
@ -537,6 +541,8 @@ public class DesignerActivator extends Activator implements Prepare {
public void stop() {
unloadLogAppender();
DesignerSocketIO.close();
TemplateLockInfoReSave.stopListener();
RPCConnectHandlerCenter.stopListener();
}
@Override

Loading…
Cancel
Save