org.neo4j.ogm.config.Configuration Maven / Gradle / Ivy
Show all versions of neo4j-ogm-api Show documentation
/*
* Copyright (c) 2002-2024 "Neo4j,"
* Neo4j Sweden AB [http://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
*
* http://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.ogm.config;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.neo4j.ogm.support.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A generic configuration class that can be set up programmatically
* or via a properties file.
*
* @author Vince Bickers
* @author Mark Angrish
* @author Michael J. Simons
*/
public class Configuration {
private static final Logger LOGGER = LoggerFactory.getLogger(Configuration.class);
private static final int DEFAULT_SESSION_POOL_SIZE = 50;
/**
* Configuration to change the precedence from the current threads context
*/
public enum ClassLoaderPrecedence {
/**
* Use the current threads context class loader.
*/
CONTEXT_CLASS_LOADER,
/**
* Use the class loader into which OGM was loaded.
*/
OGM_CLASS_LOADER
}
/**
* Set the class loader precedence for interacting with classes during the mapping process.
* This is necessary when running Neo4j-OGM in async environments (like {@code CompletableFuture} usage, Spring Boot's @Async , etc.).
* In those cases, please use {@link Configuration#setClassLoaderPrecedence(ClassLoaderPrecedence)} with {@link ClassLoaderPrecedence#OGM_CLASS_LOADER}.
*
* @deprecated The direct access to this static field has been deprecated.
* Please use the {@link Configuration#setClassLoaderPrecedence(ClassLoaderPrecedence)} method.
*/
@Deprecated
private static final AtomicReference CLASS_LOADER_PRECEDENCE = new AtomicReference(ClassLoaderPrecedence.CONTEXT_CLASS_LOADER);
/**
* Set the class loader precedence for interacting with classes during the mapping process.
* This is necessary when running Neo4j-OGM in async environments (like {@code CompletableFuture} usage, Spring Boot's @Async , etc.).
* In those cases, please use {@link Configuration#setClassLoaderPrecedence(ClassLoaderPrecedence)} with {@link ClassLoaderPrecedence#OGM_CLASS_LOADER}.
*
*/
public static void setClassLoaderPrecedence(ClassLoaderPrecedence classLoaderPrecedence) {
CLASS_LOADER_PRECEDENCE.set(classLoaderPrecedence);
}
/**
* Retrieve the current set class loader precedence that is used for working with classes during the mapping process.
*
* @return current configured {@link ClassLoaderPrecedence}
*/
public static ClassLoaderPrecedence getClassLoaderPrecedence() {
return CLASS_LOADER_PRECEDENCE.get();
}
/**
* @return The classloader to be used by OGM.
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoaderPrecedence precedence = Configuration.CLASS_LOADER_PRECEDENCE.get();
boolean ctclFirst = precedence == null || precedence == ClassLoaderPrecedence.CONTEXT_CLASS_LOADER;
ClassLoader cl = null;
try {
cl = ctclFirst ? Thread.currentThread().getContextClassLoader() : ClassUtils.class.getClassLoader();
} catch (Throwable ex) {
}
if (cl == null) {
cl = ctclFirst ? ClassUtils.class.getClassLoader() : Thread.currentThread().getContextClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
private String uri;
private int connectionPoolSize;
private String encryptionLevel;
private String trustStrategy;
private String trustCertFile;
private AutoIndexMode autoIndex;
private String generatedIndexesOutputDir;
private String generatedIndexesOutputFilename;
/**
* The url of a neo4j.conf (properties) file to configure the embedded driver.
*/
private String neo4jConfLocation;
private String driverName;
private Credentials credentials;
private Integer connectionLivenessCheckTimeout;
private Boolean verifyConnection;
private Boolean useNativeTypes;
private Map customProperties;
/**
* This flag instructs OGM to use all static labels when querying domain objects. Until 3.1.16 only the label of the
* concrete domain has been used to query domain objects in inheritance scenarios. When storing those objects again,
* OGM writes all labels in any case.
*
* Using all reachable, static labels in a class hierarchy for querying and thus enforcing strict queries is the default
* in Neo4j-OGM 4.0. Use this flag to restore the old behaviour.
*/
private Boolean useStrictQuerying;
/**
* Base packages to scan for annotated components. They will be merged into a unique list
* of packages with the programmatically registered packages to scan.
*/
private String[] basePackages;
private String database;
private DatabaseSelectionProvider databaseSelectionProvider;
private UserSelectionProvider userSelectionProvider;
/**
* Protected constructor of the Configuration class.
* Use {@link Builder} to create an instance of Configuration class
*/
Configuration(Builder builder) {
this.uri = builder.uri;
this.connectionPoolSize = builder.connectionPoolSize != null ? builder.connectionPoolSize : DEFAULT_SESSION_POOL_SIZE;
this.encryptionLevel = builder.encryptionLevel;
this.trustStrategy = builder.trustStrategy;
this.trustCertFile = builder.trustCertFile;
this.connectionLivenessCheckTimeout = builder.connectionLivenessCheckTimeout;
this.verifyConnection = builder.verifyConnection != null ? builder.verifyConnection : false;
this.autoIndex = builder.autoIndex != null ? AutoIndexMode.fromString(builder.autoIndex) : AutoIndexMode.NONE;
this.generatedIndexesOutputDir =
builder.generatedIndexesOutputDir != null ? builder.generatedIndexesOutputDir : ".";
this.generatedIndexesOutputFilename = builder.generatedIndexesOutputFilename != null ?
builder.generatedIndexesOutputFilename :
"generated_indexes.cql";
this.neo4jConfLocation = builder.neo4jConfLocation;
this.customProperties = builder.customProperties;
this.useNativeTypes = builder.useNativeTypes;
this.basePackages = builder.basePackages;
this.useStrictQuerying = builder.useStrictQuerying;
this.database = Optional.ofNullable(builder.database).map(String::trim).filter(s -> !s.isEmpty()).orElse(null);
this.databaseSelectionProvider = builder.databaseSelectionProvider;
this.userSelectionProvider = builder.userSelectionProvider;
URI parsedUri = getSingleURI();
if (parsedUri != null) {
parseAndSetParametersFromURI(parsedUri);
} else {
this.driverName = Drivers.EMBEDDED.driverClassName();
}
if (builder.username != null && builder.password != null) {
if (this.credentials != null) {
LOGGER.warn("Overriding credentials supplied in URI with supplied username and password.");
}
credentials = new UsernamePasswordCredentials(builder.username, builder.password);
}
}
private void parseAndSetParametersFromURI(URI parsedUri) {
String userInfo = parsedUri.getUserInfo();
if (userInfo != null) {
String[] userPass = userInfo.split(":");
credentials = new UsernamePasswordCredentials(userPass[0], userPass[1]);
this.uri = parsedUri.toString().replace(parsedUri.getUserInfo() + "@", "");
}
if (getDriverClassName() == null) {
this.driverName = Drivers.getDriverFor(parsedUri.getScheme()).driverClassName();
}
}
private URI getSingleURI() {
try {
if (uri != null) {
return new URI(uri);
}
} catch (URISyntaxException e) {
throw new RuntimeException("Could not configure supplied URI in Configuration", e);
}
return null;
}
public AutoIndexMode getAutoIndex() {
return autoIndex;
}
public String getDumpDir() {
return generatedIndexesOutputDir;
}
public String getDumpFilename() {
return generatedIndexesOutputFilename;
}
public String getURI() {
return uri;
}
public String getDriverClassName() {
return driverName;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public String getEncryptionLevel() {
return encryptionLevel;
}
public String getTrustStrategy() {
return trustStrategy;
}
public String getTrustCertFile() {
return trustCertFile;
}
public Integer getConnectionLivenessCheckTimeout() {
return connectionLivenessCheckTimeout;
}
public Boolean getVerifyConnection() {
return verifyConnection;
}
public String getNeo4jConfLocation() {
return this.neo4jConfLocation;
}
public String getDatabase() {
return database;
}
public DatabaseSelectionProvider getDatabaseSelectionProvider() {
return databaseSelectionProvider;
}
public UserSelectionProvider getUserSelectionProvider() {
return userSelectionProvider;
}
/**
* @return True if current configuration is setup to use embedded HA.
*/
public boolean isEmbeddedHA() {
boolean isEmbeddedHA = false;
if (this.neo4jConfLocation != null) {
try {
URL url = ConfigurationUtils.getResourceUrl(neo4jConfLocation);
Properties neo4Properties = new Properties();
neo4Properties.load(url.openStream());
isEmbeddedHA = !"SINGLE".equalsIgnoreCase(neo4Properties.getProperty("dbms.mode", "SINGLE"));
} catch (IOException e) {
throw new UncheckedIOException("Could not load neo4j.conf at location " + neo4jConfLocation, e);
}
}
return isEmbeddedHA;
}
public URL getResourceUrl(String resourceLocation) throws FileNotFoundException {
return ConfigurationUtils.getResourceUrl(resourceLocation);
}
public Credentials getCredentials() {
return credentials;
}
public Map getCustomProperties() {
return Collections.unmodifiableMap(customProperties);
}
public Boolean getUseNativeTypes() {
return useNativeTypes;
}
public Boolean getUseStrictQuerying() {
return useStrictQuerying;
}
public String[] getBasePackages() {
return basePackages;
}
public String[] mergeBasePackagesWith(String... anotherSetOfBasePackages) {
String[] set1 = Optional.ofNullable(this.basePackages).orElseGet(() -> new String[0]);
String[] set2 = Optional.ofNullable(anotherSetOfBasePackages).orElseGet(() -> new String[0]);
return Stream.concat(Arrays.stream(set1), Arrays.stream(set2))
.filter(s -> s != null)
.distinct()
.toArray(String[]::new);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Configuration)) {
return false;
}
Configuration that = (Configuration) o;
return connectionPoolSize == that.connectionPoolSize &&
Objects.equals(uri, that.uri) &&
Objects.equals(encryptionLevel, that.encryptionLevel) &&
Objects.equals(trustStrategy, that.trustStrategy) &&
Objects.equals(trustCertFile, that.trustCertFile) &&
autoIndex == that.autoIndex &&
Objects.equals(generatedIndexesOutputDir, that.generatedIndexesOutputDir) &&
Objects.equals(generatedIndexesOutputFilename, that.generatedIndexesOutputFilename) &&
Objects.equals(neo4jConfLocation, that.neo4jConfLocation) &&
Objects.equals(driverName, that.driverName) &&
Objects.equals(credentials, that.credentials) &&
Objects.equals(connectionLivenessCheckTimeout, that.connectionLivenessCheckTimeout) &&
Objects.equals(verifyConnection, that.verifyConnection) &&
Objects.equals(useNativeTypes, that.useNativeTypes) &&
Arrays.equals(basePackages, that.basePackages) &&
Objects.equals(useStrictQuerying, that.useStrictQuerying);
}
@Override
public int hashCode() {
int result = Objects.hash(uri, connectionPoolSize, encryptionLevel, trustStrategy, trustCertFile, autoIndex,
generatedIndexesOutputDir, generatedIndexesOutputFilename, neo4jConfLocation, driverName, credentials,
connectionLivenessCheckTimeout, verifyConnection, useNativeTypes);
result = 31 * result + Arrays.hashCode(basePackages);
return result;
}
/**
* Builder for {@link Configuration} class
*/
@SuppressWarnings("HiddenField")
public static class Builder {
// Those are the keys inside ogm.properties, not configuration values.
private static final String URI = "URI";
private static final String URIS = "URIS";
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private static final String CONNECTION_POOL_SIZE = "connection.pool.size";
private static final String ENCRYPTION_LEVEL = "encryption.level";
private static final String TRUST_STRATEGY = "trust.strategy";
private static final String TRUST_CERT_FILE = "trust.certificate.file";
private static final String CONNECTION_LIVENESS_CHECK_TIMEOUT = "connection.liveness.check.timeout";
private static final String VERIFY_CONNECTION = "verify.connection";
private static final String AUTO_INDEX = "indexes.auto";
private static final String GENERATED_INDEXES_OUTPUT_DIR = "indexes.auto.dump.dir";
private static final String GENERATED_INDEXES_OUTPUT_FILENAME = "indexes.auto.dump.filename";
private static final String NEO4J_CONF_LOCATION = "neo4j.conf.location";
private static final String USE_NATIVE_TYPES = "use-native-types";
private static final String BASE_PACKAGES = "base-packages";
private static final String USE_STRICT_QUERYING = "use-strict-querying";
private static final String DATABASE = "database";
private String uri;
private Integer connectionPoolSize;
private String encryptionLevel;
private String trustStrategy;
private String trustCertFile;
private Integer connectionLivenessCheckTimeout;
private Boolean verifyConnection;
private String autoIndex;
private String generatedIndexesOutputDir;
private String generatedIndexesOutputFilename;
private String neo4jConfLocation;
private String username;
private String password;
private boolean useNativeTypes;
private Map customProperties = new HashMap<>();
private String[] basePackages;
private boolean useStrictQuerying = true;
private String database;
public DatabaseSelectionProvider databaseSelectionProvider;
public UserSelectionProvider userSelectionProvider;
/**
* Creates new Configuration builder
* Use for Java configuration.
*/
public Builder() {
}
/**
* Creates new Configuration builder
*
* @param configurationSource source of the configuration, file on classpath or filesystem
*/
public Builder(ConfigurationSource configurationSource) {
for (Map.Entry