changeset 903:778959b2e0a4

Send ClientInfo on startup Configurable through the client_info=bool, client_application=NAME, client_remark=MESSAGE properties.
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Fri, 14 Jun 2024 15:57:49 +0200 (9 months ago)
parents 65d42db0c831
children 2d880f90be2a
files src/main/java/org/monetdb/jdbc/MonetConnection.java src/main/java/org/monetdb/mcl/net/ClientInfo.java src/main/java/org/monetdb/mcl/net/MapiSocket.java src/main/java/org/monetdb/mcl/net/Parameter.java src/main/java/org/monetdb/mcl/net/Target.java tests/javaspecific.md
diffstat 6 files changed, 170 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java
@@ -224,6 +224,11 @@ public class MonetConnection
 			throw sqle;
 		}
 
+		// send any clientinfo
+		if (server.hasClientInfo()) {
+			sendControlCommand("clientinfo " + server.getClientInfo().format());
+		}
+
 		// Now take care of any options not handled during the handshake
 		curReplySize = defaultFetchSize;
 		if (lang == LANG_SQL) {
new file mode 100644
--- /dev/null
+++ b/src/main/java/org/monetdb/mcl/net/ClientInfo.java
@@ -0,0 +1,74 @@
+package org.monetdb.mcl.net;
+
+import org.monetdb.jdbc.MonetDriver;
+
+import java.sql.ClientInfoStatus;
+import java.sql.SQLClientInfoException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+
+public class ClientInfo {
+	private static final String defaultHostname = findHostname();
+
+	private static final String defaultClientLibrary = findClientLibrary();
+
+	private static final int defaultPid = findPid();
+
+	private final Properties props;
+
+	public ClientInfo() {
+		props = new Properties();
+		props.setProperty("ClientHostname", defaultHostname);
+		props.setProperty("ClientLibrary", defaultClientLibrary);
+		props.setProperty("ClientPid", "" + defaultPid);
+		props.setProperty("ApplicationName", "");
+		props.setProperty("ClientRemark", "");
+	}
+
+	private static String findHostname() {
+		return "my host";
+	}
+
+	private static int findPid() {
+		return 42;
+	}
+
+	private static String findClientLibrary() {
+		return "monetdb-java " + MonetDriver.getDriverVersion();
+	}
+
+	public String format() {
+		StringBuilder builder = new StringBuilder(200);
+		for (String name : props.stringPropertyNames()) {
+			String value = props.getProperty(name);
+			builder.append(name);
+			builder.append('=');
+			builder.append(value);
+			builder.append('\n');
+		}
+		return builder.toString();
+	}
+
+	public Properties get() {
+		Properties ret = new Properties();
+		ret.putAll(props);
+		return ret;
+	}
+
+	public boolean set(String name, String value) throws SQLClientInfoException {
+		if (value == null)
+			value = "";
+		if (value.contains("\n")) {
+			Map<String, ClientInfoStatus> map = Collections.singletonMap(name, ClientInfoStatus.REASON_VALUE_INVALID);
+			throw new SQLClientInfoException(map);
+		}
+		if (props.containsKey(name)) {
+			props.setProperty(name, value);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+}
--- a/src/main/java/org/monetdb/mcl/net/MapiSocket.java
+++ b/src/main/java/org/monetdb/mcl/net/MapiSocket.java
@@ -25,6 +25,7 @@ import java.net.*;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.sql.SQLClientInfoException;
 import java.util.*;
 
 import javax.net.ssl.SSLException;
@@ -117,6 +118,7 @@ public final class MapiSocket {
 	private BufferedMCLWriter writer;
 	/** protocol version of the connection */
 	private int version;
+	private ClientInfo clientInfo;
 
 	/** Whether we should follow redirects.
 	 * Not sure why this needs to be separate
@@ -497,6 +499,17 @@ public final class MapiSocket {
 		String optionsPart = parts.length > 6 ? parts[6] : null;
 //		String binaryPart = parts.length > 7 ? parts[7] : null;
 
+		if (parts.length > 9 && target.isClientInfo()) {
+			clientInfo = new ClientInfo();
+			try {
+				clientInfo.set("ApplicationName", target.getClientApplication());
+				clientInfo.set("ClientRemark", target.getClientRemark());
+			} catch (SQLClientInfoException e) {
+				String keys = String.join(", ", e.getFailedProperties().keySet());
+				throw new MCLException("Could not set ClientInfo properties: " + keys, e);
+			}
+		}
+
 		String userResponse;
 		String password = target.getPassword();
 		if (serverTypePart.equals("merovingian") && !target.getLanguage().equals("control")) {
@@ -771,6 +784,15 @@ public final class MapiSocket {
 		return target.isDebug();
 	}
 
+	public boolean hasClientInfo() {
+		return clientInfo != null;
+	}
+
+	public ClientInfo getClientInfo() {
+		if (clientInfo == null)
+			clientInfo = new ClientInfo();
+		return clientInfo;
+	}
 
 	/**
 	 * Inner class that is used to write data on a normal stream as a
--- a/src/main/java/org/monetdb/mcl/net/Parameter.java
+++ b/src/main/java/org/monetdb/mcl/net/Parameter.java
@@ -45,6 +45,10 @@ public enum Parameter {
 	SO_TIMEOUT("so_timeout", ParameterType.Int, 0, "abort if network I/O does not complete in this many milliseconds, 0 means no timeout", false),
 	CLOB_AS_VARCHAR("treat_clob_as_varchar", ParameterType.Bool, true, "return CLOB/TEXT data as type VARCHAR instead of type CLOB", false),
 	BLOB_AS_BINARY("treat_blob_as_binary", ParameterType.Bool, true, "return BLOB data as type BINARY instead of type BLOB", false),
+
+	CLIENT_INFO("client_info", ParameterType.Bool, true, "whether to send ClientInfo when connecting", false),
+	CLIENT_APPLICATION("client_application", ParameterType.Str, "", "application name to send in ClientInfo", false),
+	CLIENT_REMARK("client_remark", ParameterType.Str, "", "application name to send in ClientInfo", false),
 	;
 
 	public final String name;
@@ -117,6 +121,12 @@ public enum Parameter {
 				return CLOB_AS_VARCHAR;
 			case "treat_blob_as_binary":
 				return BLOB_AS_BINARY;
+			case "client_info":
+				return CLIENT_INFO;
+			case "client_application":
+				return CLIENT_APPLICATION;
+			case "client_remark":
+				return CLIENT_REMARK;
 			default:
 				return null;
 		}
--- a/src/main/java/org/monetdb/mcl/net/Target.java
+++ b/src/main/java/org/monetdb/mcl/net/Target.java
@@ -46,6 +46,9 @@ public class Target {
 	private int soTimeout = 0;
 	private boolean treatClobAsVarchar = true;
 	private boolean treatBlobAsBinary = true;
+	private boolean clientInfo = true;
+	private String clientApplication = "";
+	private String clientRemark = "";
 	private boolean userWasSet = false;
 	private boolean passwordWasSet = false;
 	private Validated validated = null;
@@ -202,6 +205,15 @@ public class Target {
 			case BLOB_AS_BINARY:
 				setTreatBlobAsBinary((boolean) value);
 				break;
+			case CLIENT_INFO:
+				setClientInfo((boolean) value);
+				break;
+			case CLIENT_APPLICATION:
+				setClientApplication((String) value);
+				break;
+			case CLIENT_REMARK:
+				setClientRemark((String) value);
+				break;
 
 			default:
 				throw new IllegalStateException("unreachable -- missing case: " + parm.name);
@@ -269,6 +281,12 @@ public class Target {
 				return treatClobAsVarchar;
 			case BLOB_AS_BINARY:
 				return treatBlobAsBinary;
+			case CLIENT_INFO:
+				return clientInfo;
+			case CLIENT_APPLICATION:
+				return clientApplication;
+			case CLIENT_REMARK:
+				return clientRemark;
 			default:
 				throw new IllegalStateException("unreachable -- missing case");
 		}
@@ -510,6 +528,30 @@ public class Target {
 		validated = null;
 	}
 
+	public boolean isClientInfo() {
+		return clientInfo;
+	}
+
+	public void setClientInfo(boolean clientInfo) {
+		this.clientInfo = clientInfo;
+	}
+
+	public String getClientApplication() {
+		return clientApplication;
+	}
+
+	public void setClientApplication(String clientApplication) {
+		this.clientApplication = clientApplication;
+	}
+
+	public String getClientRemark() {
+		return clientRemark;
+	}
+
+	public void setClientRemark(String clientRemark) {
+		this.clientRemark = clientRemark;
+	}
+
 	public Validated validate() throws ValidationError {
 		if (validated == null)
 			validated = new Validated();
--- a/tests/javaspecific.md
+++ b/tests/javaspecific.md
@@ -28,3 +28,20 @@ EXPECT treat_blob_as_binary=false
 ACCEPT monetdb://?treat_blob_as_binary=yes
 EXPECT treat_blob_as_binary=on
 ```
+
+```test
+ONLY jdbc
+EXPECT client_info=true
+EXPECT client_application=
+EXPECT client_remark=
+```
+
+```test
+ONLY jdbc
+SET client_info=false
+SET client_application=myapp
+SET client_remark=a remark
+EXPECT client_info=false
+EXPECT client_application=myapp
+EXPECT client_remark=a remark
+```