diff --git a/VERSION.md b/VERSION.md index 1604aca..8d5ec0d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -2,7 +2,7 @@ ------------------ * feat: Support the graph array * fix: Cannot convert jsonb string to number. (AG-120) -* Upgrade PostgreSQL JDBC Driver to 42.2.27.jre7 +* Upgrade PostgreSQL JDBC Driver to 42.5.1(JDK8+), 42.2.27.jre7(JDK7) 1.4.2 / 2018-01-11 ------------------ diff --git a/build.gradle.kts b/build.gradle.kts index 2631c84..ca36b52 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,7 @@ repositories { } dependencies { - implementation("org.postgresql:postgresql:42.2.27.jre7") + implementation("org.postgresql:postgresql:42.5.1") implementation("com.googlecode.json-simple:json-simple:1.1.1") testImplementation("junit:junit:4.13.2") @@ -29,9 +29,9 @@ dependencies { } group = "net.bitnine" -version = "1.4.3.42_2_27.jre7" +version = "1.4.3" description = "Agensgraph JDBC" -java.sourceCompatibility = JavaVersion.VERSION_1_7 +java.sourceCompatibility = JavaVersion.VERSION_1_8 val githubUrl = "github.com/bitnine-oss/agensgraph-jdbc" java { diff --git a/src/main/java/net/bitnine/agensgraph/Driver.java b/src/main/java/net/bitnine/agensgraph/Driver.java index d92c387..69cf5fd 100644 --- a/src/main/java/net/bitnine/agensgraph/Driver.java +++ b/src/main/java/net/bitnine/agensgraph/Driver.java @@ -22,13 +22,15 @@ import net.bitnine.agensgraph.jdbc.AgConnection; import org.postgresql.PGProperty; -import org.postgresql.util.DriverInfo; +import org.postgresql.jdbcurlresolver.PgPassParser; +import org.postgresql.jdbcurlresolver.PgServiceConfParser; import org.postgresql.util.*; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; -import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.sql.*; @@ -37,7 +39,8 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.logging.*; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.postgresql.util.internal.Nullness.castNonNull; @@ -55,697 +58,731 @@ * DriverManager. This means that a user can load and register a driver by doing * Class.forName("foo.bah.Driver")

* - * @see org.postgresql.PGConnection + * @see net.bitnine.agensgraph.jdbc.AgConnection * @see java.sql.Driver */ public class Driver implements java.sql.Driver { - private static Driver registeredDriver; - private static final Logger PARENT_LOGGER = Logger.getLogger("org.postgresql"); - private static final Logger LOGGER = Logger.getLogger("org.postgresql.Driver"); - private static final SharedTimer SHARED_TIMER = new SharedTimer(); - private static final String DEFAULT_PORT = "5432"; - - static { - try { - // moved the registerDriver from the constructor to here - // because some clients call the driver themselves (I know, as - // my early jdbc work did - and that was based on other examples). - // Placing it here, means that the driver is registered once only. - register(); - } catch (SQLException e) { - throw new ExceptionInInitializerError(e); + private static Driver registeredDriver; + private static final Logger PARENT_LOGGER = Logger.getLogger("org.postgresql"); + private static final Logger LOGGER = Logger.getLogger("org.postgresql.Driver"); + private static final SharedTimer SHARED_TIMER = new SharedTimer(); + + static { + try { + // moved the registerDriver from the constructor to here + // because some clients call the driver themselves (I know, as + // my early jdbc work did - and that was based on other examples). + // Placing it here, means that the driver is registered once only. + register(); + } catch (SQLException e) { + throw new ExceptionInInitializerError(e); + } } - } - // Helper to retrieve default properties from classloader resource - // properties files. - private Properties defaultProperties; + // Helper to retrieve default properties from classloader resource + // properties files. + private Properties defaultProperties; - private synchronized Properties getDefaultProperties() throws IOException { - if (defaultProperties != null) { - return defaultProperties; - } + private synchronized Properties getDefaultProperties() throws IOException { + if (defaultProperties != null) { + return defaultProperties; + } - // Make sure we load properties with the maximum possible privileges. - try { - defaultProperties = - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Properties run() throws IOException { - return loadDefaultProperties(); + // Make sure we load properties with the maximum possible privileges. + try { + defaultProperties = + doPrivileged(new PrivilegedExceptionAction() { + public Properties run() throws IOException { + return loadDefaultProperties(); + } + }); + } catch (PrivilegedActionException e) { + Exception ex = e.getException(); + if (ex instanceof IOException) { + throw (IOException) ex; + } + throw new RuntimeException(e); + } catch (Throwable e) { + if (e instanceof IOException) { + throw (IOException) e; + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; } - }); - } catch (PrivilegedActionException e) { - throw (IOException) e.getException(); + if (e instanceof Error) { + throw (Error) e; + } + throw new RuntimeException(e); + } + + return defaultProperties; } - return defaultProperties; - } + private static T doPrivileged(PrivilegedExceptionAction action) throws Throwable { + try { + Class accessControllerClass = Class.forName("java.security.AccessController"); + Method doPrivileged = accessControllerClass.getMethod("doPrivileged", + PrivilegedExceptionAction.class); + //noinspection unchecked + return (T) doPrivileged.invoke(null, action); + } catch (ClassNotFoundException e) { + return action.run(); + } catch (InvocationTargetException e) { + throw castNonNull(e.getCause()); + } + } - private Properties loadDefaultProperties() throws IOException { - Properties merged = new Properties(); + private Properties loadDefaultProperties() throws IOException { + Properties merged = new Properties(); - try { - PGProperty.USER.set(merged, System.getProperty("user.name")); - } catch (SecurityException se) { - // We're just trying to set a default, so if we can't - // it's not a big deal. - } + try { + PGProperty.USER.set(merged, System.getProperty("user.name")); + } catch (SecurityException se) { + // We're just trying to set a default, so if we can't + // it's not a big deal. + } - // If we are loaded by the bootstrap classloader, getClassLoader() - // may return null. In that case, try to fall back to the system - // classloader. - // - // We should not need to catch SecurityException here as we are - // accessing either our own classloader, or the system classloader - // when our classloader is null. The ClassLoader javadoc claims - // neither case can throw SecurityException. - ClassLoader cl = getClass().getClassLoader(); - if (cl == null) { - LOGGER.log(Level.FINE, "Can't find our classloader for the Driver; " - + "attempt to use the system class loader"); - cl = ClassLoader.getSystemClassLoader(); - } + // If we are loaded by the bootstrap classloader, getClassLoader() + // may return null. In that case, try to fall back to the system + // classloader. + // + // We should not need to catch SecurityException here as we are + // accessing either our own classloader, or the system classloader + // when our classloader is null. The ClassLoader javadoc claims + // neither case can throw SecurityException. + ClassLoader cl = getClass().getClassLoader(); + if (cl == null) { + LOGGER.log(Level.FINE, "Can't find our classloader for the Driver; " + + "attempt to use the system class loader"); + cl = ClassLoader.getSystemClassLoader(); + } - if (cl == null) { - LOGGER.log(Level.WARNING, "Can't find a classloader for the Driver; not loading driver " - + "configuration from org/postgresql/driverconfig.properties"); - return merged; // Give up on finding defaults. - } + if (cl == null) { + LOGGER.log(Level.WARNING, "Can't find a classloader for the Driver; not loading driver " + + "configuration from org/postgresql/driverconfig.properties"); + return merged; // Give up on finding defaults. + } - LOGGER.log(Level.FINE, "Loading driver configuration via classloader {0}", cl); + LOGGER.log(Level.FINE, "Loading driver configuration via classloader {0}", cl); - // When loading the driver config files we don't want settings found - // in later files in the classpath to override settings specified in - // earlier files. To do this we've got to read the returned - // Enumeration into temporary storage. - ArrayList urls = new ArrayList<>(); - Enumeration urlEnum = cl.getResources("org/postgresql/driverconfig.properties"); - while (urlEnum.hasMoreElements()) { - urls.add(urlEnum.nextElement()); - } + // When loading the driver config files we don't want settings found + // in later files in the classpath to override settings specified in + // earlier files. To do this we've got to read the returned + // Enumeration into temporary storage. + ArrayList urls = new ArrayList(); + Enumeration urlEnum = cl.getResources("org/postgresql/driverconfig.properties"); + while (urlEnum.hasMoreElements()) { + urls.add(urlEnum.nextElement()); + } - for (int i = urls.size() - 1; i >= 0; i--) { - URL url = urls.get(i); - LOGGER.log(Level.FINE, "Loading driver configuration from: {0}", url); - InputStream is = url.openStream(); - merged.load(is); - is.close(); - } + for (int i = urls.size() - 1; i >= 0; i--) { + URL url = urls.get(i); + LOGGER.log(Level.FINE, "Loading driver configuration from: {0}", url); + InputStream is = url.openStream(); + merged.load(is); + is.close(); + } - return merged; - } - - /** - *

Try to make a database connection to the given URL. The driver should return "null" if it - * realizes it is the wrong kind of driver to connect to the given URL. This will be common, as - * when the JDBC driverManager is asked to connect to a given URL, it passes the URL to each - * loaded driver in turn.

- * - *

The driver should raise an SQLException if it is the right driver to connect to the given URL, - * but has trouble connecting to the database.

- * - *

The java.util.Properties argument can be used to pass arbitrary string tag/value pairs as - * connection arguments.

- * - *
    - *
  • user - (required) The user to connect as
  • - *
  • password - (optional) The password for the user
  • - *
  • ssl -(optional) Use SSL when connecting to the server
  • - *
  • readOnly - (optional) Set connection to read-only by default
  • - *
  • charSet - (optional) The character set to be used for converting to/from - * the database to unicode. If multibyte is enabled on the server then the character set of the - * database is used as the default, otherwise the jvm character encoding is used as the default. - * This value is only used when connecting to a 7.2 or older server.
  • - *
  • loglevel - (optional) Enable logging of messages from the driver. The value is an integer - * from 0 to 2 where: OFF = 0, INFO =1, DEBUG = 2 The output is sent to - * DriverManager.getPrintWriter() if set, otherwise it is sent to System.out.
  • - *
  • compatible - (optional) This is used to toggle between different functionality - * as it changes across different releases of the jdbc driver code. The values here are versions - * of the jdbc client and not server versions. For example in 7.1 get/setBytes worked on - * LargeObject values, in 7.2 these methods were changed to work on bytea values. This change in - * functionality could be disabled by setting the compatible level to be "7.1", in which case the - * driver will revert to the 7.1 functionality.
  • - *
- * - *

Normally, at least "user" and "password" properties should be included in the properties. For a - * list of supported character encoding , see - * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html Note that you will - * probably want to have set up the Postgres database itself to use the same encoding, with the - * {@code -E } argument to createdb.

- * - *

Our protocol takes the forms:

- * - *
-   *  jdbc:agensgraph://host:port/database?param1=val1&...
-   * 
- * - * @param url the URL of the database to connect to - * @param info a list of arbitrary tag/value pairs as connection arguments - * @return a connection to the URL or null if it isnt us - * @exception SQLException if a database access error occurs or the url is - * {@code null} - * @see java.sql.Driver#connect - */ - @Override - public Connection connect(String url, Properties info) throws SQLException { - if (url == null) { - throw new SQLException("url is null"); + return merged; } - // get defaults - Properties defaults; - if (!url.startsWith("jdbc:agensgraph:")) { - return null; - } - try { - defaults = getDefaultProperties(); - } catch (IOException ioe) { - throw new PSQLException(GT.tr("Error loading default settings from driverconfig.properties"), - PSQLState.UNEXPECTED_ERROR, ioe); - } + /** + *

Try to make a database connection to the given URL. The driver should return "null" if it + * realizes it is the wrong kind of driver to connect to the given URL. This will be common, as + * when the JDBC driverManager is asked to connect to a given URL, it passes the URL to each + * loaded driver in turn.

+ * + *

The driver should raise an SQLException if it is the right driver to connect to the given URL, + * but has trouble connecting to the database.

+ * + *

The java.util.Properties argument can be used to pass arbitrary string tag/value pairs as + * connection arguments.

+ * + *
    + *
  • user - (required) The user to connect as
  • + *
  • password - (optional) The password for the user
  • + *
  • ssl -(optional) Use SSL when connecting to the server
  • + *
  • readOnly - (optional) Set connection to read-only by default
  • + *
  • charSet - (optional) The character set to be used for converting to/from + * the database to unicode. If multibyte is enabled on the server then the character set of the + * database is used as the default, otherwise the jvm character encoding is used as the default. + * This value is only used when connecting to a 7.2 or older server.
  • + *
  • loglevel - (optional) Enable logging of messages from the driver. The value is an integer + * from 0 to 2 where: OFF = 0, INFO =1, DEBUG = 2 The output is sent to + * DriverManager.getPrintWriter() if set, otherwise it is sent to System.out.
  • + *
  • compatible - (optional) This is used to toggle between different functionality + * as it changes across different releases of the jdbc driver code. The values here are versions + * of the jdbc client and not server versions. For example in 7.1 get/setBytes worked on + * LargeObject values, in 7.2 these methods were changed to work on bytea values. This change in + * functionality could be disabled by setting the compatible level to be "7.1", in which case the + * driver will revert to the 7.1 functionality.
  • + *
+ * + *

Normally, at least "user" and "password" properties should be included in the properties. For a + * list of supported character encoding , see + * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html Note that you will + * probably want to have set up the Postgres database itself to use the same encoding, with the + * {@code -E } argument to createdb.

+ * + *

Our protocol takes the forms:

+ * + *
+     *  jdbc:agensgraph://host:port/database?param1=val1&...
+     * 
+ * + * @param url the URL of the database to connect to + * @param info a list of arbitrary tag/value pairs as connection arguments + * @return a connection to the URL or null if it isnt us + * @throws SQLException if a database access error occurs or the url is + * {@code null} + * @see java.sql.Driver#connect + */ + @Override + public Connection connect(String url, Properties info) throws SQLException { + if (url == null) { + throw new SQLException("url is null"); + } + // get defaults + Properties defaults; - // override defaults with provided properties - Properties props = new Properties(defaults); - if (info != null) { - Set e = info.stringPropertyNames(); - for (String propName : e) { - String propValue = info.getProperty(propName); - if (propValue == null) { - throw new PSQLException( - GT.tr("Properties for the driver contains a non-string value for the key ") - + propName, - PSQLState.UNEXPECTED_ERROR); - } - props.setProperty(propName, propValue); - } - } - // parse URL and add more properties - if ((props = parseURL(url, props)) == null) { - return null; - } - try { - // Setup java.util.logging.Logger using connection properties. - setupLoggerFromProperties(props); - - LOGGER.log(Level.FINE, "Connecting with URL: {0}", url); - - // Enforce login timeout, if specified, by running the connection - // attempt in a separate thread. If we hit the timeout without the - // connection completing, we abandon the connection attempt in - // the calling thread, but the separate thread will keep trying. - // Eventually, the separate thread will either fail or complete - // the connection; at that point we clean up the connection if - // we managed to establish one after all. See ConnectThread for - // more details. - long timeout = timeout(props); - if (timeout <= 0) { - return makeConnection(url, props); - } - - ConnectThread ct = new ConnectThread(url, props); - Thread thread = new Thread(ct, "AgensGraph JDBC driver connection thread"); - thread.setDaemon(true); // Don't prevent the VM from shutting down - thread.start(); - return ct.getResult(timeout); - } catch (PSQLException ex1) { - LOGGER.log(Level.FINE, "Connection error: ", ex1); - // re-throw the exception, otherwise it will be caught next, and a - // org.postgresql.unusual error will be returned instead. - throw ex1; - } catch (java.security.AccessControlException ace) { - throw new PSQLException( - GT.tr( - "Your security policy has prevented the connection from being attempted. You probably need to grant the connect java.net.SocketPermission to the database server host and port that you wish to connect to."), - PSQLState.UNEXPECTED_ERROR, ace); - } catch (Exception ex2) { - LOGGER.log(Level.FINE, "Unexpected connection error: ", ex2); - throw new PSQLException( - GT.tr( - "Something unusual has occurred to cause the driver to fail. Please report this exception."), - PSQLState.UNEXPECTED_ERROR, ex2); + if (!url.startsWith("jdbc:agensgraph:")) { + return null; + } + try { + defaults = getDefaultProperties(); + } catch (IOException ioe) { + throw new PSQLException(GT.tr("Error loading default settings from driverconfig.properties"), + PSQLState.UNEXPECTED_ERROR, ioe); + } + + // override defaults with provided properties + Properties props = new Properties(defaults); + if (info != null) { + Set e = info.stringPropertyNames(); + for (String propName : e) { + String propValue = info.getProperty(propName); + if (propValue == null) { + throw new PSQLException( + GT.tr("Properties for the driver contains a non-string value for the key ") + + propName, + PSQLState.UNEXPECTED_ERROR); + } + props.setProperty(propName, propValue); + } + } + // parse URL and add more properties + if ((props = parseURL(url, props)) == null) { + throw new PSQLException( + GT.tr("Unable to parse URL {0}", url), + PSQLState.UNEXPECTED_ERROR); + } + try { + + LOGGER.log(Level.FINE, "Connecting with URL: {0}", url); + + // Enforce login timeout, if specified, by running the connection + // attempt in a separate thread. If we hit the timeout without the + // connection completing, we abandon the connection attempt in + // the calling thread, but the separate thread will keep trying. + // Eventually, the separate thread will either fail or complete + // the connection; at that point we clean up the connection if + // we managed to establish one after all. See ConnectThread for + // more details. + long timeout = timeout(props); + if (timeout <= 0) { + return makeConnection(url, props); + } + + ConnectThread ct = new ConnectThread(url, props); + Thread thread = new Thread(ct, "AgensGraph JDBC driver connection thread"); + thread.setDaemon(true); // Don't prevent the VM from shutting down + thread.start(); + return ct.getResult(timeout); + } catch (PSQLException ex1) { + LOGGER.log(Level.FINE, "Connection error: ", ex1); + // re-throw the exception, otherwise it will be caught next, and a + // org.postgresql.unusual error will be returned instead. + throw ex1; + } catch (Exception ex2) { + if ("java.security.AccessControlException".equals(ex2.getClass().getName())) { + // java.security.AccessControlException has been deprecated for removal, so compare the class name + throw new PSQLException( + GT.tr( + "Your security policy has prevented the connection from being attempted. You probably need to grant the connect java.net.SocketPermission to the database server host and port that you wish to connect to."), + PSQLState.UNEXPECTED_ERROR, ex2); + } + LOGGER.log(Level.FINE, "Unexpected connection error: ", ex2); + throw new PSQLException( + GT.tr( + "Something unusual has occurred to cause the driver to fail. Please report this exception."), + PSQLState.UNEXPECTED_ERROR, ex2); + } } - } - - // Used to check if the handler file is the same - private static String loggerHandlerFile; - - /** - *

Setup java.util.logging.Logger using connection properties.

- * - *

See {@link PGProperty#LOGGER_FILE} and {@link PGProperty#LOGGER_FILE}

- * - * @param props Connection Properties - */ - private void setupLoggerFromProperties(final Properties props) { - final String driverLogLevel = PGProperty.LOGGER_LEVEL.get(props); - if (driverLogLevel == null) { - return; // Don't mess with Logger if not set + + /** + * this is an empty method left here for graalvm + * we removed the ability to setup the logger from properties + * due to a security issue + * + * @param props Connection Properties + */ + private void setupLoggerFromProperties(final Properties props) { } - if ("OFF".equalsIgnoreCase(driverLogLevel)) { - PARENT_LOGGER.setLevel(Level.OFF); - return; // Don't mess with Logger if set to OFF - } else if ("DEBUG".equalsIgnoreCase(driverLogLevel)) { - PARENT_LOGGER.setLevel(Level.FINE); - } else if ("TRACE".equalsIgnoreCase(driverLogLevel)) { - PARENT_LOGGER.setLevel(Level.FINEST); + + /** + * Perform a connect in a separate thread; supports getting the results from the original thread + * while enforcing a login timeout. + */ + private static class ConnectThread implements Runnable { + ConnectThread(String url, Properties props) { + this.url = url; + this.props = props; + } + + public void run() { + Connection conn; + Throwable error; + + try { + conn = makeConnection(url, props); + error = null; + } catch (Throwable t) { + conn = null; + error = t; + } + + synchronized (this) { + if (abandoned) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } else { + result = conn; + resultException = error; + notify(); + } + } + } + + /** + * Get the connection result from this (assumed running) thread. If the timeout is reached + * without a result being available, a SQLException is thrown. + * + * @param timeout timeout in milliseconds + * @return the new connection, if successful + * @throws SQLException if a connection error occurs or the timeout is reached + */ + public Connection getResult(long timeout) throws SQLException { + long expiry = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) + timeout; + synchronized (this) { + while (true) { + if (result != null) { + return result; + } + + if (resultException != null) { + if (resultException instanceof SQLException) { + resultException.fillInStackTrace(); + throw (SQLException) resultException; + } else { + throw new PSQLException( + GT.tr( + "Something unusual has occurred to cause the driver to fail. Please report this exception."), + PSQLState.UNEXPECTED_ERROR, resultException); + } + } + + long delay = expiry - TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + if (delay <= 0) { + abandoned = true; + throw new PSQLException(GT.tr("Connection attempt timed out."), + PSQLState.CONNECTION_UNABLE_TO_CONNECT); + } + + try { + wait(delay); + } catch (InterruptedException ie) { + + // reset the interrupt flag + Thread.currentThread().interrupt(); + abandoned = true; + + // throw an unchecked exception which will hopefully not be ignored by the calling code + throw new RuntimeException(GT.tr("Interrupted while attempting to connect.")); + } + } + } + } + + private final String url; + private final Properties props; + private Connection result; + private Throwable resultException; + private boolean abandoned; } - ExpressionProperties exprProps = new ExpressionProperties(props, System.getProperties()); - final String driverLogFile = PGProperty.LOGGER_FILE.get(exprProps); - if (driverLogFile != null && driverLogFile.equals(loggerHandlerFile)) { - return; // Same file output, do nothing. + /** + * Create a connection from URL and properties. Always does the connection work in the current + * thread without enforcing a timeout, regardless of any timeout specified in the properties. + * + * @param url the original URL + * @param props the parsed/defaulted connection properties + * @return a new connection + * @throws SQLException if the connection could not be made + */ + private static Connection makeConnection(String url, Properties props) throws SQLException { + return new AgConnection(hostSpecs(props), props, url); } - for (java.util.logging.Handler handlers : PARENT_LOGGER.getHandlers()) { - // Remove previously set Handlers - handlers.close(); - PARENT_LOGGER.removeHandler(handlers); - loggerHandlerFile = null; + /** + * Returns true if the driver thinks it can open a connection to the given URL. Typically, drivers + * will return true if they understand the subprotocol specified in the URL and false if they + * don't. Our protocols start with jdbc:agensgraph: + * + * @param url the URL of the driver + * @return true if this driver accepts the given URL + * @see java.sql.Driver#acceptsURL + */ + @Override + public boolean acceptsURL(String url) { + return parseURL(url, null) != null; } - java.util.logging.Handler handler = null; - if (driverLogFile != null) { - try { - handler = new java.util.logging.FileHandler(driverLogFile); - loggerHandlerFile = driverLogFile; - } catch (Exception ex) { - System.err.println("Cannot enable FileHandler, fallback to ConsoleHandler."); - } + /** + *

The getPropertyInfo method is intended to allow a generic GUI tool to discover what properties + * it should prompt a human for in order to get enough information to connect to a database.

+ * + *

Note that depending on the values the human has supplied so far, additional values may become + * necessary, so it may be necessary to iterate through several calls to getPropertyInfo

+ * + * @param url the Url of the database to connect to + * @param info a proposed list of tag/value pairs that will be sent on connect open. + * @return An array of DriverPropertyInfo objects describing possible properties. This array may + * be an empty array if no properties are required + * @see java.sql.Driver#getPropertyInfo + */ + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { + Properties copy = new Properties(info); + Properties parse = parseURL(url, copy); + if (parse != null) { + copy = parse; + } + + PGProperty[] knownProperties = PGProperty.values(); + DriverPropertyInfo[] props = new DriverPropertyInfo[knownProperties.length]; + for (int i = 0; i < props.length; ++i) { + props[i] = knownProperties[i].toDriverPropertyInfo(copy); + } + + return props; } - Formatter formatter = new SimpleFormatter(); - - if ( handler == null ) { - if (DriverManager.getLogWriter() != null) { - handler = new LogWriterHandler(DriverManager.getLogWriter()); - } else if ( DriverManager.getLogStream() != null) { - handler = new StreamHandler(DriverManager.getLogStream(), formatter); - } else { - handler = new StreamHandler(System.err, formatter); - } - } else { - handler.setFormatter(formatter); + @Override + public int getMajorVersion() { + return net.bitnine.agensgraph.util.DriverInfo.MAJOR_VERSION; } - Level loggerLevel = PARENT_LOGGER.getLevel(); - if (loggerLevel != null) { - handler.setLevel(loggerLevel); + @Override + public int getMinorVersion() { + return net.bitnine.agensgraph.util.DriverInfo.MINOR_VERSION; } - PARENT_LOGGER.setUseParentHandlers(false); - PARENT_LOGGER.addHandler(handler); - } - - /** - * Perform a connect in a separate thread; supports getting the results from the original thread - * while enforcing a login timeout. - */ - private static class ConnectThread implements Runnable { - ConnectThread(String url, Properties props) { - this.url = url; - this.props = props; + + /** + * Returns the server version series of this driver and the specific build number. + * + * @return JDBC driver version + * @deprecated use {@link #getMajorVersion()} and {@link #getMinorVersion()} instead + */ + @Deprecated + public static String getVersion() { + return net.bitnine.agensgraph.util.DriverInfo.DRIVER_FULL_NAME; } - public void run() { - Connection conn; - Throwable error; - - try { - conn = makeConnection(url, props); - error = null; - } catch (Throwable t) { - conn = null; - error = t; - } - - synchronized (this) { - if (abandoned) { - if (conn != null) { - try { - conn.close(); - } catch (SQLException ignored) { - } - } - } else { - result = conn; - resultException = error; - notify(); - } - } + /** + *

Report whether the driver is a genuine JDBC compliant driver. A driver may only report "true" + * here if it passes the JDBC compliance tests, otherwise it is required to return false. JDBC + * compliance requires full support for the JDBC API and full support for SQL 92 Entry Level.

+ * + *

For PostgreSQL, this is not yet possible, as we are not SQL92 compliant (yet).

+ */ + @Override + public boolean jdbcCompliant() { + return false; } /** - * Get the connection result from this (assumed running) thread. If the timeout is reached - * without a result being available, a SQLException is thrown. + * Constructs a new DriverURL, splitting the specified URL into its component parts. * - * @param timeout timeout in milliseconds - * @return the new connection, if successful - * @throws SQLException if a connection error occurs or the timeout is reached + * @param url JDBC URL to parse + * @param defaults Default properties + * @return Properties with elements added from the url */ - public Connection getResult(long timeout) throws SQLException { - long expiry = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) + timeout; - synchronized (this) { - while (true) { - if (result != null) { - return result; - } - - if (resultException != null) { - if (resultException instanceof SQLException) { - resultException.fillInStackTrace(); - throw (SQLException) resultException; + public static Properties parseURL(String url, Properties defaults) { + // priority 1 - URL values + Properties priority1Url = new Properties(); + // priority 2 - Properties given as argument to DriverManager.getConnection() + // argument "defaults" EXCLUDING defaults + // priority 3 - Values retrieved by "service" + Properties priority3Service = new Properties(); + // priority 4 - Properties loaded by Driver.loadDefaultProperties() (user, org/postgresql/driverconfig.properties) + // argument "defaults" INCLUDING defaults + // priority 5 - PGProperty defaults for PGHOST, PGPORT, PGDBNAME + + String urlServer = url; + String urlArgs = ""; + + int qPos = url.indexOf('?'); + if (qPos != -1) { + urlServer = url.substring(0, qPos); + urlArgs = url.substring(qPos + 1); + } + + if (!urlServer.startsWith("jdbc:agensgraph:")) { + LOGGER.log(Level.FINE, "JDBC URL must start with \"jdbc:agensgraph:\" but was: {0}", url); + return null; + } + urlServer = urlServer.substring("jdbc:agensgraph:".length()); + + if (urlServer.equals("//") || urlServer.equals("///")) { + urlServer = ""; + } else if (urlServer.startsWith("//")) { + urlServer = urlServer.substring(2); + long slashCount = urlServer.chars().filter(ch -> ch == '/').count(); + if (slashCount > 1) { + LOGGER.log(Level.WARNING, "JDBC URL contains too many / characters: {0}", url); + return null; + } + int slash = urlServer.indexOf('/'); + if (slash == -1) { + LOGGER.log(Level.WARNING, "JDBC URL must contain a / at the end of the host or port: {0}", url); + return null; + } + if (!urlServer.endsWith("/")) { + String value = urlDecode(urlServer.substring(slash + 1)); + if (value == null) { + return null; + } + PGProperty.PG_DBNAME.set(priority1Url, value); + } + urlServer = urlServer.substring(0, slash); + + String[] addresses = urlServer.split(","); + StringBuilder hosts = new StringBuilder(); + StringBuilder ports = new StringBuilder(); + for (String address : addresses) { + int portIdx = address.lastIndexOf(':'); + if (portIdx != -1 && address.lastIndexOf(']') < portIdx) { + String portStr = address.substring(portIdx + 1); + ports.append(portStr); + CharSequence hostStr = address.subSequence(0, portIdx); + if (hostStr.length() == 0) { + hosts.append(PGProperty.PG_HOST.getDefaultValue()); + } else { + hosts.append(hostStr); + } + } else { + ports.append(PGProperty.PG_PORT.getDefaultValue()); + hosts.append(address); + } + ports.append(','); + hosts.append(','); + } + ports.setLength(ports.length() - 1); + hosts.setLength(hosts.length() - 1); + PGProperty.PG_HOST.set(priority1Url, hosts.toString()); + PGProperty.PG_PORT.set(priority1Url, ports.toString()); + } else if (urlServer.startsWith("/")) { + return null; + } else { + String value = urlDecode(urlServer); + if (value == null) { + return null; + } + priority1Url.setProperty(PGProperty.PG_DBNAME.getName(), value); + } + + // parse the args part of the url + String[] args = urlArgs.split("&"); + String serviceName = null; + for (String token : args) { + if (token.isEmpty()) { + continue; + } + int pos = token.indexOf('='); + if (pos == -1) { + priority1Url.setProperty(token, ""); } else { - throw new PSQLException( - GT.tr( - "Something unusual has occurred to cause the driver to fail. Please report this exception."), - PSQLState.UNEXPECTED_ERROR, resultException); + String pName = PGPropertyUtil.translatePGServiceToPGProperty(token.substring(0, pos)); + String pValue = urlDecode(token.substring(pos + 1)); + if (pValue == null) { + return null; + } + if (PGProperty.SERVICE.getName().equals(pName)) { + serviceName = pValue; + } else { + priority1Url.setProperty(pName, pValue); + } } - } + } - long delay = expiry - TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - if (delay <= 0) { - abandoned = true; - throw new PSQLException(GT.tr("Connection attempt timed out."), - PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } + // load pg_service.conf + if (serviceName != null) { + LOGGER.log(Level.FINE, "Processing option [?service={0}]", serviceName); + Properties result = PgServiceConfParser.getServiceProperties(serviceName); + if (result == null) { + LOGGER.log(Level.WARNING, "Definition of service [{0}] not found", serviceName); + return null; + } + priority3Service.putAll(result); + } - try { - wait(delay); - } catch (InterruptedException ie) { + // combine result based on order of priority + Properties result = new Properties(); + result.putAll(priority1Url); + if (defaults != null) { + // priority 2 - forEach() returns all entries EXCEPT defaults + defaults.forEach(result::putIfAbsent); + } + priority3Service.forEach(result::putIfAbsent); + if (defaults != null) { + // priority 4 - stringPropertyNames() returns all entries INCLUDING defaults + defaults.stringPropertyNames().forEach(s -> result.putIfAbsent(s, castNonNull(defaults.getProperty(s)))); + } + // priority 5 - PGProperty defaults for PGHOST, PGPORT, PGDBNAME + result.putIfAbsent(PGProperty.PG_PORT.getName(), castNonNull(PGProperty.PG_PORT.getDefaultValue())); + result.putIfAbsent(PGProperty.PG_HOST.getName(), castNonNull(PGProperty.PG_HOST.getDefaultValue())); + if (PGProperty.USER.getOrDefault(result) != null) { + result.putIfAbsent(PGProperty.PG_DBNAME.getName(), castNonNull(PGProperty.USER.getOrDefault(result))); + } - // reset the interrupt flag - Thread.currentThread().interrupt(); - abandoned = true; + // consistency check + if (!PGPropertyUtil.propertiesConsistencyCheck(result)) { + return null; + } - // throw an unchecked exception which will hopefully not be ignored by the calling code - throw new RuntimeException(GT.tr("Interrupted while attempting to connect.")); - } + // try to load .pgpass if password is missing + if (PGProperty.PASSWORD.getOrDefault(result) == null) { + String password = PgPassParser.getPassword( + PGProperty.PG_HOST.getOrDefault(result), PGProperty.PG_PORT.getOrDefault(result), PGProperty.PG_DBNAME.getOrDefault(result), PGProperty.USER.getOrDefault(result) + ); + if (password != null && !password.isEmpty()) { + PGProperty.PASSWORD.set(result, password); + } } - } + // + return result; } - private final String url; - private final Properties props; - private Connection result; - private Throwable resultException; - private boolean abandoned; - } - - /** - * Create a connection from URL and properties. Always does the connection work in the current - * thread without enforcing a timeout, regardless of any timeout specified in the properties. - * - * @param url the original URL - * @param props the parsed/defaulted connection properties - * @return a new connection - * @throws SQLException if the connection could not be made - */ - private static Connection makeConnection(String url, Properties props) throws SQLException { - return new AgConnection(hostSpecs(props), user(props), database(props), props, url); - } - - /** - * Returns true if the driver thinks it can open a connection to the given URL. Typically, drivers - * will return true if they understand the subprotocol specified in the URL and false if they - * don't. Our protocols start with jdbc:agensgraph: - * - * @param url the URL of the driver - * @return true if this driver accepts the given URL - * @see java.sql.Driver#acceptsURL - */ - @Override - public boolean acceptsURL(String url) { - return parseURL(url, null) != null; - } - - /** - *

The getPropertyInfo method is intended to allow a generic GUI tool to discover what properties - * it should prompt a human for in order to get enough information to connect to a database.

- * - *

Note that depending on the values the human has supplied so far, additional values may become - * necessary, so it may be necessary to iterate through several calls to getPropertyInfo

- * - * @param url the Url of the database to connect to - * @param info a proposed list of tag/value pairs that will be sent on connect open. - * @return An array of DriverPropertyInfo objects describing possible properties. This array may - * be an empty array if no properties are required - * @see java.sql.Driver#getPropertyInfo - */ - @Override - public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { - Properties copy = new Properties(info); - Properties parse = parseURL(url, copy); - if (parse != null) { - copy = parse; + // decode url, on failure log and return null + private static String urlDecode(String url) { + try { + return URLCoder.decode(url); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.FINE, "Url [{0}] parsing failed with error [{1}]", new Object[]{url, e.getMessage()}); + } + return null; } - PGProperty[] knownProperties = PGProperty.values(); - DriverPropertyInfo[] props = new DriverPropertyInfo[knownProperties.length]; - for (int i = 0; i < props.length; ++i) { - props[i] = knownProperties[i].toDriverPropertyInfo(copy); + /** + * @return the address portion of the URL + */ + private static HostSpec[] hostSpecs(Properties props) { + String[] hosts = castNonNull(PGProperty.PG_HOST.getOrDefault(props)).split(","); + String[] ports = castNonNull(PGProperty.PG_PORT.getOrDefault(props)).split(","); + String localSocketAddress = PGProperty.LOCAL_SOCKET_ADDRESS.getOrDefault(props); + HostSpec[] hostSpecs = new HostSpec[hosts.length]; + for (int i = 0; i < hostSpecs.length; ++i) { + hostSpecs[i] = new HostSpec(hosts[i], Integer.parseInt(ports[i]), localSocketAddress); + } + return hostSpecs; } - return props; - } - - @Override - public int getMajorVersion() { - return org.postgresql.util.DriverInfo.MAJOR_VERSION; - } - - @Override - public int getMinorVersion() { - return org.postgresql.util.DriverInfo.MINOR_VERSION; - } - - /** - * Returns the server version series of this driver and the specific build number. - * - * @return JDBC driver version - * @deprecated use {@link #getMajorVersion()} and {@link #getMinorVersion()} instead - */ - @Deprecated - public static String getVersion() { - return DriverInfo.DRIVER_FULL_NAME; - } - - /** - *

Report whether the driver is a genuine JDBC compliant driver. A driver may only report "true" - * here if it passes the JDBC compliance tests, otherwise it is required to return false. JDBC - * compliance requires full support for the JDBC API and full support for SQL 92 Entry Level.

- * - *

For PostgreSQL, this is not yet possible, as we are not SQL92 compliant (yet).

- */ - @Override - public boolean jdbcCompliant() { - return false; - } - - /** - * Constructs a new DriverURL, splitting the specified URL into its component parts. - * - * @param url JDBC URL to parse - * @param defaults Default properties - * @return Properties with elements added from the url - */ - public static Properties parseURL(String url, Properties defaults) { - Properties urlProps = new Properties(defaults); - - String urlServer = url; - String urlArgs = ""; - - int qPos = url.indexOf('?'); - if (qPos != -1) { - urlServer = url.substring(0, qPos); - urlArgs = url.substring(qPos + 1); + /** + * @return the timeout from the URL, in milliseconds + */ + private static long timeout(Properties props) { + String timeout = PGProperty.LOGIN_TIMEOUT.getOrDefault(props); + if (timeout != null) { + try { + return (long) (Float.parseFloat(timeout) * 1000); + } catch (NumberFormatException e) { + LOGGER.log(Level.WARNING, "Couldn't parse loginTimeout value: {0}", timeout); + } + } + return (long) DriverManager.getLoginTimeout() * 1000; } - if (!urlServer.startsWith("jdbc:agensgraph:")) { - LOGGER.log(Level.FINE, "JDBC URL must start with \"jdbc:agensgraph:\" but was: {0}", url); - return null; + /** + * This method was added in v6.5, and simply throws an SQLException for an unimplemented method. I + * decided to do it this way while implementing the JDBC2 extensions to JDBC, as it should help + * keep the overall driver size down. It now requires the call Class and the function name to help + * when the driver is used with closed software that don't report the stack trace + * + * @param callClass the call Class + * @param functionName the name of the unimplemented function with the type of its arguments + * @return PSQLException with a localized message giving the complete description of the + * unimplemented function + */ + public static SQLFeatureNotSupportedException notImplemented(Class callClass, + String functionName) { + return new SQLFeatureNotSupportedException( + GT.tr("Method {0} is not yet implemented.", callClass.getName() + "." + functionName), + PSQLState.NOT_IMPLEMENTED.getState()); } - urlServer = urlServer.substring("jdbc:agensgraph:".length()); - if (urlServer.startsWith("//")) { - urlServer = urlServer.substring(2); - int slash = urlServer.indexOf('/'); - if (slash == -1) { - LOGGER.log(Level.WARNING, "JDBC URL must contain a / at the end of the host or port: {0}", url); - return null; - } - urlProps.setProperty("PGDBNAME", URLCoder.decode(urlServer.substring(slash + 1))); - - String[] addresses = urlServer.substring(0, slash).split(","); - StringBuilder hosts = new StringBuilder(); - StringBuilder ports = new StringBuilder(); - for (String address : addresses) { - int portIdx = address.lastIndexOf(':'); - if (portIdx != -1 && address.lastIndexOf(']') < portIdx) { - String portStr = address.substring(portIdx + 1); - try { - int port = Integer.parseInt(portStr); - if (port < 1 || port > 65535) { - LOGGER.log(Level.WARNING, "JDBC URL port: {0} not valid (1:65535) ", portStr); - return null; - } - } catch (NumberFormatException ignore) { - LOGGER.log(Level.WARNING, "JDBC URL invalid port number: {0}", portStr); - return null; - } - ports.append(portStr); - hosts.append(address.subSequence(0, portIdx)); - } else { - ports.append(DEFAULT_PORT); - hosts.append(address); - } - ports.append(','); - hosts.append(','); - } - ports.setLength(ports.length() - 1); - hosts.setLength(hosts.length() - 1); - urlProps.setProperty("PGPORT", ports.toString()); - urlProps.setProperty("PGHOST", hosts.toString()); - } else { - /* - if there are no defaults set or any one of PORT, HOST, DBNAME not set - then set it to default - */ - if (defaults == null || !defaults.containsKey("PGPORT")) { - urlProps.setProperty("PGPORT", DEFAULT_PORT); - } - if (defaults == null || !defaults.containsKey("PGHOST")) { - urlProps.setProperty("PGHOST", "localhost"); - } - if (defaults == null || !defaults.containsKey("PGDBNAME")) { - urlProps.setProperty("PGDBNAME", URLCoder.decode(urlServer)); - } + @Override + public Logger getParentLogger() { + return PARENT_LOGGER; } - // parse the args part of the url - String[] args = urlArgs.split("&"); - for (String token : args) { - if (token.isEmpty()) { - continue; - } - int pos = token.indexOf('='); - if (pos == -1) { - urlProps.setProperty(token, ""); - } else { - urlProps.setProperty(token.substring(0, pos), URLCoder.decode(token.substring(pos + 1))); - } + public static SharedTimer getSharedTimer() { + return SHARED_TIMER; } - return urlProps; - } - - /** - * @return the address portion of the URL - */ - private static HostSpec[] hostSpecs(Properties props) { - String[] hosts = castNonNull(props.getProperty("PGHOST")).split(","); - String[] ports = castNonNull(props.getProperty("PGPORT")).split(","); - HostSpec[] hostSpecs = new HostSpec[hosts.length]; - for (int i = 0; i < hostSpecs.length; ++i) { - hostSpecs[i] = new HostSpec(hosts[i], Integer.parseInt(ports[i])); - } - return hostSpecs; - } - - /** - * @return the username of the URL - */ - private static String user(Properties props) { - return props.getProperty("user", ""); - } - - /** - * @return the database name of the URL - */ - private static String database(Properties props) { - return props.getProperty("PGDBNAME", ""); - } - - /** - * @return the timeout from the URL, in milliseconds - */ - private static long timeout(Properties props) { - String timeout = PGProperty.LOGIN_TIMEOUT.get(props); - if (timeout != null) { - try { - return (long) (Float.parseFloat(timeout) * 1000); - } catch (NumberFormatException e) { - LOGGER.log(Level.WARNING, "Couldn't parse loginTimeout value: {0}", timeout); - } + /** + * Register the driver against {@link DriverManager}. This is done automatically when the class is + * loaded. Dropping the driver from DriverManager's list is possible using {@link #deregister()} + * method. + * + * @throws IllegalStateException if the driver is already registered + * @throws SQLException if registering the driver fails + */ + public static void register() throws SQLException { + if (isRegistered()) { + throw new IllegalStateException( + "Driver is already registered. It can only be registered once."); + } + Driver registeredDriver = new Driver(); + DriverManager.registerDriver(registeredDriver); + Driver.registeredDriver = registeredDriver; } - return (long) DriverManager.getLoginTimeout() * 1000; - } - - /** - * This method was added in v6.5, and simply throws an SQLException for an unimplemented method. I - * decided to do it this way while implementing the JDBC2 extensions to JDBC, as it should help - * keep the overall driver size down. It now requires the call Class and the function name to help - * when the driver is used with closed software that don't report the stack strace - * - * @param callClass the call Class - * @param functionName the name of the unimplemented function with the type of its arguments - * @return PSQLException with a localized message giving the complete description of the - * unimplemeted function - */ - public static SQLFeatureNotSupportedException notImplemented(Class callClass, - String functionName) { - return new SQLFeatureNotSupportedException( - GT.tr("Method {0} is not yet implemented.", callClass.getName() + "." + functionName), - PSQLState.NOT_IMPLEMENTED.getState()); - } - - @Override - public java.util.logging.Logger getParentLogger() { - return PARENT_LOGGER; - } - - public static SharedTimer getSharedTimer() { - return SHARED_TIMER; - } - - /** - * Register the driver against {@link DriverManager}. This is done automatically when the class is - * loaded. Dropping the driver from DriverManager's list is possible using {@link #deregister()} - * method. - * - * @throws IllegalStateException if the driver is already registered - * @throws SQLException if registering the driver fails - */ - public static void register() throws SQLException { - if (isRegistered()) { - throw new IllegalStateException( - "Driver is already registered. It can only be registered once."); + + /** + * According to JDBC specification, this driver is registered against {@link DriverManager} when + * the class is loaded. To avoid leaks, this method allow unregistering the driver so that the + * class can be gc'ed if necessary. + * + * @throws IllegalStateException if the driver is not registered + * @throws SQLException if deregistering the driver fails + */ + public static void deregister() throws SQLException { + if (registeredDriver == null) { + throw new IllegalStateException( + "Driver is not registered (or it has not been registered using Driver.register() method)"); + } + DriverManager.deregisterDriver(registeredDriver); + registeredDriver = null; } - Driver registeredDriver = new Driver(); - DriverManager.registerDriver(registeredDriver); - Driver.registeredDriver = registeredDriver; - } - - /** - * According to JDBC specification, this driver is registered against {@link DriverManager} when - * the class is loaded. To avoid leaks, this method allow unregistering the driver so that the - * class can be gc'ed if necessary. - * - * @throws IllegalStateException if the driver is not registered - * @throws SQLException if deregistering the driver fails - */ - public static void deregister() throws SQLException { - if (registeredDriver == null) { - throw new IllegalStateException( - "Driver is not registered (or it has not been registered using Driver.register() method)"); + + /** + * @return {@code true} if the driver is registered against {@link DriverManager} + */ + public static boolean isRegistered() { + return registeredDriver != null; } - DriverManager.deregisterDriver(registeredDriver); - registeredDriver = null; - } - - /** - * @return {@code true} if the driver is registered against {@link DriverManager} - */ - public static boolean isRegistered() { - return registeredDriver != null; - } } diff --git a/src/main/java/net/bitnine/agensgraph/ds/AGConnectionPoolDataSource.java b/src/main/java/net/bitnine/agensgraph/ds/AGConnectionPoolDataSource.java index b4a6d93..1c4655d 100644 --- a/src/main/java/net/bitnine/agensgraph/ds/AGConnectionPoolDataSource.java +++ b/src/main/java/net/bitnine/agensgraph/ds/AGConnectionPoolDataSource.java @@ -28,7 +28,7 @@ public class AGConnectionPoolDataSource extends BaseDataSource * Gets a description of this DataSource. */ public String getDescription() { - return "ConnectionPoolDataSource from " + org.postgresql.util.DriverInfo.DRIVER_FULL_NAME; + return "ConnectionPoolDataSource from " + net.bitnine.agensgraph.util.DriverInfo.DRIVER_FULL_NAME; } /** @@ -36,7 +36,7 @@ public String getDescription() { * DataSource. * * @throws java.sql.SQLException Occurs when the physical database connection cannot be - * established. + * established. */ public PooledConnection getPooledConnection() throws SQLException { return new PGPooledConnection(getConnection(), defaultAutoCommit); @@ -47,7 +47,7 @@ public PooledConnection getPooledConnection() throws SQLException { * DataSource. * * @throws java.sql.SQLException Occurs when the physical database connection cannot be - * established. + * established. */ public PooledConnection getPooledConnection(String user, String password) throws SQLException { return new PGPooledConnection(getConnection(user, password), defaultAutoCommit); diff --git a/src/main/java/net/bitnine/agensgraph/ds/AGPoolingDataSource.java b/src/main/java/net/bitnine/agensgraph/ds/AGPoolingDataSource.java index 1926d50..50e942f 100644 --- a/src/main/java/net/bitnine/agensgraph/ds/AGPoolingDataSource.java +++ b/src/main/java/net/bitnine/agensgraph/ds/AGPoolingDataSource.java @@ -58,411 +58,410 @@ *

* * @author Aaron Mulder (ammulder@chariotsolutions.com) - * * @deprecated Since 42.0.0, instead of this class you should use a fully featured connection pool - * like HikariCP, vibur-dbcp, commons-dbcp, c3p0, etc. + * like HikariCP, vibur-dbcp, commons-dbcp, c3p0, etc. */ @Deprecated public class AGPoolingDataSource extends BaseDataSource implements DataSource { - protected static ConcurrentMap dataSources = - new ConcurrentHashMap(); - - public static AGPoolingDataSource getDataSource(String name) { - return dataSources.get(name); - } - - // Additional Data Source properties - protected String dataSourceName; // Must be protected for subclasses to sync updates to it - private int initialConnections = 0; - private int maxConnections = 0; - // State variables - private boolean initialized = false; - private final Stack available = new Stack(); - private final Stack used = new Stack(); - private boolean isClosed; - private final Object lock = new Object(); - private AGConnectionPoolDataSource source; - - /** - * Gets a description of this DataSource. - */ - public String getDescription() { - return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.util.DriverInfo.DRIVER_FULL_NAME; - } - - /** - * Ensures the DataSource properties are not changed after the DataSource has been used. - * - * @throws IllegalStateException The Server Name cannot be changed after the DataSource has been - * used. - */ - public void setServerName(String serverName) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + protected static ConcurrentMap dataSources = + new ConcurrentHashMap(); + + public static AGPoolingDataSource getDataSource(String name) { + return dataSources.get(name); } - super.setServerName(serverName); - } - - /** - * Ensures the DataSource properties are not changed after the DataSource has been used. - * - * @throws IllegalStateException The Database Name cannot be changed after the DataSource has been - * used. - */ - public void setDatabaseName(String databaseName) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + // Additional Data Source properties + protected String dataSourceName; // Must be protected for subclasses to sync updates to it + private int initialConnections = 0; + private int maxConnections = 0; + // State variables + private boolean initialized = false; + private final Stack available = new Stack(); + private final Stack used = new Stack(); + private boolean isClosed; + private final Object lock = new Object(); + private AGConnectionPoolDataSource source; + + /** + * Gets a description of this DataSource. + */ + public String getDescription() { + return "Pooling DataSource '" + dataSourceName + " from " + net.bitnine.agensgraph.util.DriverInfo.DRIVER_FULL_NAME; + } + + /** + * Ensures the DataSource properties are not changed after the DataSource has been used. + * + * @throws IllegalStateException The Server Name cannot be changed after the DataSource has been + * used. + */ + public void setServerName(String serverName) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + super.setServerName(serverName); } - super.setDatabaseName(databaseName); - } - - /** - * Ensures the DataSource properties are not changed after the DataSource has been used. - * - * @throws IllegalStateException The User cannot be changed after the DataSource has been used. - */ - public void setUser(String user) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Ensures the DataSource properties are not changed after the DataSource has been used. + * + * @throws IllegalStateException The Database Name cannot be changed after the DataSource has been + * used. + */ + public void setDatabaseName(String databaseName) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + super.setDatabaseName(databaseName); } - super.setUser(user); - } - - /** - * Ensures the DataSource properties are not changed after the DataSource has been used. - * - * @throws IllegalStateException The Password cannot be changed after the DataSource has been - * used. - */ - public void setPassword(String password) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Ensures the DataSource properties are not changed after the DataSource has been used. + * + * @throws IllegalStateException The User cannot be changed after the DataSource has been used. + */ + public void setUser(String user) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + super.setUser(user); } - super.setPassword(password); - } - - /** - * Ensures the DataSource properties are not changed after the DataSource has been used. - * - * @throws IllegalStateException The Port Number cannot be changed after the DataSource has been - * used. - */ - public void setPortNumber(int portNumber) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Ensures the DataSource properties are not changed after the DataSource has been used. + * + * @throws IllegalStateException The Password cannot be changed after the DataSource has been + * used. + */ + public void setPassword(String password) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + super.setPassword(password); } - super.setPortNumber(portNumber); - } - - /** - * Gets the number of connections that will be created when this DataSource is initialized. If you - * do not call initialize explicitly, it will be initialized the first time a connection is drawn - * from it. - * - * @return number of connections that will be created when this DataSource is initialized - */ - public int getInitialConnections() { - return initialConnections; - } - - /** - * Sets the number of connections that will be created when this DataSource is initialized. If you - * do not call initialize explicitly, it will be initialized the first time a connection is drawn - * from it. - * - * @param initialConnections number of initial connections - * @throws IllegalStateException The Initial Connections cannot be changed after the DataSource - * has been used. - */ - public void setInitialConnections(int initialConnections) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Ensures the DataSource properties are not changed after the DataSource has been used. + * + * @throws IllegalStateException The Port Number cannot be changed after the DataSource has been + * used. + */ + public void setPortNumber(int portNumber) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + super.setPortNumber(portNumber); } - this.initialConnections = initialConnections; - } - - /** - * Gets the maximum number of connections that the pool will allow. If a request comes in and this - * many connections are in use, the request will block until a connection is available. Note that - * connections for a user other than the default user will not be pooled and don't count against - * this limit. - * - * @return The maximum number of pooled connection allowed, or 0 for no maximum. - */ - public int getMaxConnections() { - return maxConnections; - } - - /** - * Sets the maximum number of connections that the pool will allow. If a request comes in and this - * many connections are in use, the request will block until a connection is available. Note that - * connections for a user other than the default user will not be pooled and don't count against - * this limit. - * - * @param maxConnections The maximum number of pooled connection to allow, or 0 for no maximum. - * @throws IllegalStateException The Maximum Connections cannot be changed after the DataSource - * has been used. - */ - public void setMaxConnections(int maxConnections) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Gets the number of connections that will be created when this DataSource is initialized. If you + * do not call initialize explicitly, it will be initialized the first time a connection is drawn + * from it. + * + * @return number of connections that will be created when this DataSource is initialized + */ + public int getInitialConnections() { + return initialConnections; } - this.maxConnections = maxConnections; - } - - /** - * Gets the name of this DataSource. This uniquely identifies the DataSource. You cannot use more - * than one DataSource in the same VM with the same name. - * - * @return name of this DataSource - */ - public String getDataSourceName() { - return dataSourceName; - } - - /** - * Sets the name of this DataSource. This is required, and uniquely identifies the DataSource. You - * cannot create or use more than one DataSource in the same VM with the same name. - * - * @param dataSourceName datasource name - * @throws IllegalStateException The Data Source Name cannot be changed after the DataSource has - * been used. - * @throws IllegalArgumentException Another PoolingDataSource with the same dataSourceName already - * exists. - */ - public void setDataSourceName(String dataSourceName) { - if (initialized) { - throw new IllegalStateException( - "Cannot set Data Source properties after DataSource has been used"); + + /** + * Sets the number of connections that will be created when this DataSource is initialized. If you + * do not call initialize explicitly, it will be initialized the first time a connection is drawn + * from it. + * + * @param initialConnections number of initial connections + * @throws IllegalStateException The Initial Connections cannot be changed after the DataSource + * has been used. + */ + public void setInitialConnections(int initialConnections) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + this.initialConnections = initialConnections; } - if (this.dataSourceName != null && dataSourceName != null - && dataSourceName.equals(this.dataSourceName)) { - return; + + /** + * Gets the maximum number of connections that the pool will allow. If a request comes in and this + * many connections are in use, the request will block until a connection is available. Note that + * connections for a user other than the default user will not be pooled and don't count against + * this limit. + * + * @return The maximum number of pooled connection allowed, or 0 for no maximum. + */ + public int getMaxConnections() { + return maxConnections; } - AGPoolingDataSource previous = dataSources.putIfAbsent(dataSourceName, this); - if (previous != null) { - throw new IllegalArgumentException( - "DataSource with name '" + dataSourceName + "' already exists!"); + + /** + * Sets the maximum number of connections that the pool will allow. If a request comes in and this + * many connections are in use, the request will block until a connection is available. Note that + * connections for a user other than the default user will not be pooled and don't count against + * this limit. + * + * @param maxConnections The maximum number of pooled connection to allow, or 0 for no maximum. + * @throws IllegalStateException The Maximum Connections cannot be changed after the DataSource + * has been used. + */ + public void setMaxConnections(int maxConnections) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + this.maxConnections = maxConnections; } - if (this.dataSourceName != null) { - dataSources.remove(this.dataSourceName); + + /** + * Gets the name of this DataSource. This uniquely identifies the DataSource. You cannot use more + * than one DataSource in the same VM with the same name. + * + * @return name of this DataSource + */ + public String getDataSourceName() { + return dataSourceName; } - this.dataSourceName = dataSourceName; - } - - /** - * Initializes this DataSource. If the initialConnections is greater than zero, that number of - * connections will be created. After this method is called, the DataSource properties cannot be - * changed. If you do not call this explicitly, it will be called the first time you get a - * connection from the DataSource. - * - * @throws SQLException Occurs when the initialConnections is greater than zero, but the - * DataSource is not able to create enough physical connections. - */ - public void initialize() throws SQLException { - synchronized (lock) { - AGConnectionPoolDataSource source = createConnectionPool(); - this.source = source; - try { - source.initializeFrom(this); - } catch (Exception e) { - throw new PSQLException(GT.tr("Failed to setup DataSource."), PSQLState.UNEXPECTED_ERROR, - e); - } - - while (available.size() < initialConnections) { - available.push(source.getPooledConnection()); - } - - initialized = true; + + /** + * Sets the name of this DataSource. This is required, and uniquely identifies the DataSource. You + * cannot create or use more than one DataSource in the same VM with the same name. + * + * @param dataSourceName datasource name + * @throws IllegalStateException The Data Source Name cannot be changed after the DataSource has + * been used. + * @throws IllegalArgumentException Another PoolingDataSource with the same dataSourceName already + * exists. + */ + public void setDataSourceName(String dataSourceName) { + if (initialized) { + throw new IllegalStateException( + "Cannot set Data Source properties after DataSource has been used"); + } + if (this.dataSourceName != null && dataSourceName != null + && dataSourceName.equals(this.dataSourceName)) { + return; + } + AGPoolingDataSource previous = dataSources.putIfAbsent(dataSourceName, this); + if (previous != null) { + throw new IllegalArgumentException( + "DataSource with name '" + dataSourceName + "' already exists!"); + } + if (this.dataSourceName != null) { + dataSources.remove(this.dataSourceName); + } + this.dataSourceName = dataSourceName; } - } - - protected boolean isInitialized() { - return initialized; - } - - /** - * Creates the appropriate ConnectionPool to use for this DataSource. - * - * @return appropriate ConnectionPool to use for this DataSource - */ - protected AGConnectionPoolDataSource createConnectionPool() { - return new AGConnectionPoolDataSource(); - } - - /** - * Gets a non-pooled connection, unless the user and password are the same as the default - * values for this connection pool. - * - * @return A pooled connection. - * @throws SQLException Occurs when no pooled connection is available, and a new physical - * connection cannot be created. - */ - public Connection getConnection(String user, String password) - throws SQLException { - // If this is for the default user/password, use a pooled connection - if (user == null || (user.equals(getUser()) && ((password == null && getPassword() == null) - || (password != null && password.equals(getPassword()))))) { - return getConnection(); + + /** + * Initializes this DataSource. If the initialConnections is greater than zero, that number of + * connections will be created. After this method is called, the DataSource properties cannot be + * changed. If you do not call this explicitly, it will be called the first time you get a + * connection from the DataSource. + * + * @throws SQLException Occurs when the initialConnections is greater than zero, but the + * DataSource is not able to create enough physical connections. + */ + public void initialize() throws SQLException { + synchronized (lock) { + AGConnectionPoolDataSource source = createConnectionPool(); + this.source = source; + try { + source.initializeFrom(this); + } catch (Exception e) { + throw new PSQLException(GT.tr("Failed to setup DataSource."), PSQLState.UNEXPECTED_ERROR, + e); + } + + while (available.size() < initialConnections) { + available.push(source.getPooledConnection()); + } + + initialized = true; + } } - // Otherwise, use a non-pooled connection - if (!initialized) { - initialize(); + + protected boolean isInitialized() { + return initialized; } - return super.getConnection(user, password); - } - - /** - * Gets a connection from the connection pool. - * - * @return A pooled connection. - * @throws SQLException Occurs when no pooled connection is available, and a new physical - * connection cannot be created. - */ - public Connection getConnection() throws SQLException { - if (!initialized) { - initialize(); + + /** + * Creates the appropriate ConnectionPool to use for this DataSource. + * + * @return appropriate ConnectionPool to use for this DataSource + */ + protected AGConnectionPoolDataSource createConnectionPool() { + return new AGConnectionPoolDataSource(); } - return getPooledConnection(); - } - - /** - * Closes this DataSource, and all the pooled connections, whether in use or not. - */ - public void close() { - synchronized (lock) { - isClosed = true; - while (!available.isEmpty()) { - PooledConnection pci = available.pop(); - try { - pci.close(); - } catch (SQLException ignored) { + + /** + * Gets a non-pooled connection, unless the user and password are the same as the default + * values for this connection pool. + * + * @return A pooled connection. + * @throws SQLException Occurs when no pooled connection is available, and a new physical + * connection cannot be created. + */ + public Connection getConnection(String user, String password) + throws SQLException { + // If this is for the default user/password, use a pooled connection + if (user == null || (user.equals(getUser()) && ((password == null && getPassword() == null) + || (password != null && password.equals(getPassword()))))) { + return getConnection(); } - } - while (!used.isEmpty()) { - PooledConnection pci = used.pop(); - pci.removeConnectionEventListener(connectionEventListener); - try { - pci.close(); - } catch (SQLException ignored) { + // Otherwise, use a non-pooled connection + if (!initialized) { + initialize(); } - } + return super.getConnection(user, password); } - removeStoredDataSource(); - } - - protected void removeStoredDataSource() { - dataSources.remove(castNonNull(dataSourceName)); - } - - protected void addDataSource(String dataSourceName) { - dataSources.put(dataSourceName, this); - } - - /** - * Gets a connection from the pool. Will get an available one if present, or create a new one if - * under the max limit. Will block if all used and a new one would exceed the max. - */ - private Connection getPooledConnection() throws SQLException { - PooledConnection pc = null; - synchronized (lock) { - if (isClosed) { - throw new PSQLException(GT.tr("DataSource has been closed."), - PSQLState.CONNECTION_DOES_NOT_EXIST); - } - while (true) { - if (!available.isEmpty()) { - pc = available.pop(); - used.push(pc); - break; - } - if (maxConnections == 0 || used.size() < maxConnections) { - pc = castNonNull(source).getPooledConnection(); - used.push(pc); - break; - } else { - try { - // Wake up every second at a minimum - lock.wait(1000L); - } catch (InterruptedException ignored) { - } + + /** + * Gets a connection from the connection pool. + * + * @return A pooled connection. + * @throws SQLException Occurs when no pooled connection is available, and a new physical + * connection cannot be created. + */ + public Connection getConnection() throws SQLException { + if (!initialized) { + initialize(); } - } + return getPooledConnection(); } - pc.addConnectionEventListener(connectionEventListener); - return pc.getConnection(); - } - - /** - * Notified when a pooled connection is closed, or a fatal error occurs on a pooled connection. - * This is the only way connections are marked as unused. - */ - private final ConnectionEventListener connectionEventListener = new ConnectionEventListener() { - public void connectionClosed(ConnectionEvent event) { - ((PooledConnection) event.getSource()).removeConnectionEventListener(this); - synchronized (lock) { - if (isClosed) { - return; // DataSource has been closed - } - boolean removed = used.remove(event.getSource()); - if (removed) { - available.push((PooledConnection) event.getSource()); - // There's now a new connection available - lock.notify(); - } else { - // a connection error occurred + + /** + * Closes this DataSource, and all the pooled connections, whether in use or not. + */ + public void close() { + synchronized (lock) { + isClosed = true; + while (!available.isEmpty()) { + PooledConnection pci = available.pop(); + try { + pci.close(); + } catch (SQLException ignored) { + } + } + while (!used.isEmpty()) { + PooledConnection pci = used.pop(); + pci.removeConnectionEventListener(connectionEventListener); + try { + pci.close(); + } catch (SQLException ignored) { + } + } } - } + removeStoredDataSource(); + } + + protected void removeStoredDataSource() { + dataSources.remove(castNonNull(dataSourceName)); + } + + protected void addDataSource(String dataSourceName) { + dataSources.put(dataSourceName, this); } /** - * This is only called for fatal errors, where the physical connection is useless afterward and - * should be removed from the pool. + * Gets a connection from the pool. Will get an available one if present, or create a new one if + * under the max limit. Will block if all used and a new one would exceed the max. */ - public void connectionErrorOccurred(ConnectionEvent event) { - ((PooledConnection) event.getSource()).removeConnectionEventListener(this); - synchronized (lock) { - if (isClosed) { - return; // DataSource has been closed + private Connection getPooledConnection() throws SQLException { + PooledConnection pc = null; + synchronized (lock) { + if (isClosed) { + throw new PSQLException(GT.tr("DataSource has been closed."), + PSQLState.CONNECTION_DOES_NOT_EXIST); + } + while (true) { + if (!available.isEmpty()) { + pc = available.pop(); + used.push(pc); + break; + } + if (maxConnections == 0 || used.size() < maxConnections) { + pc = castNonNull(source).getPooledConnection(); + used.push(pc); + break; + } else { + try { + // Wake up every second at a minimum + lock.wait(1000L); + } catch (InterruptedException ignored) { + } + } + } } - used.remove(event.getSource()); - // We're now at least 1 connection under the max - lock.notify(); - } - } - }; - - /** - * Adds custom properties for this DataSource to the properties defined in the superclass. - */ - public Reference getReference() throws NamingException { - Reference ref = super.getReference(); - ref.add(new StringRefAddr("dataSourceName", dataSourceName)); - if (initialConnections > 0) { - ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections))); + pc.addConnectionEventListener(connectionEventListener); + return pc.getConnection(); } - if (maxConnections > 0) { - ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections))); + + /** + * Notified when a pooled connection is closed, or a fatal error occurs on a pooled connection. + * This is the only way connections are marked as unused. + */ + private final ConnectionEventListener connectionEventListener = new ConnectionEventListener() { + public void connectionClosed(ConnectionEvent event) { + ((PooledConnection) event.getSource()).removeConnectionEventListener(this); + synchronized (lock) { + if (isClosed) { + return; // DataSource has been closed + } + boolean removed = used.remove(event.getSource()); + if (removed) { + available.push((PooledConnection) event.getSource()); + // There's now a new connection available + lock.notify(); + } else { + // a connection error occurred + } + } + } + + /** + * This is only called for fatal errors, where the physical connection is useless afterward and + * should be removed from the pool. + */ + public void connectionErrorOccurred(ConnectionEvent event) { + ((PooledConnection) event.getSource()).removeConnectionEventListener(this); + synchronized (lock) { + if (isClosed) { + return; // DataSource has been closed + } + used.remove(event.getSource()); + // We're now at least 1 connection under the max + lock.notify(); + } + } + }; + + /** + * Adds custom properties for this DataSource to the properties defined in the superclass. + */ + public Reference getReference() throws NamingException { + Reference ref = super.getReference(); + ref.add(new StringRefAddr("dataSourceName", dataSourceName)); + if (initialConnections > 0) { + ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections))); + } + if (maxConnections > 0) { + ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections))); + } + return ref; } - return ref; - } - public boolean isWrapperFor(Class iface) throws SQLException { - return iface.isAssignableFrom(getClass()); - } + public boolean isWrapperFor(Class iface) throws SQLException { + return iface.isAssignableFrom(getClass()); + } - public T unwrap(Class iface) throws SQLException { - if (iface.isAssignableFrom(getClass())) { - return iface.cast(this); + public T unwrap(Class iface) throws SQLException { + if (iface.isAssignableFrom(getClass())) { + return iface.cast(this); + } + throw new SQLException("Cannot unwrap to " + iface.getName()); } - throw new SQLException("Cannot unwrap to " + iface.getName()); - } } diff --git a/src/main/java/net/bitnine/agensgraph/ds/common/BaseDataSource.java b/src/main/java/net/bitnine/agensgraph/ds/common/BaseDataSource.java index ed4fe0a..c8e7e25 100644 --- a/src/main/java/net/bitnine/agensgraph/ds/common/BaseDataSource.java +++ b/src/main/java/net/bitnine/agensgraph/ds/common/BaseDataSource.java @@ -36,1637 +36,1772 @@ */ public abstract class BaseDataSource implements CommonDataSource, Referenceable { - private static final Logger LOGGER = Logger.getLogger(BaseDataSource.class.getName()); - - // Standard properties, defined in the JDBC 2.0 Optional Package spec - private String[] serverNames = new String[] {"localhost"}; - private String databaseName = ""; - private String user; - private String password; - private int[] portNumbers = new int[] {0}; - - // Map for all other properties - private Properties properties = new Properties(); - - /* - * Ensure the driver is loaded as JDBC Driver might be invisible to Java's ServiceLoader. - * Usually, {@code Class.forName(...)} is not required as {@link DriverManager} detects JDBC drivers - * via {@code META-INF/services/java.sql.Driver} entries. However there might be cases when the driver - * is located at the application level classloader, thus it might be required to perform manual - * registration of the driver. - */ - static { - try { - Class.forName("net.bitnine.agensgraph.Driver"); - } catch (ClassNotFoundException e) { - throw new IllegalStateException( - "BaseDataSource is unable to load net.bitnine.agensgraph.Driver. Please check if you have proper AgensGraph JDBC Driver jar on the classpath", - e); - } - } - - /** - * Gets a connection to the PostgreSQL database. The database is identified by the DataSource - * properties serverName, databaseName, and portNumber. The user to connect as is identified by - * the DataSource properties user and password. - * - * @return A valid database connection. - * @throws SQLException Occurs when the database connection cannot be established. - */ - public Connection getConnection() throws SQLException { - return getConnection(user, password); - } - - /** - * Gets a connection to the PostgreSQL database. The database is identified by the DataSource - * properties serverName, databaseName, and portNumber. The user to connect as is identified by - * the arguments user and password, which override the DataSource properties by the same name. - * - * @param user user - * @param password password - * @return A valid database connection. - * @throws SQLException Occurs when the database connection cannot be established. - */ - public Connection getConnection( String user, String password) - throws SQLException { - try { - Connection con = DriverManager.getConnection(getUrl(), user, password); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "Created a {0} for {1} at {2}", - new Object[] {getDescription(), user, getUrl()}); - } - return con; - } catch (SQLException e) { - LOGGER.log(Level.FINE, "Failed to create a {0} for {1} at {2}: {3}", - new Object[] {getDescription(), user, getUrl(), e}); - throw e; - } - } - - /** - * This implementation don't use a LogWriter. - */ - @Override - public PrintWriter getLogWriter() { - return null; - } - - /** - * This implementation don't use a LogWriter. - * - * @param printWriter Not used - */ - @Override - public void setLogWriter( PrintWriter printWriter) { - // NOOP - } - - /** - * Gets the name of the host the PostgreSQL database is running on. - * - * @return name of the host the PostgreSQL database is running on - * @deprecated use {@link #getServerNames()} - */ - @Deprecated - public String getServerName() { - return serverNames[0]; - } - - /** - * Gets the name of the host(s) the PostgreSQL database is running on. - * - * @return name of the host(s) the PostgreSQL database is running on - */ - public String[] getServerNames() { - return serverNames; - } - - /** - * Sets the name of the host the PostgreSQL database is running on. If this is changed, it will - * only affect future calls to getConnection. The default value is {@code localhost}. - * - * @param serverName name of the host the PostgreSQL database is running on - * @deprecated use {@link #setServerNames(String[])} - */ - @Deprecated - public void setServerName(String serverName) { - this.setServerNames(new String[] { serverName }); - } - - /** - * Sets the name of the host(s) the PostgreSQL database is running on. If this is changed, it will - * only affect future calls to getConnection. The default value is {@code localhost}. - * - * @param serverNames name of the host(s) the PostgreSQL database is running on - */ - @SuppressWarnings("nullness") - public void setServerNames( String [] serverNames) { - if (serverNames == null || serverNames.length == 0) { - this.serverNames = new String[] {"localhost"}; - } else { - serverNames = serverNames.clone(); - for (int i = 0; i < serverNames.length; i++) { - String serverName = serverNames[i]; - if (serverName == null || serverName.equals("")) { - serverNames[i] = "localhost"; + private static final Logger LOGGER = Logger.getLogger(BaseDataSource.class.getName()); + + // Standard properties, defined in the JDBC 2.0 Optional Package spec + private String[] serverNames = new String[]{"localhost"}; + private String databaseName = ""; + private String user; + private String password; + private int[] portNumbers = new int[]{0}; + + // Map for all other properties + private Properties properties = new Properties(); + + /* + * Ensure the driver is loaded as JDBC Driver might be invisible to Java's ServiceLoader. + * Usually, {@code Class.forName(...)} is not required as {@link DriverManager} detects JDBC drivers + * via {@code META-INF/services/java.sql.Driver} entries. However there might be cases when the driver + * is located at the application level classloader, thus it might be required to perform manual + * registration of the driver. + */ + static { + try { + Class.forName("net.bitnine.agensgraph.Driver"); + } catch (ClassNotFoundException e) { + throw new IllegalStateException( + "BaseDataSource is unable to load net.bitnine.agensgraph.Driver. Please check if you have proper AgensGraph JDBC Driver jar on the classpath", + e); } - } - this.serverNames = serverNames; - } - } - - /** - * Gets the name of the PostgreSQL database, running on the server identified by the serverName - * property. - * - * @return name of the PostgreSQL database - */ - public String getDatabaseName() { - return databaseName; - } - - /** - * Sets the name of the PostgreSQL database, running on the server identified by the serverName - * property. If this is changed, it will only affect future calls to getConnection. - * - * @param databaseName name of the PostgreSQL database - */ - public void setDatabaseName( String databaseName) { - this.databaseName = databaseName; - } - - /** - * Gets a description of this DataSource-ish thing. Must be customized by subclasses. - * - * @return description of this DataSource-ish thing - */ - public abstract String getDescription(); - - /** - * Gets the user to connect as by default. If this is not specified, you must use the - * getConnection method which takes a user and password as parameters. - * - * @return user to connect as by default - */ - public String getUser() { - return user; - } - - /** - * Sets the user to connect as by default. If this is not specified, you must use the - * getConnection method which takes a user and password as parameters. If this is changed, it will - * only affect future calls to getConnection. - * - * @param user user to connect as by default - */ - public void setUser( String user) { - this.user = user; - } - - /** - * Gets the password to connect with by default. If this is not specified but a password is needed - * to log in, you must use the getConnection method which takes a user and password as parameters. - * - * @return password to connect with by default - */ - public String getPassword() { - return password; - } - - /** - * Sets the password to connect with by default. If this is not specified but a password is needed - * to log in, you must use the getConnection method which takes a user and password as parameters. - * If this is changed, it will only affect future calls to getConnection. - * - * @param password password to connect with by default - */ - public void setPassword( String password) { - this.password = password; - } - - /** - * Gets the port which the PostgreSQL server is listening on for TCP/IP connections. - * - * @return The port, or 0 if the default port will be used. - * @deprecated use {@link #getPortNumbers()} - */ - @Deprecated - public int getPortNumber() { - if (portNumbers == null || portNumbers.length == 0) { - return 0; - } - return portNumbers[0]; - } - - /** - * Gets the port(s) which the PostgreSQL server is listening on for TCP/IP connections. - * - * @return The port(s), or 0 if the default port will be used. - */ - public int[] getPortNumbers() { - return portNumbers; - } - - /** - * Sets the port which the PostgreSQL server is listening on for TCP/IP connections. Be sure the - * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0, - * the default port will be used. - * - * @param portNumber port which the PostgreSQL server is listening on for TCP/IP - * @deprecated use {@link #setPortNumbers(int[])} - */ - @Deprecated - public void setPortNumber(int portNumber) { - setPortNumbers(new int[] { portNumber }); - } - - /** - * Sets the port(s) which the PostgreSQL server is listening on for TCP/IP connections. Be sure the - * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0, - * the default port will be used. - * - * @param portNumbers port(s) which the PostgreSQL server is listening on for TCP/IP - */ - public void setPortNumbers(int [] portNumbers) { - if (portNumbers == null || portNumbers.length == 0) { - portNumbers = new int[] { 0 }; - } - this.portNumbers = Arrays.copyOf(portNumbers, portNumbers.length); - } - - /** - * @return command line options for this connection - */ - public String getOptions() { - return PGProperty.OPTIONS.get(properties); - } - - /** - * Set command line options for this connection - * - * @param options string to set options to - */ - public void setOptions( String options) { - PGProperty.OPTIONS.set(properties, options); - } - - /** - * @return login timeout - * @see PGProperty#LOGIN_TIMEOUT - */ - @Override - public int getLoginTimeout() { - return PGProperty.LOGIN_TIMEOUT.getIntNoCheck(properties); - } - - /** - * @param loginTimeout login timeout - * @see PGProperty#LOGIN_TIMEOUT - */ - @Override - public void setLoginTimeout(int loginTimeout) { - PGProperty.LOGIN_TIMEOUT.set(properties, loginTimeout); - } - - /** - * @return connect timeout - * @see PGProperty#CONNECT_TIMEOUT - */ - public int getConnectTimeout() { - return PGProperty.CONNECT_TIMEOUT.getIntNoCheck(properties); - } - - /** - * @param connectTimeout connect timeout - * @see PGProperty#CONNECT_TIMEOUT - */ - public void setConnectTimeout(int connectTimeout) { - PGProperty.CONNECT_TIMEOUT.set(properties, connectTimeout); - } - - /** - * @return protocol version - * @see PGProperty#PROTOCOL_VERSION - */ - public int getProtocolVersion() { - if (!PGProperty.PROTOCOL_VERSION.isPresent(properties)) { - return 0; - } else { - return PGProperty.PROTOCOL_VERSION.getIntNoCheck(properties); - } - } - - /** - * @param protocolVersion protocol version - * @see PGProperty#PROTOCOL_VERSION - */ - public void setProtocolVersion(int protocolVersion) { - if (protocolVersion == 0) { - PGProperty.PROTOCOL_VERSION.set(properties, null); - } else { - PGProperty.PROTOCOL_VERSION.set(properties, protocolVersion); - } - } - - /** - * @return receive buffer size - * @see PGProperty#RECEIVE_BUFFER_SIZE - */ - public int getReceiveBufferSize() { - return PGProperty.RECEIVE_BUFFER_SIZE.getIntNoCheck(properties); - } - - /** - * @param nbytes receive buffer size - * @see PGProperty#RECEIVE_BUFFER_SIZE - */ - public void setReceiveBufferSize(int nbytes) { - PGProperty.RECEIVE_BUFFER_SIZE.set(properties, nbytes); - } - - /** - * @return send buffer size - * @see PGProperty#SEND_BUFFER_SIZE - */ - public int getSendBufferSize() { - return PGProperty.SEND_BUFFER_SIZE.getIntNoCheck(properties); - } - - /** - * @param nbytes send buffer size - * @see PGProperty#SEND_BUFFER_SIZE - */ - public void setSendBufferSize(int nbytes) { - PGProperty.SEND_BUFFER_SIZE.set(properties, nbytes); - } - - /** - * @param count prepare threshold - * @see PGProperty#PREPARE_THRESHOLD - */ - public void setPrepareThreshold(int count) { - PGProperty.PREPARE_THRESHOLD.set(properties, count); - } - - /** - * @return prepare threshold - * @see PGProperty#PREPARE_THRESHOLD - */ - public int getPrepareThreshold() { - return PGProperty.PREPARE_THRESHOLD.getIntNoCheck(properties); - } - - /** - * @return prepared statement cache size (number of statements per connection) - * @see PGProperty#PREPARED_STATEMENT_CACHE_QUERIES - */ - public int getPreparedStatementCacheQueries() { - return PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.getIntNoCheck(properties); - } - - /** - * @param cacheSize prepared statement cache size (number of statements per connection) - * @see PGProperty#PREPARED_STATEMENT_CACHE_QUERIES - */ - public void setPreparedStatementCacheQueries(int cacheSize) { - PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.set(properties, cacheSize); - } - - /** - * @return prepared statement cache size (number of megabytes per connection) - * @see PGProperty#PREPARED_STATEMENT_CACHE_SIZE_MIB - */ - public int getPreparedStatementCacheSizeMiB() { - return PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.getIntNoCheck(properties); - } - - /** - * @param cacheSize statement cache size (number of megabytes per connection) - * @see PGProperty#PREPARED_STATEMENT_CACHE_SIZE_MIB - */ - public void setPreparedStatementCacheSizeMiB(int cacheSize) { - PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.set(properties, cacheSize); - } - - /** - * @return database metadata cache fields size (number of fields cached per connection) - * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS - */ - public int getDatabaseMetadataCacheFields() { - return PGProperty.DATABASE_METADATA_CACHE_FIELDS.getIntNoCheck(properties); - } - - /** - * @param cacheSize database metadata cache fields size (number of fields cached per connection) - * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS - */ - public void setDatabaseMetadataCacheFields(int cacheSize) { - PGProperty.DATABASE_METADATA_CACHE_FIELDS.set(properties, cacheSize); - } - - /** - * @return database metadata cache fields size (number of megabytes per connection) - * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB - */ - public int getDatabaseMetadataCacheFieldsMiB() { - return PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.getIntNoCheck(properties); - } - - /** - * @param cacheSize database metadata cache fields size (number of megabytes per connection) - * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB - */ - public void setDatabaseMetadataCacheFieldsMiB(int cacheSize) { - PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.set(properties, cacheSize); - } - - /** - * @param fetchSize default fetch size - * @see PGProperty#DEFAULT_ROW_FETCH_SIZE - */ - public void setDefaultRowFetchSize(int fetchSize) { - PGProperty.DEFAULT_ROW_FETCH_SIZE.set(properties, fetchSize); - } - - /** - * @return default fetch size - * @see PGProperty#DEFAULT_ROW_FETCH_SIZE - */ - public int getDefaultRowFetchSize() { - return PGProperty.DEFAULT_ROW_FETCH_SIZE.getIntNoCheck(properties); - } - - /** - * @param unknownLength unknown length - * @see PGProperty#UNKNOWN_LENGTH - */ - public void setUnknownLength(int unknownLength) { - PGProperty.UNKNOWN_LENGTH.set(properties, unknownLength); - } - - /** - * @return unknown length - * @see PGProperty#UNKNOWN_LENGTH - */ - public int getUnknownLength() { - return PGProperty.UNKNOWN_LENGTH.getIntNoCheck(properties); - } - - /** - * @param seconds socket timeout - * @see PGProperty#SOCKET_TIMEOUT - */ - public void setSocketTimeout(int seconds) { - PGProperty.SOCKET_TIMEOUT.set(properties, seconds); - } - - /** - * @return socket timeout - * @see PGProperty#SOCKET_TIMEOUT - */ - public int getSocketTimeout() { - return PGProperty.SOCKET_TIMEOUT.getIntNoCheck(properties); - } - - /** - * @param seconds timeout that is used for sending cancel command - * @see PGProperty#CANCEL_SIGNAL_TIMEOUT - */ - public void setCancelSignalTimeout(int seconds) { - PGProperty.CANCEL_SIGNAL_TIMEOUT.set(properties, seconds); - } - - /** - * @return timeout that is used for sending cancel command in seconds - * @see PGProperty#CANCEL_SIGNAL_TIMEOUT - */ - public int getCancelSignalTimeout() { - return PGProperty.CANCEL_SIGNAL_TIMEOUT.getIntNoCheck(properties); - } - - /** - * @param enabled if SSL is enabled - * @see PGProperty#SSL - */ - public void setSsl(boolean enabled) { - if (enabled) { - PGProperty.SSL.set(properties, true); - } else { - PGProperty.SSL.set(properties, false); - } - } - - /** - * @return true if SSL is enabled - * @see PGProperty#SSL - */ - public boolean getSsl() { - // "true" if "ssl" is set but empty - return PGProperty.SSL.getBoolean(properties) || "".equals(PGProperty.SSL.get(properties)); - } - - /** - * @param classname SSL factory class name - * @see PGProperty#SSL_FACTORY - */ - public void setSslfactory(String classname) { - PGProperty.SSL_FACTORY.set(properties, classname); - } - - /** - * @return SSL factory class name - * @see PGProperty#SSL_FACTORY - */ - public String getSslfactory() { - return PGProperty.SSL_FACTORY.get(properties); - } - - /** - * @return SSL mode - * @see PGProperty#SSL_MODE - */ - public String getSslMode() { - return PGProperty.SSL_MODE.get(properties); - } - - /** - * @param mode SSL mode - * @see PGProperty#SSL_MODE - */ - public void setSslMode( String mode) { - PGProperty.SSL_MODE.set(properties, mode); - } - - /** - * @return SSL mode - * @see PGProperty#SSL_FACTORY_ARG - */ - public String getSslFactoryArg() { - return PGProperty.SSL_FACTORY_ARG.get(properties); - } - - /** - * @param arg argument forwarded to SSL factory - * @see PGProperty#SSL_FACTORY_ARG - */ - public void setSslFactoryArg( String arg) { - PGProperty.SSL_FACTORY_ARG.set(properties, arg); - } - - /** - * @return argument forwarded to SSL factory - * @see PGProperty#SSL_HOSTNAME_VERIFIER - */ - public String getSslHostnameVerifier() { - return PGProperty.SSL_HOSTNAME_VERIFIER.get(properties); - } - - /** - * @param className SSL hostname verifier - * @see PGProperty#SSL_HOSTNAME_VERIFIER - */ - public void setSslHostnameVerifier( String className) { - PGProperty.SSL_HOSTNAME_VERIFIER.set(properties, className); - } - - /** - * @return className SSL hostname verifier - * @see PGProperty#SSL_CERT - */ - public String getSslCert() { - return PGProperty.SSL_CERT.get(properties); - } - - /** - * @param file SSL certificate - * @see PGProperty#SSL_CERT - */ - public void setSslCert( String file) { - PGProperty.SSL_CERT.set(properties, file); - } - - /** - * @return SSL certificate - * @see PGProperty#SSL_KEY - */ - public String getSslKey() { - return PGProperty.SSL_KEY.get(properties); - } - - /** - * @param file SSL key - * @see PGProperty#SSL_KEY - */ - public void setSslKey( String file) { - PGProperty.SSL_KEY.set(properties, file); - } - - /** - * @return SSL root certificate - * @see PGProperty#SSL_ROOT_CERT - */ - public String getSslRootCert() { - return PGProperty.SSL_ROOT_CERT.get(properties); - } - - /** - * @param file SSL root certificate - * @see PGProperty#SSL_ROOT_CERT - */ - public void setSslRootCert( String file) { - PGProperty.SSL_ROOT_CERT.set(properties, file); - } - - /** - * @return SSL password - * @see PGProperty#SSL_PASSWORD - */ - public String getSslPassword() { - return PGProperty.SSL_PASSWORD.get(properties); - } - - /** - * @param password SSL password - * @see PGProperty#SSL_PASSWORD - */ - public void setSslPassword( String password) { - PGProperty.SSL_PASSWORD.set(properties, password); - } - - /** - * @return SSL password callback - * @see PGProperty#SSL_PASSWORD_CALLBACK - */ - public String getSslPasswordCallback() { - return PGProperty.SSL_PASSWORD_CALLBACK.get(properties); - } - - /** - * @param className SSL password callback class name - * @see PGProperty#SSL_PASSWORD_CALLBACK - */ - public void setSslPasswordCallback( String className) { - PGProperty.SSL_PASSWORD_CALLBACK.set(properties, className); - } - - /** - * @param applicationName application name - * @see PGProperty#APPLICATION_NAME - */ - public void setApplicationName( String applicationName) { - PGProperty.APPLICATION_NAME.set(properties, applicationName); - } - - /** - * @return application name - * @see PGProperty#APPLICATION_NAME - */ - public String getApplicationName() { - return castNonNull(PGProperty.APPLICATION_NAME.get(properties)); - } - - /** - * @param targetServerType target server type - * @see PGProperty#TARGET_SERVER_TYPE - */ - public void setTargetServerType( String targetServerType) { - PGProperty.TARGET_SERVER_TYPE.set(properties, targetServerType); - } - - /** - * @return target server type - * @see PGProperty#TARGET_SERVER_TYPE - */ - public String getTargetServerType() { - return castNonNull(PGProperty.TARGET_SERVER_TYPE.get(properties)); - } - - /** - * @param loadBalanceHosts load balance hosts - * @see PGProperty#LOAD_BALANCE_HOSTS - */ - public void setLoadBalanceHosts(boolean loadBalanceHosts) { - PGProperty.LOAD_BALANCE_HOSTS.set(properties, loadBalanceHosts); - } - - /** - * @return load balance hosts - * @see PGProperty#LOAD_BALANCE_HOSTS - */ - public boolean getLoadBalanceHosts() { - return PGProperty.LOAD_BALANCE_HOSTS.isPresent(properties); - } - - /** - * @param hostRecheckSeconds host recheck seconds - * @see PGProperty#HOST_RECHECK_SECONDS - */ - public void setHostRecheckSeconds(int hostRecheckSeconds) { - PGProperty.HOST_RECHECK_SECONDS.set(properties, hostRecheckSeconds); - } - - /** - * @return host recheck seconds - * @see PGProperty#HOST_RECHECK_SECONDS - */ - public int getHostRecheckSeconds() { - return PGProperty.HOST_RECHECK_SECONDS.getIntNoCheck(properties); - } - - /** - * @param enabled if TCP keep alive should be enabled - * @see PGProperty#TCP_KEEP_ALIVE - */ - public void setTcpKeepAlive(boolean enabled) { - PGProperty.TCP_KEEP_ALIVE.set(properties, enabled); - } - - /** - * @return true if TCP keep alive is enabled - * @see PGProperty#TCP_KEEP_ALIVE - */ - public boolean getTcpKeepAlive() { - return PGProperty.TCP_KEEP_ALIVE.getBoolean(properties); - } - - /** - * @param enabled if binary transfer should be enabled - * @see PGProperty#BINARY_TRANSFER - */ - public void setBinaryTransfer(boolean enabled) { - PGProperty.BINARY_TRANSFER.set(properties, enabled); - } - - /** - * @return true if binary transfer is enabled - * @see PGProperty#BINARY_TRANSFER - */ - public boolean getBinaryTransfer() { - return PGProperty.BINARY_TRANSFER.getBoolean(properties); - } - - /** - * @param oidList list of OIDs that are allowed to use binary transfer - * @see PGProperty#BINARY_TRANSFER_ENABLE - */ - public void setBinaryTransferEnable( String oidList) { - PGProperty.BINARY_TRANSFER_ENABLE.set(properties, oidList); - } - - /** - * @return list of OIDs that are allowed to use binary transfer - * @see PGProperty#BINARY_TRANSFER_ENABLE - */ - public String getBinaryTransferEnable() { - return castNonNull(PGProperty.BINARY_TRANSFER_ENABLE.get(properties)); - } - - /** - * @param oidList list of OIDs that are not allowed to use binary transfer - * @see PGProperty#BINARY_TRANSFER_DISABLE - */ - public void setBinaryTransferDisable( String oidList) { - PGProperty.BINARY_TRANSFER_DISABLE.set(properties, oidList); - } - - /** - * @return list of OIDs that are not allowed to use binary transfer - * @see PGProperty#BINARY_TRANSFER_DISABLE - */ - public String getBinaryTransferDisable() { - return castNonNull(PGProperty.BINARY_TRANSFER_DISABLE.get(properties)); - } - - /** - * @return string type - * @see PGProperty#STRING_TYPE - */ - public String getStringType() { - return PGProperty.STRING_TYPE.get(properties); - } - - /** - * @param stringType string type - * @see PGProperty#STRING_TYPE - */ - public void setStringType( String stringType) { - PGProperty.STRING_TYPE.set(properties, stringType); - } - - /** - * @return true if column sanitizer is disabled - * @see PGProperty#DISABLE_COLUMN_SANITISER - */ - public boolean isColumnSanitiserDisabled() { - return PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(properties); - } - - /** - * @return true if column sanitizer is disabled - * @see PGProperty#DISABLE_COLUMN_SANITISER - */ - public boolean getDisableColumnSanitiser() { - return PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(properties); - } - - /** - * @param disableColumnSanitiser if column sanitizer should be disabled - * @see PGProperty#DISABLE_COLUMN_SANITISER - */ - public void setDisableColumnSanitiser(boolean disableColumnSanitiser) { - PGProperty.DISABLE_COLUMN_SANITISER.set(properties, disableColumnSanitiser); - } - - /** - * @return current schema - * @see PGProperty#CURRENT_SCHEMA - */ - public String getCurrentSchema() { - return PGProperty.CURRENT_SCHEMA.get(properties); - } - - /** - * @param currentSchema current schema - * @see PGProperty#CURRENT_SCHEMA - */ - public void setCurrentSchema( String currentSchema) { - PGProperty.CURRENT_SCHEMA.set(properties, currentSchema); - } - - /** - * @return true if connection is readonly - * @see PGProperty#READ_ONLY - */ - public boolean getReadOnly() { - return PGProperty.READ_ONLY.getBoolean(properties); - } - - /** - * @param readOnly if connection should be readonly - * @see PGProperty#READ_ONLY - */ - public void setReadOnly(boolean readOnly) { - PGProperty.READ_ONLY.set(properties, readOnly); - } - - /** - * @return The behavior when set read only - * @see PGProperty#READ_ONLY_MODE - */ - public String getReadOnlyMode() { - return castNonNull(PGProperty.READ_ONLY_MODE.get(properties)); - } - - /** - * @param mode the behavior when set read only - * @see PGProperty#READ_ONLY_MODE - */ - public void setReadOnlyMode( String mode) { - PGProperty.READ_ONLY_MODE.set(properties, mode); - } - - /** - * @return true if driver should log unclosed connections - * @see PGProperty#LOG_UNCLOSED_CONNECTIONS - */ - public boolean getLogUnclosedConnections() { - return PGProperty.LOG_UNCLOSED_CONNECTIONS.getBoolean(properties); - } - - /** - * @param enabled true if driver should log unclosed connections - * @see PGProperty#LOG_UNCLOSED_CONNECTIONS - */ - public void setLogUnclosedConnections(boolean enabled) { - PGProperty.LOG_UNCLOSED_CONNECTIONS.set(properties, enabled); - } - - /** - * @return true if driver should log include detail in server error messages - * @see PGProperty#LOG_SERVER_ERROR_DETAIL - */ - public boolean getLogServerErrorDetail() { - return PGProperty.LOG_SERVER_ERROR_DETAIL.getBoolean(properties); - } - - /** - * @param enabled true if driver should include detail in server error messages - * @see PGProperty#LOG_SERVER_ERROR_DETAIL - */ - public void setLogServerErrorDetail(boolean enabled) { - PGProperty.LOG_SERVER_ERROR_DETAIL.set(properties, enabled); - } - - /** - * @return assumed minimal server version - * @see PGProperty#ASSUME_MIN_SERVER_VERSION - */ - public String getAssumeMinServerVersion() { - return PGProperty.ASSUME_MIN_SERVER_VERSION.get(properties); - } - - /** - * @param minVersion assumed minimal server version - * @see PGProperty#ASSUME_MIN_SERVER_VERSION - */ - public void setAssumeMinServerVersion( String minVersion) { - PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, minVersion); - } - - /** - * @return JAAS application name - * @see PGProperty#JAAS_APPLICATION_NAME - */ - public String getJaasApplicationName() { - return PGProperty.JAAS_APPLICATION_NAME.get(properties); - } - - /** - * @param name JAAS application name - * @see PGProperty#JAAS_APPLICATION_NAME - */ - public void setJaasApplicationName( String name) { - PGProperty.JAAS_APPLICATION_NAME.set(properties, name); - } - - /** - * @return true if perform JAAS login before GSS authentication - * @see PGProperty#JAAS_LOGIN - */ - public boolean getJaasLogin() { - return PGProperty.JAAS_LOGIN.getBoolean(properties); - } - - /** - * @param doLogin true if perform JAAS login before GSS authentication - * @see PGProperty#JAAS_LOGIN - */ - public void setJaasLogin(boolean doLogin) { - PGProperty.JAAS_LOGIN.set(properties, doLogin); - } - - /** - * @return Kerberos server name - * @see PGProperty#KERBEROS_SERVER_NAME - */ - public String getKerberosServerName() { - return PGProperty.KERBEROS_SERVER_NAME.get(properties); - } - - /** - * @param serverName Kerberos server name - * @see PGProperty#KERBEROS_SERVER_NAME - */ - public void setKerberosServerName( String serverName) { - PGProperty.KERBEROS_SERVER_NAME.set(properties, serverName); - } - - /** - * @return true if use SPNEGO - * @see PGProperty#USE_SPNEGO - */ - public boolean getUseSpNego() { - return PGProperty.USE_SPNEGO.getBoolean(properties); - } - - /** - * @param use true if use SPNEGO - * @see PGProperty#USE_SPNEGO - */ - public void setUseSpNego(boolean use) { - PGProperty.USE_SPNEGO.set(properties, use); - } - - /** - * @return GSS mode: auto, sspi, or gssapi - * @see PGProperty#GSS_LIB - */ - public String getGssLib() { - return PGProperty.GSS_LIB.get(properties); - } - - /** - * @param lib GSS mode: auto, sspi, or gssapi - * @see PGProperty#GSS_LIB - */ - public void setGssLib( String lib) { - PGProperty.GSS_LIB.set(properties, lib); - } - - /** - * - * @return GSS encryption mode: disable, prefer or require - */ - public String getGssEncMode() { - return castNonNull(PGProperty.GSS_ENC_MODE.get(properties)); - } - - /** - * - * @param mode encryption mode: disable, prefer or require - */ - public void setGssEncMode( String mode) { - PGProperty.GSS_ENC_MODE.set(properties, mode); - } - - /** - * @return SSPI service class - * @see PGProperty#SSPI_SERVICE_CLASS - */ - public String getSspiServiceClass() { - return PGProperty.SSPI_SERVICE_CLASS.get(properties); - } - - /** - * @param serviceClass SSPI service class - * @see PGProperty#SSPI_SERVICE_CLASS - */ - public void setSspiServiceClass( String serviceClass) { - PGProperty.SSPI_SERVICE_CLASS.set(properties, serviceClass); - } - - /** - * @return if connection allows encoding changes - * @see PGProperty#ALLOW_ENCODING_CHANGES - */ - public boolean getAllowEncodingChanges() { - return PGProperty.ALLOW_ENCODING_CHANGES.getBoolean(properties); - } - - /** - * @param allow if connection allows encoding changes - * @see PGProperty#ALLOW_ENCODING_CHANGES - */ - public void setAllowEncodingChanges(boolean allow) { - PGProperty.ALLOW_ENCODING_CHANGES.set(properties, allow); - } - - /** - * @return socket factory class name - * @see PGProperty#SOCKET_FACTORY - */ - public String getSocketFactory() { - return PGProperty.SOCKET_FACTORY.get(properties); - } - - /** - * @param socketFactoryClassName socket factory class name - * @see PGProperty#SOCKET_FACTORY - */ - public void setSocketFactory( String socketFactoryClassName) { - PGProperty.SOCKET_FACTORY.set(properties, socketFactoryClassName); - } - - /** - * @return socket factory argument - * @see PGProperty#SOCKET_FACTORY_ARG - */ - public String getSocketFactoryArg() { - return PGProperty.SOCKET_FACTORY_ARG.get(properties); - } - - /** - * @param socketFactoryArg socket factory argument - * @see PGProperty#SOCKET_FACTORY_ARG - */ - public void setSocketFactoryArg( String socketFactoryArg) { - PGProperty.SOCKET_FACTORY_ARG.set(properties, socketFactoryArg); - } - - /** - * @param replication set to 'database' for logical replication or 'true' for physical replication - * @see PGProperty#REPLICATION - */ - public void setReplication( String replication) { - PGProperty.REPLICATION.set(properties, replication); - } - - /** - * @return 'select', "callIfNoReturn', or 'call' - * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE - */ - public String getEscapeSyntaxCallMode() { - return castNonNull(PGProperty.ESCAPE_SYNTAX_CALL_MODE.get(properties)); - } - - /** - * @param callMode the call mode to use for JDBC escape call syntax - * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE - */ - public void setEscapeSyntaxCallMode( String callMode) { - PGProperty.ESCAPE_SYNTAX_CALL_MODE.set(properties, callMode); - } - - /** - * @return null, 'database', or 'true - * @see PGProperty#REPLICATION - */ - public String getReplication() { - return PGProperty.REPLICATION.get(properties); - } - - /** - * @return Logger Level of the JDBC Driver - * @see PGProperty#LOGGER_LEVEL - */ - public String getLoggerLevel() { - return PGProperty.LOGGER_LEVEL.get(properties); - } - - /** - * @param loggerLevel of the JDBC Driver - * @see PGProperty#LOGGER_LEVEL - */ - public void setLoggerLevel( String loggerLevel) { - PGProperty.LOGGER_LEVEL.set(properties, loggerLevel); - } - - /** - * @return File output of the Logger. - * @see PGProperty#LOGGER_FILE - */ - public String getLoggerFile() { - ExpressionProperties exprProps = new ExpressionProperties(properties, System.getProperties()); - return PGProperty.LOGGER_FILE.get(exprProps); - } - - /** - * @param loggerFile File output of the Logger. - * @see PGProperty#LOGGER_LEVEL - */ - public void setLoggerFile( String loggerFile) { - PGProperty.LOGGER_FILE.set(properties, loggerFile); - } - - /** - * Generates a {@link DriverManager} URL from the other properties supplied. - * - * @return {@link DriverManager} URL from the other properties supplied - */ - public String getUrl() { - StringBuilder url = new StringBuilder(100); - url.append("jdbc:agensgraph://"); - for (int i = 0; i < serverNames.length; i++) { - if (i > 0) { - url.append(","); - } - url.append(serverNames[i]); - if (portNumbers != null && portNumbers.length >= i && portNumbers[i] != 0) { - url.append(":").append(portNumbers[i]); - } - } - url.append("/"); - if (databaseName != null) { - url.append(URLCoder.encode(databaseName)); - } - - StringBuilder query = new StringBuilder(100); - for (PGProperty property : PGProperty.values()) { - if (property.isPresent(properties)) { - if (query.length() != 0) { - query.append("&"); + } + + /** + * Gets a connection to the PostgreSQL database. The database is identified by the DataSource + * properties serverName, databaseName, and portNumber. The user to connect as is identified by + * the DataSource properties user and password. + * + * @return A valid database connection. + * @throws SQLException Occurs when the database connection cannot be established. + */ + public Connection getConnection() throws SQLException { + return getConnection(user, password); + } + + /** + * Gets a connection to the PostgreSQL database. The database is identified by the DataSource + * properties serverName, databaseName, and portNumber. The user to connect as is identified by + * the arguments user and password, which override the DataSource properties by the same name. + * + * @param user user + * @param password password + * @return A valid database connection. + * @throws SQLException Occurs when the database connection cannot be established. + */ + public Connection getConnection(String user, String password) + throws SQLException { + try { + Connection con = DriverManager.getConnection(getUrl(), user, password); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "Created a {0} for {1} at {2}", + new Object[]{getDescription(), user, getUrl()}); + } + return con; + } catch (SQLException e) { + LOGGER.log(Level.FINE, "Failed to create a {0} for {1} at {2}: {3}", + new Object[]{getDescription(), user, getUrl(), e}); + throw e; } - query.append(property.getName()); - query.append("="); - String value = castNonNull(property.get(properties)); - query.append(URLCoder.encode(value)); - } - } - - if (query.length() > 0) { - url.append("?"); - url.append(query); - } - - return url.toString(); - } - - /** - * Generates a {@link DriverManager} URL from the other properties supplied. - * - * @return {@link DriverManager} URL from the other properties supplied - */ - public String getURL() { - return getUrl(); - } - - /** - * Sets properties from a {@link DriverManager} URL. - * - * @param url properties to set - */ - public void setUrl(String url) { - - Properties p = Driver.parseURL(url, null); - - if (p == null) { - throw new IllegalArgumentException("URL invalid " + url); - } - for (PGProperty property : PGProperty.values()) { - if (!this.properties.containsKey(property.getName())) { - setProperty(property, property.get(p)); - } - } - } - - /** - * Sets properties from a {@link DriverManager} URL. - * Added to follow convention used in other DBMS. - * - * @param url properties to set - */ - public void setURL(String url) { - setUrl(url); - } - - public String getProperty(String name) throws SQLException { - PGProperty pgProperty = PGProperty.forName(name); - if (pgProperty != null) { - return getProperty(pgProperty); - } else { - throw new PSQLException(GT.tr("Unsupported property name: {0}", name), - PSQLState.INVALID_PARAMETER_VALUE); - } - } - - public void setProperty(String name, String value) throws SQLException { - PGProperty pgProperty = PGProperty.forName(name); - if (pgProperty != null) { - setProperty(pgProperty, value); - } else { - throw new PSQLException(GT.tr("Unsupported property name: {0}", name), - PSQLState.INVALID_PARAMETER_VALUE); - } - } - - public String getProperty(PGProperty property) { - return property.get(properties); - } - - public void setProperty(PGProperty property, String value) { - if (value == null) { - // TODO: this is not consistent with PGProperty.PROPERTY.set(prop, null) - // PGProperty removes an entry for put(null) call, however here we just ignore null - return; - } - switch (property) { - case PG_HOST: - setServerNames(value.split(",")); - break; - case PG_PORT: - String[] ps = value.split(","); - int[] ports = new int[ps.length]; - for (int i = 0 ; i < ps.length; i++) { - try { - ports[i] = Integer.parseInt(ps[i]); - } catch (NumberFormatException e) { - ports[i] = 0; - } + } + + /** + * This implementation don't use a LogWriter. + */ + @Override + public PrintWriter getLogWriter() { + return null; + } + + /** + * This implementation don't use a LogWriter. + * + * @param printWriter Not used + */ + @Override + public void setLogWriter(PrintWriter printWriter) { + // NOOP + } + + /** + * Gets the name of the host the PostgreSQL database is running on. + * + * @return name of the host the PostgreSQL database is running on + * @deprecated use {@link #getServerNames()} + */ + @Deprecated + public String getServerName() { + return serverNames[0]; + } + + /** + * Gets the name of the host(s) the PostgreSQL database is running on. + * + * @return name of the host(s) the PostgreSQL database is running on + */ + public String[] getServerNames() { + return serverNames; + } + + /** + * Sets the name of the host the PostgreSQL database is running on. If this is changed, it will + * only affect future calls to getConnection. The default value is {@code localhost}. + * + * @param serverName name of the host the PostgreSQL database is running on + * @deprecated use {@link #setServerNames(String[])} + */ + @Deprecated + public void setServerName(String serverName) { + this.setServerNames(new String[]{serverName}); + } + + /** + * Sets the name of the host(s) the PostgreSQL database is running on. If this is changed, it will + * only affect future calls to getConnection. The default value is {@code localhost}. + * + * @param serverNames name of the host(s) the PostgreSQL database is running on + */ + @SuppressWarnings("nullness") + public void setServerNames(String[] serverNames) { + if (serverNames == null || serverNames.length == 0) { + this.serverNames = new String[]{"localhost"}; + } else { + serverNames = serverNames.clone(); + for (int i = 0; i < serverNames.length; i++) { + String serverName = serverNames[i]; + if (serverName == null || serverName.equals("")) { + serverNames[i] = "localhost"; + } + } + this.serverNames = serverNames; } - setPortNumbers(ports); - break; - case PG_DBNAME: - setDatabaseName(value); - break; - case USER: - setUser(value); - break; - case PASSWORD: - setPassword(value); - break; - default: - properties.setProperty(property.getName(), value); - } - } - - /** - * Generates a reference using the appropriate object factory. - * - * @return reference using the appropriate object factory - */ - protected Reference createReference() { - return new Reference(getClass().getName(), PGObjectFactory.class.getName(), null); - } - - public Reference getReference() throws NamingException { - Reference ref = createReference(); - StringBuilder serverString = new StringBuilder(); - for (int i = 0; i < serverNames.length; i++) { - if (i > 0) { - serverString.append(","); - } - String serverName = serverNames[i]; - serverString.append(serverName); - } - ref.add(new StringRefAddr("serverName", serverString.toString())); - - StringBuilder portString = new StringBuilder(); - for (int i = 0; i < portNumbers.length; i++) { - if (i > 0) { - portString.append(","); - } - int p = portNumbers[i]; - portString.append(Integer.toString(p)); - } - ref.add(new StringRefAddr("portNumber", portString.toString())); - ref.add(new StringRefAddr("databaseName", databaseName)); - if (user != null) { - ref.add(new StringRefAddr("user", user)); - } - if (password != null) { - ref.add(new StringRefAddr("password", password)); - } - - for (PGProperty property : PGProperty.values()) { - if (property.isPresent(properties)) { - String value = castNonNull(property.get(properties)); - ref.add(new StringRefAddr(property.getName(), value)); - } - } - - return ref; - } - - public void setFromReference(Reference ref) { - databaseName = getReferenceProperty(ref, "databaseName"); - String portNumberString = getReferenceProperty(ref, "portNumber"); - if (portNumberString != null) { - String[] ps = portNumberString.split(","); - int[] ports = new int[ps.length]; - for (int i = 0; i < ps.length; i++) { - try { - ports[i] = Integer.parseInt(ps[i]); - } catch (NumberFormatException e) { - ports[i] = 0; + } + + /** + * Gets the name of the PostgreSQL database, running on the server identified by the serverName + * property. + * + * @return name of the PostgreSQL database + */ + public String getDatabaseName() { + return databaseName; + } + + /** + * Sets the name of the PostgreSQL database, running on the server identified by the serverName + * property. If this is changed, it will only affect future calls to getConnection. + * + * @param databaseName name of the PostgreSQL database + */ + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + /** + * Gets a description of this DataSource-ish thing. Must be customized by subclasses. + * + * @return description of this DataSource-ish thing + */ + public abstract String getDescription(); + + /** + * Gets the user to connect as by default. If this is not specified, you must use the + * getConnection method which takes a user and password as parameters. + * + * @return user to connect as by default + */ + public String getUser() { + return user; + } + + /** + * Sets the user to connect as by default. If this is not specified, you must use the + * getConnection method which takes a user and password as parameters. If this is changed, it will + * only affect future calls to getConnection. + * + * @param user user to connect as by default + */ + public void setUser(String user) { + this.user = user; + } + + /** + * Gets the password to connect with by default. If this is not specified but a password is needed + * to log in, you must use the getConnection method which takes a user and password as parameters. + * + * @return password to connect with by default + */ + public String getPassword() { + return password; + } + + /** + * Sets the password to connect with by default. If this is not specified but a password is needed + * to log in, you must use the getConnection method which takes a user and password as parameters. + * If this is changed, it will only affect future calls to getConnection. + * + * @param password password to connect with by default + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Gets the port which the PostgreSQL server is listening on for TCP/IP connections. + * + * @return The port, or 0 if the default port will be used. + * @deprecated use {@link #getPortNumbers()} + */ + @Deprecated + public int getPortNumber() { + if (portNumbers == null || portNumbers.length == 0) { + return 0; + } + return portNumbers[0]; + } + + /** + * Gets the port(s) which the PostgreSQL server is listening on for TCP/IP connections. + * + * @return The port(s), or 0 if the default port will be used. + */ + public int[] getPortNumbers() { + return portNumbers; + } + + /** + * Sets the port which the PostgreSQL server is listening on for TCP/IP connections. Be sure the + * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0, + * the default port will be used. + * + * @param portNumber port which the PostgreSQL server is listening on for TCP/IP + * @deprecated use {@link #setPortNumbers(int[])} + */ + @Deprecated + public void setPortNumber(int portNumber) { + setPortNumbers(new int[]{portNumber}); + } + + /** + * Sets the port(s) which the PostgreSQL server is listening on for TCP/IP connections. Be sure the + * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0, + * the default port will be used. + * + * @param portNumbers port(s) which the PostgreSQL server is listening on for TCP/IP + */ + public void setPortNumbers(int[] portNumbers) { + if (portNumbers == null || portNumbers.length == 0) { + portNumbers = new int[]{0}; + } + this.portNumbers = Arrays.copyOf(portNumbers, portNumbers.length); + } + + /** + * @return command line options for this connection + */ + public String getOptions() { + return PGProperty.OPTIONS.getOrDefault(properties); + } + + /** + * Set command line options for this connection + * + * @param options string to set options to + */ + public void setOptions(String options) { + PGProperty.OPTIONS.set(properties, options); + } + + /** + * @return login timeout + * @see PGProperty#LOGIN_TIMEOUT + */ + @Override + public int getLoginTimeout() { + return PGProperty.LOGIN_TIMEOUT.getIntNoCheck(properties); + } + + /** + * @param loginTimeout login timeout + * @see PGProperty#LOGIN_TIMEOUT + */ + @Override + public void setLoginTimeout(int loginTimeout) { + PGProperty.LOGIN_TIMEOUT.set(properties, loginTimeout); + } + + /** + * @return connect timeout + * @see PGProperty#CONNECT_TIMEOUT + */ + public int getConnectTimeout() { + return PGProperty.CONNECT_TIMEOUT.getIntNoCheck(properties); + } + + /** + * @param connectTimeout connect timeout + * @see PGProperty#CONNECT_TIMEOUT + */ + public void setConnectTimeout(int connectTimeout) { + PGProperty.CONNECT_TIMEOUT.set(properties, connectTimeout); + } + + /** + * @return SSL ResponseTimeout + * @see PGProperty#SSL_RESPONSE_TIMEOUT + */ + public int getSslResponseTimeout() { + return PGProperty.SSL_RESPONSE_TIMEOUT.getIntNoCheck(properties); + } + + /** + * @param sslResponseTimeout ssl response timeout + * @see PGProperty#SSL_RESPONSE_TIMEOUT + */ + public void setSslResponseTimeout(int sslResponseTimeout) { + PGProperty.SSL_RESPONSE_TIMEOUT.set(properties, sslResponseTimeout); + } + + /** + * @return protocol version + * @see PGProperty#PROTOCOL_VERSION + */ + public int getProtocolVersion() { + if (!PGProperty.PROTOCOL_VERSION.isPresent(properties)) { + return 0; + } else { + return PGProperty.PROTOCOL_VERSION.getIntNoCheck(properties); + } + } + + /** + * @param protocolVersion protocol version + * @see PGProperty#PROTOCOL_VERSION + */ + public void setProtocolVersion(int protocolVersion) { + if (protocolVersion == 0) { + PGProperty.PROTOCOL_VERSION.set(properties, null); + } else { + PGProperty.PROTOCOL_VERSION.set(properties, protocolVersion); + } + } + + /** + * @return quoteReturningIdentifiers + * @see PGProperty#QUOTE_RETURNING_IDENTIFIERS + */ + public boolean getQuoteReturningIdentifiers() { + return PGProperty.QUOTE_RETURNING_IDENTIFIERS.getBoolean(properties); + } + + /** + * @param quoteIdentifiers indicate whether to quote identifiers + * @see PGProperty#QUOTE_RETURNING_IDENTIFIERS + */ + public void setQuoteReturningIdentifiers(boolean quoteIdentifiers) { + PGProperty.QUOTE_RETURNING_IDENTIFIERS.set(properties, quoteIdentifiers); + } + + /** + * @return receive buffer size + * @see PGProperty#RECEIVE_BUFFER_SIZE + */ + public int getReceiveBufferSize() { + return PGProperty.RECEIVE_BUFFER_SIZE.getIntNoCheck(properties); + } + + /** + * @param nbytes receive buffer size + * @see PGProperty#RECEIVE_BUFFER_SIZE + */ + public void setReceiveBufferSize(int nbytes) { + PGProperty.RECEIVE_BUFFER_SIZE.set(properties, nbytes); + } + + /** + * @return send buffer size + * @see PGProperty#SEND_BUFFER_SIZE + */ + public int getSendBufferSize() { + return PGProperty.SEND_BUFFER_SIZE.getIntNoCheck(properties); + } + + /** + * @param nbytes send buffer size + * @see PGProperty#SEND_BUFFER_SIZE + */ + public void setSendBufferSize(int nbytes) { + PGProperty.SEND_BUFFER_SIZE.set(properties, nbytes); + } + + /** + * @param count prepare threshold + * @see PGProperty#PREPARE_THRESHOLD + */ + public void setPrepareThreshold(int count) { + PGProperty.PREPARE_THRESHOLD.set(properties, count); + } + + /** + * @return prepare threshold + * @see PGProperty#PREPARE_THRESHOLD + */ + public int getPrepareThreshold() { + return PGProperty.PREPARE_THRESHOLD.getIntNoCheck(properties); + } + + /** + * @return prepared statement cache size (number of statements per connection) + * @see PGProperty#PREPARED_STATEMENT_CACHE_QUERIES + */ + public int getPreparedStatementCacheQueries() { + return PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.getIntNoCheck(properties); + } + + /** + * @param cacheSize prepared statement cache size (number of statements per connection) + * @see PGProperty#PREPARED_STATEMENT_CACHE_QUERIES + */ + public void setPreparedStatementCacheQueries(int cacheSize) { + PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.set(properties, cacheSize); + } + + /** + * @return prepared statement cache size (number of megabytes per connection) + * @see PGProperty#PREPARED_STATEMENT_CACHE_SIZE_MIB + */ + public int getPreparedStatementCacheSizeMiB() { + return PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.getIntNoCheck(properties); + } + + /** + * @param cacheSize statement cache size (number of megabytes per connection) + * @see PGProperty#PREPARED_STATEMENT_CACHE_SIZE_MIB + */ + public void setPreparedStatementCacheSizeMiB(int cacheSize) { + PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.set(properties, cacheSize); + } + + /** + * @return database metadata cache fields size (number of fields cached per connection) + * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS + */ + public int getDatabaseMetadataCacheFields() { + return PGProperty.DATABASE_METADATA_CACHE_FIELDS.getIntNoCheck(properties); + } + + /** + * @param cacheSize database metadata cache fields size (number of fields cached per connection) + * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS + */ + public void setDatabaseMetadataCacheFields(int cacheSize) { + PGProperty.DATABASE_METADATA_CACHE_FIELDS.set(properties, cacheSize); + } + + /** + * @return database metadata cache fields size (number of megabytes per connection) + * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB + */ + public int getDatabaseMetadataCacheFieldsMiB() { + return PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.getIntNoCheck(properties); + } + + /** + * @param cacheSize database metadata cache fields size (number of megabytes per connection) + * @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB + */ + public void setDatabaseMetadataCacheFieldsMiB(int cacheSize) { + PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.set(properties, cacheSize); + } + + /** + * @param fetchSize default fetch size + * @see PGProperty#DEFAULT_ROW_FETCH_SIZE + */ + public void setDefaultRowFetchSize(int fetchSize) { + PGProperty.DEFAULT_ROW_FETCH_SIZE.set(properties, fetchSize); + } + + /** + * @return default fetch size + * @see PGProperty#DEFAULT_ROW_FETCH_SIZE + */ + public int getDefaultRowFetchSize() { + return PGProperty.DEFAULT_ROW_FETCH_SIZE.getIntNoCheck(properties); + } + + /** + * @param unknownLength unknown length + * @see PGProperty#UNKNOWN_LENGTH + */ + public void setUnknownLength(int unknownLength) { + PGProperty.UNKNOWN_LENGTH.set(properties, unknownLength); + } + + /** + * @return unknown length + * @see PGProperty#UNKNOWN_LENGTH + */ + public int getUnknownLength() { + return PGProperty.UNKNOWN_LENGTH.getIntNoCheck(properties); + } + + /** + * @param seconds socket timeout + * @see PGProperty#SOCKET_TIMEOUT + */ + public void setSocketTimeout(int seconds) { + PGProperty.SOCKET_TIMEOUT.set(properties, seconds); + } + + /** + * @return socket timeout + * @see PGProperty#SOCKET_TIMEOUT + */ + public int getSocketTimeout() { + return PGProperty.SOCKET_TIMEOUT.getIntNoCheck(properties); + } + + /** + * @param seconds timeout that is used for sending cancel command + * @see PGProperty#CANCEL_SIGNAL_TIMEOUT + */ + public void setCancelSignalTimeout(int seconds) { + PGProperty.CANCEL_SIGNAL_TIMEOUT.set(properties, seconds); + } + + /** + * @return timeout that is used for sending cancel command in seconds + * @see PGProperty#CANCEL_SIGNAL_TIMEOUT + */ + public int getCancelSignalTimeout() { + return PGProperty.CANCEL_SIGNAL_TIMEOUT.getIntNoCheck(properties); + } + + /** + * @param enabled if SSL is enabled + * @see PGProperty#SSL + */ + public void setSsl(boolean enabled) { + if (enabled) { + PGProperty.SSL.set(properties, true); + } else { + PGProperty.SSL.set(properties, false); + } + } + + /** + * @return true if SSL is enabled + * @see PGProperty#SSL + */ + public boolean getSsl() { + // "true" if "ssl" is set but empty + return PGProperty.SSL.getBoolean(properties) || "".equals(PGProperty.SSL.getOrDefault(properties)); + } + + /** + * @param classname SSL factory class name + * @see PGProperty#SSL_FACTORY + */ + public void setSslfactory(String classname) { + PGProperty.SSL_FACTORY.set(properties, classname); + } + + /** + * @return SSL factory class name + * @see PGProperty#SSL_FACTORY + */ + public String getSslfactory() { + return PGProperty.SSL_FACTORY.getOrDefault(properties); + } + + /** + * @return SSL mode + * @see PGProperty#SSL_MODE + */ + public String getSslMode() { + return PGProperty.SSL_MODE.getOrDefault(properties); + } + + /** + * @param mode SSL mode + * @see PGProperty#SSL_MODE + */ + public void setSslMode(String mode) { + PGProperty.SSL_MODE.set(properties, mode); + } + + /** + * @return SSL mode + * @see PGProperty#SSL_FACTORY_ARG + */ + public String getSslFactoryArg() { + return PGProperty.SSL_FACTORY_ARG.getOrDefault(properties); + } + + /** + * @param arg argument forwarded to SSL factory + * @see PGProperty#SSL_FACTORY_ARG + */ + public void setSslFactoryArg(String arg) { + PGProperty.SSL_FACTORY_ARG.set(properties, arg); + } + + /** + * @return argument forwarded to SSL factory + * @see PGProperty#SSL_HOSTNAME_VERIFIER + */ + public String getSslHostnameVerifier() { + return PGProperty.SSL_HOSTNAME_VERIFIER.getOrDefault(properties); + } + + /** + * @param className SSL hostname verifier + * @see PGProperty#SSL_HOSTNAME_VERIFIER + */ + public void setSslHostnameVerifier(String className) { + PGProperty.SSL_HOSTNAME_VERIFIER.set(properties, className); + } + + /** + * @return className SSL hostname verifier + * @see PGProperty#SSL_CERT + */ + public String getSslCert() { + return PGProperty.SSL_CERT.getOrDefault(properties); + } + + /** + * @param file SSL certificate + * @see PGProperty#SSL_CERT + */ + public void setSslCert(String file) { + PGProperty.SSL_CERT.set(properties, file); + } + + /** + * @return SSL certificate + * @see PGProperty#SSL_KEY + */ + public String getSslKey() { + return PGProperty.SSL_KEY.getOrDefault(properties); + } + + /** + * @param file SSL key + * @see PGProperty#SSL_KEY + */ + public void setSslKey(String file) { + PGProperty.SSL_KEY.set(properties, file); + } + + /** + * @return SSL root certificate + * @see PGProperty#SSL_ROOT_CERT + */ + public String getSslRootCert() { + return PGProperty.SSL_ROOT_CERT.getOrDefault(properties); + } + + /** + * @param file SSL root certificate + * @see PGProperty#SSL_ROOT_CERT + */ + public void setSslRootCert(String file) { + PGProperty.SSL_ROOT_CERT.set(properties, file); + } + + /** + * @return SSL password + * @see PGProperty#SSL_PASSWORD + */ + public String getSslPassword() { + return PGProperty.SSL_PASSWORD.getOrDefault(properties); + } + + /** + * @param password SSL password + * @see PGProperty#SSL_PASSWORD + */ + public void setSslPassword(String password) { + PGProperty.SSL_PASSWORD.set(properties, password); + } + + /** + * @return SSL password callback + * @see PGProperty#SSL_PASSWORD_CALLBACK + */ + public String getSslPasswordCallback() { + return PGProperty.SSL_PASSWORD_CALLBACK.getOrDefault(properties); + } + + /** + * @param className SSL password callback class name + * @see PGProperty#SSL_PASSWORD_CALLBACK + */ + public void setSslPasswordCallback(String className) { + PGProperty.SSL_PASSWORD_CALLBACK.set(properties, className); + } + + /** + * @param applicationName application name + * @see PGProperty#APPLICATION_NAME + */ + public void setApplicationName(String applicationName) { + PGProperty.APPLICATION_NAME.set(properties, applicationName); + } + + /** + * @return application name + * @see PGProperty#APPLICATION_NAME + */ + public String getApplicationName() { + return castNonNull(PGProperty.APPLICATION_NAME.getOrDefault(properties)); + } + + /** + * @param targetServerType target server type + * @see PGProperty#TARGET_SERVER_TYPE + */ + public void setTargetServerType(String targetServerType) { + PGProperty.TARGET_SERVER_TYPE.set(properties, targetServerType); + } + + /** + * @return target server type + * @see PGProperty#TARGET_SERVER_TYPE + */ + public String getTargetServerType() { + return castNonNull(PGProperty.TARGET_SERVER_TYPE.getOrDefault(properties)); + } + + /** + * @param loadBalanceHosts load balance hosts + * @see PGProperty#LOAD_BALANCE_HOSTS + */ + public void setLoadBalanceHosts(boolean loadBalanceHosts) { + PGProperty.LOAD_BALANCE_HOSTS.set(properties, loadBalanceHosts); + } + + /** + * @return load balance hosts + * @see PGProperty#LOAD_BALANCE_HOSTS + */ + public boolean getLoadBalanceHosts() { + return PGProperty.LOAD_BALANCE_HOSTS.isPresent(properties); + } + + /** + * @param hostRecheckSeconds host recheck seconds + * @see PGProperty#HOST_RECHECK_SECONDS + */ + public void setHostRecheckSeconds(int hostRecheckSeconds) { + PGProperty.HOST_RECHECK_SECONDS.set(properties, hostRecheckSeconds); + } + + /** + * @return host recheck seconds + * @see PGProperty#HOST_RECHECK_SECONDS + */ + public int getHostRecheckSeconds() { + return PGProperty.HOST_RECHECK_SECONDS.getIntNoCheck(properties); + } + + /** + * @param enabled if TCP keep alive should be enabled + * @see PGProperty#TCP_KEEP_ALIVE + */ + public void setTcpKeepAlive(boolean enabled) { + PGProperty.TCP_KEEP_ALIVE.set(properties, enabled); + } + + /** + * @return true if TCP keep alive is enabled + * @see PGProperty#TCP_KEEP_ALIVE + */ + public boolean getTcpKeepAlive() { + return PGProperty.TCP_KEEP_ALIVE.getBoolean(properties); + } + + /** + * @param enabled if TCP no delay should be enabled + * @see PGProperty#TCP_NO_DELAY + */ + public void setTcpNoDelay(boolean enabled) { + PGProperty.TCP_NO_DELAY.set(properties, enabled); + } + + /** + * @return true if TCP no delay is enabled + * @see PGProperty#TCP_NO_DELAY + */ + public boolean getTcpNoDelay() { + return PGProperty.TCP_NO_DELAY.getBoolean(properties); + } + + /** + * @param enabled if binary transfer should be enabled + * @see PGProperty#BINARY_TRANSFER + */ + public void setBinaryTransfer(boolean enabled) { + PGProperty.BINARY_TRANSFER.set(properties, enabled); + } + + /** + * @return true if binary transfer is enabled + * @see PGProperty#BINARY_TRANSFER + */ + public boolean getBinaryTransfer() { + return PGProperty.BINARY_TRANSFER.getBoolean(properties); + } + + /** + * @param oidList list of OIDs that are allowed to use binary transfer + * @see PGProperty#BINARY_TRANSFER_ENABLE + */ + public void setBinaryTransferEnable(String oidList) { + PGProperty.BINARY_TRANSFER_ENABLE.set(properties, oidList); + } + + /** + * @return list of OIDs that are allowed to use binary transfer + * @see PGProperty#BINARY_TRANSFER_ENABLE + */ + public String getBinaryTransferEnable() { + return castNonNull(PGProperty.BINARY_TRANSFER_ENABLE.getOrDefault(properties)); + } + + /** + * @param oidList list of OIDs that are not allowed to use binary transfer + * @see PGProperty#BINARY_TRANSFER_DISABLE + */ + public void setBinaryTransferDisable(String oidList) { + PGProperty.BINARY_TRANSFER_DISABLE.set(properties, oidList); + } + + /** + * @return list of OIDs that are not allowed to use binary transfer + * @see PGProperty#BINARY_TRANSFER_DISABLE + */ + public String getBinaryTransferDisable() { + return castNonNull(PGProperty.BINARY_TRANSFER_DISABLE.getOrDefault(properties)); + } + + /** + * @return string type + * @see PGProperty#STRING_TYPE + */ + public String getStringType() { + return PGProperty.STRING_TYPE.getOrDefault(properties); + } + + /** + * @param stringType string type + * @see PGProperty#STRING_TYPE + */ + public void setStringType(String stringType) { + PGProperty.STRING_TYPE.set(properties, stringType); + } + + /** + * @return true if column sanitizer is disabled + * @see PGProperty#DISABLE_COLUMN_SANITISER + */ + public boolean isColumnSanitiserDisabled() { + return PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(properties); + } + + /** + * @return true if column sanitizer is disabled + * @see PGProperty#DISABLE_COLUMN_SANITISER + */ + public boolean getDisableColumnSanitiser() { + return PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(properties); + } + + /** + * @param disableColumnSanitiser if column sanitizer should be disabled + * @see PGProperty#DISABLE_COLUMN_SANITISER + */ + public void setDisableColumnSanitiser(boolean disableColumnSanitiser) { + PGProperty.DISABLE_COLUMN_SANITISER.set(properties, disableColumnSanitiser); + } + + /** + * @return current schema + * @see PGProperty#CURRENT_SCHEMA + */ + public String getCurrentSchema() { + return PGProperty.CURRENT_SCHEMA.getOrDefault(properties); + } + + /** + * @param currentSchema current schema + * @see PGProperty#CURRENT_SCHEMA + */ + public void setCurrentSchema(String currentSchema) { + PGProperty.CURRENT_SCHEMA.set(properties, currentSchema); + } + + /** + * @return true if connection is readonly + * @see PGProperty#READ_ONLY + */ + public boolean getReadOnly() { + return PGProperty.READ_ONLY.getBoolean(properties); + } + + /** + * @param readOnly if connection should be readonly + * @see PGProperty#READ_ONLY + */ + public void setReadOnly(boolean readOnly) { + PGProperty.READ_ONLY.set(properties, readOnly); + } + + /** + * @return The behavior when set read only + * @see PGProperty#READ_ONLY_MODE + */ + public String getReadOnlyMode() { + return castNonNull(PGProperty.READ_ONLY_MODE.getOrDefault(properties)); + } + + /** + * @param mode the behavior when set read only + * @see PGProperty#READ_ONLY_MODE + */ + public void setReadOnlyMode(String mode) { + PGProperty.READ_ONLY_MODE.set(properties, mode); + } + + /** + * @return true if driver should log unclosed connections + * @see PGProperty#LOG_UNCLOSED_CONNECTIONS + */ + public boolean getLogUnclosedConnections() { + return PGProperty.LOG_UNCLOSED_CONNECTIONS.getBoolean(properties); + } + + /** + * @param enabled true if driver should log unclosed connections + * @see PGProperty#LOG_UNCLOSED_CONNECTIONS + */ + public void setLogUnclosedConnections(boolean enabled) { + PGProperty.LOG_UNCLOSED_CONNECTIONS.set(properties, enabled); + } + + /** + * @return true if driver should log include detail in server error messages + * @see PGProperty#LOG_SERVER_ERROR_DETAIL + */ + public boolean getLogServerErrorDetail() { + return PGProperty.LOG_SERVER_ERROR_DETAIL.getBoolean(properties); + } + + /** + * @param enabled true if driver should include detail in server error messages + * @see PGProperty#LOG_SERVER_ERROR_DETAIL + */ + public void setLogServerErrorDetail(boolean enabled) { + PGProperty.LOG_SERVER_ERROR_DETAIL.set(properties, enabled); + } + + /** + * @return assumed minimal server version + * @see PGProperty#ASSUME_MIN_SERVER_VERSION + */ + public String getAssumeMinServerVersion() { + return PGProperty.ASSUME_MIN_SERVER_VERSION.getOrDefault(properties); + } + + /** + * @param minVersion assumed minimal server version + * @see PGProperty#ASSUME_MIN_SERVER_VERSION + */ + public void setAssumeMinServerVersion(String minVersion) { + PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, minVersion); + } + + /** + * This is important in pool-by-transaction scenarios in order to make sure that all the statements + * reaches the same connection that is being initialized. If set then we will group the startup + * parameters in a transaction + * + * @return whether to group startup parameters or not + * @see PGProperty#GROUP_STARTUP_PARAMETERS + */ + public boolean getGroupStartupParameters() { + return PGProperty.GROUP_STARTUP_PARAMETERS.getBoolean(properties); + } + + /** + * @param groupStartupParameters whether to group startup Parameters in a transaction or not + * @see PGProperty#GROUP_STARTUP_PARAMETERS + */ + public void setGroupStartupParameters(boolean groupStartupParameters) { + PGProperty.GROUP_STARTUP_PARAMETERS.set(properties, groupStartupParameters); + } + + /** + * @return JAAS application name + * @see PGProperty#JAAS_APPLICATION_NAME + */ + public String getJaasApplicationName() { + return PGProperty.JAAS_APPLICATION_NAME.getOrDefault(properties); + } + + /** + * @param name JAAS application name + * @see PGProperty#JAAS_APPLICATION_NAME + */ + public void setJaasApplicationName(String name) { + PGProperty.JAAS_APPLICATION_NAME.set(properties, name); + } + + /** + * @return true if perform JAAS login before GSS authentication + * @see PGProperty#JAAS_LOGIN + */ + public boolean getJaasLogin() { + return PGProperty.JAAS_LOGIN.getBoolean(properties); + } + + /** + * @param doLogin true if perform JAAS login before GSS authentication + * @see PGProperty#JAAS_LOGIN + */ + public void setJaasLogin(boolean doLogin) { + PGProperty.JAAS_LOGIN.set(properties, doLogin); + } + + /** + * @return Kerberos server name + * @see PGProperty#KERBEROS_SERVER_NAME + */ + public String getKerberosServerName() { + return PGProperty.KERBEROS_SERVER_NAME.getOrDefault(properties); + } + + /** + * @param serverName Kerberos server name + * @see PGProperty#KERBEROS_SERVER_NAME + */ + public void setKerberosServerName(String serverName) { + PGProperty.KERBEROS_SERVER_NAME.set(properties, serverName); + } + + /** + * @return true if use SPNEGO + * @see PGProperty#USE_SPNEGO + */ + public boolean getUseSpNego() { + return PGProperty.USE_SPNEGO.getBoolean(properties); + } + + /** + * @param use true if use SPNEGO + * @see PGProperty#USE_SPNEGO + */ + public void setUseSpNego(boolean use) { + PGProperty.USE_SPNEGO.set(properties, use); + } + + /** + * @return GSS mode: auto, sspi, or gssapi + * @see PGProperty#GSS_LIB + */ + public String getGssLib() { + return PGProperty.GSS_LIB.getOrDefault(properties); + } + + /** + * @param lib GSS mode: auto, sspi, or gssapi + * @see PGProperty#GSS_LIB + */ + public void setGssLib(String lib) { + PGProperty.GSS_LIB.set(properties, lib); + } + + /** + * @return GSS encryption mode: disable, prefer or require + */ + public String getGssEncMode() { + return castNonNull(PGProperty.GSS_ENC_MODE.getOrDefault(properties)); + } + + /** + * @param mode encryption mode: disable, prefer or require + */ + public void setGssEncMode(String mode) { + PGProperty.GSS_ENC_MODE.set(properties, mode); + } + + /** + * @return SSPI service class + * @see PGProperty#SSPI_SERVICE_CLASS + */ + public String getSspiServiceClass() { + return PGProperty.SSPI_SERVICE_CLASS.getOrDefault(properties); + } + + /** + * @param serviceClass SSPI service class + * @see PGProperty#SSPI_SERVICE_CLASS + */ + public void setSspiServiceClass(String serviceClass) { + PGProperty.SSPI_SERVICE_CLASS.set(properties, serviceClass); + } + + /** + * @return if connection allows encoding changes + * @see PGProperty#ALLOW_ENCODING_CHANGES + */ + public boolean getAllowEncodingChanges() { + return PGProperty.ALLOW_ENCODING_CHANGES.getBoolean(properties); + } + + /** + * @param allow if connection allows encoding changes + * @see PGProperty#ALLOW_ENCODING_CHANGES + */ + public void setAllowEncodingChanges(boolean allow) { + PGProperty.ALLOW_ENCODING_CHANGES.set(properties, allow); + } + + /** + * @return socket factory class name + * @see PGProperty#SOCKET_FACTORY + */ + public String getSocketFactory() { + return PGProperty.SOCKET_FACTORY.getOrDefault(properties); + } + + /** + * @param socketFactoryClassName socket factory class name + * @see PGProperty#SOCKET_FACTORY + */ + public void setSocketFactory(String socketFactoryClassName) { + PGProperty.SOCKET_FACTORY.set(properties, socketFactoryClassName); + } + + /** + * @return socket factory argument + * @see PGProperty#SOCKET_FACTORY_ARG + */ + public String getSocketFactoryArg() { + return PGProperty.SOCKET_FACTORY_ARG.getOrDefault(properties); + } + + /** + * @param socketFactoryArg socket factory argument + * @see PGProperty#SOCKET_FACTORY_ARG + */ + public void setSocketFactoryArg(String socketFactoryArg) { + PGProperty.SOCKET_FACTORY_ARG.set(properties, socketFactoryArg); + } + + /** + * @param replication set to 'database' for logical replication or 'true' for physical replication + * @see PGProperty#REPLICATION + */ + public void setReplication(String replication) { + PGProperty.REPLICATION.set(properties, replication); + } + + /** + * @return 'select', "callIfNoReturn', or 'call' + * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE + */ + public String getEscapeSyntaxCallMode() { + return castNonNull(PGProperty.ESCAPE_SYNTAX_CALL_MODE.getOrDefault(properties)); + } + + /** + * @param callMode the call mode to use for JDBC escape call syntax + * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE + */ + public void setEscapeSyntaxCallMode(String callMode) { + PGProperty.ESCAPE_SYNTAX_CALL_MODE.set(properties, callMode); + } + + /** + * @return null, 'database', or 'true + * @see PGProperty#REPLICATION + */ + public String getReplication() { + return PGProperty.REPLICATION.getOrDefault(properties); + } + + /** + * @return the localSocketAddress + * @see PGProperty#LOCAL_SOCKET_ADDRESS + */ + public String getLocalSocketAddress() { + return PGProperty.LOCAL_SOCKET_ADDRESS.getOrDefault(properties); + } + + /** + * @param localSocketAddress local address to bind client side to + * @see PGProperty#LOCAL_SOCKET_ADDRESS + */ + public void setLocalSocketAddress(String localSocketAddress) { + PGProperty.LOCAL_SOCKET_ADDRESS.set(properties, localSocketAddress); + } + + /** + * This property is no longer used by the driver and will be ignored. + * + * @return loggerLevel in properties + * @deprecated Configure via java.util.logging + */ + @Deprecated + public String getLoggerLevel() { + return PGProperty.LOGGER_LEVEL.getOrDefault(properties); + } + + /** + * This property is no longer used by the driver and will be ignored. + * + * @param loggerLevel loggerLevel to set, will be ignored + * @deprecated Configure via java.util.logging + */ + @Deprecated + public void setLoggerLevel(String loggerLevel) { + PGProperty.LOGGER_LEVEL.set(properties, loggerLevel); + } + + /** + * This property is no longer used by the driver and will be ignored. + * + * @return loggerFile in properties + * @deprecated Configure via java.util.logging + */ + @Deprecated + public String getLoggerFile() { + ExpressionProperties exprProps = new ExpressionProperties(properties, System.getProperties()); + return PGProperty.LOGGER_FILE.getOrDefault(exprProps); + } + + /** + * This property is no longer used by the driver and will be ignored. + * + * @param loggerFile will be ignored + * @deprecated Configure via java.util.logging + */ + @Deprecated + public void setLoggerFile(String loggerFile) { + PGProperty.LOGGER_FILE.set(properties, loggerFile); + } + + /** + * Generates a {@link DriverManager} URL from the other properties supplied. + * + * @return {@link DriverManager} URL from the other properties supplied + */ + public String getUrl() { + StringBuilder url = new StringBuilder(100); + url.append("jdbc:agensgraph://"); + for (int i = 0; i < serverNames.length; i++) { + if (i > 0) { + url.append(","); + } + url.append(serverNames[i]); + if (portNumbers != null && portNumbers.length >= i && portNumbers[i] != 0) { + url.append(":").append(portNumbers[i]); + } + } + url.append("/"); + if (databaseName != null) { + url.append(URLCoder.encode(databaseName)); + } + + StringBuilder query = new StringBuilder(100); + for (PGProperty property : PGProperty.values()) { + if (property.isPresent(properties)) { + if (query.length() != 0) { + query.append("&"); + } + query.append(property.getName()); + query.append("="); + String value = castNonNull(property.getOrDefault(properties)); + query.append(URLCoder.encode(value)); + } + } + + if (query.length() > 0) { + url.append("?"); + url.append(query); + } + + return url.toString(); + } + + /** + * Generates a {@link DriverManager} URL from the other properties supplied. + * + * @return {@link DriverManager} URL from the other properties supplied + */ + public String getURL() { + return getUrl(); + } + + /** + * Sets properties from a {@link DriverManager} URL. + * + * @param url properties to set + */ + public void setUrl(String url) { + + Properties p = Driver.parseURL(url, null); + + if (p == null) { + throw new IllegalArgumentException("URL invalid " + url); + } + for (PGProperty property : PGProperty.values()) { + if (!this.properties.containsKey(property.getName())) { + setProperty(property, property.getOrDefault(p)); + } + } + } + + /** + * Sets properties from a {@link DriverManager} URL. + * Added to follow convention used in other DBMS. + * + * @param url properties to set + */ + public void setURL(String url) { + setUrl(url); + } + + /** + * @return the class name to use for the Authentication Plugin. + * This can be null in which case the default password authentication plugin will be used + */ + public String getAuthenticationPluginClassName() { + return PGProperty.AUTHENTICATION_PLUGIN_CLASS_NAME.getOrDefault(properties); + } + + /** + * @param className name of a class which implements {@link org.postgresql.plugin.AuthenticationPlugin} + * This class will be used to get the encoded bytes to be sent to the server as the + * password to authenticate the user. + */ + public void setAuthenticationPluginClassName(String className) { + PGProperty.AUTHENTICATION_PLUGIN_CLASS_NAME.set(properties, className); + } + + public String getProperty(String name) throws SQLException { + PGProperty pgProperty = PGProperty.forName(name); + if (pgProperty != null) { + return getProperty(pgProperty); + } else { + throw new PSQLException(GT.tr("Unsupported property name: {0}", name), + PSQLState.INVALID_PARAMETER_VALUE); + } + } + + public void setProperty(String name, String value) throws SQLException { + PGProperty pgProperty = PGProperty.forName(name); + if (pgProperty != null) { + setProperty(pgProperty, value); + } else { + throw new PSQLException(GT.tr("Unsupported property name: {0}", name), + PSQLState.INVALID_PARAMETER_VALUE); + } + } + + public String getProperty(PGProperty property) { + return property.getOrDefault(properties); + } + + public void setProperty(PGProperty property, String value) { + if (value == null) { + // TODO: this is not consistent with PGProperty.PROPERTY.set(prop, null) + // PGProperty removes an entry for put(null) call, however here we just ignore null + return; + } + switch (property) { + case PG_HOST: + setServerNames(value.split(",")); + break; + case PG_PORT: + String[] ps = value.split(","); + int[] ports = new int[ps.length]; + for (int i = 0; i < ps.length; i++) { + try { + ports[i] = Integer.parseInt(ps[i]); + } catch (NumberFormatException e) { + ports[i] = 0; + } + } + setPortNumbers(ports); + break; + case PG_DBNAME: + setDatabaseName(value); + break; + case USER: + setUser(value); + break; + case PASSWORD: + setPassword(value); + break; + default: + properties.setProperty(property.getName(), value); + } + } + + /** + * Generates a reference using the appropriate object factory. + * + * @return reference using the appropriate object factory + */ + protected Reference createReference() { + return new Reference(getClass().getName(), PGObjectFactory.class.getName(), null); + } + + public Reference getReference() throws NamingException { + Reference ref = createReference(); + StringBuilder serverString = new StringBuilder(); + for (int i = 0; i < serverNames.length; i++) { + if (i > 0) { + serverString.append(","); + } + String serverName = serverNames[i]; + serverString.append(serverName); + } + ref.add(new StringRefAddr("serverName", serverString.toString())); + + StringBuilder portString = new StringBuilder(); + for (int i = 0; i < portNumbers.length; i++) { + if (i > 0) { + portString.append(","); + } + int p = portNumbers[i]; + portString.append(Integer.toString(p)); + } + ref.add(new StringRefAddr("portNumber", portString.toString())); + ref.add(new StringRefAddr("databaseName", databaseName)); + if (user != null) { + ref.add(new StringRefAddr("user", user)); + } + if (password != null) { + ref.add(new StringRefAddr("password", password)); + } + + for (PGProperty property : PGProperty.values()) { + if (property.isPresent(properties)) { + String value = castNonNull(property.getOrDefault(properties)); + ref.add(new StringRefAddr(property.getName(), value)); + } + } + + return ref; + } + + public void setFromReference(Reference ref) { + databaseName = getReferenceProperty(ref, "databaseName"); + String portNumberString = getReferenceProperty(ref, "portNumber"); + if (portNumberString != null) { + String[] ps = portNumberString.split(","); + int[] ports = new int[ps.length]; + for (int i = 0; i < ps.length; i++) { + try { + ports[i] = Integer.parseInt(ps[i]); + } catch (NumberFormatException e) { + ports[i] = 0; + } + } + setPortNumbers(ports); + } else { + setPortNumbers(null); + } + String serverName = castNonNull(getReferenceProperty(ref, "serverName")); + setServerNames(serverName.split(",")); + + for (PGProperty property : PGProperty.values()) { + setProperty(property, getReferenceProperty(ref, property.getName())); } - } - setPortNumbers(ports); - } else { - setPortNumbers(null); - } - String serverName = castNonNull(getReferenceProperty(ref, "serverName")); - setServerNames(serverName.split(",")); - - for (PGProperty property : PGProperty.values()) { - setProperty(property, getReferenceProperty(ref, property.getName())); - } - } - - private static String getReferenceProperty(Reference ref, String propertyName) { - RefAddr addr = ref.get(propertyName); - if (addr == null) { - return null; - } - return (String) addr.getContent(); - } - - protected void writeBaseObject(ObjectOutputStream out) throws IOException { - out.writeObject(serverNames); - out.writeObject(databaseName); - out.writeObject(user); - out.writeObject(password); - out.writeObject(portNumbers); - - out.writeObject(properties); - } - - protected void readBaseObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - serverNames = (String[]) in.readObject(); - databaseName = (String) in.readObject(); - user = (String) in.readObject(); - password = (String) in.readObject(); - portNumbers = (int[]) in.readObject(); - - properties = (Properties) in.readObject(); - } - - public void initializeFrom(BaseDataSource source) throws IOException, ClassNotFoundException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - source.writeBaseObject(oos); - oos.close(); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - ObjectInputStream ois = new ObjectInputStream(bais); - readBaseObject(ois); - } - - /** - * @return preferred query execution mode - * @see PGProperty#PREFER_QUERY_MODE - */ - public PreferQueryMode getPreferQueryMode() { - return PreferQueryMode.of(castNonNull(PGProperty.PREFER_QUERY_MODE.get(properties))); - } - - /** - * @param preferQueryMode extended, simple, extendedForPrepared, or extendedCacheEverything - * @see PGProperty#PREFER_QUERY_MODE - */ - public void setPreferQueryMode(PreferQueryMode preferQueryMode) { - PGProperty.PREFER_QUERY_MODE.set(properties, preferQueryMode.value()); - } - - /** - * @return connection configuration regarding automatic per-query savepoints - * @see PGProperty#AUTOSAVE - */ - public AutoSave getAutosave() { - return AutoSave.of(castNonNull(PGProperty.AUTOSAVE.get(properties))); - } - - /** - * @param autoSave connection configuration regarding automatic per-query savepoints - * @see PGProperty#AUTOSAVE - */ - public void setAutosave(AutoSave autoSave) { - PGProperty.AUTOSAVE.set(properties, autoSave.value()); - } - - /** - * see PGProperty#CLEANUP_SAVEPOINTS - * - * @return boolean indicating property set - */ - public boolean getCleanupSavepoints() { - return PGProperty.CLEANUP_SAVEPOINTS.getBoolean(properties); - } - - /** - * see PGProperty#CLEANUP_SAVEPOINTS - * - * @param cleanupSavepoints will cleanup savepoints after a successful transaction - */ - public void setCleanupSavepoints(boolean cleanupSavepoints) { - PGProperty.CLEANUP_SAVEPOINTS.set(properties, cleanupSavepoints); - } - - /** - * @return boolean indicating property is enabled or not. - * @see PGProperty#REWRITE_BATCHED_INSERTS - */ - public boolean getReWriteBatchedInserts() { - return PGProperty.REWRITE_BATCHED_INSERTS.getBoolean(properties); - } - - /** - * @param reWrite boolean value to set the property in the properties collection - * @see PGProperty#REWRITE_BATCHED_INSERTS - */ - public void setReWriteBatchedInserts(boolean reWrite) { - PGProperty.REWRITE_BATCHED_INSERTS.set(properties, reWrite); - } - - /** - * @return boolean indicating property is enabled or not. - * @see PGProperty#HIDE_UNPRIVILEGED_OBJECTS - */ - public boolean getHideUnprivilegedObjects() { - return PGProperty.HIDE_UNPRIVILEGED_OBJECTS.getBoolean(properties); - } - - /** - * @param hideUnprivileged boolean value to set the property in the properties collection - * @see PGProperty#HIDE_UNPRIVILEGED_OBJECTS - */ - public void setHideUnprivilegedObjects(boolean hideUnprivileged) { - PGProperty.HIDE_UNPRIVILEGED_OBJECTS.set(properties, hideUnprivileged); - } - - public String getMaxResultBuffer() { - return PGProperty.MAX_RESULT_BUFFER.get(properties); - } - - public void setMaxResultBuffer( String maxResultBuffer) { - PGProperty.MAX_RESULT_BUFFER.set(properties, maxResultBuffer); - } - - @Override - public Logger getParentLogger() { - return Logger.getLogger("org.postgresql"); - } - - public String getXmlFactoryFactory() { - return castNonNull(PGProperty.XML_FACTORY_FACTORY.get(properties)); - } - - public void setXmlFactoryFactory( String xmlFactoryFactory) { - PGProperty.XML_FACTORY_FACTORY.set(properties, xmlFactoryFactory); - } - - /* - * Alias methods below, these are to help with ease-of-use with other database tools / frameworks - * which expect normal java bean getters / setters to exist for the property names. - */ - - public boolean isSsl() { - return getSsl(); - } - - public String getSslfactoryarg() { - return getSslFactoryArg(); - } - - public void setSslfactoryarg(final String arg) { - setSslFactoryArg(arg); - } - - public String getSslcert() { - return getSslCert(); - } - - public void setSslcert(final String file) { - setSslCert(file); - } - - public String getSslmode() { - return getSslMode(); - } - - public void setSslmode(final String mode) { - setSslMode(mode); - } - - public String getSslhostnameverifier() { - return getSslHostnameVerifier(); - } - - public void setSslhostnameverifier(final String className) { - setSslHostnameVerifier(className); - } - - public String getSslkey() { - return getSslKey(); - } - - public void setSslkey(final String file) { - setSslKey(file); - } - - public String getSslrootcert() { - return getSslRootCert(); - } - - public void setSslrootcert(final String file) { - setSslRootCert(file); - } - - public String getSslpasswordcallback() { - return getSslPasswordCallback(); - } - - public void setSslpasswordcallback(final String className) { - setSslPasswordCallback(className); - } - - public String getSslpassword() { - return getSslPassword(); - } - - public void setSslpassword(final String sslpassword) { - setSslPassword(sslpassword); - } - - public int getRecvBufferSize() { - return getReceiveBufferSize(); - } - - public void setRecvBufferSize(final int nbytes) { - setReceiveBufferSize(nbytes); - } - - public boolean isAllowEncodingChanges() { - return getAllowEncodingChanges(); - } - - public boolean isLogUnclosedConnections() { - return getLogUnclosedConnections(); - } - - public boolean isTcpKeepAlive() { - return getTcpKeepAlive(); - } - - public boolean isReadOnly() { - return getReadOnly(); - } - - public boolean isDisableColumnSanitiser() { - return getDisableColumnSanitiser(); - } - - public boolean isLoadBalanceHosts() { - return getLoadBalanceHosts(); - } + } + + private static String getReferenceProperty(Reference ref, String propertyName) { + RefAddr addr = ref.get(propertyName); + if (addr == null) { + return null; + } + return (String) addr.getContent(); + } + + protected void writeBaseObject(ObjectOutputStream out) throws IOException { + out.writeObject(serverNames); + out.writeObject(databaseName); + out.writeObject(user); + out.writeObject(password); + out.writeObject(portNumbers); + + out.writeObject(properties); + } + + protected void readBaseObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + serverNames = (String[]) in.readObject(); + databaseName = (String) in.readObject(); + user = (String) in.readObject(); + password = (String) in.readObject(); + portNumbers = (int[]) in.readObject(); + + properties = (Properties) in.readObject(); + } + + public void initializeFrom(BaseDataSource source) throws IOException, ClassNotFoundException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + source.writeBaseObject(oos); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + readBaseObject(ois); + } + + /** + * @return preferred query execution mode + * @see PGProperty#PREFER_QUERY_MODE + */ + public PreferQueryMode getPreferQueryMode() { + return PreferQueryMode.of(castNonNull(PGProperty.PREFER_QUERY_MODE.getOrDefault(properties))); + } + + /** + * @param preferQueryMode extended, simple, extendedForPrepared, or extendedCacheEverything + * @see PGProperty#PREFER_QUERY_MODE + */ + public void setPreferQueryMode(PreferQueryMode preferQueryMode) { + PGProperty.PREFER_QUERY_MODE.set(properties, preferQueryMode.value()); + } + + /** + * @return connection configuration regarding automatic per-query savepoints + * @see PGProperty#AUTOSAVE + */ + public AutoSave getAutosave() { + return AutoSave.of(castNonNull(PGProperty.AUTOSAVE.getOrDefault(properties))); + } + + /** + * @param autoSave connection configuration regarding automatic per-query savepoints + * @see PGProperty#AUTOSAVE + */ + public void setAutosave(AutoSave autoSave) { + PGProperty.AUTOSAVE.set(properties, autoSave.value()); + } + + /** + * see PGProperty#CLEANUP_SAVEPOINTS + * + * @return boolean indicating property set + */ + public boolean getCleanupSavepoints() { + return PGProperty.CLEANUP_SAVEPOINTS.getBoolean(properties); + } + + /** + * see PGProperty#CLEANUP_SAVEPOINTS + * + * @param cleanupSavepoints will cleanup savepoints after a successful transaction + */ + public void setCleanupSavepoints(boolean cleanupSavepoints) { + PGProperty.CLEANUP_SAVEPOINTS.set(properties, cleanupSavepoints); + } + + /** + * @return boolean indicating property is enabled or not. + * @see PGProperty#REWRITE_BATCHED_INSERTS + */ + public boolean getReWriteBatchedInserts() { + return PGProperty.REWRITE_BATCHED_INSERTS.getBoolean(properties); + } + + /** + * @param reWrite boolean value to set the property in the properties collection + * @see PGProperty#REWRITE_BATCHED_INSERTS + */ + public void setReWriteBatchedInserts(boolean reWrite) { + PGProperty.REWRITE_BATCHED_INSERTS.set(properties, reWrite); + } + + /** + * @return boolean indicating property is enabled or not. + * @see PGProperty#HIDE_UNPRIVILEGED_OBJECTS + */ + public boolean getHideUnprivilegedObjects() { + return PGProperty.HIDE_UNPRIVILEGED_OBJECTS.getBoolean(properties); + } + + /** + * @param hideUnprivileged boolean value to set the property in the properties collection + * @see PGProperty#HIDE_UNPRIVILEGED_OBJECTS + */ + public void setHideUnprivilegedObjects(boolean hideUnprivileged) { + PGProperty.HIDE_UNPRIVILEGED_OBJECTS.set(properties, hideUnprivileged); + } + + public String getMaxResultBuffer() { + return PGProperty.MAX_RESULT_BUFFER.getOrDefault(properties); + } + + public void setMaxResultBuffer(String maxResultBuffer) { + PGProperty.MAX_RESULT_BUFFER.set(properties, maxResultBuffer); + } + + public boolean getAdaptiveFetch() { + return PGProperty.ADAPTIVE_FETCH.getBoolean(properties); + } + + public void setAdaptiveFetch(boolean adaptiveFetch) { + PGProperty.ADAPTIVE_FETCH.set(properties, adaptiveFetch); + } + + public int getAdaptiveFetchMaximum() { + return PGProperty.ADAPTIVE_FETCH_MAXIMUM.getIntNoCheck(properties); + } + + public void setAdaptiveFetchMaximum(int adaptiveFetchMaximum) { + PGProperty.ADAPTIVE_FETCH_MAXIMUM.set(properties, adaptiveFetchMaximum); + } + + public int getAdaptiveFetchMinimum() { + return PGProperty.ADAPTIVE_FETCH_MINIMUM.getIntNoCheck(properties); + } - public boolean isCleanupSavePoints() { - return getCleanupSavepoints(); - } + public void setAdaptiveFetchMinimum(int adaptiveFetchMinimum) { + PGProperty.ADAPTIVE_FETCH_MINIMUM.set(properties, adaptiveFetchMinimum); + } + + @Override + public Logger getParentLogger() { + return Logger.getLogger("org.postgresql"); + } + + public String getXmlFactoryFactory() { + return castNonNull(PGProperty.XML_FACTORY_FACTORY.getOrDefault(properties)); + } + + public void setXmlFactoryFactory(String xmlFactoryFactory) { + PGProperty.XML_FACTORY_FACTORY.set(properties, xmlFactoryFactory); + } - public void setCleanupSavePoints(final boolean cleanupSavepoints) { - setCleanupSavepoints(cleanupSavepoints); - } + /* + * Alias methods below, these are to help with ease-of-use with other database tools / frameworks + * which expect normal java bean getters / setters to exist for the property names. + */ - public boolean isReWriteBatchedInserts() { - return getReWriteBatchedInserts(); - } + public boolean isSsl() { + return getSsl(); + } + + public String getSslfactoryarg() { + return getSslFactoryArg(); + } + + public void setSslfactoryarg(final String arg) { + setSslFactoryArg(arg); + } + + public String getSslcert() { + return getSslCert(); + } + + public void setSslcert(final String file) { + setSslCert(file); + } + + public String getSslmode() { + return getSslMode(); + } + + public void setSslmode(final String mode) { + setSslMode(mode); + } + + public String getSslhostnameverifier() { + return getSslHostnameVerifier(); + } + + public void setSslhostnameverifier(final String className) { + setSslHostnameVerifier(className); + } + + public String getSslkey() { + return getSslKey(); + } + + public void setSslkey(final String file) { + setSslKey(file); + } + + public String getSslrootcert() { + return getSslRootCert(); + } + + public void setSslrootcert(final String file) { + setSslRootCert(file); + } + + public String getSslpasswordcallback() { + return getSslPasswordCallback(); + } + + public void setSslpasswordcallback(final String className) { + setSslPasswordCallback(className); + } + + public String getSslpassword() { + return getSslPassword(); + } + + public void setSslpassword(final String sslpassword) { + setSslPassword(sslpassword); + } + + public int getRecvBufferSize() { + return getReceiveBufferSize(); + } + + public void setRecvBufferSize(final int nbytes) { + setReceiveBufferSize(nbytes); + } + + public boolean isAllowEncodingChanges() { + return getAllowEncodingChanges(); + } + + public boolean isLogUnclosedConnections() { + return getLogUnclosedConnections(); + } + + public boolean isTcpKeepAlive() { + return getTcpKeepAlive(); + } + + public boolean isReadOnly() { + return getReadOnly(); + } + + public boolean isDisableColumnSanitiser() { + return getDisableColumnSanitiser(); + } + + public boolean isLoadBalanceHosts() { + return getLoadBalanceHosts(); + } + + public boolean isCleanupSavePoints() { + return getCleanupSavepoints(); + } + + public void setCleanupSavePoints(final boolean cleanupSavepoints) { + setCleanupSavepoints(cleanupSavepoints); + } + + public boolean isReWriteBatchedInserts() { + return getReWriteBatchedInserts(); + } } diff --git a/src/main/java/net/bitnine/agensgraph/jdbc/AgConnection.java b/src/main/java/net/bitnine/agensgraph/jdbc/AgConnection.java index 13265b3..d319071 100644 --- a/src/main/java/net/bitnine/agensgraph/jdbc/AgConnection.java +++ b/src/main/java/net/bitnine/agensgraph/jdbc/AgConnection.java @@ -41,8 +41,10 @@ * This class defines connection to AgensGraph. */ public class AgConnection extends PgConnection { - public AgConnection(HostSpec[] hostSpecs, String user, String database, Properties info, String url) throws SQLException { - super(hostSpecs, user, database, info, url); + public AgConnection(HostSpec[] hostSpecs, + Properties info, + String url) throws SQLException { + super(hostSpecs, info, url); addDataType("jsonb", Jsonb.class); addDataType("graphid", GraphId.class); diff --git a/src/main/java/net/bitnine/agensgraph/jdbc/AgResultSet.java b/src/main/java/net/bitnine/agensgraph/jdbc/AgResultSet.java index 30f8db3..2af978b 100644 --- a/src/main/java/net/bitnine/agensgraph/jdbc/AgResultSet.java +++ b/src/main/java/net/bitnine/agensgraph/jdbc/AgResultSet.java @@ -31,21 +31,7 @@ import java.io.Reader; import java.math.BigDecimal; import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.NClob; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Statement; -import java.sql.Time; -import java.sql.Timestamp; +import java.sql.*; import java.util.Calendar; import java.util.Map; diff --git a/src/main/java/net/bitnine/agensgraph/jdbc/AgStatement.java b/src/main/java/net/bitnine/agensgraph/jdbc/AgStatement.java index 2f65aac..3e0cd63 100644 --- a/src/main/java/net/bitnine/agensgraph/jdbc/AgStatement.java +++ b/src/main/java/net/bitnine/agensgraph/jdbc/AgStatement.java @@ -109,7 +109,11 @@ public boolean execute(String sql) throws SQLException { @Override public ResultSet getResultSet() throws SQLException { - return new AgResultSet(stmt.getResultSet()); + ResultSet pgResultSet = stmt.getResultSet(); + if (pgResultSet == null) { + return null; + } + return new AgResultSet(pgResultSet); } @Override @@ -317,4 +321,14 @@ public void setPrepareThreshold(int threshold) throws SQLException { public int getPrepareThreshold() { return stmt.getPrepareThreshold(); } + + @Override + public void setAdaptiveFetch(boolean adaptiveFetch) { + stmt.setAdaptiveFetch(adaptiveFetch); + } + + @Override + public boolean getAdaptiveFetch() { + return stmt.getAdaptiveFetch(); + } } diff --git a/src/main/java/net/bitnine/agensgraph/util/AgTokenizer.java b/src/main/java/net/bitnine/agensgraph/util/AgTokenizer.java index 5ad1204..253aef9 100644 --- a/src/main/java/net/bitnine/agensgraph/util/AgTokenizer.java +++ b/src/main/java/net/bitnine/agensgraph/util/AgTokenizer.java @@ -41,7 +41,7 @@ public static ArrayList tokenize(String string) throws SQLException { // ignore wrapping '[' and ']' characters int pos = 1; - int len = string.length() -1; + int len = string.length() - 1; int start = pos; int depth = 0; diff --git a/src/main/java/net/bitnine/agensgraph/util/DriverInfo.java b/src/main/java/net/bitnine/agensgraph/util/DriverInfo.java index d00dda6..5a6b944 100644 --- a/src/main/java/net/bitnine/agensgraph/util/DriverInfo.java +++ b/src/main/java/net/bitnine/agensgraph/util/DriverInfo.java @@ -31,7 +31,7 @@ private DriverInfo() { /** * The driver version. */ - public static final String DRIVER_VERSION = "1.4.2"; + public static final String DRIVER_VERSION = "1.4.3"; /** * The driver full name. @@ -51,5 +51,5 @@ private DriverInfo() { /** * The patch version. */ - public static final int PATCH_VERSION = 2; + public static final int PATCH_VERSION = 3; } diff --git a/src/main/java/net/bitnine/agensgraph/util/Jsonb.java b/src/main/java/net/bitnine/agensgraph/util/Jsonb.java index 37ecebb..a9a0e11 100644 --- a/src/main/java/net/bitnine/agensgraph/util/Jsonb.java +++ b/src/main/java/net/bitnine/agensgraph/util/Jsonb.java @@ -149,7 +149,7 @@ private boolean isDouble(String s) { try { Double.parseDouble(s); return true; - } catch(NumberFormatException e) { + } catch (NumberFormatException e) { return false; } } diff --git a/src/main/java/net/bitnine/agensgraph/util/JsonbObject.java b/src/main/java/net/bitnine/agensgraph/util/JsonbObject.java index 7b23b1d..34ac44f 100644 --- a/src/main/java/net/bitnine/agensgraph/util/JsonbObject.java +++ b/src/main/java/net/bitnine/agensgraph/util/JsonbObject.java @@ -46,7 +46,7 @@ public interface JsonbObject { /** * Returns the value stored at the key. * - * @param key the given key + * @param key the given key * @param defaultValue returned the default value if the value stored at the key is null * @return the string value stored at the key */ @@ -63,7 +63,7 @@ public interface JsonbObject { /** * Returns the value stored at the key. * - * @param key the given key + * @param key the given key * @param defaultValue returned the default value if the value stored at the key is null * @return the int value stored at the key */ @@ -80,7 +80,7 @@ public interface JsonbObject { /** * Returns the value stored at the key. * - * @param key the given key + * @param key the given key * @param defaultValue returned the default value if the value stored at the key is null * @return the long value stored at the key */ @@ -97,7 +97,7 @@ public interface JsonbObject { /** * Returns the value stored at the key. * - * @param key the given key + * @param key the given key * @param defaultValue returned the default value if the value stored at the key is null * @return the double value stored at the key */ @@ -114,7 +114,7 @@ public interface JsonbObject { /** * Returns the value stored at the key. * - * @param key the given key + * @param key the given key * @param defaultValue returned the default value if the value stored at the key is null * @return the boolean value stored at the key */ diff --git a/src/main/java/net/bitnine/agensgraph/util/JsonbObjectBuilder.java b/src/main/java/net/bitnine/agensgraph/util/JsonbObjectBuilder.java index 6a39db5..1fb4049 100644 --- a/src/main/java/net/bitnine/agensgraph/util/JsonbObjectBuilder.java +++ b/src/main/java/net/bitnine/agensgraph/util/JsonbObjectBuilder.java @@ -99,7 +99,7 @@ public JsonbObjectBuilder add(String name, boolean value) { * Adds a name/null pair to the Jsonb object associated with this object builder where the value is null. * If the object contains a mapping for the specified name, this method replaces the old value with null. * - * @param name name in the name/value pair + * @param name name in the name/value pair * @return this object builder */ public JsonbObjectBuilder addNull(String name) { @@ -111,8 +111,8 @@ public JsonbObjectBuilder addNull(String name) { * Adds a name/Jsonb pair to the Jsonb object associated with this object builder. * If the object contains a mapping for the specified name, this method replaces the old value with the Jsonb. * - * @param name name in the name/value pair - * @param j the value is the object associated with Jsonb + * @param name name in the name/value pair + * @param j the value is the object associated with Jsonb * @return this object builder */ public JsonbObjectBuilder add(String name, Jsonb j) { @@ -138,7 +138,7 @@ public JsonbObjectBuilder add(String name, Object value) { * Adds a name/JsonbArray pair to the Jsonb object associated with this object builder. * If the object contains a mapping for the specified name, this method replaces the old value with JsonbArray. * - * @param name name in the name/value pair + * @param name name in the name/value pair * @param builder the value is the object array with this builder * @return this object builder */ @@ -151,7 +151,7 @@ public JsonbObjectBuilder add(String name, JsonbArrayBuilder builder) { * Adds a name/JsonbObject pair to the Jsonb object associated with this object builder. * If the object contains a mapping for the specified name, this method replaces the old value with JsonbObject. * - * @param name name in the name/value pair + * @param name name in the name/value pair * @param builder the value is the object associated with this builder * @return this object builder */