diff --git a/core/src/main/java/org/apache/hop/core/Const.java b/core/src/main/java/org/apache/hop/core/Const.java index 80809b9e72..b4b5dc52a5 100644 --- a/core/src/main/java/org/apache/hop/core/Const.java +++ b/core/src/main/java/org/apache/hop/core/Const.java @@ -401,6 +401,30 @@ public String getMessage() { public static final String INTERNAL_VARIABLE_ACTION_ID = INTERNAL_VARIABLE_PREFIX + ".Action.ID"; + /** The Hop server name as configured in hop-server.xml */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_NAME = + INTERNAL_VARIABLE_PREFIX + ".Server.Name"; + + /** The Hop server hostname */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_HOSTNAME = + INTERNAL_VARIABLE_PREFIX + ".Server.Hostname"; + + /** The Hop server HTTP port */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_PORT = + INTERNAL_VARIABLE_PREFIX + ".Server.Port"; + + /** The Hop server web application name */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_WEB_APP_NAME = + INTERNAL_VARIABLE_PREFIX + ".Server.WebAppName"; + + /** The Hop server username */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_USERNAME = + INTERNAL_VARIABLE_PREFIX + ".Server.Username"; + + /** Whether the Hop server is running in SSL mode */ + public static final String INTERNAL_VARIABLE_HOP_SERVER_SSL_MODE = + INTERNAL_VARIABLE_PREFIX + ".Server.SslMode"; + /** The default maximum for the nr of lines in the GUI logs */ public static final int MAX_NR_LOG_LINES = 5000; diff --git a/docs/hop-user-manual/modules/ROOT/pages/variables.adoc b/docs/hop-user-manual/modules/ROOT/pages/variables.adoc index fa2dbdff78..fd767e1096 100644 --- a/docs/hop-user-manual/modules/ROOT/pages/variables.adoc +++ b/docs/hop-user-manual/modules/ROOT/pages/variables.adoc @@ -364,3 +364,18 @@ Additionally, the following environment variables can help you to add even more |${Internal.Transform.BundleNr} |N |The bundle number for partitioned execution, helpful in load-balancing or distributing data across partitions. |${Internal.Action.ID} |N |The unique ID of the current action (entry) in a workflow. Useful for tracking specific actions within a larger workflow. |=== + +=== Hop server internal variables + +The variables below are only set when a pipeline or workflow is executed on a Hop server. They are populated from the `hop-server.xml` configuration file at server startup and inherited by every pipeline and workflow that runs on that server, so you can reference the server's own coordinates from within your pipelines and workflows (for example to build callback URLs or to log the server that processed a request). + +[%header, width="90%", cols="2,1,5"] +|=== +|Variable |Default |Description +|${Internal.Server.Name} |N |The name of the Hop server as configured in `hop-server.xml` (the `` element of the `hop-server` definition). +|${Internal.Server.Hostname} |N |The hostname the Hop server is bound to, as configured in `hop-server.xml`. If a `network_interface` was configured, this is the resolved IP address for that interface. +|${Internal.Server.Port} |N |The HTTP port the Hop server is listening on. This is the actual port the server bound to, which may differ from the value in `hop-server.xml` when the server was started with command-line overrides. +|${Internal.Server.WebAppName} |N |The web application name (context path) the Hop server is exposed under, as configured in `hop-server.xml`. Empty when no web app name was set. +|${Internal.Server.Username} |N |The username configured for the Hop server in `hop-server.xml`. Empty when authentication is not configured. +|${Internal.Server.SslMode} |N |`true` when the Hop server is configured to use SSL/TLS, `false` otherwise. +|=== diff --git a/engine/src/main/java/org/apache/hop/www/HopServer.java b/engine/src/main/java/org/apache/hop/www/HopServer.java index f961cadeee..3dab45388d 100644 --- a/engine/src/main/java/org/apache/hop/www/HopServer.java +++ b/engine/src/main/java/org/apache/hop/www/HopServer.java @@ -206,6 +206,11 @@ public void runHopServer() throws Exception { } if (allOK) { + // Expose the hop-server.xml details as Internal.Server.* variables so they + // are inherited by pipelines and workflows executing on this server. + config.setInternalHopServerVariables(config.getVariables(), port); + config.setInternalHopServerVariables(variables, port); + boolean shouldJoin = config.isJoining(); if (joinOverride != null) { shouldJoin = joinOverride; diff --git a/engine/src/main/java/org/apache/hop/www/HopServerConfig.java b/engine/src/main/java/org/apache/hop/www/HopServerConfig.java index 6b47f5b164..cd40132503 100644 --- a/engine/src/main/java/org/apache/hop/www/HopServerConfig.java +++ b/engine/src/main/java/org/apache/hop/www/HopServerConfig.java @@ -212,6 +212,30 @@ public HopServerConfig(String hostname, int port, int shutdownPort, boolean join hostname + ":" + port, hostname, "" + port, "" + shutdownPort, null, null); } + /** + * Expose the configured Hop server details (hop-server.xml) as Internal.Server.* variables on the + * given variable space, so pipelines and workflows running on this server can reference them. + * + * @param variables the variable space to populate + * @param resolvedPort the actual HTTP port the server is bound to + */ + public void setInternalHopServerVariables(IVariables variables, int resolvedPort) { + if (variables == null || hopServer == null) { + return; + } + variables.setVariable( + Const.INTERNAL_VARIABLE_HOP_SERVER_NAME, Const.NVL(hopServer.getName(), "")); + variables.setVariable( + Const.INTERNAL_VARIABLE_HOP_SERVER_HOSTNAME, Const.NVL(hopServer.getHostname(), "")); + variables.setVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_PORT, Integer.toString(resolvedPort)); + variables.setVariable( + Const.INTERNAL_VARIABLE_HOP_SERVER_WEB_APP_NAME, Const.NVL(hopServer.getWebAppName(), "")); + variables.setVariable( + Const.INTERNAL_VARIABLE_HOP_SERVER_USERNAME, Const.NVL(hopServer.getUsername(), "")); + variables.setVariable( + Const.INTERNAL_VARIABLE_HOP_SERVER_SSL_MODE, Boolean.toString(hopServer.isSslMode())); + } + /** * @return the hop server.
* The user name and password defined in here are used to contact this server by the masters. diff --git a/engine/src/test/java/org/apache/hop/www/HopServerConfigTest.java b/engine/src/test/java/org/apache/hop/www/HopServerConfigTest.java index 188a4b0154..5daee773c9 100644 --- a/engine/src/test/java/org/apache/hop/www/HopServerConfigTest.java +++ b/engine/src/test/java/org/apache/hop/www/HopServerConfigTest.java @@ -27,7 +27,10 @@ import java.util.Map.Entry; import org.apache.hop.core.Const; import org.apache.hop.core.exception.HopXmlException; +import org.apache.hop.core.variables.IVariables; +import org.apache.hop.core.variables.Variables; import org.apache.hop.core.xml.XmlHandler; +import org.apache.hop.server.HopServerMeta; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -217,6 +220,74 @@ void testParseJettyOption_NoOptionsNode() throws HopXmlException { assertNull(parseJettyOptions); } + @Test + void testSetInternalHopServerVariables_populatesAllVariables() { + HopServerMeta meta = + new HopServerMeta("my-server", "host.example.com", "8181", "8180", "admin", "secret"); + meta.setWebAppName("hop"); + meta.setSslMode(true); + slServerConfig.setHopServer(meta); + + IVariables variables = new Variables(); + slServerConfig.setInternalHopServerVariables(variables, 8181); + + assertEquals("my-server", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_NAME)); + assertEquals( + "host.example.com", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_HOSTNAME)); + assertEquals("8181", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_PORT)); + assertEquals("hop", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_WEB_APP_NAME)); + assertEquals("admin", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_USERNAME)); + assertEquals("true", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_SSL_MODE)); + } + + @Test + void testSetInternalHopServerVariables_resolvedPortOverridesConfigured() { + HopServerMeta meta = new HopServerMeta("local8080", "localhost", "8080", "8079", null, null); + slServerConfig.setHopServer(meta); + + IVariables variables = new Variables(); + slServerConfig.setInternalHopServerVariables(variables, 9090); + + assertEquals("9090", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_PORT)); + } + + @Test + void testSetInternalHopServerVariables_handlesEmptyOptionalFields() { + HopServerMeta meta = new HopServerMeta(); + meta.setName("minimal"); + meta.setHostname("localhost"); + slServerConfig.setHopServer(meta); + + IVariables variables = new Variables(); + slServerConfig.setInternalHopServerVariables(variables, 8080); + + assertEquals("minimal", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_NAME)); + assertEquals("localhost", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_HOSTNAME)); + assertEquals("", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_WEB_APP_NAME)); + assertEquals("", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_USERNAME)); + assertEquals("false", variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_SSL_MODE)); + } + + @Test + void testSetInternalHopServerVariables_nullServerIsNoop() { + // No HopServerMeta on the config — should not throw and should not set any variable. + IVariables variables = new Variables(); + slServerConfig.setInternalHopServerVariables(variables, 8080); + + assertNull(variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_NAME)); + assertNull(variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_HOSTNAME)); + assertNull(variables.getVariable(Const.INTERNAL_VARIABLE_HOP_SERVER_PORT)); + } + + @Test + void testSetInternalHopServerVariables_nullVariablesIsNoop() { + HopServerMeta meta = new HopServerMeta("server", "host", "8181", "8180", null, null); + slServerConfig.setHopServer(meta); + + // Should not throw on null variables. + slServerConfig.setInternalHopServerVariables(null, 8181); + } + private Node getConfigNode(String configString) throws HopXmlException { Document document = XmlHandler.loadXmlString(configString); return XmlHandler.getSubNode(document, HopServerConfig.XML_TAG);