帆软报表设计器源代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

316 lines
12 KiB

package com.fr.design.mainframe.socketio;
import com.fr.config.RemoteConfigEvent;
import com.fr.decision.webservice.utils.DecisionServiceConstants;
import com.fr.design.DesignerEnvManager;
import com.fr.design.EnvChangeEntrance;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.env.DesignerWorkspaceInfo;
import com.fr.design.env.DesignerWorkspaceInfoContext;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.loghandler.DesignerLogger;
import com.fr.design.ui.util.UIUtil;
import com.fr.event.EventDispatcher;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.report.RemoteDesignConstants;
import com.fr.serialization.SerializerHelper;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StringUtils;
import com.fr.third.apache.log4j.spi.LoggingEvent;
import com.fr.third.org.apache.http.conn.ssl.NoopHostnameVerifier;
import com.fr.third.org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import com.fr.third.org.apache.http.ssl.SSLContexts;
import com.fr.web.WebSocketConfig;
import com.fr.web.socketio.WebSocketProtocol;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.base.WorkspaceConstants;
import com.fr.workspace.connect.WorkspaceConnection;
import com.fr.workspace.connect.WorkspaceConnectionInfo;
import com.fr.workspace.server.socket.SocketInfoOperator;
import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.SSLContext;
import javax.swing.*;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
public class DesignerSocketIO {
enum Status {
Connected,
Disconnected,
Disconnecting
}
private static final String HTTPS = "https";
private static final String HTTP = "http";
private static Socket socket = null;
private static Status status = Status.Disconnected;
private static Timer disConnectHintTimer = null;
private static long disConnectHintTimerDelay = 3000;
//维护一个当前工作环境的uri列表
private static String[] uri;
//维护一个关于uri列表的计数器
private static int count;
// 当前webSocket选择的协议
private static String currentProtocol;
public static void close() {
if (socket != null) {
status = Status.Disconnecting;
socket.close();
socket = null;
}
}
public static void update() {
Workspace current = WorkContext.getCurrent();
if (current.isLocal()) {
return;
}
//每当更换工作环境,更新uri列表,同时更新计数器count
try {
uri = getSocketUri();
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
count = 0;
//建立socket并注册监听
createSocket();
}
private static void createSocket() {
//根据uri和计数器建立连接,并注册监听
try {
if (count < uri.length) {
socket = IO.socket(new URI(uri[count]), createOptions());
socket.on(WorkspaceConstants.WS_LOGRECORD, printLog);
socket.on(WorkspaceConstants.CONFIG_MODIFY, modifyConfig);
socket.on(Socket.EVENT_CONNECT_ERROR, failRetry);
socket.on(Socket.EVENT_DISCONNECT, disConnectHint);
socket.on(Socket.EVENT_CONNECT, handleConnect);
socket.connect();
status = Status.Connected;
} else {
//表示所有的uri都连接不成功
FineLoggerFactory.getLogger().warn("All uris failed to connect");
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private static IO.Options createOptions() {
IO.Options options = new IO.Options();
try {
if (ComparatorUtils.equals(currentProtocol, HTTPS)) {
options.sslContext = getSSLContext();
options.hostnameVerifier = NoopHostnameVerifier.INSTANCE;
options.secure = true;
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return options;
}
private static SSLContext getSSLContext() throws Exception {
WorkspaceConnectionInfo connection = getConnectionInfo();
String certPath = connection.getCertPath();
String certSecretKey = connection.getCertSecretKey();
if (StringUtils.isBlank(certPath) || StringUtils.isBlank(certSecretKey)) {
return SSLContexts.createDefault();
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream keystore = new FileInputStream(new File(certPath))) {
trustStore.load(keystore, certSecretKey.toCharArray());
}
return SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build();
}
private static WorkspaceConnectionInfo getConnectionInfo() {
if (DesignerWorkspaceInfoContext.getWorkspaceInfo() == null) {
String currentName = DesignerEnvManager.getEnvManager().getCurEnvName();
DesignerWorkspaceInfo info = DesignerEnvManager.getEnvManager().getWorkspaceInfo(currentName);
return info.getConnection();
} else {
return DesignerWorkspaceInfoContext.getWorkspaceInfo().getConnection();
}
}
private static String[] getSocketUri() throws IOException {
Workspace current = WorkContext.getCurrent();
URL url = new URL(current.getPath());
Integer[] ports = current.get(SocketInfoOperator.class).getPort();
WorkspaceConnection connection = current.getConnection();
// 服务器配置https webSocket可能是wss也可能是ws webSocket的协议可以单独配置
WebSocketProtocol webSocketProtocol = WebSocketConfig.getInstance().getProtocol();
currentProtocol = webSocketProtocol == WebSocketProtocol.PLAIN ? HTTP : HTTPS;
String[] result = new String[ports.length];
for (int i = 0; i < ports.length; i++) {
result[i] = String.format("%s://%s:%s%s?%s=%s&%s=%s",
currentProtocol,
url.getHost(),
ports[i],
WorkspaceConstants.WS_NAMESPACE,
DecisionServiceConstants.WEB_SOCKET_TOKEN_NAME,
connection.getToken(),
RemoteDesignConstants.USER_LOCK_ID,
connection.getId());
}
FineLoggerFactory.getLogger().info("Available ports: {}, current Protocol: {}", Arrays.toString(ports), currentProtocol);
return result;
}
//失败重试监听器:1、关闭失败的socket 2、计数器加1 3、调用创建socket方法
private static final Emitter.Listener failRetry = new Emitter.Listener() {
@Override
public void call(Object... args) {
printLog(args, PrintEventLogImpl.WARN, "failed args: {}");
status = Status.Disconnecting;
socket.close();
count++;
createSocket();
}
};
//日志输出监听器
private static final Emitter.Listener printLog = new Emitter.Listener() {
@Override
public void call(Object... objects) {
if (ArrayUtils.isNotEmpty(objects)) {
try {
LoggingEvent event = SerializerHelper.deserialize((byte[]) objects[0]);
DesignerLogger.log(event);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
}
};
private static final Emitter.Listener handleConnect = new Emitter.Listener() {
@Override
public void call(Object... objects) {
if (disConnectHintTimer != null) {
FineLoggerFactory.getLogger().info("cancel disConnectHintTimer");
disConnectHintTimer.cancel();
}
}
};
//断开连接提醒监听器
private static final Emitter.Listener disConnectHint = new Emitter.Listener() {
@Override
public void call(Object... objects) {
FineLoggerFactory.getLogger().info("start disConnectHintTimer");
disConnectHintTimer = new Timer();
disConnectHintTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
/*
* todo 远程心跳断开不一定 socket 断开 和远程紧密相关的业务都绑定在心跳上,切换成心跳断开之后进行提醒,
* socket 只用推日志和通知配置变更
*/
printLog(objects, PrintEventLogImpl.ERROR, "disConnected args: {}");
if (status != Status.Disconnecting) {
showConnectionLostDialog();
}
status = Status.Disconnected;
} finally {
disConnectHintTimer.cancel();
disConnectHintTimer = null;
}
}
}, disConnectHintTimerDelay);
}
};
private static void showConnectionLostDialog() {
try {
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();
}
});
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
//配置变更监听器
private static final Emitter.Listener modifyConfig = new Emitter.Listener() {
@Override
public void call(Object... objects) {
assert objects != null && objects.length == 1;
String param = (String) objects[0];
EventDispatcher.fire(RemoteConfigEvent.EDIT, param);
}
};
private static void printLog(Object[] objects, PrintEventLog printEventLog, String prefix) {
for (Object object : objects) {
if (object instanceof Throwable) {
Throwable throwable = (Throwable) object;
printEventLog.printThrowable(throwable);
} else {
printEventLog.print(prefix, object);
}
}
}
interface PrintEventLog {
void printThrowable(Throwable throwable);
void print(String s, Object ...object);
}
enum PrintEventLogImpl implements PrintEventLog {
ERROR {
@Override
public void printThrowable(Throwable throwable) {
FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable);
}
@Override
public void print(String s, Object... object) {
FineLoggerFactory.getLogger().error(s, object);
}
},
WARN {
@Override
public void printThrowable(Throwable throwable) {
FineLoggerFactory.getLogger().warn(throwable.getMessage(), throwable);
}
@Override
public void print(String s, Object... object) {
FineLoggerFactory.getLogger().warn(s, object);
}
};
}
}