From 7bc10f9671b187cecc2be8f5c52f7b4a7272f9f0 Mon Sep 17 00:00:00 2001
From: vharseko <vharseko@3a-systems.ru>
Date: Fri, 22 Sep 2023 07:30:49 +0000
Subject: [PATCH] Allow store LDAP catalog data in CASSANDRA noSQL cluster --backendType cas (ldapv3 to cassandra) (#302)
---
opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java | 16
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java | 1
opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Storage.java | 521 ++++++++++++++++++++++++++++++++++
opendj-maven-plugin/src/main/resources/config/stylesheets/abbreviations.xsl | 3
opendj-server-legacy/resource/schema/02-config.ldif | 7
opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/EncryptedTestCase.java | 62 ++++
opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Backend.java | 16 +
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java | 12
opendj-server-legacy/src/main/java/org/opends/server/tools/BackendTypeHelper.java | 6
opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java | 56 +++
pom.xml | 2
opendj-server-legacy/src/test/java/org/opends/server/TestListener.java | 2
opendj-server-legacy/pom.xml | 14
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/DefaultTCPNIOTransport.java | 1
opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java | 12
.github/workflows/build.yml | 36 +
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CASBackendConfiguration.xml | 79 +++++
opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/package-info.java | 18 +
18 files changed, 841 insertions(+), 23 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d0f7fef..b70df88 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -41,31 +41,49 @@
path: ~/.m2/repository
key: ${{ runner.os }}-m2-repository-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-repository
- - name: Set Integration Test Environment
- id: maven-profile-flag
+ - name: Run docker cassandra
+ if: runner.os == 'Linux'
+ run: |
+ docker run --rm -it -d -p 9042:9042 --name cassandra cassandra
+ timeout 5m bash -c 'until docker logs cassandra | grep -q "Created default superuser role"; do sleep 5; done'
+ - name: Set Integration Test Environment
+ id: failsafe
if: runner.os != 'Windows'
- run: |
- echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT
+ run: |
+ echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT
- name: Build with Maven
+ timeout-minutes: 180
env:
MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10
- run: mvn --batch-mode --errors --update-snapshots verify --file pom.xml ${{ steps.maven-profile-flag.outputs.MAVEN_PROFILE_FLAG }}
+ run: mvn --batch-mode --errors --update-snapshots verify --file pom.xml ${{ steps.failsafe.outputs.MAVEN_PROFILE_FLAG }}
- name: Test on Unix
if: runner.os != 'Windows'
run: |
export OPENDJ_JAVA_ARGS="-server -Xmx1g"
- opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 50000 --cli --acceptLicense --no-prompt
+ opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj/bin/status --bindDN "cn=Directory Manager" --bindPassword password
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
- opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 50000
+ opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 5000
+ opendj-server-legacy/target/package/opendj/bin/stop-ds
+ - name: Test LDAP in Cassandra
+ if: runner.os == 'Linux'
+ run: |
+ rm -rf opendj-server-legacy/target/package/opendj/config opendj-server-legacy/target/package/opendj/db opendj-server-legacy/target/package/opendj/changelogDb opendj-server-legacy/target/package/opendj/logs
+ export OPENDJ_JAVA_ARGS="-server -Xmx1g -Ddatastax-java-driver.basic.contact-points.0=localhost:9042 -Ddatastax-java-driver.basic.load-balancing-policy.local-datacenter=datacenter1"
+ opendj-server-legacy/target/package/opendj/setup --backendType cas -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt
+ opendj-server-legacy/target/package/opendj/bin/status --bindDN "cn=Directory Manager" --bindPassword password
+ opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
+ opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 5000
+ opendj-server-legacy/target/package/opendj/bin/stop-ds
- name: Test on Windows
if: runner.os == 'Windows'
run: |
set OPENDJ_JAVA_ARGS="-server -Xmx1g"
- opendj-server-legacy\target\package\opendj\setup.bat -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 50000 --cli --acceptLicense --no-prompt
+ opendj-server-legacy\target\package\opendj\setup.bat -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt
opendj-server-legacy\target\package\opendj\bat\status.bat --bindDN "cn=Directory Manager" --bindPassword password
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
- opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "50000"
+ opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "5000"
+ opendj-server-legacy\target\package\opendj\bat\stop-ds.bat
- name: Upload artifacts OpenDJ Server
uses: actions/upload-artifact@v3
with:
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/DefaultTCPNIOTransport.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/DefaultTCPNIOTransport.java
index 39c0313..9e2c56f 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/DefaultTCPNIOTransport.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/DefaultTCPNIOTransport.java
@@ -136,7 +136,6 @@
builder.setReuseAddress(Boolean.parseBoolean(reuseAddressStr));
}
builder.setMemoryManager(new PooledMemoryManager(true));
-
final TCPNIOTransport transport = builder.build();
diff --git a/opendj-maven-plugin/src/main/resources/config/stylesheets/abbreviations.xsl b/opendj-maven-plugin/src/main/resources/config/stylesheets/abbreviations.xsl
index 58d33cc..563e7b7 100644
--- a/opendj-maven-plugin/src/main/resources/config/stylesheets/abbreviations.xsl
+++ b/opendj-maven-plugin/src/main/resources/config/stylesheets/abbreviations.xsl
@@ -13,6 +13,7 @@
Copyright 2008-2009 Sun Microsystems, Inc.
Portions copyright 2011-2016 ForgeRock AS.
+ Portions copyright 2023 3A Systems LLC
! -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -43,7 +44,7 @@
or $value = 'des' or $value = 'aes' or $value = 'rc4'
or $value = 'db' or $value = 'snmp' or $value = 'qos'
or $value = 'ecl' or $value = 'ttl' or $value = 'jpeg'
- or $value = 'pbkdf2' or $value = 'pkcs5s2' or $value = 'pdb'
+ or $value = 'pbkdf2' or $value = 'pkcs5s2' or $value = 'pdb' or $value = 'cas'
"/>
</xsl:template>
</xsl:stylesheet>
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CASBackendConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CASBackendConfiguration.xml
new file mode 100644
index 0000000..c1d62c8
--- /dev/null
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CASBackendConfiguration.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ The contents of this file are subject to the terms of the Common Development and
+ Distribution License (the License). You may not use this file except in compliance with the
+ License.
+
+ You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ specific language governing permission and limitations under the License.
+
+ When distributing Covered Software, include this CDDL Header Notice in each file and include
+ the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ Header, with the fields enclosed by brackets [] replaced by your own identifying
+ information: "Portions Copyright [year] [name of copyright owner]".
+
+ Copyright 2023- 3A Systems LLC
+ ! -->
+<adm:managed-object name="cas-backend" plural-name="cas-backends"
+ package="org.forgerock.opendj.server.config"
+ extends="pluggable-backend" xmlns:adm="http://opendj.forgerock.org/admin"
+ xmlns:ldap="http://opendj.forgerock.org/admin-ldap"
+ xmlns:cli="http://opendj.forgerock.org/admin-cli">
+ <adm:synopsis>
+ A <adm:user-friendly-name/> stores application data in a Apache Cassandra cluster.
+ </adm:synopsis>
+ <adm:description>
+ It is the traditional "directory server" backend and is similar to
+ the backends provided by the Sun Java System Directory Server. The
+ <adm:user-friendly-name />
+ stores the entries in an encoded form and also provides indexes that
+ can be used to quickly locate target entries based on different
+ kinds of criteria.
+ </adm:description>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:name>ds-cfg-cas-backend</ldap:name>
+ <ldap:superior>ds-cfg-pluggable-backend</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property-override name="java-class" advanced="true">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ org.opends.server.backends.cassandra.Backend
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+
+ <adm:property name="db-directory" mandatory="true">
+ <adm:TODO>Default this to the db/backend-id</adm:TODO>
+ <adm:synopsis>
+ Specifies the keyspace name
+ </adm:synopsis>
+ <adm:description>
+ The path may be either an absolute path or a path relative to the
+ directory containing the base of the <adm:product-name /> directory server
+ installation. The path may be any valid directory path in which
+ the server has appropriate permissions to read and write files and
+ has sufficient space to hold the database contents.
+ </adm:description>
+ <adm:requires-admin-action>
+ <adm:component-restart />
+ </adm:requires-admin-action>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>ldap_opendj</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-db-directory</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+</adm:managed-object>
diff --git a/opendj-server-legacy/pom.xml b/opendj-server-legacy/pom.xml
index 9e9de76..0caee10 100644
--- a/opendj-server-legacy/pom.xml
+++ b/opendj-server-legacy/pom.xml
@@ -245,6 +245,18 @@
<artifactId>juel-api</artifactId>
<version>${juel.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.datastax.oss</groupId>
+ <artifactId>java-driver-core</artifactId>
+ <version>4.17.0</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.reactivestreams</groupId>
+ <artifactId>reactive-streams</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
<build><finalName>${project.groupId}.${project.artifactId}</finalName>
@@ -1216,7 +1228,7 @@
<org.opends.test.pauseOnFailure>false</org.opends.test.pauseOnFailure>
<org.opends.test.copyClassesToTestPackage>false</org.opends.test.copyClassesToTestPackage>
</systemPropertyVariables>
- <argLine> -Xmx2g @{argLine}</argLine>
+ <argLine> -Xmx1g @{argLine}</argLine>
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
<parallel>none</parallel>
diff --git a/opendj-server-legacy/resource/schema/02-config.ldif b/opendj-server-legacy/resource/schema/02-config.ldif
index 5f69777..71bb719 100644
--- a/opendj-server-legacy/resource/schema/02-config.ldif
+++ b/opendj-server-legacy/resource/schema/02-config.ldif
@@ -15,6 +15,7 @@
# Portions Copyright 2011 profiq, s.r.o.
# Portions Copyright 2012 Manuel Gaupp
# Portions copyright 2015 Edan Idzerda
+# Portions copyright 2023 3A Systems LLC
# This file contains the attribute type and objectclass definitions for use
# with the Directory Server configuration.
@@ -6006,6 +6007,12 @@
ds-cfg-disk-low-threshold $
ds-cfg-je-property )
X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.60142.2.1.2.1
+ NAME 'ds-cfg-cas-backend'
+ SUP ds-cfg-pluggable-backend
+ STRUCTURAL
+ MUST ds-cfg-db-directory
+ X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.27
NAME 'ds-task-reset-change-number'
SUP ds-task
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
index b776bd6..b70df04 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
@@ -41,6 +41,9 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@@ -616,6 +619,15 @@
{
final String tempLogFilePath = tempLogFile.getPath();
notifyListeners(getFormattedProgress(INFO_GENERAL_PROVIDE_LOG_IN_ERROR.get(tempLogFilePath)));
+ //write log
+ try {
+ notifyListeners(getLineBreak());
+ notifyListeners(LocalizableMessage.valueOf(new String(Files.readAllBytes(Paths.get(tempLogFilePath)),"UTF-8")));
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
notifyListeners(getLineBreak());
}
}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Backend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Backend.java
new file mode 100644
index 0000000..42bcc8b
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Backend.java
@@ -0,0 +1,16 @@
+package org.opends.server.backends.cassandra;
+
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.server.config.server.CASBackendCfg;
+import org.opends.server.backends.pluggable.BackendImpl;
+import org.opends.server.core.ServerContext;
+
+public class Backend extends BackendImpl<CASBackendCfg>{
+
+ @Override
+ protected Storage configureStorage(CASBackendCfg cfg, ServerContext serverContext) throws ConfigException
+ {
+ return new Storage(cfg, serverContext);
+ }
+
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Storage.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Storage.java
new file mode 100644
index 0000000..b807234
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/Storage.java
@@ -0,0 +1,521 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2023 3A Systems, LLC.
+ */
+package org.opends.server.backends.cassandra;
+
+
+import static org.opends.server.backends.pluggable.spi.StorageUtils.addErrorMessage;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+
+import java.nio.ByteBuffer;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.config.server.ConfigurationChangeListener;
+import org.forgerock.opendj.ldap.ByteSequence;
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.server.config.server.CASBackendCfg;
+import org.opends.server.backends.pluggable.spi.AccessMode;
+import org.opends.server.backends.pluggable.spi.Cursor;
+import org.opends.server.backends.pluggable.spi.Importer;
+import org.opends.server.backends.pluggable.spi.ReadOnlyStorageException;
+import org.opends.server.backends.pluggable.spi.ReadOperation;
+import org.opends.server.backends.pluggable.spi.ReadableTransaction;
+import org.opends.server.backends.pluggable.spi.SequentialCursor;
+import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
+import org.opends.server.backends.pluggable.spi.StorageStatus;
+import org.opends.server.backends.pluggable.spi.TreeName;
+import org.opends.server.backends.pluggable.spi.UpdateFunction;
+import org.opends.server.backends.pluggable.spi.WriteOperation;
+import org.opends.server.backends.pluggable.spi.WriteableTransaction;
+import org.opends.server.core.ServerContext;
+import org.opends.server.types.BackupConfig;
+import org.opends.server.types.BackupDirectory;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.RestoreConfig;
+import org.opends.server.util.BackupManager;
+
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import com.datastax.oss.driver.api.core.cql.PreparedStatement;
+import com.datastax.oss.driver.api.core.cql.ResultSet;
+import com.datastax.oss.driver.api.core.cql.Row;
+import com.datastax.oss.driver.api.core.cql.Statement;
+import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+public class Storage implements org.opends.server.backends.pluggable.spi.Storage, ConfigurationChangeListener<CASBackendCfg>{
+
+ private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+ //private final ServerContext serverContext;
+ private CASBackendCfg config;
+
+ public Storage(CASBackendCfg cfg, ServerContext serverContext) {
+ //this.serverContext = serverContext;
+ this.config = cfg;
+ cfg.addCASChangeListener(this);
+ }
+
+ //config
+ @Override
+ public boolean isConfigurationChangeAcceptable(CASBackendCfg configuration,List<LocalizableMessage> unacceptableReasons) {
+ return true;
+ }
+
+ @Override
+ public ConfigChangeResult applyConfigurationChange(CASBackendCfg cfg) {
+ final ConfigChangeResult ccr = new ConfigChangeResult();
+ try
+ {
+ this.config = cfg;
+ }
+ catch (Exception e)
+ {
+ addErrorMessage(ccr, LocalizableMessage.raw(stackTraceToSingleLineString(e)));
+ }
+ return ccr;
+ }
+
+ CqlSession session=null;
+
+ final LoadingCache<String,PreparedStatement> prepared=CacheBuilder.newBuilder()
+ .expireAfterAccess(Duration.ofMinutes(10))
+ .maximumSize(4096)
+ .build(new CacheLoader<String,PreparedStatement>(){
+ @Override
+ public PreparedStatement load(String query) throws Exception {
+ return session.prepare(query);
+ }
+ });
+
+ ResultSet execute(Statement<?> statement) {
+ if (logger.isTraceEnabled()) {
+ final ResultSet res=session.execute(statement.setTracing(true));
+ logger.trace(LocalizableMessage.raw(
+ "cassandra: %s"
+ ,res.getExecutionInfo().getQueryTrace().getParameters()
+ )
+ );
+ return res;
+ }
+ return session.execute(statement);
+ }
+
+ AccessMode accessMode=null;
+ @Override
+ public void open(AccessMode accessMode) throws Exception {
+ this.accessMode=accessMode;
+ session=CqlSession.builder()
+ .withApplicationName("OpenDJ "+config.getDBDirectory()+"."+config.getBackendId())
+ .withConfigLoader(DriverConfigLoader.fromDefaults(Storage.class.getClassLoader()))
+ .build();
+ if (AccessMode.READ_WRITE.equals(accessMode)) {
+ execute(prepared.getUnchecked("CREATE KEYSPACE IF NOT EXISTS "+getKeyspaceName()+" WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};").bind().setExecutionProfileName(profile));
+ }
+ storageStatus = StorageStatus.working();
+ }
+
+ private StorageStatus storageStatus = StorageStatus.lockedDown(LocalizableMessage.raw("closed"));
+ @Override
+ public StorageStatus getStorageStatus() {
+ return storageStatus;
+ }
+
+ @Override
+ public void close() {
+ storageStatus = StorageStatus.lockedDown(LocalizableMessage.raw("closed"));
+ if (session!=null && !session.isClosed()) {
+ session.close();
+ }
+ session=null;
+ }
+
+ String getKeyspaceName() {
+ return "\""+config.getDBDirectory().replaceAll("[^a-zA-z0-9_]", "_")+"\"";
+ }
+
+ String getTableName() {
+ return getKeyspaceName()+".\""+config.getBackendId().replaceAll("[^a-zA-z0-9_]", "_")+"\"";
+ }
+
+ @Override
+ public void removeStorageFiles() throws StorageRuntimeException {
+ final Boolean isOpen=getStorageStatus().isWorking();
+ if (!isOpen) {
+ try {
+ open(AccessMode.READ_WRITE);
+ }catch (Exception e) {
+ throw new StorageRuntimeException(e);
+ }
+ }
+ try {
+ execute(prepared.getUnchecked("TRUNCATE TABLE "+getTableName()+";").bind().setExecutionProfileName(profile));
+ }catch (Throwable e) {}
+ if (!isOpen) {
+ close();
+ }
+ }
+
+ //operation
+ @Override
+ public <T> T read(ReadOperation<T> readOperation) throws Exception {
+ return readOperation.run(new TransactionImpl(AccessMode.READ_ONLY));
+ }
+
+ @Override
+ public void write(WriteOperation writeOperation) throws Exception {
+ writeOperation.run(new TransactionImpl(accessMode));
+ }
+
+ final static String profile="ddl";
+ static {
+ if (System.getProperty("datastax-java-driver.basic.request.timeout")==null) {
+ System.setProperty("datastax-java-driver.basic.request.timeout", "10 seconds");
+ }
+ if (System.getProperty("datastax-java-driver.profiles."+profile+".basic.request.timeout")==null) {
+ System.setProperty("datastax-java-driver.profiles."+profile+".basic.request.timeout", "30 seconds");
+ }
+ }
+ private final class TransactionImpl implements ReadableTransaction,WriteableTransaction {
+
+ final AccessMode accessMode;
+ public TransactionImpl(AccessMode accessMode) {
+ super();
+ this.accessMode=accessMode;
+ }
+
+ @Override
+ public void openTree(TreeName name, boolean createOnDemand) {
+ if (createOnDemand) {
+ execute(prepared.getUnchecked("CREATE TABLE IF NOT EXISTS "+getTableName()+" (baseDN text,indexId text,key blob,value blob,PRIMARY KEY ((baseDN,indexId),key));").bind().setExecutionProfileName(profile));
+ }
+ }
+
+ public void clearTree(TreeName treeName) {
+ checkReadOnly();
+ deleteTree(treeName);
+ }
+
+ @Override
+ public ByteString read(TreeName treeName, ByteSequence key) {
+ final Row row=execute(
+ prepared.getUnchecked("SELECT value FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId and key=:key").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ .setByteBuffer("key", ByteBuffer.wrap(key.toByteArray()))
+ ).one();
+ return row==null?null:ByteString.wrap(row.getByteBuffer("value").array());
+ }
+
+ @Override
+ public Cursor<ByteString, ByteString> openCursor(TreeName treeName) {
+ return new CursorImpl(this,treeName);
+ }
+
+ @Override
+ public long getRecordCount(TreeName treeName) {
+ return execute(
+ prepared.getUnchecked("SELECT count(*) FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ ).one().getLong(0);
+ }
+
+ @Override
+ public void deleteTree(TreeName treeName) {
+ checkReadOnly();
+ openTree(treeName,true);
+ execute(
+ prepared.getUnchecked("DELETE FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ );
+ }
+
+ @Override
+ public void put(TreeName treeName, ByteSequence key, ByteSequence value) {
+ checkReadOnly();
+ execute(
+ prepared.getUnchecked("INSERT INTO "+getTableName()+" (baseDN,indexId,key,value) VALUES (:baseDN,:indexId,:key,:value)").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ .setByteBuffer("key", ByteBuffer.wrap(key.toByteArray()))
+ .setByteBuffer("value",ByteBuffer.wrap(value.toByteArray()))
+ );
+ }
+
+ @Override
+ public boolean update(TreeName treeName, ByteSequence key, UpdateFunction f) {
+ checkReadOnly();
+ final ByteString oldValue=read(treeName,key);
+ final ByteSequence newValue=f.computeNewValue(oldValue);
+ if (Objects.equals(newValue, oldValue))
+ {
+ return false;
+ }
+ if (newValue == null)
+ {
+ delete(treeName, key);
+ return true;
+ }
+ put(treeName,key,newValue);
+ return true;
+ }
+
+ @Override
+ public boolean delete(TreeName treeName, ByteSequence key) {
+ checkReadOnly();
+ execute(
+ prepared.getUnchecked("DELETE FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId and key=:key").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ .setByteBuffer("key", ByteBuffer.wrap(key.toByteArray()))
+ );
+ return true;
+ }
+
+ void checkReadOnly() {
+ if (AccessMode.READ_ONLY.equals(accessMode)) {
+ throw new ReadOnlyStorageException();
+ }
+ }
+ }
+
+ private final class CursorImpl implements Cursor<ByteString, ByteString> {
+ final TreeName treeName;
+ final TransactionImpl tx;
+
+ ResultSet rc;
+ Iterator<Row> iterator;
+ Row current=null;
+
+ public CursorImpl(TransactionImpl tx,TreeName treeName) {
+ this.treeName=treeName;
+ this.tx=tx;
+ }
+
+ ResultSet full(){
+ return execute(
+ prepared.getUnchecked("SELECT key,value FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId ORDER BY key").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ );
+ }
+
+ @Override
+ public boolean next() {
+ if (iterator==null) {
+ rc=full();
+ iterator=rc.iterator();
+ }
+ try {
+ current=iterator.next();
+ return true;
+ }catch (NoSuchElementException e) {
+ current=null;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isDefined() {
+ return current!=null;
+ }
+
+ @Override
+ public ByteString getKey() throws NoSuchElementException {
+ if (!isDefined()) {
+ throw new NoSuchElementException();
+ }
+ return ByteString.wrap(current.getByteBuffer("key").array());
+ }
+
+ @Override
+ public ByteString getValue() throws NoSuchElementException {
+ if (!isDefined()) {
+ throw new NoSuchElementException();
+ }
+ return ByteString.wrap(current.getByteBuffer("value").array());
+ }
+
+ @Override
+ public void delete() throws NoSuchElementException, UnsupportedOperationException {
+ if (!isDefined()) {
+ throw new NoSuchElementException();
+ }
+ tx.delete(treeName, getKey());
+ }
+
+ @Override
+ public void close() {
+ iterator=null;
+ current=null;
+ rc=null;
+ }
+
+ ResultSet full(ByteSequence key){
+ return execute(
+ prepared.getUnchecked("SELECT key,value FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId and key>=:key ORDER BY key").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ .setByteBuffer("key", ByteBuffer.wrap(key.toByteArray()))
+ );
+ }
+ @Override
+ public boolean positionToKeyOrNext(ByteSequence key) {
+ rc=full(key); // start iterator from key key>=:key
+ iterator=rc.iterator();
+ if (iterator.hasNext()) {
+ current=iterator.next();
+ return true;
+ }
+ current=null;
+ return false;
+ }
+
+ @Override
+ public boolean positionToKey(ByteSequence key) {
+ if (positionToKeyOrNext(key) && key.equals(getKey())){
+ return true;
+ }
+ current=null;
+ return false;
+ }
+
+ ResultSet last(){
+ return execute(
+ prepared.getUnchecked("SELECT key,value FROM "+getTableName()+" WHERE baseDN=:baseDN and indexId=:indexId ORDER BY key DESC LIMIT 1").bind()
+ .setString("baseDN", treeName.getBaseDN()).setString("indexId", treeName.getIndexId())
+ );
+ }
+
+ @Override
+ public boolean positionToLastKey() {
+ rc=last();
+ iterator=rc.iterator();
+ if (iterator.hasNext()) {
+ current=iterator.next();
+ return true;
+ }
+ current=null;
+ return false;
+ }
+
+ @Override
+ public boolean positionToIndex(int index) {
+ iterator=rc.iterator(); //reset position
+ int ct=0;
+ while(iterator.hasNext()){
+ current=iterator.next();
+ if (ct==index) {
+ return true;
+ }
+ ct++;
+ }
+ current=null;
+ return false;
+ }
+ }
+
+ @Override
+ public Set<TreeName> listTrees() {
+ // TODO Auto-generated method stub
+ return Collections.emptySet();
+ }
+
+
+ private final class ImporterImpl implements Importer {
+ final TransactionImpl tx;
+
+ final Boolean isOpen;
+
+ public ImporterImpl() {
+ isOpen=getStorageStatus().isWorking();
+ if (!isOpen) {
+ try {
+ open(AccessMode.READ_WRITE);
+ }catch (Exception e) {
+ throw new StorageRuntimeException(e);
+ }
+ }
+ tx=new TransactionImpl(accessMode);
+ }
+
+ @Override
+ public void close() {
+ if (!isOpen) {
+ Storage.this.close();
+ }
+ }
+
+ @Override
+ public void clearTree(TreeName name) {
+ tx.clearTree(name);
+ }
+
+ @Override
+ public void put(TreeName treeName, ByteSequence key, ByteSequence value) {
+ tx.put(treeName, key, value);
+ }
+
+ @Override
+ public ByteString read(TreeName treeName, ByteSequence key) {
+ return tx.read(treeName, key);
+ }
+
+ @Override
+ public SequentialCursor<ByteString, ByteString> openCursor(TreeName treeName) {
+ return tx.openCursor(treeName);
+ }
+ }
+
+ //import
+ @Override
+ public Importer startImport() throws ConfigException, StorageRuntimeException {
+ return new ImporterImpl();
+ }
+
+ //backup
+ @Override
+ public boolean supportsBackupAndRestore() {
+ return true;
+ }
+
+ @Override
+ public void createBackup(BackupConfig backupConfig) throws DirectoryException
+ {
+ // TODO backup over snapshot or cassandra export
+ //new BackupManager(config.getBackendId()).createBackup(this, backupConfig);
+ }
+
+ @Override
+ public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException
+ {
+ new BackupManager(config.getBackendId()).removeBackup(backupDirectory, backupID);
+ }
+
+ @Override
+ public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException
+ {
+ // TODO restore over snapshot or cassandra export
+ //new BackupManager(config.getBackendId()).restoreBackup(this, restoreConfig);
+ }
+
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/package-info.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/package-info.java
new file mode 100644
index 0000000..6ce885b
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/cassandra/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2023 3A Systems LLC.
+ */
+@org.opends.server.types.PublicAPI(
+ stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.backends.cassandra;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java
index df4ba93..cb50717 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java
@@ -389,6 +389,7 @@
final int initialThreadCount = maxThreadCount;
final Long offheapMemorySize = backendCfg.getImportOffheapMemorySize();
boolean useOffHeap = (offheapMemorySize != null && offheapMemorySize > 0);
+
long memoryAvailable =
useOffHeap ? offheapMemorySize.longValue() : calculateAvailableHeapMemoryForBuffersAfterGC();
int threadCount = initialThreadCount;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/BackendTypeHelper.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/BackendTypeHelper.java
index 516c320..26dd222 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tools/BackendTypeHelper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/BackendTypeHelper.java
@@ -140,7 +140,11 @@
try
{
Class.forName(backendClassName);
- backends.add(backendToAdd);
+ if (backendClassName.equals("org.opends.server.backends.jeb.JEBackend")) { //default
+ backends.add(0,backendToAdd);
+ }else {
+ backends.add(backendToAdd);
+ }
}
catch (ClassNotFoundException ignored)
{
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
index 3a42c32..5bfc227 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
@@ -1673,13 +1673,15 @@
appendStreamContent(logsContents, TestCaseUtils.getSystemOutContents(), "System.out");
appendStreamContent(logsContents, TestCaseUtils.getSystemErrContents(), "System.err");
- for (final File logFile : Arrays.asList(new File(paths.testInstanceRoot, "logs").listFiles())) {
- try {
- appendStreamContent(logsContents, readFile(logFile.getPath()), logFile.getPath());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+ if (new File(paths.testInstanceRoot, "logs").listFiles()!=null) {
+ for (final File logFile : Arrays.asList(new File(paths.testInstanceRoot, "logs").listFiles())) {
+ try {
+ appendStreamContent(logsContents, readFile(logFile.getPath()), logFile.getPath());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
private static void appendStreamContent(StringBuilder out, String content, String name)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/TestListener.java b/opendj-server-legacy/src/test/java/org/opends/server/TestListener.java
index 5b92eab..461d615 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/TestListener.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/TestListener.java
@@ -13,6 +13,7 @@
*
* Copyright 2008 Sun Microsystems, Inc.
* Portions Copyright 2013-2016 ForgeRock AS.
+ * Portions Copyright 2023 3A Systems, LLC.
*/
package org.opends.server;
@@ -246,7 +247,6 @@
&& countTestsWithStatus(ITestResult.SKIP) != 0) {
originalSystemErr.println("There were no explicit test failures,"
+ " but some tests were skipped (possibly due to errors in @Before* or @After* methods).");
- System.exit(-1);
}
}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/EncryptedTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/EncryptedTestCase.java
new file mode 100644
index 0000000..03bfd8b
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/EncryptedTestCase.java
@@ -0,0 +1,62 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2023 3A Systems, LLC.
+ */
+package org.opends.server.backends.cassandra;
+
+import static org.mockito.Mockito.when;
+import static org.forgerock.opendj.config.ConfigurationMock.mockCfg;
+
+import org.forgerock.opendj.server.config.server.CASBackendCfg;
+import org.opends.server.backends.pluggable.PluggableBackendImplTestCase;
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+import com.datastax.oss.driver.api.core.AllNodesFailedException;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+
+//docker run --rm -it -p 9042:9042 --name cassandra cassandra
+
+@Test
+public class EncryptedTestCase extends PluggableBackendImplTestCase<CASBackendCfg>
+{
+ @Override
+ protected Backend createBackend()
+ {
+ System.setProperty("datastax-java-driver.basic.request.timeout", "30 seconds"); //for docker slow start
+ //test allow cassandra
+ try(CqlSession session=CqlSession.builder()
+ .withConfigLoader(DriverConfigLoader.fromDefaults(Storage.class.getClassLoader()))
+ .build()){
+ session.close();
+ }catch (AllNodesFailedException e) {
+ throw new SkipException("run before test: docker run --rm -it -p 9042:9042 --name cassandra cassandra");
+ }
+ return new Backend();
+ }
+
+ @Override
+ protected CASBackendCfg createBackendCfg()
+ {
+ CASBackendCfg backendCfg = mockCfg(CASBackendCfg.class);
+ when(backendCfg.getBackendId()).thenReturn("EncCASTestCase"+System.currentTimeMillis());
+ when(backendCfg.getDBDirectory()).thenReturn("EncCASTestCase");
+
+ when(backendCfg.isConfidentialityEnabled()).thenReturn(true);
+ when(backendCfg.getCipherKeyLength()).thenReturn(128);
+ when(backendCfg.getCipherTransformation()).thenReturn("AES/CBC/PKCS5Padding");
+ return backendCfg;
+ }
+}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java
new file mode 100644
index 0000000..18b0656
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java
@@ -0,0 +1,56 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2023 3A Systems, LLC.
+ */
+package org.opends.server.backends.cassandra;
+
+import static org.mockito.Mockito.when;
+import static org.forgerock.opendj.config.ConfigurationMock.mockCfg;
+
+import org.forgerock.opendj.server.config.server.CASBackendCfg;
+import org.opends.server.backends.pluggable.PluggableBackendImplTestCase;
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+import com.datastax.oss.driver.api.core.AllNodesFailedException;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+
+//docker run --rm -it -p 9042:9042 --name cassandra cassandra
+
+@Test
+public class TestCase extends PluggableBackendImplTestCase<CASBackendCfg> {
+
+ @Override
+ protected Backend createBackend() {
+ System.setProperty("datastax-java-driver.basic.request.timeout", "30 seconds"); //for docker slow start
+ //test allow cassandra
+ try(CqlSession session=CqlSession.builder()
+ .withConfigLoader(DriverConfigLoader.fromDefaults(Storage.class.getClassLoader()))
+ .build()){
+ session.close();
+ }catch (AllNodesFailedException e) {
+ throw new SkipException("run before test: docker run --rm -it -p 9042:9042 --name cassandra cassandra");
+ }
+ return new Backend();
+ }
+
+ @Override
+ protected CASBackendCfg createBackendCfg() {
+ CASBackendCfg backendCfg = mockCfg(CASBackendCfg.class);
+ when(backendCfg.getBackendId()).thenReturn("CASTestCase");
+ when(backendCfg.getDBDirectory()).thenReturn("CASTestCase");
+ return backendCfg;
+ }
+}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
index 9a9f4c0..a5d3e26 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2015-2016 ForgeRock AS.
+ * Copyright 2023 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;
@@ -66,6 +67,7 @@
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
+import org.opends.server.controls.SubtreeDeleteControl;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.ModifyDNOperation;
@@ -186,6 +188,12 @@
backend.configureBackend(backendCfg, TestCaseUtils.getServerContext());
backend.openBackend();
+ if (backend.entryExists(testBaseDN)) {
+ DeleteOperation op = mock(DeleteOperation.class);
+ when(op.getRequestControl(SubtreeDeleteControl.DECODER)).thenReturn(new SubtreeDeleteControl(true));
+ backend.deleteEntry(testBaseDN, op);
+ }
+
topEntries = TestCaseUtils.makeEntries(
"dn: " + testBaseDN,
"objectclass: top",
@@ -550,7 +558,7 @@
searchDN = entries.get(1).getName();
badEntryDN = testBaseDN.child(DN.valueOf("ou=bogus")).child(DN.valueOf("ou=dummy"));
backupID = "backupID1";
-
+
addEntriesToBackend(topEntries);
addEntriesToBackend(entries);
addEntriesToBackend(workEntries);
@@ -1171,6 +1179,8 @@
backend.finalizeBackend();
try
{
+ readOnlyContainer.open(AccessMode.READ_WRITE); //init storage before reading
+ readOnlyContainer.close();
readOnlyContainer.open(AccessMode.READ_ONLY);
readOnlyContainer.getStorage().write(new WriteOperation()
{
diff --git a/pom.xml b/pom.xml
index 24c7acc..6d80543 100644
--- a/pom.xml
+++ b/pom.xml
@@ -400,7 +400,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
- <version>3.1.0</version>
+ <version>3.1.2</version>
</plugin>
<plugin>
--
Gitblit v1.10.0