Mercurial > hg > monetdb-java
diff src/main/java/org/monetdb/mcl/net/Target.java @ 791:4de810c22328 monetdbs
Refactor
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Fri, 01 Dec 2023 14:18:01 +0100 (17 months ago) |
parents | 547eca89fc5e |
children | 9dea0795a926 |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/mcl/net/Target.java +++ b/src/main/java/org/monetdb/mcl/net/Target.java @@ -5,326 +5,570 @@ import java.util.Properties; import java.util.regex.Pattern; public class Target { + private boolean tls = false; + private String host = ""; + private int port = -1; + private String database = ""; + private String tableschema = ""; + private String table = ""; + private String sock = ""; + private String sockdir = "/tmp"; + private String cert = ""; + private String certhash = ""; + private String clientkey = ""; + private String clientcert = ""; + private String user = ""; + private String password = ""; + private String language = "sql"; + private boolean autocommit = true; + private String schema = ""; + private int timezone; + private String binary = "on"; + private int replysize = 200; + private String hash = ""; + private boolean debug = false; + private String logfile = ""; + + private boolean userWasSet = false; + private boolean passwordWasSet = false; + protected static final Target defaults = new Target(); + private static Pattern namePattern = Pattern.compile("^[a-zzA-Z_][-a-zA-Z0-9_.]*$"); private static Pattern hashPattern = Pattern.compile("^sha256:[0-9a-fA-F:]*$"); - private final boolean tls; - private final String host; - private final int port; - private final String database; - private final String tableschema; - private final String table; - private final String sock; - private final String sockdir; - private final String cert; - private final String certhash; - private final String clientkey; - private final String clientcert; - private final String user; - private final String password; - private final String language; - private final boolean autocommit; - private final String schema; - private final int timezone; - private final int binary; - private final int replysize; - private final String hash; - private final boolean debug; - private final String logfile; - public Target(Properties properties) throws ValidationError { - - // 1. The parameters have the types listed in the table in [Section - // Parameters](#parameters). - tls = validateBoolean(properties, Parameter.TLS); - host = validateString(properties, Parameter.HOST); - port = validateInt(properties, Parameter.PORT); - database = validateString(properties, Parameter.DATABASE); - tableschema = validateString(properties, Parameter.TABLESCHEMA); - table = validateString(properties, Parameter.TABLE); - sock = validateString(properties, Parameter.SOCK); - sockdir = validateString(properties, Parameter.SOCKDIR); - cert = validateString(properties, Parameter.CERT); - certhash = validateString(properties, Parameter.CERTHASH); - clientkey = validateString(properties, Parameter.CLIENTKEY); - clientcert = validateString(properties, Parameter.CLIENTCERT); - user = validateString(properties, Parameter.USER); - password = validateString(properties, Parameter.PASSWORD); - language = validateString(properties, Parameter.LANGUAGE); - autocommit = validateBoolean(properties, Parameter.AUTOCOMMIT); - schema = validateString(properties, Parameter.SCHEMA); - timezone = validateInt(properties, Parameter.TIMEZONE); - replysize = validateInt(properties, Parameter.REPLYSIZE); - hash = validateString(properties, Parameter.HASH); - debug = validateBoolean(properties, Parameter.DEBUG); - logfile = validateString(properties, Parameter.LOGFILE); - - for (String name: properties.stringPropertyNames()) { - if (Parameter.forName(name) != null) - continue; - if (name.contains("_")) - continue; - throw new ValidationError("unknown parameter: " + name); - } - - String binaryString = validateString(properties, Parameter.BINARY); - int binaryInt; - try { - binaryInt = (int) ParameterType.Int.parse(Parameter.BINARY.name, binaryString); - } catch (ValidationError e) { - try { - boolean b = (boolean) ParameterType.Bool.parse(Parameter.BINARY.name, binaryString); - binaryInt = b ? 65535 : 0; - } catch (ValidationError ee) { - throw new ValidationError("binary= must be either a number or true/yes/on/false/no/off"); - } - } - if (binaryInt < 0) - throw new ValidationError("binary= cannot be negative"); - binary = binaryInt; - - - // 2. At least one of **sock** and **host** must be empty. - if (!sock.isEmpty() && !host.isEmpty()) - throw new ValidationError("sock=" + sock + " cannot be combined with host=" + host); - - // 3. The string parameter **binary** must either parse as a boolean or as a - // non-negative integer. - // - // (checked above) - - // 4. If **sock** is not empty, **tls** must be 'off'. - if (!sock.isEmpty() && tls) throw new ValidationError("monetdbs:// cannot be combined with sock="); - - // 5. If **certhash** is not empty, it must be of the form `{sha256}hexdigits` - // where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons. - // TODO - if (!certhash.isEmpty()) { - if (!certhash.toLowerCase().startsWith("sha256:")) - throw new ValidationError("certificate hash must start with 'sha256:'"); - if (!hashPattern.matcher(certhash).matches()) - throw new ValidationError("invalid certificate hash"); - } - - // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well. - if (!tls) { - if (!cert.isEmpty() || !certhash.isEmpty()) - throw new ValidationError("cert= and certhash= are only allowed in combination with monetdbs://"); - } - - // 7. Parameters **database**, **tableschema** and **table** must consist only of - // upper- and lowercase letters, digits, periods, dashes and underscores. They must not - // start with a dash. - // If **table** is not empty, **tableschema** must also not be empty. - // If **tableschema** is not empty, **database** must also not be empty. - if (database.isEmpty() && !tableschema.isEmpty()) - throw new ValidationError("table schema cannot be set without database"); - if (tableschema.isEmpty() && !table.isEmpty()) - throw new ValidationError("table cannot be set without schema"); - if (!database.isEmpty() && !namePattern.matcher(database).matches()) - throw new ValidationError("invalid database name"); - if (!tableschema.isEmpty() && !namePattern.matcher(tableschema).matches()) - throw new ValidationError("invalid table schema name"); - if (!table.isEmpty() && !namePattern.matcher(table).matches()) - throw new ValidationError("invalid table name"); - - - // 8. Parameter **port** must be -1 or in the range 1-65535. - if (port < -1 || port == 0 || port > 65535) throw new ValidationError("invalid port number " + port); - - // 9. If **clientcert** is set, **clientkey** must also be set. - if (!clientcert.isEmpty() && clientkey.isEmpty()) - throw new ValidationError("clientcert= is only valid in combination with clientkey="); + public Target() { + this.timezone = (int)Parameter.TIMEZONE.getDefault(); } - public static boolean validateBoolean(Properties props, Parameter parm) throws ValidationError { - Object value = props.get(parm.name); - if (value != null) { - return (Boolean) parm.type.parse(parm.name, (String) value); - } else { - return (Boolean) getDefault(parm); + public void barrier() { + if (userWasSet && !passwordWasSet) + password = ""; + userWasSet = false; + passwordWasSet = false; + } + + public static String packHost(String host) { + switch (host) { + case "localhost": + return "localhost."; + case "": + return "localhost"; + default: + return host; } } - public static int validateInt(Properties props, Parameter parm) throws ValidationError { - Object value = props.get(parm.name); - if (value != null) { - return (Integer) parm.type.parse(parm.name, (String) value); - } else { - return (Integer) getDefault(parm); - } + public void setString(String key, String value) throws ValidationError { + Parameter parm = Parameter.forName(key); + if (parm != null) + setString(parm, value); + else if (!Parameter.isIgnored(key)) + throw new ValidationError(key, "unknown parameter"); + } + + public void setString(Parameter parm, String value) throws ValidationError { + if (value == null) + throw new NullPointerException("'value' must not be null"); + assign(parm, parm.type.parse(parm.name, value)); + } + + public void clear(Parameter parm) { + assign(parm, parm.getDefault()); } - public static String validateString(Properties props, Parameter parm) throws ValidationError { - Object value = props.get(parm.name); - if (value != null) { - return (String) parm.type.parse(parm.name, (String) value); - } else { - return (String) getDefault(parm); + private void assign(Parameter parm, Object value) { + switch (parm) { + case TLS: setTls((boolean)value); break; + case HOST: setHost((String)value); break; + case PORT: setPort((int)value); break; + case DATABASE: setDatabase((String)value); break; + case TABLESCHEMA: setTableschema((String)value); break; + case TABLE: setTable((String)value); break; + case SOCK: setSock((String)value); break; + case SOCKDIR: setSockdir((String)value); break; + case CERT: setCert((String)value); break; + case CERTHASH: setCerthash((String)value); break; + case CLIENTKEY: setClientkey((String)value); break; + case CLIENTCERT: setClientcert((String)value); break; + case USER: setUser((String)value); break; + case PASSWORD: setPassword((String)value); break; + case LANGUAGE: setLanguage((String)value); break; + case AUTOCOMMIT: setAutocommit((boolean)value); break; + case SCHEMA: setSchema((String)value); break; + case TIMEZONE: setTimezone((int)value); break; + case BINARY: setBinary((String)value); break; + case REPLYSIZE: setReplysize((int)value); break; + case FETCHSIZE: setReplysize((int)value); break; + case HASH: setHash((String)value); break; + case DEBUG: setDebug((boolean)value); break; + case LOGFILE: setLogfile((String)value); break; + default: + throw new IllegalStateException("unreachable -- missing case"); } } - private static int timezone() { - Calendar cal = Calendar.getInstance(); - int offsetMillis = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); - int offsetSeconds = offsetMillis / 1000; - return offsetSeconds; + public String getString(Parameter parm) { + Object value = getObject(parm); + return parm.type.format(value); } - public static Object getDefault(Parameter parm) { - if (parm == Parameter.TIMEZONE) return timezone(); - else return parm.defaultValue; + public Object getObject(Parameter parm) { + switch (parm) { + case TLS: return tls; + case HOST: return host; + case PORT: return port; + case DATABASE: return database; + case TABLESCHEMA: return tableschema; + case TABLE: return table; + case SOCK: return sock; + case SOCKDIR: return sockdir; + case CERT: return cert; + case CERTHASH: return certhash; + case CLIENTKEY: return clientkey; + case CLIENTCERT: return clientcert; + case USER: return user; + case PASSWORD: return password; + case LANGUAGE: return language; + case AUTOCOMMIT: return autocommit; + case SCHEMA: return schema; + case TIMEZONE: return timezone; + case BINARY: return binary; + case REPLYSIZE: return replysize; + case FETCHSIZE: return replysize; + case HASH: return hash; + case DEBUG: return debug; + case LOGFILE: return logfile; + default: + throw new IllegalStateException("unreachable -- missing case"); + } } - public static Properties defaultProperties() { - Properties props = new Properties(); - return props; + public static String unpackHost(String host) { + switch (host) { + case "localhost.": + return "localhost"; + case "localhost": + return ""; + default: + return host; + } } - public boolean getTls() { + public boolean isTls() { return tls; } - // Getter is private because you probably want connectTcp() instead - private String getHost() { + public void setTls(boolean tls) { + this.tls = tls; + } + + public String getHost() { return host; } - // Getter is private because you probably want connectPort() instead - private int getPort() { + public void setHost(String host) { + this.host = host; + } + + public int getPort() { return port; } + public void setPort(int port) { + this.port = port; + } + public String getDatabase() { return database; } + public void setDatabase(String database) { + this.database = database; + } + public String getTableschema() { return tableschema; } + public void setTableschema(String tableschema) { + this.tableschema = tableschema; + } + public String getTable() { return table; } - // Getter is private because you probably want connectUnix() instead - private String getSock() { + public void setTable(String table) { + this.table = table; + } + + public String getSock() { return sock; } + public void setSock(String sock) { + this.sock = sock; + } + public String getSockdir() { return sockdir; } + public void setSockdir(String sockdir) { + this.sockdir = sockdir; + } + public String getCert() { return cert; } + public void setCert(String cert) { + this.cert = cert; + } + public String getCerthash() { return certhash; } + public void setCerthash(String certhash) { + this.certhash = certhash; + } + public String getClientkey() { return clientkey; } + public void setClientkey(String clientkey) { + this.clientkey = clientkey; + } + public String getClientcert() { return clientcert; } + public void setClientcert(String clientcert) { + this.clientcert = clientcert; + } + public String getUser() { return user; } + public void setUser(String user) { + this.user = user; + this.userWasSet = true; + } + public String getPassword() { return password; } + public void setPassword(String password) { + this.password = password; + this.passwordWasSet = true; + } + public String getLanguage() { return language; } - public boolean getAutocommit() { + public void setLanguage(String language) { + this.language = language; + } + + public boolean isAutocommit() { return autocommit; } + public void setAutocommit(boolean autocommit) { + this.autocommit = autocommit; + } + public String getSchema() { return schema; } + public void setSchema(String schema) { + this.schema = schema; + } + public int getTimezone() { return timezone; } - // Getter is private because you probably want connectBinary() instead - public int getBinary() { + public void setTimezone(int timezone) { + this.timezone = timezone; + } + + public String getBinary() { return binary; } + public void setBinary(String binary) { + this.binary = binary; + } + public int getReplysize() { return replysize; } + public void setReplysize(int replysize) { + this.replysize = replysize; + } + public String getHash() { return hash; } - public boolean getDebug() { + public void setHash(String hash) { + this.hash = hash; + } + + public boolean isDebug() { return debug; } + public void setDebug(boolean debug) { + this.debug = debug; + } + public String getLogfile() { return logfile; } - public boolean connectScan() { - if (database.isEmpty()) return false; - if (!sock.isEmpty() || !host.isEmpty() || port != -1) return false; - return !tls; - } - - public int connectPort() { - return port == -1 ? 50000 : port; + public void setLogfile(String logfile) { + this.logfile = logfile; } - public String connectUnix() { - if (!sock.isEmpty()) return sock; - if (tls) return ""; - if (host.isEmpty()) return sockdir + "/.s.monetdb." + connectPort(); - return ""; - } - - public String connectTcp() { - if (!sock.isEmpty()) return ""; - if (host.isEmpty()) return "localhost"; - return host; + public Validated validate() throws ValidationError { + return new Validated(); } - public Verify connectVerify() { - if (!tls) return Verify.None; - if (!certhash.isEmpty()) return Verify.Hash; - if (!cert.isEmpty()) return Verify.Cert; - return Verify.System; - } + public class Validated { + + private final int nbinary; + + Validated() throws ValidationError { + + // 1. The parameters have the types listed in the table in [Section + // Parameters](#parameters). + + String binaryString = binary; + int binaryInt; + try { + binaryInt = (int) ParameterType.Int.parse(Parameter.BINARY.name, binaryString); + } catch (ValidationError e) { + try { + boolean b = (boolean) ParameterType.Bool.parse(Parameter.BINARY.name, binaryString); + binaryInt = b ? 65535 : 0; + } catch (ValidationError ee) { + throw new ValidationError("binary= must be either a number or true/yes/on/false/no/off"); + } + } + if (binaryInt < 0) + throw new ValidationError("binary= cannot be negative"); + nbinary = binaryInt; + + + // 2. At least one of **sock** and **host** must be empty. + if (!sock.isEmpty() && !host.isEmpty()) + throw new ValidationError("sock=" + sock + " cannot be combined with host=" + host); + + // 3. The string parameter **binary** must either parse as a boolean or as a + // non-negative integer. + // + // (checked above) + + // 4. If **sock** is not empty, **tls** must be 'off'. + if (!sock.isEmpty() && tls) throw new ValidationError("monetdbs:// cannot be combined with sock="); + + // 5. If **certhash** is not empty, it must be of the form `{sha256}hexdigits` + // where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons. + // TODO + if (!certhash.isEmpty()) { + if (!certhash.toLowerCase().startsWith("sha256:")) + throw new ValidationError("certificate hash must start with 'sha256:'"); + if (!hashPattern.matcher(certhash).matches()) + throw new ValidationError("invalid certificate hash"); + } + + // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well. + if (!tls) { + if (!cert.isEmpty() || !certhash.isEmpty()) + throw new ValidationError("cert= and certhash= are only allowed in combination with monetdbs://"); + } - public String connectCertHashDigits() { - if (!tls) return null; - StringBuilder builder = new StringBuilder(certhash.length()); - for (int i = "sha256:".length(); i < certhash.length(); i++) { - char c = certhash.charAt(i); - if (Character.digit(c, 16) >= 0) builder.append(Character.toLowerCase(c)); + // 7. Parameters **database**, **tableschema** and **table** must consist only of + // upper- and lowercase letters, digits, periods, dashes and underscores. They must not + // start with a dash. + // If **table** is not empty, **tableschema** must also not be empty. + // If **tableschema** is not empty, **database** must also not be empty. + if (database.isEmpty() && !tableschema.isEmpty()) + throw new ValidationError("table schema cannot be set without database"); + if (tableschema.isEmpty() && !table.isEmpty()) + throw new ValidationError("table cannot be set without schema"); + if (!database.isEmpty() && !namePattern.matcher(database).matches()) + throw new ValidationError("invalid database name"); + if (!tableschema.isEmpty() && !namePattern.matcher(tableschema).matches()) + throw new ValidationError("invalid table schema name"); + if (!table.isEmpty() && !namePattern.matcher(table).matches()) + throw new ValidationError("invalid table name"); + + + // 8. Parameter **port** must be -1 or in the range 1-65535. + if (port < -1 || port == 0 || port > 65535) throw new ValidationError("invalid port number " + port); + + // 9. If **clientcert** is set, **clientkey** must also be set. + if (!clientcert.isEmpty() && clientkey.isEmpty()) + throw new ValidationError("clientcert= is only valid in combination with clientkey="); + } + + public boolean getTls() { + return tls; + } + + // Getter is private because you probably want connectTcp() instead + private String getHost() { + return host; + } + + // Getter is private because you probably want connectPort() instead + private int getPort() { + return port; + } + + public String getDatabase() { + return database; + } + + public String getTableschema() { + return tableschema; + } + + public String getTable() { + return table; + } + + // Getter is private because you probably want connectUnix() instead + private String getSock() { + return sock; + } + + public String getSockdir() { + return sockdir; } - return builder.toString(); - } + + public String getCert() { + return cert; + } + + public String getCerthash() { + return certhash; + } + + public String getClientkey() { + return clientkey; + } + + public String getClientcert() { + return clientcert; + } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } - public int connectBinary() { - return binary; - } + public String getLanguage() { + return language; + } + + public boolean getAutocommit() { + return autocommit; + } + + public String getSchema() { + return schema; + } + + public int getTimezone() { + return timezone; + } + + // Getter is private because you probably want connectBinary() instead + public int getBinary() { + return nbinary; + } + + public int getReplysize() { + return replysize; + } + + public String getHash() { + return hash; + } - public String connectClientKey() { - return clientkey; - } + public boolean getDebug() { + return debug; + } + + public String getLogfile() { + return logfile; + } + + public boolean connectScan() { + if (database.isEmpty()) return false; + if (!sock.isEmpty() || !host.isEmpty() || port != -1) return false; + return !tls; + } + + public int connectPort() { + return port == -1 ? 50000 : port; + } + + public String connectUnix() { + if (!sock.isEmpty()) return sock; + if (tls) return ""; + if (host.isEmpty()) return sockdir + "/.s.monetdb." + connectPort(); + return ""; + } - public String connectClientCert() { - return clientcert.isEmpty() ? clientkey : clientcert; + public String connectTcp() { + if (!sock.isEmpty()) return ""; + if (host.isEmpty()) return "localhost"; + return host; + } + + public Verify connectVerify() { + if (!tls) return Verify.None; + if (!certhash.isEmpty()) return Verify.Hash; + if (!cert.isEmpty()) return Verify.Cert; + return Verify.System; + } + + public String connectCertHashDigits() { + if (!tls) return null; + StringBuilder builder = new StringBuilder(certhash.length()); + for (int i = "sha256:".length(); i < certhash.length(); i++) { + char c = certhash.charAt(i); + if (Character.digit(c, 16) >= 0) builder.append(Character.toLowerCase(c)); + } + return builder.toString(); + } + + public int connectBinary() { + return nbinary; + } + + public String connectClientKey() { + return clientkey; + } + + public String connectClientCert() { + return clientcert.isEmpty() ? clientkey : clientcert; + } } }