diff --git a/deploy/scripts/run-server.sh b/deploy/scripts/run-server.sh index e9f14f67e9..779aacfa4f 100755 --- a/deploy/scripts/run-server.sh +++ b/deploy/scripts/run-server.sh @@ -1,4 +1,8 @@ #!/bin/bash + +# Detect host machine IP Address (we need this when run in docker container) +export CB_LOCAL_HOST_ADDR=$(ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1) + launcherJar=( server/plugins/org.eclipse.equinox.launcher*.jar ) echo "Starting Cloudbeaver Server" diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 3ff35f9e1d..2d7bf047bf 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -83,6 +83,7 @@ type ServerConfig { supportsConnectionBrowser: Boolean supportsWorkspaces: Boolean sessionExpireTime: Int + localHostAddress: String configurationMode: Boolean developmentMode: Boolean diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java index 375241166e..cca0d4a67a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java @@ -92,6 +92,11 @@ public class WebServerConfig { return application.getMaxSessionIdleTime(); } + @Property + public String getLocalHostAddress() { + return application.getLocalHostAddress(); + } + @Property public WebServerLanguage[] getSupportedLanguages() { List langs = PlatformLanguageRegistry.getInstance().getLanguages(); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index 504ed06614..ea8dbfad8e 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -46,10 +46,11 @@ import org.jkiss.utils.CommonUtils; import org.jkiss.utils.StandardConstants; import java.io.*; +import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * This class controls all aspects of the application's execution @@ -93,6 +94,8 @@ public class CBApplication extends BaseApplicationImpl { private boolean develMode = false; private boolean configurationMode = false; + private String localHostAddress; + private final List localInetAddresses = new ArrayList<>(); public CBApplication() { } @@ -181,6 +184,15 @@ public class CBApplication extends BaseApplicationImpl { configurationMode = CommonUtils.isEmpty(serverName); //|| CommonUtils.isEmpty(databaseConfiguration.getUser()) || CommonUtils.isEmpty(databaseConfiguration.getPassword()); + // Determine address for local host + localHostAddress = System.getenv(CBConstants.VAR_CB_LOCAL_HOST_ADDR); + if (CommonUtils.isEmpty(localHostAddress)) { + localHostAddress = System.getProperty(CBConstants.VAR_CB_LOCAL_HOST_ADDR); + } + if (CommonUtils.isEmpty(localHostAddress) || "127.0.0.1".equals(localHostAddress) || "::0".equals(localHostAddress)) { + localHostAddress = "localhost"; + } + final Runtime runtime = Runtime.getRuntime(); Location instanceLoc = Platform.getInstanceLocation(); @@ -222,6 +234,13 @@ public class CBApplication extends BaseApplicationImpl { if (configurationMode) { log.debug("\tServer is in configuration mode!"); } + { + log.debug("\tLocal host addresses:"); + determineLocalAddresses(); + for (InetAddress ia : localInetAddresses) { + log.debug("\t\t" + ia.getHostAddress() + " (" + ia.getCanonicalHostName() + ")"); + } + } { try { @@ -248,6 +267,33 @@ public class CBApplication extends BaseApplicationImpl { return null; } + private void determineLocalAddresses() { + try { +// InetAddress localHost = InetAddress.getLocalHost(); +// InetAddress[] allMyIps = InetAddress.getAllByName(localHost.getCanonicalHostName()); +// for (InetAddress addr : allMyIps) { +// System.out.println("Local addr: " + addr); +// } + boolean hasLoopbackAddress = false; + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface intf = en.nextElement(); + for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { + InetAddress localInetAddress = enumIpAddr.nextElement(); + boolean loopbackAddress = localInetAddress.isLoopbackAddress(); + if (loopbackAddress ? !hasLoopbackAddress : !localInetAddress.isLinkLocalAddress()) { + if (loopbackAddress) { + hasLoopbackAddress = true; + } + localInetAddresses.add(localInetAddress); + } + } + } + } catch (Exception e) { + log.error(e); + } + + } + @NotNull private File getRuntimeConfigFile() { return new File(getDataDirectory(false), CBConstants.RUNTIME_CONFIG_FILE_NAME); @@ -403,6 +449,23 @@ public class CBApplication extends BaseApplicationImpl { return configurationMode; } + public String getLocalHostAddress() { + return localHostAddress; + } + + public boolean isLocalInetAddress(String hostName) { + for (InetAddress addr : localInetAddresses) { + if (addr.getHostAddress().equals(hostName)) { + return true; + } + } + return false; + } + + public List getLocalInetAddresses() { + return localInetAddresses; + } + public synchronized void finishConfiguration( String newServerName, String adminName, diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java index fede3115bc..1f9ae14c23 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java @@ -65,4 +65,7 @@ public class CBConstants { public static final String DEFAUL_APP_ANONYMOUS_ROLE_NAME = "user"; + public static final String VAR_CB_LOCAL_HOST_ADDR = "CB_LOCAL_HOST_ADDR"; + + } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java index 020626f844..b5988f5244 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java @@ -81,7 +81,6 @@ public class CBPlatform extends BasePlatformImpl { private WebSessionManager sessionManager; private final List applicableDrivers = new ArrayList<>(); - public static CBPlatform getInstance() { if (instance == null) { synchronized (CBPlatform.class) { diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminConnectionSearchInfo.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminConnectionSearchInfo.java index 4db444e81d..ec28aba5cf 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminConnectionSearchInfo.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminConnectionSearchInfo.java @@ -20,18 +20,28 @@ import java.util.ArrayList; import java.util.List; public class AdminConnectionSearchInfo { - private final String host; + private final String displayName; + private final String hostAddr; private final int port; private List possibleDrivers = new ArrayList<>(); private String defaultDriver; - public AdminConnectionSearchInfo(String host, int driverPort) { - this.host = host; + public AdminConnectionSearchInfo(String displayName, String hostAddr, int driverPort) { + this.displayName = displayName; + this.hostAddr = hostAddr; this.port = driverPort; } + public String getDisplayName() { + return displayName; + } + + public String getHostAddr() { + return hostAddr; + } + public String getHost() { - return host; + return hostAddr; } public int getPort() { diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java index a29a212031..e66f0fdc9d 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java @@ -20,14 +20,13 @@ import io.cloudbeaver.WebServiceUtils; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.service.admin.AdminConnectionSearchInfo; -import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.connection.DBPDriver; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.registry.DataSourceRegistry; import org.jkiss.utils.CommonUtils; -import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; @@ -44,7 +43,7 @@ public class ConnectionSearcher implements DBRRunnableWithProgress { private final WebSession webSession; private final String[] hostNames; private final DataSourceRegistry tempRegistry; - private List foundConnections = new ArrayList<>(); + private final List foundConnections = new ArrayList<>(); private List availableDrivers = new ArrayList<>(); public ConnectionSearcher(WebSession webSession, String[] hostNames) { @@ -53,47 +52,83 @@ public class ConnectionSearcher implements DBRRunnableWithProgress { CBPlatform platform = CBPlatform.getInstance(); this.tempRegistry = new DataSourceRegistry(platform, platform.getWorkspace().getActiveProject()); - availableDrivers.addAll(CBPlatform.getInstance().getApplicableDrivers()); + this.availableDrivers.addAll(CBPlatform.getInstance().getApplicableDrivers()); } public List getFoundConnections() { - return foundConnections; + synchronized (foundConnections) { + return new ArrayList<>(foundConnections); + } } @Override - public void run(DBRProgressMonitor monitor) throws InvocationTargetException { - monitor.beginTask("Search connections", hostNames.length * availableDrivers.size()); + public void run(DBRProgressMonitor monitor) { + List finalHostNames = new ArrayList<>(); + Map localHostNames = new HashMap<>(); + for (String hostName : hostNames) { + monitor.subTask("Search connections on '" + hostName + "'"); + if (hostName.equals("localhost") || hostName.equals("local") || hostName.equals("127.0.0.1")) { + for (InetAddress addr : CBPlatform.getInstance().getApplication().getLocalInetAddresses()) { + String localHostAddress = addr.getHostAddress(); + finalHostNames.add(localHostAddress); + localHostNames.put(localHostAddress, hostName); + } + } else { + finalHostNames.add(hostName); + } + } + + monitor.beginTask("Search connections", finalHostNames.size()); try { - for (String hostName : hostNames) { + ThreadGroup tg = new ThreadGroup("Connection search"); + List threadList = new ArrayList<>(); + for (String hostName : finalHostNames) { monitor.subTask("Search connections on '" + hostName + "'"); - searchConnections(monitor, hostName); + String localName = localHostNames.get(hostName); + if (localName == null) { + localName = hostName; + } + String finalLocalName = localName; + Runnable searchFunc = () -> searchConnections(monitor, hostName, finalLocalName); + Thread t = new Thread(tg, searchFunc, "Search connection @" + hostName); + threadList.add(t); + t.start(); + } + for (Thread t : threadList) { + try { + t.join(); + } catch (InterruptedException e) { + // ignore + } } - } catch (DBException e) { - throw new InvocationTargetException(e); } finally { monitor.done(); } } - private void searchConnections(DBRProgressMonitor monitor, String hostName) throws DBException { - int checkTimeout = 250; + private void searchConnections(DBRProgressMonitor monitor, String hostName, String displayName) { + int checkTimeout = 150; Map portCache = new HashMap<>(); for (DBPDriver driver : availableDrivers) { monitor.subTask("Check '" + driver.getName() + "' on '" + hostName + "'"); if (!CommonUtils.isEmpty(driver.getDefaultPort())) { - updatePortInfo(portCache, hostName, driver, checkTimeout); + updatePortInfo(portCache, hostName, displayName, driver, checkTimeout); } monitor.worked(1); } for (AdminConnectionSearchInfo si : portCache.values()) { if (si.getDefaultDriver() != null) { - foundConnections.add(si); + synchronized (foundConnections) { + if (foundConnections.stream().noneMatch(fsi -> fsi.getDisplayName().equals(displayName) && fsi.getPort() == si.getPort())) { + foundConnections.add(si); + } + } } } } - private void updatePortInfo(Map portCache, String hostName, DBPDriver driver, int timeout) { + private void updatePortInfo(Map portCache, String hostName, String displayName, DBPDriver driver, int timeout) { int driverPort = CommonUtils.toInt(driver.getDefaultPort()); if (driverPort <= 0) { return; @@ -105,7 +140,7 @@ public class ConnectionSearcher implements DBRRunnableWithProgress { searchInfo.getPossibleDrivers().add(driverId); return; } - searchInfo = new AdminConnectionSearchInfo(hostName, driverPort); + searchInfo = new AdminConnectionSearchInfo(displayName, hostName, driverPort); portCache.put(driverPort, searchInfo); try { diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java index 0f2b54e36d..cd229569c8 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java @@ -41,7 +41,6 @@ import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings; import org.jkiss.utils.CommonUtils; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -250,11 +249,7 @@ public class WebServiceAdmin implements DBWServiceAdmin { @Override public List searchConnections(@NotNull WebSession webSession, @NotNull List hostNames) throws DBWebException { ConnectionSearcher searcher = new ConnectionSearcher(webSession, hostNames.toArray(new String[0])); - try { - searcher.run(webSession.getProgressMonitor()); - } catch (InvocationTargetException e) { - throw new DBWebException("Error searching connections", e.getTargetException()); - } + searcher.run(webSession.getProgressMonitor()); return searcher.getFoundConnections(); }