diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CqlSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CqlSessionFactoryBean.java index 3f42ae2ec..dff59a893 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CqlSessionFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CqlSessionFactoryBean.java @@ -67,6 +67,7 @@ * @author John Blum * @author Mark Paluch * @author Tomasz Lelek + * @author Ammar Khaku * @since 3.0 */ public class CqlSessionFactoryBean @@ -450,11 +451,20 @@ public void afterPropertiesSet() { this.session = buildSession(sessionBuilder); - executeCql(getStartupScripts().stream(), this.session); - performSchemaAction(); + try { + SchemaRefreshUtils.withDisabledSchema(this.session, () -> { + executeCql(getStartupScripts().stream(), this.session); + performSchemaAction(); + }); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IllegalStateException("Unexpected checked exception thrown", e); + } - this.systemSession.refreshSchema(); - this.session.refreshSchema(); + if (this.systemSession.isSchemaMetadataEnabled()) { + this.systemSession.refreshSchema(); + } } protected CqlSessionBuilder buildBuilder() { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaRefreshUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaRefreshUtils.java new file mode 100644 index 000000000..beb5c144d --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaRefreshUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 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.config; + +import com.datastax.oss.driver.api.core.session.Session; + +/** + * Utility methods for executing schema actions with refresh disabled. + * + * @author Ammar Khaku + */ +class SchemaRefreshUtils { + @FunctionalInterface + interface ThrowingRunnable { + void run() throws Exception; + } + + /** + * Programmatically disables schema refreshes on the session and runs the provided Runnable, + * taking care to restore the previous state of schema refresh config on the provided session. + * Note that the session could have had schema refreshes enabled/disabled either + * programmatically or via config. + */ + static void withDisabledSchema(Session session, ThrowingRunnable r) throws Exception { + boolean schemaEnabledPreviously = session.isSchemaMetadataEnabled(); + session.setSchemaMetadataEnabled(false); + r.run(); + session.setSchemaMetadataEnabled(null); // triggers schema refresh if results in true + if (schemaEnabledPreviously != session.isSchemaMetadataEnabled()) { + // user may have set it programmatically so set it back programmatically + session.setSchemaMetadataEnabled(schemaEnabledPreviously); + } + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SessionFactoryFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SessionFactoryFactoryBean.java index 0f0d838d5..6e5fb8e76 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SessionFactoryFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SessionFactoryFactoryBean.java @@ -37,6 +37,7 @@ * keyspace before applying {@link SchemaAction schema actions} such as creating user-defined types and tables. * * @author Mark Paluch + * @author Ammar Khaku * @since 3.0 * @see SessionFactoryInitializer */ @@ -128,9 +129,7 @@ public void afterPropertiesSet() throws Exception { this.keyspacePopulator.populate(getObject().getSession()); } - performSchemaAction(); - - this.session.refreshSchema(); + SchemaRefreshUtils.withDisabledSchema(session, this::performSchemaAction); } @Override diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/SchemaRefreshUtilsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/SchemaRefreshUtilsUnitTests.java new file mode 100644 index 000000000..ecef2ab33 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/SchemaRefreshUtilsUnitTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 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.config; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.datastax.oss.driver.api.core.session.Session; + +/** + * Test suite of unit tests testing the contract and functionality of the {@link SchemaRefreshUtils} class. + */ +@ExtendWith(MockitoExtension.class) +class SchemaRefreshUtilsUnitTests { + @Mock Session session; + + @Test + void withDisabledSchemaRevert() throws Exception { + when(session.isSchemaMetadataEnabled()).thenReturn(true); + SchemaRefreshUtils.withDisabledSchema(session, () -> {}); + verify(session).setSchemaMetadataEnabled(false); + verify(session).setSchemaMetadataEnabled(null); + } + + @Test + void withDisabledSchemaDisabledPreviously() throws Exception { + when(session.isSchemaMetadataEnabled()).thenReturn(false); + SchemaRefreshUtils.withDisabledSchema(session, () -> {}); + verify(session).setSchemaMetadataEnabled(false); + verify(session).setSchemaMetadataEnabled(null); + } + + @Test + void withDisabledSchemaDisabledProgrammaticallyPreviously() throws Exception { + when(session.isSchemaMetadataEnabled()).thenReturn(false).thenReturn(true); + SchemaRefreshUtils.withDisabledSchema(session, () -> {}); + verify(session, times(2)).setSchemaMetadataEnabled(false); + verify(session).setSchemaMetadataEnabled(null); + } + + @Test + void withDisabledSchemaEnabledProgrammaticallyPreviously() throws Exception { + when(session.isSchemaMetadataEnabled()).thenReturn(true).thenReturn(false); + SchemaRefreshUtils.withDisabledSchema(session, () -> {}); + verify(session).setSchemaMetadataEnabled(true); + verify(session).setSchemaMetadataEnabled(null); + } +}