All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.jdbc.Neo4jDataSource Maven / Gradle / Ivy

/*
 * Copyright (c) 2023-2024 "Neo4j,"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.neo4j.jdbc;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.logging.Logger;

import javax.sql.DataSource;

/**
 * A Neo4j specific extension of {@link DataSource}. It may be referred to for use with
 * {@link #unwrap(Class)} to access specific Neo4j functionality.
 *
 * @author Michael J. Simons
 * @since 6.0.0
 */
public final class Neo4jDataSource implements Neo4jDataSourceExtensions {

	/**
	 * The name of a particular database on a server.
	 */
	private String databaseName;

	/**
	 * The network protocol used to communicate with the server. Valid options are all
	 * valid sub-protocols that are supported by {@link Neo4jDriver#acceptsURL(String)}.
	 * The main protocol {@literal neo4j} does not need to be configured, it is implied.
	 */
	private String transportProtocol;

	/**
	 * A database password.
	 */
	private char[] password;

	/**
	 * The port number where a server is listening for requests, defaults to the standard
	 * bolt port {@literal 7687}.
	 */
	private int portNumber = 7687;

	/**
	 * The fully qualified name of the cluster or single instance or an ip address.
	 */
	private String serverName;

	/**
	 * The user to be authenticated.
	 */
	private String user;

	/**
	 * Timeout in seconds.
	 */
	private int loginTimeout = 1;

	/**
	 * A log writer, which we currently don't use.
	 */
	private PrintWriter logWriter = new PrintWriter(System.out);

	private final Properties connectionProperties = new Properties();

	/**
	 * Creates a new {@link DataSource} which is not yet usable without further
	 * configuration. This constructor is mainly used for tooling that loads data sources
	 * via reflection.
	 */
	public Neo4jDataSource() {
	}

	@Override
	public String getDatabaseName() {
		return this.databaseName;
	}

	@Override
	public void setDatabaseName(String databaseName) {
		this.databaseName = databaseName;
	}

	@Override
	public String getPassword() {
		return (this.password == null) ? null : new String(this.password);
	}

	@Override
	public void setPassword(String password) {
		this.password = (password != null) ? password.toCharArray() : null;
	}

	@Override
	public int getPortNumber() {
		return this.portNumber;
	}

	@Override
	public void setPortNumber(int portNumber) {
		this.portNumber = portNumber;
	}

	@Override
	public String getServerName() {
		return this.serverName;
	}

	@Override
	public void setServerName(String serverName) {
		this.serverName = serverName;
	}

	@Override
	public String getUser() {
		return this.user;
	}

	@Override
	public void setUser(String user) {
		this.user = user;
	}

	@Override
	public String getTransportProtocol() {
		return this.transportProtocol;
	}

	@Override
	public void setTransportProtocol(String transportProtocol) {
		this.transportProtocol = transportProtocol;
	}

	String getUrl() {
		return "jdbc:neo4j%s://%s:%d/%s?timeout=%d".formatted(
				Optional.ofNullable(this.transportProtocol)
					.map(String::trim)
					.filter(Predicate.not(String::isBlank))
					.map(p -> "+" + p)
					.orElse(""),
				Objects.requireNonNull(this.serverName, "The server name must be specified on the data source"),
				this.portNumber, Optional.ofNullable(this.databaseName).orElse("neo4j"), this.loginTimeout * 1000);
	}

	@Override
	public Connection getConnection() throws SQLException {
		return getConnection(this.user, this.getPassword());
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		var newProperties = new Properties();
		this.connectionProperties.stringPropertyNames()
			.forEach(k -> newProperties.put(k, this.connectionProperties.getProperty(k)));

		if (username != null && password != null) {
			newProperties.setProperty("user", username);
			newProperties.setProperty("password", password);
		}

		return DriverManager.getConnection(getUrl(), newProperties);
	}

	@Override
	public PrintWriter getLogWriter() {
		return this.logWriter;
	}

	@Override
	public void setLogWriter(PrintWriter out) {
		this.logWriter = Objects.requireNonNull(out, "Log writer must not be null");
	}

	@Override
	public void setLoginTimeout(int seconds) {
		this.loginTimeout = seconds;
	}

	@Override
	public int getLoginTimeout() {
		return this.loginTimeout;
	}

	@Override
	public Logger getParentLogger() {
		return Logger.getLogger(this.getClass().getPackageName());
	}

	@Override
	public  T unwrap(Class iface) throws SQLException {
		if (iface.isAssignableFrom(getClass())) {
			return iface.cast(this);
		}
		else {
			throw new SQLException("This object does not implement the given interface");
		}
	}

	@Override
	public boolean isWrapperFor(Class iface) throws SQLException {
		return iface.isAssignableFrom(getClass());
	}

	@Override
	public void setConnectionProperty(String name, String value) {
		this.connectionProperties.setProperty(name, value);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy