org.springframework.data.cassandra.core.cql.session.init.ResourceKeyspacePopulator Maven / Gradle / Ivy
Show all versions of spring-data-cassandra Show documentation
/*
* Copyright 2019-2024 the original author or authors.
*
* 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.springframework.data.cassandra.core.cql.session.init;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Populates, initializes, or cleans up a keyspace using CQL scripts defined in external resources.
*
* - Call {@link #addScript} to add a single CQL script location.
*
- Call {@link #addScripts} to add multiple CQL script locations.
*
- Consult the setter methods in this class for further configuration options.
*
- Call {@link #populate} or {@link #execute} to initialize or clean up the database using the configured scripts.
*
*
* @author Mark Paluch
* @since 3.0
* @see ScriptUtils
*/
public class ResourceKeyspacePopulator implements KeyspacePopulator {
List scripts = new ArrayList<>();
@Nullable private String cqlScriptEncoding;
private String separator = ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
private String[] commentPrefixes = ScriptUtils.DEFAULT_COMMENT_PREFIXES;
private String blockCommentStartDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
private String blockCommentEndDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
private boolean continueOnError = false;
private boolean ignoreFailedDrops = false;
/**
* Construct a new {@link ResourceKeyspacePopulator} with default settings.
*/
public ResourceKeyspacePopulator() {}
/**
* Construct a new {@link ResourceKeyspacePopulator} with default settings for the supplied scripts.
*
* @param scripts the scripts to execute to initialize or clean up the database (never {@literal null})
*/
public ResourceKeyspacePopulator(Resource... scripts) {
setScripts(scripts);
}
/**
* Construct a new {@link ResourceKeyspacePopulator} with the supplied values.
*
* @param continueOnError flag to indicate that all failures in CQL should be logged but not cause a failure
* @param ignoreFailedDrops flag to indicate that a failed CQL {@code DROP} statement can be ignored
* @param cqlScriptEncoding the encoding for the supplied CQL scripts (may be {@literal null} or empty to
* indicate platform encoding)
* @param scripts the scripts to execute to initialize or clean up the database (never {@literal null})
*/
public ResourceKeyspacePopulator(boolean continueOnError, boolean ignoreFailedDrops,
@Nullable String cqlScriptEncoding, Resource... scripts) {
this.continueOnError = continueOnError;
this.ignoreFailedDrops = ignoreFailedDrops;
setCqlScriptEncoding(cqlScriptEncoding);
setScripts(scripts);
}
/**
* Add a script to execute to initialize or clean up the database.
*
* @param script the path to an CQL script (never {@literal null}).
*/
public void addScript(Resource script) {
Assert.notNull(script, "'script' must not be null");
this.scripts.add(script);
}
/**
* Add multiple scripts to execute to initialize or clean up the database.
*
* @param scripts the scripts to execute (never {@literal null}).
*/
public void addScripts(Resource... scripts) {
assertContentsOfScriptArray(scripts);
this.scripts.addAll(Arrays.asList(scripts));
}
/**
* Set the scripts to execute to initialize or clean up the database, replacing any previously added scripts.
*
* @param scripts the scripts to execute (never {@literal null}).
*/
public void setScripts(Resource... scripts) {
assertContentsOfScriptArray(scripts);
// Ensure that the list is modifiable
this.scripts = new ArrayList<>(Arrays.asList(scripts));
}
private void assertContentsOfScriptArray(Resource... scripts) {
Assert.notNull(scripts, "'scripts' must not be null");
Assert.noNullElements(scripts, "'scripts' must not contain null elements");
}
/**
* Specify the encoding for the configured CQL scripts, if different from the platform encoding.
*
* @param cqlScriptEncoding the encoding used in scripts (may be {@literal null} or empty to indicate platform
* encoding).
* @see #addScript(Resource)
*/
public void setCqlScriptEncoding(@Nullable String cqlScriptEncoding) {
this.cqlScriptEncoding = (StringUtils.hasText(cqlScriptEncoding) ? cqlScriptEncoding : null);
}
/**
* Specify the statement separator, if a custom one.
*
* Defaults to {@code ";"} if not specified and falls back to {@code "\n"} as a last resort; may be set to
* {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} to signal that each script contains a single statement without a
* separator.
*
* @param separator the script statement separator.
*/
public void setSeparator(String separator) {
this.separator = separator;
}
/**
* Set the prefix that identifies single-line comments within the CQL scripts.
*
* Defaults to {@code "--"}.
*
* @param commentPrefix the prefix for single-line comments.
* @see #setCommentPrefixes(String...)
*/
public void setCommentPrefix(String commentPrefix) {
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty");
this.commentPrefixes = new String[] { commentPrefix };
}
/**
* Set the prefixes that identify single-line comments within the CQL scripts.
*
* Defaults to {@code ["--"]}.
*
* @param commentPrefixes the prefixes for single-line comments.
*/
public void setCommentPrefixes(String... commentPrefixes) {
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty");
Assert.noNullElements(commentPrefixes, "'commentPrefixes' must not contain null elements");
this.commentPrefixes = commentPrefixes;
}
/**
* Set the start delimiter that identifies block comments within the CQL scripts.
*
* Defaults to {@code "/*"}.
*
* @param blockCommentStartDelimiter the start delimiter for block comments (never {@literal null} or empty).
* @see #setBlockCommentEndDelimiter
*/
public void setBlockCommentStartDelimiter(String blockCommentStartDelimiter) {
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty");
this.blockCommentStartDelimiter = blockCommentStartDelimiter;
}
/**
* Set the end delimiter that identifies block comments within the CQL scripts.
*
* Defaults to "*/"
.
*
* @param blockCommentEndDelimiter the end delimiter for block comments (never {@literal null} or empty).
* @see #setBlockCommentStartDelimiter
*/
public void setBlockCommentEndDelimiter(String blockCommentEndDelimiter) {
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty");
this.blockCommentEndDelimiter = blockCommentEndDelimiter;
}
/**
* Flag to indicate that all failures in CQL should be logged but not cause a failure.
*
* Defaults to {@literal false}.
*
* @param continueOnError {@literal true} if script execution should continue on error
*/
public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError;
}
/**
* Flag to indicate that a failed CQL {@code DROP} statement can be ignored.
*
* This is useful for a non-embedded database whose CQL dialect does not support an {@code IF EXISTS} clause in a
* {@code DROP} statement.
*
* The default is {@literal false} so that if the populator runs accidentally, it will fail fast if a script starts
* with a {@code DROP} statement.
*
* @param ignoreFailedDrops {@literal true} if failed drop statements should be ignored.
*/
public void setIgnoreFailedDrops(boolean ignoreFailedDrops) {
this.ignoreFailedDrops = ignoreFailedDrops;
}
@Override
public void populate(CqlSession session) throws ScriptException {
Assert.notNull(session, "session must not be null");
for (Resource script : this.scripts) {
EncodedResource encodedScript = new EncodedResource(script, this.cqlScriptEncoding);
ScriptUtils.executeCqlScript(session, encodedScript, this.continueOnError, this.ignoreFailedDrops,
this.commentPrefixes, this.separator, this.blockCommentStartDelimiter, this.blockCommentEndDelimiter);
}
}
/**
* Execute this {@link ResourceKeyspacePopulator} against the given {@link SessionFactory}.
*
* @param sessionFactory the {@link SessionFactory} to execute against (never {@literal null})
* @throws ScriptException if an error occurs
* @see #populate(Session)
*/
public void execute(SessionFactory sessionFactory) throws ScriptException {
populate(sessionFactory.getSession());
}
}