diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/AbstractSocketConfig.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/AbstractSocketConfig.java new file mode 100644 index 0000000000..dcd4173e69 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/AbstractSocketConfig.java @@ -0,0 +1,98 @@ +package com.fr.design.mainframe.socketio; + +import com.fr.decision.webservice.utils.DecisionServiceConstants; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.report.RemoteDesignConstants; +import com.fr.stable.StringUtils; +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.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 io.socket.client.IO; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.security.KeyStore; +import java.util.Arrays; +import javax.net.ssl.SSLContext; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2021/11/29 + */ +public abstract class AbstractSocketConfig { + + public static final String HTTPS = "https"; + public static final String HTTP = "http"; + + /** + * 当前webSocket选择的协议 + */ + private String currentProtocol; + + protected abstract String getCurrentProtocolFromUrl(URL url); + + protected abstract Integer[] getPorts(); + + public IO.Options createOptions() { + IO.Options options = new IO.Options(); + options.path = WebSocketConfig.getInstance().getSocketContext(); + 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 SSLContext getSSLContext() throws Exception { + WorkspaceConnectionInfo connection = DesignerSocketIO.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(); + } + + public String[] getUri() throws IOException { + Workspace current = WorkContext.getCurrent(); + URL url = new URL(current.getPath()); + Integer[] ports = getPorts(); + WorkspaceConnection connection = current.getConnection(); + currentProtocol = getCurrentProtocolFromUrl(url); + 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; + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfo.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfo.java new file mode 100644 index 0000000000..4315100c83 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfo.java @@ -0,0 +1,26 @@ +package com.fr.design.mainframe.socketio; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2021/11/29 + */ +public class ConnectionInfo { + + private final String uri; + + private final AbstractSocketConfig socketConfig; + + public ConnectionInfo(String uri, AbstractSocketConfig socketConfig) { + this.uri = uri; + this.socketConfig = socketConfig; + } + + public String getUri() { + return uri; + } + + public AbstractSocketConfig getSocketConfig() { + return socketConfig; + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfoFactory.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfoFactory.java new file mode 100644 index 0000000000..4212795ddc --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfoFactory.java @@ -0,0 +1,43 @@ +package com.fr.design.mainframe.socketio; + +import com.fr.rpc.ExceptionHandler; +import com.fr.rpc.RPCInvokerExceptionInfo; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.socket.SocketInfoOperator; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2021/11/29 + */ +public class ConnectionInfoFactory { + + public static ConnectionInfo[] createConnectionInfo() throws IOException { + + List connectionInfos = new ArrayList<>(); + boolean useJavaxWebsocket = WorkContext.getCurrent().get(SocketInfoOperator.class, new ExceptionHandler() { + @Override + public Object callHandler(RPCInvokerExceptionInfo exceptionInfo) { + return false; + } + }).isUseJavaxWebsocket(); + if (useJavaxWebsocket) { + // 新方案配置 + addConfig(new ContainerSocketConfig(), connectionInfos); + } + // 老方案配置 + addConfig(new IOSocketConfig(), connectionInfos); + return connectionInfos.toArray(new ConnectionInfo[0]); + } + + private static void addConfig(AbstractSocketConfig config, List connectionInfos) throws IOException { + String[] configUri = config.getUri(); + for (String uri : configUri) { + connectionInfos.add(new ConnectionInfo(uri, config)); + } + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ContainerSocketConfig.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ContainerSocketConfig.java new file mode 100644 index 0000000000..c36ecee5e0 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/ContainerSocketConfig.java @@ -0,0 +1,57 @@ +package com.fr.design.mainframe.socketio; + +import com.fr.base.FRContext; +import com.fr.log.FineLoggerFactory; +import com.fr.rpc.ExceptionHandler; +import com.fr.rpc.RPCInvokerExceptionInfo; +import com.fr.stable.ArrayUtils; +import com.fr.web.WebSocketConfig; +import com.fr.web.socketio.WebSocketConstants; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.socket.SocketInfoOperator; +import io.socket.client.IO; +import io.socket.engineio.client.transports.Polling; +import io.socket.engineio.client.transports.WebSocket; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * 连接基于容器端口的WebSocket + * + * @author hades + * @version 11.0 + * Created by hades on 2021/11/29 + */ +public class ContainerSocketConfig extends AbstractSocketConfig { + + private static final int DEFAULT_SERVER_PORT = 80; + + @Override + public IO.Options createOptions() { + IO.Options options = super.createOptions(); + options.transports = new String[]{WebSocket.NAME, Polling.NAME}; + options.path = String.format("/%s%s", FRContext.getCommonOperator().getAppName(), WebSocketConstants.WEBSOCKET_PATH); + return options; + } + + @Override + protected String getCurrentProtocolFromUrl(URL url) { + return url.getProtocol(); + } + + @Override + protected Integer[] getPorts() { + Integer[] ports = WebSocketConfig.getInstance().getPort(); + int serverPort = DEFAULT_SERVER_PORT; + URL url = null; + try { + url = new URL(WorkContext.getCurrent().getPath()); + } catch (MalformedURLException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (url != null && url.getPort() > 0) { + serverPort = url.getPort(); + } + return ArrayUtils.insert(0, ports, serverPort); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java index f3df35fb80..6b65a7bd3d 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java @@ -1,7 +1,6 @@ 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; @@ -19,34 +18,24 @@ import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.BrowseUtils; import com.fr.event.EventDispatcher; import com.fr.general.CloudCenter; -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.StableUtils; -import com.fr.stable.StringUtils; import com.fr.third.apache.log4j.spi.LoggingEvent; 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.conn.ssl.NoopHostnameVerifier; -import com.fr.third.org.apache.http.conn.ssl.TrustSelfSignedStrategy; import com.fr.third.org.apache.http.impl.client.CloseableHttpClient; import com.fr.third.org.apache.http.impl.client.HttpClients; -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 io.socket.client.IO; import io.socket.client.Socket; import io.socket.emitter.Emitter; -import javax.net.ssl.SSLContext; import javax.swing.BorderFactory; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -55,13 +44,8 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseEvent; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.net.URI; -import java.net.URL; -import java.security.KeyStore; -import java.util.Arrays; import java.util.Timer; public class DesignerSocketIO { @@ -80,8 +64,8 @@ public class DesignerSocketIO { private static Timer disConnectHintTimer = null; private static long disConnectHintTimerDelay = 3000; private static final int TIMEOUT = 5000; - //维护一个当前工作环境的uri列表 - private static String[] uri; + //维护一个当前工作环境的websocket连接信息列表 + private static ConnectionInfo[] connectionInfos; //维护一个关于uri列表的计数器 private static int count; // 当前webSocket选择的协议 @@ -104,7 +88,7 @@ public class DesignerSocketIO { } //每当更换工作环境,更新uri列表,同时更新计数器count try { - uri = getSocketUri(); + connectionInfos = ConnectionInfoFactory.createConnectionInfo(); } catch (IOException e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } @@ -116,8 +100,9 @@ public class DesignerSocketIO { private static void createSocket() { //根据uri和计数器建立连接,并注册监听 try { - if (count < uri.length) { - socket = IO.socket(new URI(uri[count]), createOptions()); + if (count < connectionInfos.length) { + ConnectionInfo connectionInfo = connectionInfos[count]; + socket = IO.socket(new URI(connectionInfo.getUri()), connectionInfo.getSocketConfig().createOptions()); socket.on(WorkspaceConstants.WS_LOGRECORD, printLog); socket.on(WorkspaceConstants.CONFIG_MODIFY, modifyConfig); socket.on(Socket.EVENT_CONNECT_ERROR, failRetry); @@ -135,37 +120,7 @@ public class DesignerSocketIO { } } - 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() { + public static WorkspaceConnectionInfo getConnectionInfo() { if (DesignerWorkspaceInfoContext.getWorkspaceInfo() == null) { String currentName = DesignerEnvManager.getEnvManager().getCurEnvName(); DesignerWorkspaceInfo info = DesignerEnvManager.getEnvManager().getWorkspaceInfo(currentName); @@ -175,34 +130,6 @@ public class DesignerSocketIO { } } - private static String[] getSocketUri() throws IOException { - Workspace current = WorkContext.getCurrent(); - URL url = new URL(current.getPath()); - Integer[] ports = WebSocketConfig.getInstance().getAvailablePorts(); - WorkspaceConnection connection = current.getConnection(); - // 服务器配置https webSocket可能是wss也可能是ws webSocket的协议可以单独配置 - WebSocketProtocol webSocketProtocol = WebSocketConfig.getInstance().getProtocol(); - currentProtocol = webSocketProtocol == WebSocketProtocol.PLAIN ? HTTP : HTTPS; - if (WebSocketConfig.getInstance().isUsingProxy()) { - // 如果配置了代理服务器跟随服务器协议 - currentProtocol = url.getProtocol(); - } - 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 diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/socketio/IOSocketConfig.java b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/IOSocketConfig.java new file mode 100644 index 0000000000..aa7106298e --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/socketio/IOSocketConfig.java @@ -0,0 +1,38 @@ +package com.fr.design.mainframe.socketio; + +import com.fr.web.WebSocketConfig; +import com.fr.web.socketio.WebSocketProtocol; +import io.socket.client.IO; +import java.net.URL; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2021/11/29 + */ +public class IOSocketConfig extends AbstractSocketConfig { + + @Override + public IO.Options createOptions() { + IO.Options options = super.createOptions(); + options.path = WebSocketConfig.getInstance().getSocketContext(); + return options; + } + + @Override + protected String getCurrentProtocolFromUrl(URL url) { + // 服务器配置https webSocket可能是wss也可能是ws webSocket的协议可以单独配置 + WebSocketProtocol webSocketProtocol = WebSocketConfig.getInstance().getProtocol(); + String currentProtocol = webSocketProtocol == WebSocketProtocol.PLAIN ? HTTP : HTTPS; + if (WebSocketConfig.getInstance().isUsingProxy()) { + // 如果配置了代理服务器跟随服务器协议 + currentProtocol = url.getProtocol(); + } + return currentProtocol; + } + + @Override + protected Integer[] getPorts() { + return WebSocketConfig.getInstance().getAvailablePorts(); + } +}