Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Buildmaster b1280ffeaa Release v5.3.7 2021-05-12 05:38:51 +00:00
132 changed files with 1548 additions and 2201 deletions
+29 -29
View File
@@ -1,7 +1,7 @@
plugins {
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.5.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.5.10' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.0' apply false
id 'org.jetbrains.dokka' version '0.10.1' apply false
id 'org.asciidoctor.jvm.convert' version '3.1.0'
id 'org.asciidoctor.jvm.pdf' version '3.1.0'
@@ -10,7 +10,7 @@ plugins {
id "com.github.ben-manes.versions" version '0.28.0'
id "com.github.johnrengelman.shadow" version "6.1.0" apply false
id "me.champeau.jmh" version "0.6.4" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.10" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.0" apply false
id "org.unbroken-dome.xjc" version '2.0.0' apply false
}
@@ -28,15 +28,15 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.3"
mavenBom "io.netty:netty-bom:4.1.65.Final"
mavenBom "io.netty:netty-bom:4.1.63.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.7"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR9"
mavenBom "io.rsocket:rsocket-bom:1.1.0"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.41.v20210516"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.10"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.0"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.1"
mavenBom "org.junit:junit-bom:5.7.2"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.40.v20210413"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.0"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.3"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.0"
mavenBom "org.junit:junit-bom:5.7.1"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') {
@@ -67,18 +67,18 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.0.13"
dependency "io.reactivex.rxjava3:rxjava:3.0.12"
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.3.0"
dependency "com.fasterxml:aalto-xml:1.2.2"
dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") {
exclude group: "stax", name: "stax-api"
}
dependency "com.google.code.gson:gson:2.8.7"
dependency "com.google.protobuf:protobuf-java-util:3.17.2"
dependency "com.google.code.gson:gson:2.8.6"
dependency "com.google.protobuf:protobuf-java-util:3.15.8"
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
dependency("com.thoughtworks.xstream:xstream:1.4.17") {
dependency("com.thoughtworks.xstream:xstream:1.4.16") {
exclude group: "xpp3", name: "xpp3_min"
exclude group: "xmlpull", name: "xmlpull"
}
@@ -100,7 +100,7 @@ configure(allprojects) { project ->
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.3"
dependency "net.sf.jopt-simple:jopt-simple:5.0.4"
dependencySet(group: 'org.apache.activemq', version: '5.16.2') {
dependencySet(group: 'org.apache.activemq', version: '5.16.1') {
entry 'activemq-broker'
entry('activemq-kahadb-store') {
exclude group: "org.springframework", name: "spring-context"
@@ -116,13 +116,13 @@ configure(allprojects) { project ->
dependency "org.apache.poi:poi-ooxml:4.1.2"
dependency "org.apache-extras.beanshell:bsh:2.0b6"
dependency "org.freemarker:freemarker:2.3.31"
dependency "org.hsqldb:hsqldb:2.5.2"
dependency "org.hsqldb:hsqldb:2.5.1"
dependency "org.quartz-scheduler:quartz:2.3.2"
dependency "org.codehaus.fabric3.api:commonj:1.1.0"
dependency "net.sf.ehcache:ehcache:2.10.6"
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.32.Final"
dependency "org.hibernate:hibernate-core:5.4.31.Final"
dependency "org.hibernate:hibernate-validator:6.2.0.Final"
dependency "org.webjars:webjars-locator-core:0.46"
dependency "org.webjars:underscorejs:1.8.3"
@@ -138,7 +138,7 @@ configure(allprojects) { project ->
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.8.Final') {
dependencySet(group: 'io.undertow', version: '2.2.7.Final') {
entry 'undertow-core'
entry('undertow-websockets-jsr') {
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
@@ -159,11 +159,11 @@ configure(allprojects) { project ->
dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.8"
dependency 'org.apache.httpcomponents.client5:httpclient5:5.0.3'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.3'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.6"
dependency "org.jruby:jruby:9.2.17.0"
dependency "org.jruby:jruby:9.2.16.0"
dependency "org.python:jython-standalone:2.7.1"
dependency "org.mozilla:rhino:1.7.11"
@@ -196,7 +196,7 @@ configure(allprojects) { project ->
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '3.11.0') {
dependencySet(group: 'org.mockito', version: '3.9.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
@@ -204,10 +204,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.10.2"
dependency("net.sourceforge.htmlunit:htmlunit:2.50.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.49.1") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.50.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.49.1") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -316,7 +316,7 @@ configure([rootProject] + javaProjects) { project ->
kotlinOptions {
languageVersion = "1.3"
apiVersion = "1.3"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings", "-Xopt-in=kotlin.RequiresOptIn"]
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
allWarningsAsErrors = true
}
}
@@ -336,7 +336,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "8.43"
toolVersion = "8.42"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -375,10 +375,10 @@ configure([rootProject] + javaProjects) { project ->
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://junit.org/junit5/docs/5.7.2/api/"
"https://junit.org/junit5/docs/5.7.1/api/"
] as String[]
}
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.8
version=5.3.7
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
+1 -2
View File
@@ -143,8 +143,7 @@ asciidoctorj {
'highlightjs-theme': 'googlecode',
stylesdir: 'css/',
stylesheet: 'stylesheet.css',
'spring-version': project.version,
'spring-framework-main-code': 'https://github.com/spring-projects/spring-framework/tree/main'
'spring-version': project.version
])
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -84,7 +84,7 @@ import org.springframework.lang.Nullable;
* (only applicable when running in a web application context)
* <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
* <li>InitializingBean's {@code afterPropertiesSet}
* <li>a custom {@code init-method} definition
* <li>a custom init-method definition
* <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
* </ol>
*
@@ -92,7 +92,7 @@ import org.springframework.lang.Nullable;
* <ol>
* <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
* <li>DisposableBean's {@code destroy}
* <li>a custom {@code destroy-method} definition
* <li>a custom destroy-method definition
* </ol>
*
* @author Rod Johnson
@@ -102,8 +102,6 @@ import org.springframework.lang.Nullable;
* @see BeanNameAware#setBeanName
* @see BeanClassLoaderAware#setBeanClassLoader
* @see BeanFactoryAware#setBeanFactory
* @see org.springframework.context.EnvironmentAware#setEnvironment
* @see org.springframework.context.EmbeddedValueResolverAware#setEmbeddedValueResolver
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
* @see org.springframework.context.MessageSourceAware#setMessageSource
@@ -113,7 +111,6 @@ import org.springframework.lang.Nullable;
* @see InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
* @see DisposableBean#destroy
* @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -35,7 +35,6 @@ import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -245,10 +244,8 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
return (bean.equals(null) ? null : bean);
}
else {
// Find target bean matching the (potentially generic) method return type
ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);
return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :
this.owner.getBeanProvider(genericReturnType).getObject());
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,25 +19,17 @@ package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Represents an override of a method that looks up an object in the same IoC context,
* either by bean name or by bean type (based on the declared method return type).
* Represents an override of a method that looks up an object in the same IoC context.
*
* <p>Methods eligible for lookup override may declare arguments in which case the
* given arguments are passed to the bean retrieval operation.
* <p>Methods eligible for lookup override must not have arguments.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.beans.factory.BeanFactory#getBean(String)
* @see org.springframework.beans.factory.BeanFactory#getBean(Class)
* @see org.springframework.beans.factory.BeanFactory#getBean(String, Object...)
* @see org.springframework.beans.factory.BeanFactory#getBean(Class, Object...)
* @see org.springframework.beans.factory.BeanFactory#getBeanProvider(ResolvableType)
*/
public class LookupOverride extends MethodOverride {
@@ -51,8 +43,8 @@ public class LookupOverride extends MethodOverride {
/**
* Construct a new LookupOverride.
* @param methodName the name of the method to override
* @param beanName the name of the bean in the current {@code BeanFactory} that the
* overridden method should return (may be {@code null} for type-based bean retrieval)
* @param beanName the name of the bean in the current {@code BeanFactory}
* that the overridden method should return (may be {@code null})
*/
public LookupOverride(String methodName, @Nullable String beanName) {
super(methodName);
@@ -61,9 +53,9 @@ public class LookupOverride extends MethodOverride {
/**
* Construct a new LookupOverride.
* @param method the method declaration to override
* @param beanName the name of the bean in the current {@code BeanFactory} that the
* overridden method should return (may be {@code null} for type-based bean retrieval)
* @param method the method to override
* @param beanName the name of the bean in the current {@code BeanFactory}
* that the overridden method should return (may be {@code null})
*/
public LookupOverride(Method method, @Nullable String beanName) {
super(method.getName());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2012 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.
@@ -19,8 +19,9 @@ package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
/**
* Interface to be implemented by classes that can reimplement any method on an
* IoC-managed object: the <b>Method Injection</b> form of Dependency Injection.
* Interface to be implemented by classes that can reimplement any method
* on an IoC-managed object: the <b>Method Injection</b> form of
* Dependency Injection.
*
* <p>Such methods may be (but need not be) abstract, in which case the
* container will create a concrete subclass to instantiate.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -25,7 +25,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Extension of {@link MethodOverride} that represents an arbitrary
* Extension of MethodOverride that represents an arbitrary
* override of a method by the IoC container.
*
* <p>Any non-final method can be overridden, irrespective of its
@@ -45,7 +45,7 @@ public class ReplaceOverride extends MethodOverride {
/**
* Construct a new ReplaceOverride.
* @param methodName the name of the method to override
* @param methodReplacerBeanName the bean name of the {@link MethodReplacer}
* @param methodReplacerBeanName the bean name of the MethodReplacer
*/
public ReplaceOverride(String methodName, String methodReplacerBeanName) {
super(methodName);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -18,14 +18,14 @@ package org.springframework.beans;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -36,21 +36,23 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @since 08.03.2004
*/
class ConcurrentBeanWrapperTests {
public class ConcurrentBeanWrapperTests {
private final Log logger = LogFactory.getLog(getClass());
private final Set<TestRun> set = ConcurrentHashMap.newKeySet();
private Set<TestRun> set = Collections.synchronizedSet(new HashSet<TestRun>());
private Throwable ex = null;
@RepeatedTest(100)
void testSingleThread() {
performSet();
@Test
public void testSingleThread() {
for (int i = 0; i < 100; i++) {
performSet();
}
}
@Test
void testConcurrent() {
public void testConcurrent() {
for (int i = 0; i < 10; i++) {
TestRun run = new TestRun(this);
set.add(run);
@@ -80,7 +82,7 @@ class ConcurrentBeanWrapperTests {
Properties p = (Properties) System.getProperties().clone();
assertThat(p).as("The System properties must not be empty").isNotEmpty();
assertThat(p.size() != 0).as("The System properties must not be empty").isTrue();
for (Iterator<?> i = p.entrySet().iterator(); i.hasNext();) {
i.next();
@@ -109,7 +111,7 @@ class ConcurrentBeanWrapperTests {
private ConcurrentBeanWrapperTests test;
TestRun(ConcurrentBeanWrapperTests test) {
public TestRun(ConcurrentBeanWrapperTests test) {
this.test = test;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -121,18 +121,6 @@ public class LookupAnnotationTests {
assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean);
}
@Test
public void testWithGenericBean() {
beanFactory.registerBeanDefinition("numberBean", new RootBeanDefinition(NumberBean.class));
beanFactory.registerBeanDefinition("doubleStore", new RootBeanDefinition(DoubleStore.class));
beanFactory.registerBeanDefinition("floatStore", new RootBeanDefinition(FloatStore.class));
NumberBean bean = (NumberBean) beanFactory.getBean("numberBean");
assertThat(bean).isNotNull();
assertThat(beanFactory.getBean(DoubleStore.class)).isSameAs(bean.getDoubleStore());
assertThat(beanFactory.getBean(FloatStore.class)).isSameAs(bean.getFloatStore());
}
public static abstract class AbstractBean {
@@ -159,26 +147,4 @@ public class LookupAnnotationTests {
AbstractBean abstractBean;
}
public static class NumberStore<T extends Number> {
}
public static class DoubleStore extends NumberStore<Double> {
}
public static class FloatStore extends NumberStore<Float> {
}
public static abstract class NumberBean {
@Lookup
public abstract NumberStore<Double> getDoubleStore();
@Lookup
public abstract NumberStore<Float> getFloatStore();
}
}
@@ -141,7 +141,6 @@ class YamlProcessorTests {
}
@Test
@SuppressWarnings("unchecked")
void standardTypesSupportedByDefault() throws Exception {
setYaml("value: !!set\n ? first\n ? second");
this.processor.process((properties, map) -> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -36,7 +36,7 @@ public class LookupMethodTests {
@BeforeEach
public void setup() {
public void setUp() {
beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("lookupMethodTests.xml", getClass()));
@@ -83,8 +83,8 @@ public class LookupMethodTests {
public void testWithThreeArgsShouldFail() {
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
assertThat(bean).isNotNull();
assertThatExceptionOfType(AbstractMethodError.class).as("does not have a three arg constructor")
.isThrownBy(() -> bean.getThreeArguments("name", 1, 2));
assertThatExceptionOfType(AbstractMethodError.class).as("does not have a three arg constructor").isThrownBy(() ->
bean.getThreeArguments("name", 1, 2));
}
@Test
@@ -97,21 +97,6 @@ public class LookupMethodTests {
assertThat(expected.isJedi()).isTrue();
}
@Test
public void testWithGenericBean() {
RootBeanDefinition bd = new RootBeanDefinition(NumberBean.class);
bd.getMethodOverrides().addOverride(new LookupOverride("getDoubleStore", null));
bd.getMethodOverrides().addOverride(new LookupOverride("getFloatStore", null));
beanFactory.registerBeanDefinition("numberBean", bd);
beanFactory.registerBeanDefinition("doubleStore", new RootBeanDefinition(DoubleStore.class));
beanFactory.registerBeanDefinition("floatStore", new RootBeanDefinition(FloatStore.class));
NumberBean bean = (NumberBean) beanFactory.getBean("numberBean");
assertThat(bean).isNotNull();
assertThat(beanFactory.getBean(DoubleStore.class)).isSameAs(bean.getDoubleStore());
assertThat(beanFactory.getBean(FloatStore.class)).isSameAs(bean.getFloatStore());
}
public static abstract class AbstractBean {
@@ -126,24 +111,4 @@ public class LookupMethodTests {
public abstract TestBean getThreeArguments(String name, int age, int anotherArg);
}
public static class NumberStore<T extends Number> {
}
public static class DoubleStore extends NumberStore<Double> {
}
public static class FloatStore extends NumberStore<Float> {
}
public static abstract class NumberBean {
public abstract NumberStore<Double> getDoubleStore();
public abstract NumberStore<Float> getFloatStore();
}
}
@@ -36,7 +36,7 @@ import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
/**
* Annotation {@link Processor} that writes a {@link CandidateComponentsMetadata}
* Annotation {@link Processor} that writes {@link CandidateComponentsMetadata}
* file for spring components.
*
* @author Stephane Nicoll
@@ -26,7 +26,7 @@ import javax.lang.model.element.ElementKind;
/**
* A {@link StereotypesProvider} implementation that extracts the stereotypes
* flagged by the {@value #INDEXED_ANNOTATION} annotation. This implementation
* flagged by the {@value INDEXED_ANNOTATION} annotation. This implementation
* honors stereotypes defined this way on meta-annotations.
*
* @author Stephane Nicoll
@@ -24,7 +24,7 @@ import javax.lang.model.element.ElementKind;
/**
* A {@link StereotypesProvider} implementation that provides the
* {@value #STEREOTYPE} stereotype for each package-info.
* {@value STEREOTYPE} stereotype for each package-info.
*
* @author Stephane Nicoll
* @since 5.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 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.
@@ -24,8 +24,8 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
/**
* A {@link StereotypesProvider} that extracts a stereotype for each
* {@code javax.*} annotation <i>present</i> on a class or interface.
* A {@link StereotypesProvider} that extract a stereotype for each
* {@code javax.*} annotation placed on a class or interface.
*
* @author Stephane Nicoll
* @since 5.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 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.
@@ -22,7 +22,7 @@ import javax.lang.model.element.Element;
/**
* Provide the list of stereotypes that match an {@link Element}.
* <p>If an element has one or more stereotypes, it is referenced in the index
* If an element has one more stereotypes, it is referenced in the index
* of candidate components and each stereotype can be queried individually.
*
* @author Stephane Nicoll
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -16,7 +16,6 @@
package org.springframework.context;
import java.time.Clock;
import java.util.EventObject;
/**
@@ -38,37 +37,18 @@ public abstract class ApplicationEvent extends EventObject {
/**
* Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}
* set to {@link System#currentTimeMillis()}.
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
* @see #ApplicationEvent(Object, Clock)
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}
* set to the value returned by {@link Clock#millis()} in the provided {@link Clock}.
* <p>This constructor is typically used in testing scenarios.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
* @param clock a clock which will provide the timestamp
* @since 5.3.8
* @see #ApplicationEvent(Object)
*/
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
/**
* Return the time in milliseconds when the event occurred.
* @see #ApplicationEvent(Object)
* @see #ApplicationEvent(Object, Clock)
* Return the system time in milliseconds when the event occurred.
*/
public final long getTimestamp() {
return this.timestamp;
@@ -84,7 +84,6 @@ import org.springframework.core.annotation.AliasFor;
* @author Sam Brannen
* @since 4.2
* @see EventListenerMethodProcessor
* @see org.springframework.transaction.event.TransactionalEventListener
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -134,7 +133,7 @@ public @interface EventListener {
* An optional identifier for the listener, defaulting to the fully-qualified
* signature of the declaring method (e.g. "mypackage.MyClass.myMethod()").
* @since 5.3.5
* @see SmartApplicationListener#getListenerId()
* @see org.springframework.context.ApplicationListener#getListenerId()
* @see ApplicationEventMulticaster#removeApplicationListeners(Predicate)
*/
String id() default "";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -192,11 +192,6 @@ final class BitsCronField extends CronField {
while (current != next && count++ < CronExpression.MAX_ATTEMPTS) {
temporal = type().elapseUntil(temporal, next);
current = type().get(temporal);
next = nextSetBit(current);
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(0);
}
}
if (count >= CronExpression.MAX_ATTEMPTS) {
return null;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -202,25 +202,6 @@ public final class CronExpression {
}
}
/**
* Determine whether the given string represents a valid cron expression.
* @param expression the expression to evaluate
* @return {@code true} if the given expression is a valid cron expression
* @since 5.3.8
*/
public static boolean isValidExpression(@Nullable String expression) {
if (expression == null) {
return false;
}
try {
parse(expression);
return true;
}
catch (IllegalArgumentException ex) {
return false;
}
}
private static String resolveMacros(String expression) {
expression = expression.trim();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 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.
@@ -18,7 +18,6 @@ package org.springframework.validation.support;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.ui.ConcurrentModel;
import org.springframework.validation.BindingResult;
@@ -37,19 +36,17 @@ import org.springframework.validation.BindingResult;
* @author Rossen Stoyanchev
* @since 5.0
* @see BindingResult
* @see BindingAwareModelMap
*/
@SuppressWarnings("serial")
public class BindingAwareConcurrentModel extends ConcurrentModel {
@Override
@Nullable
public Object put(String key, @Nullable Object value) {
public Object put(String key, Object value) {
removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
private void removeBindingResultIfNecessary(String key, @Nullable Object value) {
private void removeBindingResultIfNecessary(String key, Object value) {
if (!key.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
String resultKey = BindingResult.MODEL_KEY_PREFIX + key;
BindingResult result = (BindingResult) get(resultKey);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2017 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.
@@ -18,7 +18,6 @@ package org.springframework.validation.support;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.validation.BindingResult;
@@ -40,7 +39,7 @@ import org.springframework.validation.BindingResult;
public class BindingAwareModelMap extends ExtendedModelMap {
@Override
public Object put(String key, @Nullable Object value) {
public Object put(String key, Object value) {
removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
@@ -51,7 +50,7 @@ public class BindingAwareModelMap extends ExtendedModelMap {
super.putAll(map);
}
private void removeBindingResultIfNecessary(Object key, @Nullable Object value) {
private void removeBindingResultIfNecessary(Object key, Object value) {
if (key instanceof String) {
String attributeName = (String) key;
if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
@@ -51,16 +51,6 @@ class CronExpressionTests {
}
};
@Test
public void isValidExpression() {
assertThat(CronExpression.isValidExpression(null)).isFalse();
assertThat(CronExpression.isValidExpression("")).isFalse();
assertThat(CronExpression.isValidExpression("*")).isFalse();
assertThat(CronExpression.isValidExpression("* * * * *")).isFalse();
assertThat(CronExpression.isValidExpression("* * * * * * *")).isFalse();
assertThat(CronExpression.isValidExpression("* * * * * *")).isTrue();
}
@Test
void matchAll() {
@@ -507,29 +497,6 @@ class CronExpressionTests {
assertThat(actual.getDayOfMonth()).isEqualTo(13);
}
@Test
public void everyTenDays() {
CronExpression cronExpression = CronExpression.parse("0 15 12 */10 1-8 5");
LocalDateTime last = LocalDateTime.parse("2021-04-30T12:14:59");
LocalDateTime expected = LocalDateTime.parse("2021-05-21T12:15");
LocalDateTime actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
last = actual;
expected = LocalDateTime.parse("2021-06-11T12:15");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
last = actual;
expected = LocalDateTime.parse("2022-01-21T12:15");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
@Test
void yearly() {
CronExpression expression = CronExpression.parse("@yearly");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,7 +17,10 @@
@file:JvmName("CoroutinesUtils")
package org.springframework.core
import kotlinx.coroutines.*
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.reactive.awaitSingleOrNull
import kotlinx.coroutines.reactor.asFlux
@@ -45,8 +48,6 @@ internal fun <T: Any> deferredToMono(source: Deferred<T>) =
* @author Sebastien Deleuze
* @since 5.2
*/
@Suppress("DEPRECATION")
@OptIn(DelicateCoroutinesApi::class)
internal fun <T: Any> monoToDeferred(source: Mono<T>) =
GlobalScope.async(Dispatchers.Unconfined) { source.awaitSingleOrNull() }
@@ -88,9 +88,6 @@ public class ClassReader {
*/
static final int EXPAND_ASM_INSNS = 256;
/** The maximum size of array to allocate. */
private static final int MAX_BUFFER_SIZE = 1024 * 1024;
/** The size of the temporary byte array used to read class input streams chunk by chunk. */
private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
@@ -313,19 +310,13 @@ public class ClassReader {
if (inputStream == null) {
throw new IOException("Class not found");
}
int bufferSize = calculateBufferSize(inputStream);
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] data = new byte[bufferSize];
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
int readCount = 0;
while ((bytesRead = inputStream.read(data, 0, bufferSize)) != -1) {
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
readCount++;
}
outputStream.flush();
if (readCount == 1) {
return data;
}
return outputStream.toByteArray();
} finally {
if (close) {
@@ -334,20 +325,6 @@ public class ClassReader {
}
}
private static int calculateBufferSize(final InputStream inputStream) throws IOException {
int expectedLength = inputStream.available();
/*
* Some implementations can return 0 while holding available data
* (e.g. new FileInputStream("/proc/a_file"))
* Also in some pathological cases a very small number might be returned,
* and in this case we use default size
*/
if (expectedLength < 256) {
return INPUT_STREAM_DATA_CHUNK_SIZE;
}
return Math.min(expectedLength, MAX_BUFFER_SIZE);
}
// -----------------------------------------------------------------------------------------------
// Accessors
// -----------------------------------------------------------------------------------------------
@@ -3479,6 +3456,7 @@ public class ClassReader {
private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
char[] charBuffer = new char[maxStringLength];
int currentAttributeOffset = getFirstAttributeOffset();
int[] currentBootstrapMethodOffsets = null;
for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
// Read the attribute_info's attribute_name and attribute_length fields.
String attributeName = readUTF8(currentAttributeOffset, charBuffer);
@@ -3486,17 +3464,17 @@ public class ClassReader {
currentAttributeOffset += 6;
if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
// Read the num_bootstrap_methods field and create an array of this size.
int[] result = new int[readUnsignedShort(currentAttributeOffset)];
currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
// Compute and store the offset of each 'bootstrap_methods' array field entry.
int currentBootstrapMethodOffset = currentAttributeOffset + 2;
for (int j = 0; j < result.length; ++j) {
result[j] = currentBootstrapMethodOffset;
for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
// Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
// as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
currentBootstrapMethodOffset +=
4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
}
return result;
return currentBootstrapMethodOffsets;
}
currentAttributeOffset += attributeLength;
}
@@ -79,7 +79,7 @@ public class ClassWriter extends ClassVisitor {
/**
* The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
* access flags, such as {@link Opcodes#ACC_DEPRECATED} or {@link Opcodes#ACC_RECORD}, which are
* access flags, such as {@link Opcodes#ACC_DEPRECATED} or {}@link Opcodes#ACC_RECORD}, which are
* removed when generating the ClassFile structure.
*/
private int accessFlags;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -71,7 +71,7 @@ public abstract class DataBufferUtils {
//---------------------------------------------------------------------
/**
* Obtain an {@link InputStream} from the given supplier, and read it into a
* Obtain a {@link InputStream} from the given supplier, and read it into a
* {@code Flux} of {@code DataBuffer}s. Closes the input stream when the
* Flux is terminated.
* @param inputStreamSupplier the supplier for the input stream to read from
@@ -125,7 +125,7 @@ public abstract class DataBufferUtils {
}
/**
* Obtain an {@code AsynchronousFileChannel} from the given supplier, and
* Obtain a {@code AsynchronousFileChannel} from the given supplier, and
* read it into a {@code Flux} of {@code DataBuffer}s, starting at the given
* position. Closes the channel when the Flux is terminated.
* @param channelSupplier the supplier for the channel to read from
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -29,16 +29,16 @@ import java.util.function.Supplier;
*/
class DefaultApplicationStartup implements ApplicationStartup {
private static final DefaultStartupStep DEFAULT_STARTUP_STEP = new DefaultStartupStep();
@Override
public DefaultStartupStep start(String name) {
return DEFAULT_STARTUP_STEP;
return new DefaultStartupStep();
}
static class DefaultStartupStep implements StartupStep {
boolean recorded = false;
private final DefaultTags TAGS = new DefaultTags();
@Override
@@ -63,17 +63,23 @@ class DefaultApplicationStartup implements ApplicationStartup {
@Override
public StartupStep tag(String key, String value) {
if (this.recorded) {
throw new IllegalArgumentException();
}
return this;
}
@Override
public StartupStep tag(String key, Supplier<String> value) {
if (this.recorded) {
throw new IllegalArgumentException();
}
return this;
}
@Override
public void end() {
this.recorded = true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,9 +16,8 @@
package org.springframework.core.metrics.jfr;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
@@ -38,23 +37,24 @@ import org.springframework.core.metrics.StartupStep;
*/
public class FlightRecorderApplicationStartup implements ApplicationStartup {
private final AtomicLong currentSequenceId = new AtomicLong(0);
private long currentSequenceId;
private final Deque<Long> currentSteps;
public FlightRecorderApplicationStartup() {
this.currentSteps = new ConcurrentLinkedDeque<>();
this.currentSteps.offerFirst(this.currentSequenceId.get());
this.currentSequenceId = 0;
this.currentSteps = new ArrayDeque<>();
this.currentSteps.offerFirst(0L);
}
@Override
public StartupStep start(String name) {
long sequenceId = this.currentSequenceId.incrementAndGet();
this.currentSteps.offerFirst(sequenceId);
return new FlightRecorderStartupStep(sequenceId, name,
this.currentSteps.getFirst(), committedStep -> this.currentSteps.removeFirstOccurrence(sequenceId));
FlightRecorderStartupStep step = new FlightRecorderStartupStep(++this.currentSequenceId, name,
this.currentSteps.getFirst(), committedStep -> this.currentSteps.removeFirst());
this.currentSteps.offerFirst(this.currentSequenceId);
return step;
}
}
@@ -88,13 +88,13 @@ public abstract class ClassUtils {
* Map with primitive wrapper type as key and corresponding primitive
* type as value, for example: Integer.class -> int.class.
*/
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<>(9);
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<>(8);
/**
* Map with primitive type as key and corresponding wrapper
* type as value, for example: int.class -> Integer.class.
*/
private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(9);
private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(8);
/**
* Map with primitive type name as key and corresponding primitive
@@ -1322,7 +1322,7 @@ public abstract class ClassUtils {
* Note that, despite being synthetic, bridge methods ({@link Method#isBridge()}) are considered
* as user-level methods since they are eventually pointing to a user-declared generic method.
* @param method the method to check
* @return {@code true} if the method can be considered as user-declared; {@code false} otherwise
* @return {@code true} if the method can be considered as user-declared; [@code false} otherwise
*/
public static boolean isUserLevelMethod(Method method) {
Assert.notNull(method, "Method must not be null");
@@ -566,7 +566,7 @@ public abstract class StringUtils {
char[] chars = str.toCharArray();
chars[0] = updatedChar;
return new String(chars);
return new String(chars, 0, chars.length);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -21,18 +21,13 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link SimpleAliasRegistry}.
*
* @author Juergen Hoeller
* @author Nha Vuong
* @author Sam Brannen
*/
class SimpleAliasRegistryTests {
private final SimpleAliasRegistry registry = new SimpleAliasRegistry();
@Test
void aliasChaining() {
SimpleAliasRegistry registry = new SimpleAliasRegistry();
registry.registerAlias("test", "testAlias");
registry.registerAlias("testAlias", "testAlias2");
registry.registerAlias("testAlias2", "testAlias3");
@@ -47,6 +42,7 @@ class SimpleAliasRegistryTests {
@Test // SPR-17191
void aliasChainingWithMultipleAliases() {
SimpleAliasRegistry registry = new SimpleAliasRegistry();
registry.registerAlias("name", "alias_a");
registry.registerAlias("name", "alias_b");
assertThat(registry.hasAlias("name", "alias_a")).isTrue();
@@ -64,35 +60,4 @@ class SimpleAliasRegistryTests {
assertThat(registry.hasAlias("real_name", "alias_c")).isTrue();
}
@Test
void removeAlias() {
registry.registerAlias("real_name", "nickname");
assertThat(registry.hasAlias("real_name", "nickname")).isTrue();
registry.removeAlias("nickname");
assertThat(registry.hasAlias("real_name", "nickname")).isFalse();
}
@Test
void isAlias() {
registry.registerAlias("real_name", "nickname");
assertThat(registry.isAlias("nickname")).isTrue();
assertThat(registry.isAlias("real_name")).isFalse();
assertThat(registry.isAlias("fake")).isFalse();
}
@Test
void getAliases() {
registry.registerAlias("test", "testAlias1");
assertThat(registry.getAliases("test")).containsExactly("testAlias1");
registry.registerAlias("testAlias1", "testAlias2");
registry.registerAlias("testAlias2", "testAlias3");
assertThat(registry.getAliases("test")).containsExactlyInAnyOrder("testAlias1", "testAlias2", "testAlias3");
assertThat(registry.getAliases("testAlias1")).containsExactlyInAnyOrder("testAlias2", "testAlias3");
assertThat(registry.getAliases("testAlias2")).containsExactly("testAlias3");
assertThat(registry.getAliases("testAlias3")).isEmpty();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2014 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.
@@ -29,7 +29,7 @@ import org.springframework.core.io.support.EncodedResource;
public class CannotReadScriptException extends ScriptException {
/**
* Create a new {@code CannotReadScriptException}.
* Construct a new {@code CannotReadScriptException}.
* @param resource the resource that cannot be read from
* @param cause the underlying cause of the resource access failure
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2016 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.
@@ -23,8 +23,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.util.Assert;
/**
* Composite {@link DatabasePopulator} that delegates to a list of given
* {@code DatabasePopulator} implementations, executing all scripts.
@@ -54,7 +52,6 @@ public class CompositeDatabasePopulator implements DatabasePopulator {
* @since 4.3
*/
public CompositeDatabasePopulator(Collection<DatabasePopulator> populators) {
Assert.notNull(populators, "DatabasePopulators must not be null");
this.populators.addAll(populators);
}
@@ -64,7 +61,6 @@ public class CompositeDatabasePopulator implements DatabasePopulator {
* @since 4.3
*/
public CompositeDatabasePopulator(DatabasePopulator... populators) {
Assert.notNull(populators, "DatabasePopulators must not be null");
this.populators.addAll(Arrays.asList(populators));
}
@@ -73,7 +69,6 @@ public class CompositeDatabasePopulator implements DatabasePopulator {
* Specify one or more populators to delegate to.
*/
public void setPopulators(DatabasePopulator... populators) {
Assert.notNull(populators, "DatabasePopulators must not be null");
this.populators.clear();
this.populators.addAll(Arrays.asList(populators));
}
@@ -82,13 +77,12 @@ public class CompositeDatabasePopulator implements DatabasePopulator {
* Add one or more populators to the list of delegates.
*/
public void addPopulators(DatabasePopulator... populators) {
Assert.notNull(populators, "DatabasePopulators must not be null");
this.populators.addAll(Arrays.asList(populators));
}
@Override
public void populate(Connection connection) throws SQLException, ScriptException {
Assert.notNull(connection, "Connection must not be null");
for (DatabasePopulator populator : this.populators) {
populator.populate(connection);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2014 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.
@@ -30,7 +30,7 @@ import org.springframework.lang.Nullable;
public abstract class ScriptException extends DataAccessException {
/**
* Create a new {@code ScriptException}.
* Constructor for {@code ScriptException}.
* @param message the detail message
*/
public ScriptException(String message) {
@@ -38,7 +38,7 @@ public abstract class ScriptException extends DataAccessException {
}
/**
* Create a new {@code ScriptException}.
* Constructor for {@code ScriptException}.
* @param message the detail message
* @param cause the root cause
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2017 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.
@@ -29,7 +29,7 @@ import org.springframework.lang.Nullable;
public class ScriptParseException extends ScriptException {
/**
* Create a new {@code ScriptParseException}.
* Construct a new {@code ScriptParseException}.
* @param message detailed message
* @param resource the resource from which the SQL script was read
*/
@@ -38,7 +38,7 @@ public class ScriptParseException extends ScriptException {
}
/**
* Create a new {@code ScriptParseException}.
* Construct a new {@code ScriptParseException}.
* @param message detailed message
* @param resource the resource from which the SQL script was read
* @param cause the underlying cause of the failure
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -35,7 +35,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Generic utility methods for working with SQL scripts in conjunction with JDBC.
* Generic utility methods for working with SQL scripts.
*
* <p>Mainly for internal use within the framework.
*
@@ -50,7 +50,6 @@ import org.springframework.util.StringUtils;
* @author Nicolas Debeissat
* @author Phillip Webb
* @since 4.0.3
* @see org.springframework.r2dbc.connection.init.ScriptUtils
*/
public abstract class ScriptUtils {
@@ -102,6 +101,349 @@ public abstract class ScriptUtils {
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
/**
* Split an SQL script into separate statements delimited by the provided
* separator character. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator character separating each statement (typically a ';')
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, String, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
*/
public static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException {
splitSqlScript(script, String.valueOf(separator), statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, char, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
*/
public static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException {
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefix} will be honored:
* any text beginning with the comment prefix and extending to the end of the
* line will be omitted from the output. Similarly, the provided
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* delimiters will be honored: any text enclosed in a block comment will be
* omitted from the output. In addition, multiple adjacent whitespace characters
* will be collapsed into a single space.
* @param resource the resource from which the script was read
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param commentPrefix the prefix that identifies SQL line comments
* (typically "--")
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
*/
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String commentPrefix, String blockCommentStartDelimiter,
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty");
splitSqlScript(resource, script, separator, new String[] { commentPrefix },
blockCommentStartDelimiter, blockCommentEndDelimiter, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefixes} will be honored:
* any text beginning with one of the comment prefixes and extending to the
* end of the line will be omitted from the output. Similarly, the provided
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* delimiters will be honored: any text enclosed in a block comment will be
* omitted from the output. In addition, multiple adjacent whitespace characters
* will be collapsed into a single space.
* @param resource the resource from which the script was read
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param commentPrefixes the prefixes that identify SQL line comments
* (typically "--")
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @since 5.2
*/
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(script, "'script' must not be null or empty");
Assert.notNull(separator, "'separator' must not be null");
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty");
for (String commentPrefix : commentPrefixes) {
Assert.hasText(commentPrefix, "'commentPrefixes' must not contain null or empty elements");
}
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty");
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty");
StringBuilder sb = new StringBuilder();
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
sb.append(c);
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
sb.append(c);
continue;
}
if (!inDoubleQuote && (c == '\'')) {
inSingleQuote = !inSingleQuote;
}
else if (!inSingleQuote && (c == '"')) {
inDoubleQuote = !inDoubleQuote;
}
if (!inSingleQuote && !inDoubleQuote) {
if (script.startsWith(separator, i)) {
// We've reached the end of the current statement
if (sb.length() > 0) {
statements.add(sb.toString());
sb = new StringBuilder();
}
i += separator.length() - 1;
continue;
}
else if (startsWithAny(script, commentPrefixes, i)) {
// Skip over any content from the start of the comment to the EOL
int indexOfNextNewline = script.indexOf('\n', i);
if (indexOfNextNewline > i) {
i = indexOfNextNewline;
continue;
}
else {
// If there's no EOL, we must be at the end of the script, so stop here.
break;
}
}
else if (script.startsWith(blockCommentStartDelimiter, i)) {
// Skip over any block comments
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentEnd > i) {
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
continue;
}
else {
throw new ScriptParseException(
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource);
}
}
else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
// Avoid multiple adjacent whitespace characters
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
c = ' ';
}
else {
continue;
}
}
}
sb.append(c);
}
if (StringUtils.hasText(sb)) {
statements.add(sb.toString());
}
}
/**
* Read a script from the given resource, using "{@code --}" as the comment prefix
* and "{@code ;}" as the statement separator, and build a String containing the lines.
* @param resource the {@code EncodedResource} to be read
* @return {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
static String readScript(EncodedResource resource) throws IOException {
return readScript(resource, DEFAULT_COMMENT_PREFIXES, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
/**
* Read a script from the provided resource, using the supplied comment prefixes
* and statement separator, and build a {@code String} containing the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param resource the {@code EncodedResource} containing the script
* to be processed
* @param commentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
private static String readScript(EncodedResource resource, @Nullable String[] commentPrefixes,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
try (LineNumberReader lnr = new LineNumberReader(resource.getReader())) {
return readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
}
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefix and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with the comment prefix are excluded from the
* results; however, line comments anywhere else &mdash; for example, within
* a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param lineCommentPrefix the prefix that identifies comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
public static String readScript(LineNumberReader lineNumberReader, @Nullable String lineCommentPrefix,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String[] lineCommentPrefixes = (lineCommentPrefix != null) ? new String[] { lineCommentPrefix } : null;
return readScript(lineNumberReader, lineCommentPrefixes, separator, blockCommentEndDelimiter);
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefixes and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param lineCommentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
* @since 5.2
*/
public static String readScript(LineNumberReader lineNumberReader, @Nullable String[] lineCommentPrefixes,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String currentStatement = lineNumberReader.readLine();
StringBuilder scriptBuilder = new StringBuilder();
while (currentStatement != null) {
if ((blockCommentEndDelimiter != null && currentStatement.contains(blockCommentEndDelimiter)) ||
(lineCommentPrefixes != null && !startsWithAny(currentStatement, lineCommentPrefixes, 0))) {
if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n');
}
scriptBuilder.append(currentStatement);
}
currentStatement = lineNumberReader.readLine();
}
appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
return scriptBuilder.toString();
}
private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, @Nullable String separator) {
if (separator == null) {
return;
}
String trimmed = separator.trim();
if (trimmed.length() == separator.length()) {
return;
}
// separator ends in whitespace, so we might want to see if the script is trying
// to end the same way
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
scriptBuilder.append(separator.substring(trimmed.length()));
}
}
private static boolean startsWithAny(String script, String[] prefixes, int offset) {
for (String prefix : prefixes) {
if (script.startsWith(prefix, offset)) {
return true;
}
}
return false;
}
/**
* Does the provided SQL script contain the specified delimiter?
* @param script the SQL script
* @param delim the string delimiting each statement - typically a ';' character
*/
public static boolean containsSqlScriptDelimiters(String script, String delim) {
boolean inLiteral = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
continue;
}
if (c == '\'') {
inLiteral = !inLiteral;
}
if (!inLiteral && script.startsWith(delim, i)) {
return true;
}
}
return false;
}
/**
* Execute the given SQL script using default settings for statement
* separators, comment delimiters, and exception handling flags.
@@ -233,7 +575,7 @@ public abstract class ScriptUtils {
String script;
try {
script = readScript(resource, separator, commentPrefixes, blockCommentEndDelimiter);
script = readScript(resource, commentPrefixes, separator, blockCommentEndDelimiter);
}
catch (IOException ex) {
throw new CannotReadScriptException(resource, ex);
@@ -242,9 +584,7 @@ public abstract class ScriptUtils {
if (separator == null) {
separator = DEFAULT_STATEMENT_SEPARATOR;
}
if (!EOF_STATEMENT_SEPARATOR.equals(separator) &&
!containsStatementSeparator(resource, script, separator, commentPrefixes,
blockCommentStartDelimiter, blockCommentEndDelimiter)) {
if (!EOF_STATEMENT_SEPARATOR.equals(separator) && !containsSqlScriptDelimiters(script, separator)) {
separator = FALLBACK_STATEMENT_SEPARATOR;
}
@@ -307,424 +647,4 @@ public abstract class ScriptUtils {
}
}
/**
* Read a script from the provided resource, using the supplied comment prefixes
* and statement separator, and build a {@code String} containing the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param resource the {@code EncodedResource} containing the script
* to be processed
* @param separator the statement separator in the SQL script (typically ";")
* @param commentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
static String readScript(EncodedResource resource, @Nullable String separator,
String[] commentPrefixes, String blockCommentEndDelimiter) throws IOException {
try (LineNumberReader lnr = new LineNumberReader(resource.getReader())) {
return readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
}
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefix and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with the comment prefix are excluded from the
* results; however, line comments anywhere else &mdash; for example, within
* a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param commentPrefix the prefix that identifies comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static String readScript(LineNumberReader lineNumberReader, @Nullable String commentPrefix,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String[] commentPrefixes = (commentPrefix != null) ? new String[] { commentPrefix } : null;
return readScript(lineNumberReader, commentPrefixes, separator, blockCommentEndDelimiter);
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefixes and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param commentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
* @since 5.2
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static String readScript(LineNumberReader lineNumberReader, @Nullable String[] commentPrefixes,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String currentStatement = lineNumberReader.readLine();
StringBuilder scriptBuilder = new StringBuilder();
while (currentStatement != null) {
if ((blockCommentEndDelimiter != null && currentStatement.contains(blockCommentEndDelimiter)) ||
(commentPrefixes != null && !startsWithAny(currentStatement, commentPrefixes, 0))) {
if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n');
}
scriptBuilder.append(currentStatement);
}
currentStatement = lineNumberReader.readLine();
}
appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
return scriptBuilder.toString();
}
private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, @Nullable String separator) {
if (separator == null) {
return;
}
String trimmed = separator.trim();
if (trimmed.length() == separator.length()) {
return;
}
// separator ends in whitespace, so we might want to see if the script is trying
// to end the same way
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
scriptBuilder.append(separator.substring(trimmed.length()));
}
}
/**
* Determine if the provided SQL script contains the specified delimiter.
* <p>This method is intended to be used to find the string delimiting each
* SQL statement &mdash; for example, a ';' character.
* <p>Any occurrence of the delimiter within the script will be ignored if it
* is within a <em>literal</em> block of text enclosed in single quotes
* ({@code '}) or double quotes ({@code "}), if it is escaped with a backslash
* ({@code \}), or if it is within a single-line comment or block comment.
* @param script the SQL script to search within
* @param delimiter the statement delimiter to search for
* @see #DEFAULT_COMMENT_PREFIXES
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static boolean containsSqlScriptDelimiters(String script, String delimiter) {
return containsStatementSeparator(null, script, delimiter, DEFAULT_COMMENT_PREFIXES,
DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
/**
* Determine if the provided SQL script contains the specified statement separator.
* <p>This method is intended to be used to find the string separating each
* SQL statement &mdash; for example, a ';' character.
* <p>Any occurrence of the separator within the script will be ignored if it
* is within a <em>literal</em> block of text enclosed in single quotes
* ({@code '}) or double quotes ({@code "}), if it is escaped with a backslash
* ({@code \}), or if it is within a single-line comment or block comment.
* @param resource the resource from which the script was read, or {@code null}
* if unknown
* @param script the SQL script to search within
* @param separator the statement separator to search for
* @param commentPrefixes the prefixes that identify single-line comments
* (typically {@code "--"})
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* (typically {@code "/*"})
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* (typically <code>"*&#47;"</code>)
* @since 5.2.16
*/
private static boolean containsStatementSeparator(@Nullable EncodedResource resource, String script,
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
continue;
}
if (!inDoubleQuote && (c == '\'')) {
inSingleQuote = !inSingleQuote;
}
else if (!inSingleQuote && (c == '"')) {
inDoubleQuote = !inDoubleQuote;
}
if (!inSingleQuote && !inDoubleQuote) {
if (script.startsWith(separator, i)) {
return true;
}
else if (startsWithAny(script, commentPrefixes, i)) {
// Skip over any content from the start of the comment to the EOL
int indexOfNextNewline = script.indexOf('\n', i);
if (indexOfNextNewline > i) {
i = indexOfNextNewline;
continue;
}
else {
// If there's no EOL, we must be at the end of the script, so stop here.
break;
}
}
else if (script.startsWith(blockCommentStartDelimiter, i)) {
// Skip over any block comments
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentEnd > i) {
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
continue;
}
else {
throw new ScriptParseException(
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource);
}
}
}
}
return false;
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator character. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator character separating each statement (typically a ';')
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, String, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException {
splitSqlScript(script, String.valueOf(separator), statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, char, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException {
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefix} will be honored:
* any text beginning with the comment prefix and extending to the end of the
* line will be omitted from the output. Similarly, the provided
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* delimiters will be honored: any text enclosed in a block comment will be
* omitted from the output. In addition, multiple adjacent whitespace characters
* will be collapsed into a single space.
* @param resource the resource from which the script was read
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param commentPrefix the prefix that identifies SQL line comments
* (typically "--")
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String commentPrefix, String blockCommentStartDelimiter,
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty");
splitSqlScript(resource, script, separator, new String[] { commentPrefix },
blockCommentStartDelimiter, blockCommentEndDelimiter, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefixes} will be honored:
* any text beginning with one of the comment prefixes and extending to the
* end of the line will be omitted from the output. Similarly, the provided
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* delimiters will be honored: any text enclosed in a block comment will be
* omitted from the output. In addition, multiple adjacent whitespace characters
* will be collapsed into a single space.
* @param resource the resource from which the script was read
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param commentPrefixes the prefixes that identify SQL line comments
* (typically "--")
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @since 5.2
* @deprecated as of Spring Framework 5.2.16 with no plans for replacement.
* This is an internal API and will likely be removed in Spring Framework 6.0.
*/
@Deprecated
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(script, "'script' must not be null or empty");
Assert.notNull(separator, "'separator' must not be null");
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty");
for (String commentPrefix : commentPrefixes) {
Assert.hasText(commentPrefix, "'commentPrefixes' must not contain null or empty elements");
}
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty");
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty");
StringBuilder sb = new StringBuilder();
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
sb.append(c);
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
sb.append(c);
continue;
}
if (!inDoubleQuote && (c == '\'')) {
inSingleQuote = !inSingleQuote;
}
else if (!inSingleQuote && (c == '"')) {
inDoubleQuote = !inDoubleQuote;
}
if (!inSingleQuote && !inDoubleQuote) {
if (script.startsWith(separator, i)) {
// We've reached the end of the current statement
if (sb.length() > 0) {
statements.add(sb.toString());
sb = new StringBuilder();
}
i += separator.length() - 1;
continue;
}
else if (startsWithAny(script, commentPrefixes, i)) {
// Skip over any content from the start of the comment to the EOL
int indexOfNextNewline = script.indexOf('\n', i);
if (indexOfNextNewline > i) {
i = indexOfNextNewline;
continue;
}
else {
// If there's no EOL, we must be at the end of the script, so stop here.
break;
}
}
else if (script.startsWith(blockCommentStartDelimiter, i)) {
// Skip over any block comments
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentEnd > i) {
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
continue;
}
else {
throw new ScriptParseException(
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource);
}
}
else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
// Avoid multiple adjacent whitespace characters
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
c = ' ';
}
else {
continue;
}
}
}
sb.append(c);
}
if (StringUtils.hasText(sb)) {
statements.add(sb.toString());
}
}
private static boolean startsWithAny(String script, String[] prefixes, int offset) {
for (String prefix : prefixes) {
if (script.startsWith(prefix, offset)) {
return true;
}
}
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2014 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.
@@ -28,7 +28,7 @@ package org.springframework.jdbc.datasource.init;
public class UncategorizedScriptException extends ScriptException {
/**
* Create a new {@code UncategorizedScriptException}.
* Construct a new {@code UncategorizedScriptException}.
* @param message detailed message
*/
public UncategorizedScriptException(String message) {
@@ -36,7 +36,7 @@ public class UncategorizedScriptException extends ScriptException {
}
/**
* Create a new {@code UncategorizedScriptException}.
* Construct a new {@code UncategorizedScriptException}.
* @param message detailed message
* @param cause the root cause
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -29,13 +29,15 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for integration tests involving database initialization.
*
* @author Sam Brannen
* @since 4.0.3
*/
abstract class AbstractDatabaseInitializationTests {
public abstract class AbstractDatabaseInitializationTests {
private final ClassRelativeResourceLoader resourceLoader = new ClassRelativeResourceLoader(getClass());
@@ -45,13 +47,13 @@ abstract class AbstractDatabaseInitializationTests {
@BeforeEach
void setUp() {
public void setUp() {
db = new EmbeddedDatabaseBuilder().setType(getEmbeddedDatabaseType()).build();
jdbcTemplate = new JdbcTemplate(db);
}
@AfterEach
void shutDown() {
public void shutDown() {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clear();
TransactionSynchronizationManager.unbindResource(db);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -34,7 +34,7 @@ import static org.mockito.Mockito.verify;
* @author Juergen Hoeller
* @since 4.3
*/
class CompositeDatabasePopulatorTests {
public class CompositeDatabasePopulatorTests {
private final Connection mockedConnection = mock(Connection.class);
@@ -44,59 +44,49 @@ class CompositeDatabasePopulatorTests {
@Test
void addPopulators() throws SQLException {
public void addPopulators() throws SQLException {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2);
populator.populate(mockedConnection);
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
verify(mockedDatabasePopulator1,times(1)).populate(mockedConnection);
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
}
@Test
void setPopulatorsWithMultiple() throws SQLException {
public void setPopulatorsWithMultiple() throws SQLException {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.setPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2); // multiple
populator.populate(mockedConnection);
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
}
@Test
void setPopulatorsForOverride() throws SQLException {
public void setPopulatorsForOverride() throws SQLException {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.setPopulators(mockedDatabasePopulator1);
populator.setPopulators(mockedDatabasePopulator2); // override
populator.populate(mockedConnection);
verify(mockedDatabasePopulator1, times(0)).populate(mockedConnection);
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
}
@Test
void constructWithVarargs() throws SQLException {
public void constructWithVarargs() throws SQLException {
CompositeDatabasePopulator populator =
new CompositeDatabasePopulator(mockedDatabasePopulator1, mockedDatabasePopulator2);
populator.populate(mockedConnection);
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
}
@Test
void constructWithCollection() throws SQLException {
public void constructWithCollection() throws SQLException {
Set<DatabasePopulator> populators = new LinkedHashSet<>();
populators.add(mockedDatabasePopulator1);
populators.add(mockedDatabasePopulator2);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator(populators);
populator.populate(mockedConnection);
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
}
@@ -26,7 +26,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Sam Brannen
* @since 4.0.3
*/
class H2DatabasePopulatorIntegrationTests extends AbstractDatabasePopulatorTests {
class H2DatabasePopulatorTests extends AbstractDatabasePopulatorTests {
@Override
protected EmbeddedDatabaseType getEmbeddedDatabaseType() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -17,12 +17,12 @@
package org.springframework.jdbc.datasource.init;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.core.io.Resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.mock;
/**
* Unit tests for {@link ResourceDatabasePopulator}.
@@ -31,84 +31,84 @@ import static org.mockito.BDDMockito.mock;
* @since 4.1
* @see AbstractDatabasePopulatorTests
*/
class ResourceDatabasePopulatorUnitTests {
public class ResourceDatabasePopulatorTests {
private static final Resource script1 = mock(Resource.class);
private static final Resource script2 = mock(Resource.class);
private static final Resource script3 = mock(Resource.class);
private static final Resource script1 = Mockito.mock(Resource.class);
private static final Resource script2 = Mockito.mock(Resource.class);
private static final Resource script3 = Mockito.mock(Resource.class);
@Test
void constructWithNullResource() {
public void constructWithNullResource() {
assertThatIllegalArgumentException().isThrownBy(() ->
new ResourceDatabasePopulator((Resource) null));
}
@Test
void constructWithNullResourceArray() {
public void constructWithNullResourceArray() {
assertThatIllegalArgumentException().isThrownBy(() ->
new ResourceDatabasePopulator((Resource[]) null));
}
@Test
void constructWithResource() {
public void constructWithResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1);
assertThat(databasePopulator.scripts).hasSize(1);
assertThat(databasePopulator.scripts.size()).isEqualTo(1);
}
@Test
void constructWithMultipleResources() {
public void constructWithMultipleResources() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
assertThat(databasePopulator.scripts).hasSize(2);
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
}
@Test
void constructWithMultipleResourcesAndThenAddScript() {
public void constructWithMultipleResourcesAndThenAddScript() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
assertThat(databasePopulator.scripts).hasSize(2);
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
databasePopulator.addScript(script3);
assertThat(databasePopulator.scripts).hasSize(3);
assertThat(databasePopulator.scripts.size()).isEqualTo(3);
}
@Test
void addScriptsWithNullResource() {
public void addScriptsWithNullResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.addScripts((Resource) null));
}
@Test
void addScriptsWithNullResourceArray() {
public void addScriptsWithNullResourceArray() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.addScripts((Resource[]) null));
}
@Test
void setScriptsWithNullResource() {
public void setScriptsWithNullResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.setScripts((Resource) null));
}
@Test
void setScriptsWithNullResourceArray() {
public void setScriptsWithNullResourceArray() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.setScripts((Resource[]) null));
}
@Test
void setScriptsAndThenAddScript() {
public void setScriptsAndThenAddScript() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThat(databasePopulator.scripts).isEmpty();
assertThat(databasePopulator.scripts.size()).isEqualTo(0);
databasePopulator.setScripts(script1, script2);
assertThat(databasePopulator.scripts).hasSize(2);
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
databasePopulator.addScript(script3);
assertThat(databasePopulator.scripts).hasSize(3);
assertThat(databasePopulator.scripts.size()).isEqualTo(3);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -19,10 +19,7 @@ package org.springframework.jdbc.datasource.init;
import java.util.ArrayList;
import java.util.List;
import org.assertj.core.util.Strings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.EncodedResource;
@@ -49,7 +46,6 @@ import static org.springframework.jdbc.datasource.init.ScriptUtils.splitSqlScrip
public class ScriptUtilsUnitTests {
@Test
@SuppressWarnings("deprecation")
public void splitSqlScriptDelimitedWithSemicolon() {
String rawStatement1 = "insert into customer (id, name)\nvalues (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
String cleanedStatement1 = "insert into customer (id, name) values (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
@@ -57,72 +53,54 @@ public class ScriptUtilsUnitTests {
String cleanedStatement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String rawStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String cleanedStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String delimiter = ";";
String script = Strings.join(rawStatement1, rawStatement2, rawStatement3).with(delimiter);
char delim = ';';
String script = rawStatement1 + delim + rawStatement2 + delim + rawStatement3 + delim;
List<String> statements = new ArrayList<>();
splitSqlScript(script, delimiter, statements);
splitSqlScript(script, delim, statements);
assertThat(statements).containsExactly(cleanedStatement1, cleanedStatement2, cleanedStatement3);
}
@Test
@SuppressWarnings("deprecation")
public void splitSqlScriptDelimitedWithNewLine() {
String statement1 = "insert into customer (id, name) values (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String delimiter = "\n";
String script = Strings.join(statement1, statement2, statement3).with(delimiter);
char delim = '\n';
String script = statement1 + delim + statement2 + delim + statement3 + delim;
List<String> statements = new ArrayList<>();
splitSqlScript(script, delimiter, statements);
splitSqlScript(script, delim, statements);
assertThat(statements).containsExactly(statement1, statement2, statement3);
}
@Test
@SuppressWarnings("deprecation")
public void splitSqlScriptDelimitedWithNewLineButDefaultDelimiterSpecified() {
String statement1 = "do something";
String statement2 = "do something else";
String script = Strings.join(statement1, statement2).with("\n");
char delim = '\n';
String script = statement1 + delim + statement2 + delim;
List<String> statements = new ArrayList<>();
splitSqlScript(script, DEFAULT_STATEMENT_SEPARATOR, statements);
assertThat(statements).as("stripped but not split statements").containsExactly(script.replace('\n', ' '));
}
@Test // SPR-13218
@SuppressWarnings("deprecation")
public void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() {
public void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() throws Exception {
String statement1 = "select '1' as \"Dogbert's owner's\" from dual";
String statement2 = "select '2' as \"Dilbert's\" from dual";
String delimiter = ";";
String script = Strings.join(statement1, statement2).with(delimiter);
char delim = ';';
String script = statement1 + delim + statement2 + delim;
List<String> statements = new ArrayList<>();
splitSqlScript(script, delimiter, statements);
splitSqlScript(script, ';', statements);
assertThat(statements).containsExactly(statement1, statement2);
}
@Test // SPR-11560
@SuppressWarnings("deprecation")
public void readAndSplitScriptWithMultipleNewlinesAsSeparator() throws Exception {
String script = readScript("db-test-data-multi-newline.sql");
List<String> statements = new ArrayList<>();
splitSqlScript(script, "\n\n", statements);
String statement1 = "insert into T_TEST (NAME) values ('Keith')";
String statement2 = "insert into T_TEST (NAME) values ('Dave')";
assertThat(statements).containsExactly(statement1, statement2);
}
@@ -144,103 +122,65 @@ public class ScriptUtilsUnitTests {
splitScriptContainingComments(script, "--", "#", "^");
}
@SuppressWarnings("deprecation")
private void splitScriptContainingComments(String script, String... commentPrefixes) {
private void splitScriptContainingComments(String script, String... commentPrefixes) throws Exception {
List<String> statements = new ArrayList<>();
splitSqlScript(null, script, ";", commentPrefixes, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
String statement1 = "insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
// Statement 4 addresses the error described in SPR-9982.
String statement4 = "INSERT INTO persons( person_id , name) VALUES( 1 , 'Name' )";
assertThat(statements).containsExactly(statement1, statement2, statement3, statement4);
}
@Test // SPR-10330
@SuppressWarnings("deprecation")
public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Exception {
String script = readScript("test-data-with-comments-and-leading-tabs.sql");
List<String> statements = new ArrayList<>();
splitSqlScript(script, ';', statements);
String statement1 = "insert into customer (id, name) values (1, 'Sam Brannen')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2013-06-08', 1)";
String statement3 = "insert into orders(id, order_date, customer_id) values (2, '2013-06-08', 1)";
assertThat(statements).containsExactly(statement1, statement2, statement3);
}
@Test // SPR-9531
@SuppressWarnings("deprecation")
public void readAndSplitScriptContainingMultiLineComments() throws Exception {
String script = readScript("test-data-with-multi-line-comments.sql");
List<String> statements = new ArrayList<>();
splitSqlScript(script, ';', statements);
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )";
assertThat(statements).containsExactly(statement1, statement2);
}
@Test
@SuppressWarnings("deprecation")
public void readAndSplitScriptContainingMultiLineNestedComments() throws Exception {
String script = readScript("test-data-with-multi-line-nested-comments.sql");
List<String> statements = new ArrayList<>();
splitSqlScript(script, ';', statements);
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )";
assertThat(statements).containsExactly(statement1, statement2);
}
@ParameterizedTest
@CsvSource(delimiter = '#', value = {
// semicolon
"'select 1\n select '';''' # ; # false",
"'select 1\n select \";\"' # ; # false",
"'select 1; select 2' # ; # true",
// newline
"'select 1; select ''\n''' # '\n' # false",
"'select 1; select \"\n\"' # '\n' # false",
"'select 1\n select 2' # '\n' # true",
// double newline
"'select 1\n select 2' # '\n\n' # false",
"'select 1\n\n select 2' # '\n\n' # true",
// semicolon with MySQL style escapes '\\'
"'insert into users(first, last)\nvalues(''a\\\\'', ''b;'')' # ; # false",
"'insert into users(first, last)\nvalues(''Charles'', ''d\\''Artagnan''); select 1' # ; # true",
// semicolon inside comments
"'-- a;b;c\ninsert into colors(color_num) values(42);' # ; # true",
"'/* a;b;c */\ninsert into colors(color_num) values(42);' # ; # true",
"'-- a;b;c\ninsert into colors(color_num) values(42)' # ; # false",
"'/* a;b;c */\ninsert into colors(color_num) values(42)' # ; # false",
// single quotes inside comments
"'-- What\\''s your favorite color?\ninsert into colors(color_num) values(42);' # ; # true",
"'-- What''s your favorite color?\ninsert into colors(color_num) values(42);' # ; # true",
"'/* What\\''s your favorite color? */\ninsert into colors(color_num) values(42);' # ; # true",
"'/* What''s your favorite color? */\ninsert into colors(color_num) values(42);' # ; # true",
// double quotes inside comments
"'-- double \" quotes\ninsert into colors(color_num) values(42);' # ; # true",
"'-- double \\\" quotes\ninsert into colors(color_num) values(42);' # ; # true",
"'/* double \" quotes */\ninsert into colors(color_num) values(42);' # ; # true",
"'/* double \\\" quotes */\ninsert into colors(color_num) values(42);' # ; # true"
})
@SuppressWarnings("deprecation")
public void containsStatementSeparator(String script, String delimiter, boolean expected) {
// Indirectly tests ScriptUtils.containsStatementSeparator(EncodedResource, String, String, String[], String, String).
assertThat(containsSqlScriptDelimiters(script, delimiter)).isEqualTo(expected);
@Test
public void containsDelimiters() {
assertThat(containsSqlScriptDelimiters("select 1\n select ';'", ";")).isFalse();
assertThat(containsSqlScriptDelimiters("select 1; select 2", ";")).isTrue();
assertThat(containsSqlScriptDelimiters("select 1; select '\\n\n';", "\n")).isFalse();
assertThat(containsSqlScriptDelimiters("select 1\n select 2", "\n")).isTrue();
assertThat(containsSqlScriptDelimiters("select 1\n select 2", "\n\n")).isFalse();
assertThat(containsSqlScriptDelimiters("select 1\n\n select 2", "\n\n")).isTrue();
// MySQL style escapes '\\'
assertThat(containsSqlScriptDelimiters("insert into users(first_name, last_name)\nvalues('a\\\\', 'b;')", ";")).isFalse();
assertThat(containsSqlScriptDelimiters("insert into users(first_name, last_name)\nvalues('Charles', 'd\\'Artagnan'); select 1;", ";")).isTrue();
}
private String readScript(String path) throws Exception {
EncodedResource resource = new EncodedResource(new ClassPathResource(path, getClass()));
return ScriptUtils.readScript(resource, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_COMMENT_PREFIXES,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
return ScriptUtils.readScript(resource);
}
}
@@ -5,19 +5,16 @@
* x, y, z...
*/
-- This is a single line comment containing single (') and double quotes (").
INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller');
-- This is also a comment.
/*-------------------------------------------
-- A fancy multi-line comment that puts
-- A fancy multi-line comments that puts
-- single line comments inside of a multi-line
-- comment block.
Moreover, the block comment end delimiter
appears on a line that can potentially also
be a single-line comment if we weren't
already inside a multi-line comment run.
And here's a line containing single and double quotes (").
-------------------------------------------*/
INSERT INTO
users(first_name, last_name) -- This is a single line comment containing the block-end-comment sequence here */ but it's still a single-line comment
@@ -423,7 +423,6 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
return new CachedMessageProducer(producer);
}
@SuppressWarnings("resource")
private MessageConsumer getCachedConsumer(Destination dest, @Nullable String selector,
@Nullable Boolean noLocal, @Nullable String subscription, boolean durable) throws JMSException {
@@ -120,7 +120,8 @@ public class DefaultMessageHandlerMethodFactory
* the ones configured by default. This is an advanced option. For most use cases
* it should be sufficient to use {@link #setCustomArgumentResolvers(java.util.List)}.
*/
public void setArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
@SuppressWarnings("ConstantConditions")
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers == null) {
this.argumentResolvers.clear();
return;
@@ -131,6 +131,7 @@ public class RSocketFrameTypeMessageCondition extends AbstractMessageCondition<R
* @param message the current message
* @return the frame type or {@code null} if not found
*/
@SuppressWarnings("ConstantConditions")
@Nullable
public static FrameType getFrameType(Message<?> message) {
return (FrameType) message.getHeaders().get(RSocketFrameTypeMessageCondition.FRAME_TYPE_HEADER);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2017 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.
@@ -29,8 +29,8 @@ import org.springframework.util.Assert;
* Base class for STOMP client implementations.
*
* <p>Subclasses can connect over WebSocket or TCP using any library. When creating
* a new connection, a subclass can create an instance of {@link DefaultStompSession}
* which implements {@link org.springframework.messaging.tcp.TcpConnectionHandler}
* a new connection, a subclass can create an instance of @link DefaultStompSession}
* which extends {@link org.springframework.messaging.tcp.TcpConnectionHandler}
* whose lifecycle methods the subclass must then invoke.
*
* <p>In effect, {@code TcpConnectionHandler} and {@code TcpConnection} are the
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -102,7 +102,6 @@ inline fun <reified T : Any> RSocketRequester.RequestSpec.dataWithType(flow: Flo
* @author Sebastien Deleuze
* @since 5.2
*/
@Suppress("DEPRECATION")
suspend fun RSocketRequester.RetrieveSpec.sendAndAwait() {
send().awaitSingleOrNull()
}
@@ -122,7 +121,6 @@ suspend inline fun <reified T : Any> RSocketRequester.RetrieveSpec.retrieveAndAw
* @author Sebastien Deleuze
* @since 5.2.1
*/
@Suppress("DEPRECATION")
suspend inline fun <reified T : Any> RSocketRequester.RetrieveSpec.retrieveAndAwaitOrNull(): T? =
retrieveMono(object : ParameterizedTypeReference<T>() {}).awaitSingleOrNull()
+1 -3
View File
@@ -19,7 +19,5 @@ dependencies {
testCompile(testFixtures(project(":spring-context")))
testCompile("io.projectreactor:reactor-test")
testCompile("io.r2dbc:r2dbc-h2")
testCompile("io.r2dbc:r2dbc-spi-test:0.8.1.RELEASE") {
exclude group: "org.springframework", module: "spring-jdbc"
}
testCompile("io.r2dbc:r2dbc-spi-test:0.8.1.RELEASE")
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -39,18 +39,18 @@ import org.springframework.util.Assert;
* {@link org.springframework.transaction.ReactiveTransactionManager}
* implementation for a single R2DBC {@link ConnectionFactory}. This class is
* capable of working in any environment with any R2DBC driver, as long as the
* setup uses a {@code ConnectionFactory} as its {@link Connection} factory
* mechanism. Binds a R2DBC {@code Connection} from the specified
* {@code ConnectionFactory} to the current subscriber context, potentially
* allowing for one context-bound {@code Connection} per {@code ConnectionFactory}.
* setup uses a {@link ConnectionFactory} as its {@link Connection} factory
* mechanism. Binds a R2DBC {@link Connection} from the specified
* {@link ConnectionFactory} to the current subscriber context, potentially
* allowing for one context-bound {@link Connection} per {@link ConnectionFactory}.
*
* <p><b>Note: The {@code ConnectionFactory} that this transaction manager
* operates on needs to return independent {@code Connection}s.</b>
* The {@code Connection}s may come from a pool (the typical case), but the
* {@code ConnectionFactory} must not return scoped scoped {@code Connection}s
* or the like. This transaction manager will associate {@code Connection}
* <p><b>Note: The {@link ConnectionFactory} that this transaction manager
* operates on needs to return independent {@link Connection}s.</b>
* The {@link Connection}s may come from a pool (the typical case), but the
* {@link ConnectionFactory} must not return scoped scoped {@link Connection}s
* or the like. This transaction manager will associate {@link Connection}
* with context-bound transactions itself, according to the specified propagation
* behavior. It assumes that a separate, independent {@code Connection} can
* behavior. It assumes that a separate, independent {@link Connection} can
* be obtained even during an ongoing transaction.
*
* <p>Application code is required to retrieve the R2DBC Connection via
@@ -59,18 +59,18 @@ import org.springframework.util.Assert;
* Spring classes such as {@code DatabaseClient} use this strategy implicitly.
* If not used in combination with this transaction manager, the
* {@link ConnectionFactoryUtils} lookup strategy behaves exactly like the
* native {@code ConnectionFactory} lookup; it can thus be used in a portable fashion.
* native {@link ConnectionFactory} lookup; it can thus be used in a portable fashion.
*
* <p>Alternatively, you can allow application code to work with the standard
* R2DBC lookup pattern {@link ConnectionFactory#create()}, for example for code
* that is not aware of Spring at all. In that case, define a
* {@link TransactionAwareConnectionFactoryProxy} for your target {@code ConnectionFactory},
* and pass that proxy {@code ConnectionFactory} to your DAOs, which will automatically
* {@link TransactionAwareConnectionFactoryProxy} for your target {@link ConnectionFactory},
* and pass that proxy {@link ConnectionFactory} to your DAOs, which will automatically
* participate in Spring-managed transactions when accessing it.
*
* <p>This transaction manager triggers flush callbacks on registered transaction
* synchronizations (if synchronization is generally active), assuming resources
* operating on the underlying R2DBC {@code Connection}.
* operating on the underlying R2DBC {@link Connection}.
*
* @author Mark Paluch
* @since 5.3
@@ -88,14 +88,14 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
/**
* Create a new {@code R2dbcTransactionManager} instance.
* Create a new @link ConnectionFactoryTransactionManager} instance.
* A ConnectionFactory has to be set to be able to use it.
* @see #setConnectionFactory
*/
public R2dbcTransactionManager() {}
/**
* Create a new {@code R2dbcTransactionManager} instance.
* Create a new {@link R2dbcTransactionManager} instance.
* @param connectionFactory the R2DBC ConnectionFactory to manage transactions for
*/
public R2dbcTransactionManager(ConnectionFactory connectionFactory) {
@@ -107,10 +107,10 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
/**
* Set the R2DBC {@link ConnectionFactory} that this instance should manage transactions for.
* <p>This will typically be a locally defined {@code ConnectionFactory}, for example an connection pool.
* <p><b>The {@code ConnectionFactory} passed in here needs to return independent {@link Connection}s.</b>
* The {@code Connection}s may come from a pool (the typical case), but the {@code ConnectionFactory}
* must not return scoped {@code Connection}s or the like.
* <p>This will typically be a locally defined {@link ConnectionFactory}, for example an connection pool.
* <p><b>The {@link ConnectionFactory} passed in here needs to return independent {@link Connection}s.</b>
* The {@link Connection}s may come from a pool (the typical case), but the {@link ConnectionFactory}
* must not return scoped {@link Connection}s or the like.
* @see TransactionAwareConnectionFactoryProxy
*/
public void setConnectionFactory(@Nullable ConnectionFactory connectionFactory) {
@@ -127,7 +127,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
/**
* Obtain the {@link ConnectionFactory} for actual use.
* @return the {@code ConnectionFactory} (never {@code null})
* @return the {@link ConnectionFactory} (never {@code null})
* @throws IllegalStateException in case of no ConnectionFactory set
*/
protected ConnectionFactory obtainConnectionFactory() {
@@ -405,9 +405,9 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
}
/**
* Resolve the {@linkplain TransactionDefinition#getIsolationLevel() isolation level constant} to a R2DBC
* Resolve the {@link TransactionDefinition#getIsolationLevel() isolation level constant} to a R2DBC
* {@link IsolationLevel}. If you'd like to extend isolation level translation for vendor-specific
* {@code IsolationLevel}s, override this method accordingly.
* {@link IsolationLevel}s, override this method accordingly.
* @param isolationLevel the isolation level to translate.
* @return the resolved isolation level. Can be {@code null} if not resolvable or the isolation level
* should remain {@link TransactionDefinition#ISOLATION_DEFAULT default}.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -36,25 +36,25 @@ import org.springframework.util.Assert;
/**
* Implementation of {@link DelegatingConnectionFactory} that wraps a
* single R2DBC {@link Connection} which is not closed after use.
* single R2DBC Connection which is not closed after use.
* Obviously, this is not multi-threading capable.
*
* <p>Note that at shutdown, someone should close the underlying
* {@code Connection} via the {@code close()} method. Client code will
* never call close on the {@code Connection} handle if it is
* Connection via the {@code close()} method. Client code will
* never call close on the Connection handle if it is
* SmartConnectionFactory-aware (e.g. uses
* {@link ConnectionFactoryUtils#releaseConnection(Connection, ConnectionFactory)}).
*
* <p>If client code will call {@link Connection#close()} in the
* assumption of a pooled {@code Connection}, like when using persistence tools,
* set "suppressClose" to {@code true}. This will return a close-suppressing
* assumption of a pooled Connection, like when using persistence tools,
* set "suppressClose" to "true". This will return a close-suppressing
* proxy instead of the physical Connection.
*
* <p>This is primarily intended for testing and pipelining usage of connections.
* For example, it enables easy testing outside an application server for code
* For example, it enables easy testing outside an application server, for code
* that expects to work on a {@link ConnectionFactory}.
* Note that this implementation does not act as a connection pool-like utility.
* Connection pooling requires a pooling {@link ConnectionFactory} such as one from
* Connection pooling requires a {@link ConnectionFactory} implemented by e.g.
* {@code r2dbc-pool}.
*
* @author Mark Paluch
@@ -90,10 +90,10 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
}
/**
* Create a new {@code SingleConnectionFactory} using an R2DBC connection URL.
* @param url the R2DBC URL to use for accessing {@link ConnectionFactory} discovery
* @param suppressClose if the returned {@link Connection} should be a
* close-suppressing proxy or the physical {@code Connection}
* Create a new {@link SingleConnectionFactory} using a R2DBC connection URL.
* @param url the R2DBC URL to use for accessing {@link ConnectionFactory} discovery.
* @param suppressClose if the returned {@link Connection} should be a close-suppressing proxy
* or the physical {@link Connection}.
* @see ConnectionFactories#get(String)
*/
public SingleConnectionFactory(String url, boolean suppressClose) {
@@ -103,15 +103,13 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
}
/**
* Create a new {@code SingleConnectionFactory} with a given {@link Connection}
* and {@link ConnectionFactoryMetadata}.
* @param target underlying target {@code Connection}
* @param metadata {@code ConnectionFactory} metadata to be associated with
* this {@code ConnectionFactory}
* @param suppressClose {@code true} if the {@code Connection} should be wrapped
* with a {@code Connection} that suppresses {@code close()} calls (to allow
* for normal {@code close()} usage in applications that expect a pooled
* {@code Connection})
* Create a new {@link SingleConnectionFactory} with a given {@link Connection} and
* {@link ConnectionFactoryMetadata}.
* @param target underlying target {@link Connection}.
* @param metadata {@link ConnectionFactory} metadata to be associated with this {@link ConnectionFactory}.
* @param suppressClose if the {@link Connection} should be wrapped with a {@link Connection} that suppresses
* @code close()} calls (to allow for normal {@code close()} usage in applications that expect a pooled
* @link Connection}).
*/
public SingleConnectionFactory(Connection target, ConnectionFactoryMetadata metadata, boolean suppressClose) {
super(new ConnectionFactory() {
@@ -135,7 +133,7 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
/**
* Set whether the returned {@link Connection} should be a close-suppressing proxy
* or the physical {@code Connection}.
* or the physical {@link Connection}.
*/
public void setSuppressClose(boolean suppressClose) {
this.suppressClose = suppressClose;
@@ -143,23 +141,21 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
/**
* Return whether the returned {@link Connection} will be a close-suppressing proxy
* or the physical {@code Connection}.
* or the physical {@link Connection}.
*/
protected boolean isSuppressClose() {
return this.suppressClose;
}
/**
* Set whether the returned {@link Connection}'s "autoCommit" setting should
* be overridden.
* Set whether the returned {@link Connection}'s "autoCommit" setting should be overridden.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return whether the returned {@link Connection}'s "autoCommit" setting should
* be overridden.
* Return whether the returned {@link Connection}'s "autoCommit" setting should be overridden.
* @return the "autoCommit" value, or {@code null} if none to be applied
*/
@Nullable
@@ -211,9 +207,9 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
/**
* Prepare the {@link Connection} before using it.
* Applies {@linkplain #getAutoCommitValue() auto-commit} settings if configured.
* @param connection the requested {@code Connection}
* @return the prepared {@code Connection}
* Applies {@link #getAutoCommitValue() auto-commit} settings if configured.
* @param connection the requested {@link Connection}.
* @return the prepared {@link Connection}.
*/
protected Mono<Connection> prepareConnection(Connection connection) {
Boolean autoCommit = getAutoCommitValue();
@@ -226,8 +222,8 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
/**
* Wrap the given {@link Connection} with a proxy that delegates every method call to it
* but suppresses close calls.
* @param target the original {@code Connection} to wrap
* @return the wrapped Connection
* @param target the original {@link Connection} to wrap.
* @return the wrapped Connection.
*/
protected Connection getCloseSuppressingConnectionProxy(Connection target) {
return (Connection) Proxy.newProxyInstance(SingleConnectionFactory.class.getClassLoader(),
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -21,8 +21,6 @@ import org.springframework.core.io.support.EncodedResource;
/**
* Thrown by {@link ScriptUtils} if an SQL script cannot be read.
*
* @author Keith Donald
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
*/
@@ -31,8 +29,8 @@ public class CannotReadScriptException extends ScriptException {
/**
* Create a new {@code CannotReadScriptException}.
* @param resource the resource that cannot be read from
* @param cause the underlying cause of the resource access failure
* @param resource the resource that cannot be read from.
* @param cause the underlying cause of the resource access failure.
*/
public CannotReadScriptException(EncodedResource resource, Throwable cause) {
super("Cannot read SQL script from " + resource, cause);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -29,12 +29,8 @@ import org.springframework.util.Assert;
/**
* Composite {@link DatabasePopulator} that delegates to a list of given
* {@code DatabasePopulator} implementations, executing all scripts.
* {@link DatabasePopulator} implementations, executing all scripts.
*
* @author Dave Syer
* @author Juergen Hoeller
* @author Sam Brannen
* @author Kazuki Shimizu
* @author Mark Paluch
* @since 5.3
*/
@@ -48,15 +44,14 @@ public class CompositeDatabasePopulator implements DatabasePopulator {
* @see #setPopulators
* @see #addPopulators
*/
public CompositeDatabasePopulator() {
}
public CompositeDatabasePopulator() {}
/**
* Create a {@code CompositeDatabasePopulator}. with the given populators.
* @param populators one or more populators to delegate to.
*/
public CompositeDatabasePopulator(Collection<DatabasePopulator> populators) {
Assert.notNull(populators, "DatabasePopulators must not be null");
Assert.notNull(populators, "Collection of DatabasePopulator must not be null");
this.populators.addAll(populators);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -28,8 +28,6 @@ import org.springframework.util.Assert;
* initialization and {@link #setDatabaseCleaner clean up} a database during
* destruction.
*
* @author Dave Syer
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
* @see DatabasePopulator
@@ -52,7 +50,7 @@ public class ConnectionFactoryInitializer implements InitializingBean, Disposabl
* The {@link ConnectionFactory} for the database to populate when this
* component is initialized and to clean up when this component is shut down.
* <p>This property is mandatory with no default provided.
* @param connectionFactory the R2DBC {@link ConnectionFactory}
* @param connectionFactory the R2DBC {@link ConnectionFactory}.
*/
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
@@ -68,9 +66,10 @@ public class ConnectionFactoryInitializer implements InitializingBean, Disposabl
}
/**
* Set the {@link DatabasePopulator} to execute during the bean destruction
* phase, cleaning up the database and leaving it in a known state for others.
* @param databaseCleaner the {@code DatabasePopulator} to use during destruction
* Set the {@link DatabasePopulator} to execute during the bean destruction phase, cleaning up the database and
* leaving it in a known state for others.
*
* @param databaseCleaner the {@link DatabasePopulator} to use during destruction
* @see #setDatabasePopulator
*/
public void setDatabaseCleaner(DatabasePopulator databaseCleaner) {
@@ -78,8 +77,8 @@ public class ConnectionFactoryInitializer implements InitializingBean, Disposabl
}
/**
* Flag to explicitly enable or disable the {@linkplain #setDatabasePopulator
* database populator} and {@linkplain #setDatabaseCleaner database cleaner}.
* Flag to explicitly enable or disable the {@link #setDatabasePopulator database populator}
* and {@link #setDatabaseCleaner database cleaner}.
* @param enabled {@code true} if the database populator and database cleaner
* should be called on startup and shutdown, respectively
*/
@@ -89,8 +88,7 @@ public class ConnectionFactoryInitializer implements InitializingBean, Disposabl
/**
* Use the {@linkplain #setDatabasePopulator database populator} to set up
* the database.
* Use the {@link #setDatabasePopulator database populator} to set up the database.
*/
@Override
public void afterPropertiesSet() {
@@ -98,8 +96,7 @@ public class ConnectionFactoryInitializer implements InitializingBean, Disposabl
}
/**
* Use the {@linkplain #setDatabaseCleaner database cleaner} to clean up the
* database.
* Use the {@link #setDatabaseCleaner database cleaner} to clean up the database.
*/
@Override
public void destroy() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -28,8 +28,6 @@ import org.springframework.util.Assert;
* Strategy used to populate, initialize, or clean up a database.
*
* @author Mark Paluch
* @author Keith Donald
* @author Sam Brannen
* @since 5.3
* @see ResourceDatabasePopulator
* @see ConnectionFactoryInitializer
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -20,10 +20,8 @@ import org.springframework.dao.DataAccessException;
import org.springframework.lang.Nullable;
/**
* Root of the hierarchy of data access exceptions that are related to processing
* of SQL scripts.
* Root of the hierarchy of data access exceptions that are related to processing of SQL scripts.
*
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -22,7 +22,6 @@ import org.springframework.lang.Nullable;
/**
* Thrown by {@link ScriptUtils} if an SQL script cannot be properly parsed.
*
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2015 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.
@@ -22,8 +22,6 @@ import org.springframework.core.io.support.EncodedResource;
* Thrown by {@link ScriptUtils} if a statement in an SQL script failed when
* executing it against the target database.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -43,8 +43,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Generic utility methods for working with SQL scripts in conjunction with R2DBC.
*
* Generic utility methods for working with SQL scripts.
* <p>Mainly for internal use within the framework.
*
* @author Thomas Risberg
@@ -59,7 +58,6 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Mark Paluch
* @since 5.3
* @see org.springframework.jdbc.datasource.init.ScriptUtils
*/
public abstract class ScriptUtils {
@@ -86,10 +84,15 @@ public abstract class ScriptUtils {
*/
public static final String EOF_STATEMENT_SEPARATOR = "^^^ END OF SCRIPT ^^^";
/**
* Default prefix for single-line comments within SQL scripts: {@code "--"}.
*/
public static final String DEFAULT_COMMENT_PREFIX = "--";
/**
* Default prefixes for single-line comments within SQL scripts: {@code ["--"]}.
*/
public static final String[] DEFAULT_COMMENT_PREFIXES = {"--"};
public static final String[] DEFAULT_COMMENT_PREFIXES = {DEFAULT_COMMENT_PREFIX};
/**
* Default start delimiter for block comments within SQL scripts: {@code "/*"}.
@@ -106,326 +109,90 @@ public abstract class ScriptUtils {
/**
* Execute the given SQL script using default settings for statement
* separators, comment delimiters, and exception handling flags.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource to load the SQL script from; encoded with the
* current platform's default encoding
* @throws ScriptException if an error occurred while executing the SQL script
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String[], String, String, String)
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #DEFAULT_COMMENT_PREFIXES
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
* Split an SQL script into separate statements delimited by the provided
* separator character. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator character separating each statement (typically a ';')
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, String, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
*/
public static Mono<Void> executeSqlScript(Connection connection, Resource resource) throws ScriptException {
return executeSqlScript(connection, new EncodedResource(resource));
}
/**
* Execute the given SQL script using default settings for statement
* separators, comment delimiters, and exception handling flags.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @throws ScriptException if an error occurred while executing the SQL script
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String[], String, String, String)
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #DEFAULT_COMMENT_PREFIXES
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException {
return executeSqlScript(connection, resource, DefaultDataBufferFactory.sharedInstance, false, false,
DEFAULT_COMMENT_PREFIXES, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
/**
* Execute the given SQL script.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @param dataBufferFactory the factory to create data buffers with
* @param continueOnError whether or not to continue without throwing an exception
* in the event of an error
* @param ignoreFailedDrops whether or not to continue in the event of specifically
* an error on a {@code DROP} statement
* @param commentPrefix the prefix that identifies single-line comments in the
* SQL script (typically "--")
* @param separator the script statement separator; defaults to
* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to
* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a
* single statement without a separator
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @throws ScriptException if an error occurred while executing the SQL script
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #FALLBACK_STATEMENT_SEPARATOR
* @see #EOF_STATEMENT_SEPARATOR
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String commentPrefix, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
return executeSqlScript(connection, resource, dataBufferFactory, continueOnError,
ignoreFailedDrops, new String[] { commentPrefix }, separator,
blockCommentStartDelimiter, blockCommentEndDelimiter);
}
/**
* Execute the given SQL script.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @param dataBufferFactory the factory to create data buffers with
* @param continueOnError whether or not to continue without throwing an exception
* in the event of an error
* @param ignoreFailedDrops whether or not to continue in the event of specifically
* an error on a {@code DROP} statement
* @param commentPrefixes the prefixes that identify single-line comments in the
* SQL script (typically "--")
* @param separator the script statement separator; defaults to
* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to
* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a
* single statement without a separator
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @throws ScriptException if an error occurred while executing the SQL script
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #FALLBACK_STATEMENT_SEPARATOR
* @see #EOF_STATEMENT_SEPARATOR
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String[] commentPrefixes, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL script from " + resource);
}
long startTime = System.currentTimeMillis();
Mono<String> inputScript = readScript(resource, dataBufferFactory, separator)
.onErrorMap(IOException.class, ex -> new CannotReadScriptException(resource, ex));
AtomicInteger statementNumber = new AtomicInteger();
Flux<Void> executeScript = inputScript.flatMapIterable(script -> {
String separatorToUse = separator;
if (separatorToUse == null) {
separatorToUse = DEFAULT_STATEMENT_SEPARATOR;
}
if (!EOF_STATEMENT_SEPARATOR.equals(separatorToUse) &&
!containsStatementSeparator(resource, script, separatorToUse, commentPrefixes,
blockCommentStartDelimiter, blockCommentEndDelimiter)) {
separatorToUse = FALLBACK_STATEMENT_SEPARATOR;
}
return splitSqlScript(resource, script, separatorToUse, commentPrefixes,
blockCommentStartDelimiter, blockCommentEndDelimiter);
}).concatMap(statement -> {
statementNumber.incrementAndGet();
return runStatement(statement, connection, resource, continueOnError, ignoreFailedDrops, statementNumber);
});
if (logger.isDebugEnabled()) {
executeScript = executeScript.doOnComplete(() -> {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
});
}
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
ex -> new UncategorizedScriptException(
"Failed to execute database script from resource [" + resource + "]", ex))
.then();
}
/**
* Read a script from the provided resource, using the supplied statement
* separator, and build a {@code String} containing the lines.
* @param resource the {@code EncodedResource} containing the script to be
* processed
* @param dataBufferFactory the factory to create data buffers with
* @param separator the statement separator in the SQL script (typically ";")
* @return a {@link Mono} of {@link String} containing the script lines that
* completes once the resource has been loaded
*/
static Mono<String> readScript(EncodedResource resource, DataBufferFactory dataBufferFactory,
@Nullable String separator) {
return DataBufferUtils.join(DataBufferUtils.read(resource.getResource(), dataBufferFactory, 8192))
.handle((it, sink) -> {
try (InputStream is = it.asInputStream()) {
InputStreamReader in = (resource.getCharset() != null ?
new InputStreamReader(is, resource.getCharset()) : new InputStreamReader(is));
LineNumberReader lnr = new LineNumberReader(in);
String script = readScript(lnr, separator);
sink.next(script);
sink.complete();
}
catch (Exception ex) {
sink.error(ex);
}
finally {
DataBufferUtils.release(it);
}
});
}
/**
* Read a script from the provided {@code LineNumberReader} and build a
* {@code String} containing the lines.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param separator the statement separator in the SQL script (typically ";")
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
private static String readScript(LineNumberReader lineNumberReader, @Nullable String separator) throws IOException {
StringBuilder scriptBuilder = new StringBuilder();
String currentLine = lineNumberReader.readLine();
while (currentLine != null) {
if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n');
}
scriptBuilder.append(currentLine);
currentLine = lineNumberReader.readLine();
}
appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
return scriptBuilder.toString();
}
private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, @Nullable String separator) {
if (separator == null) {
return;
}
String trimmed = separator.trim();
if (trimmed.length() == separator.length()) {
return;
}
// separator ends in whitespace, so we might want to see if the script is trying
// to end the same way
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
scriptBuilder.append(separator.substring(trimmed.length()));
}
}
/**
* Determine if the provided SQL script contains the specified statement separator.
* <p>This method is intended to be used to find the string separating each
* SQL statement &mdash; for example, a ';' character.
* <p>Any occurrence of the separator within the script will be ignored if it
* is within a <em>literal</em> block of text enclosed in single quotes
* ({@code '}) or double quotes ({@code "}), if it is escaped with a backslash
* ({@code \}), or if it is within a single-line comment or block comment.
* @param resource the resource from which the script was read, or {@code null}
* if unknown
* @param script the SQL script to search within
* @param separator the statement separator to search for
* @param commentPrefixes the prefixes that identify single-line comments
* (typically {@code "--"})
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* (typically {@code "/*"})
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* (typically <code>"*&#47;"</code>)
* @since 5.3.8
*/
static boolean containsStatementSeparator(EncodedResource resource, String script,
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
continue;
}
if (!inDoubleQuote && (c == '\'')) {
inSingleQuote = !inSingleQuote;
}
else if (!inSingleQuote && (c == '"')) {
inDoubleQuote = !inDoubleQuote;
}
if (!inSingleQuote && !inDoubleQuote) {
if (script.startsWith(separator, i)) {
return true;
}
else if (startsWithAny(script, commentPrefixes, i)) {
// Skip over any content from the start of the comment to the EOL
int indexOfNextNewline = script.indexOf('\n', i);
if (indexOfNextNewline > i) {
i = indexOfNextNewline;
continue;
}
else {
// If there's no EOL, we must be at the end of the script, so stop here.
break;
}
}
else if (script.startsWith(blockCommentStartDelimiter, i)) {
// Skip over any block comments
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentEnd > i) {
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
continue;
}
else {
throw new ScriptParseException(
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource);
}
}
}
}
return false;
public static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException {
splitSqlScript(script, String.valueOf(separator), statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string and return a {@code List} containing each individual
* statement.
* separator string. Each individual statement will be added to the
* provided {@code List}.
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* comment prefix; any text beginning with the comment prefix and extending to
* the end of the line will be omitted from the output. Similarly,
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
* @see #splitSqlScript(String, char, List)
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
*/
public static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException {
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefix} will be honored:
* any text beginning with the comment prefix and extending to the end of the
* line will be omitted from the output. Similarly, the provided
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* delimiters will be honored: any text enclosed in a block comment will be
* omitted from the output. In addition, multiple adjacent whitespace characters
* will be collapsed into a single space.
* @param resource the resource from which the script was read
* @param script the SQL script
* @param separator text separating each statement
* (typically a ';' or newline character)
* @param commentPrefix the prefix that identifies SQL line comments
* (typically "--")
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
*/
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String commentPrefix, String blockCommentStartDelimiter,
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty");
splitSqlScript(resource, script, separator, new String[] { commentPrefix },
blockCommentStartDelimiter, blockCommentEndDelimiter, statements);
}
/**
* Split an SQL script into separate statements delimited by the provided
* separator string. Each individual statement will be added to the provided
* {@code List}.
* <p>Within the script, the provided {@code commentPrefixes} will be honored:
* any text beginning with one of the comment prefixes and extending to the
* end of the line will be omitted from the output. Similarly, the provided
@@ -443,12 +210,12 @@ public abstract class ScriptUtils {
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @return a list of statements
* @param statements the list that will contain the individual statements
* @throws ScriptException if an error occurred while splitting the SQL script
*/
static List<String> splitSqlScript(EncodedResource resource, String script,
public static void splitSqlScript(@Nullable EncodedResource resource, String script,
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
String blockCommentEndDelimiter, List<String> statements) throws ScriptException {
Assert.hasText(script, "'script' must not be null or empty");
Assert.notNull(separator, "'separator' must not be null");
@@ -459,7 +226,6 @@ public abstract class ScriptUtils {
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty");
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty");
List<String> statements = new ArrayList<>();
StringBuilder sb = new StringBuilder();
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
@@ -534,8 +300,126 @@ public abstract class ScriptUtils {
if (StringUtils.hasText(sb)) {
statements.add(sb.toString());
}
}
return statements;
/**
* Read a script from the given resource, using "{@code --}" as the comment prefix
* and "{@code ;}" as the statement separator, and build a String containing the lines.
* @param resource the {@code EncodedResource} to be read
* @return {@code String} containing the script lines
*/
public static Mono<String> readScript(EncodedResource resource, DataBufferFactory dataBufferFactory) {
return readScript(resource, dataBufferFactory, DEFAULT_COMMENT_PREFIXES, DEFAULT_STATEMENT_SEPARATOR,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
/**
* Read a script from the provided resource, using the supplied comment prefixes
* and statement separator, and build a {@code String} containing the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param resource the {@code EncodedResource} containing the script
* to be processed
* @param commentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@link Mono} of {@link String} containing the script lines that
* completes once the resource was loaded
*/
private static Mono<String> readScript(EncodedResource resource, DataBufferFactory dataBufferFactory,
@Nullable String[] commentPrefixes, @Nullable String separator, @Nullable String blockCommentEndDelimiter) {
return DataBufferUtils.join(DataBufferUtils.read(resource.getResource(), dataBufferFactory, 8192))
.handle((it, sink) -> {
try (InputStream is = it.asInputStream()) {
InputStreamReader in = (resource.getCharset() != null ?
new InputStreamReader(is, resource.getCharset()) : new InputStreamReader(is));
LineNumberReader lnr = new LineNumberReader(in);
String script = readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
sink.next(script);
sink.complete();
}
catch (Exception ex) {
sink.error(ex);
}
finally {
DataBufferUtils.release(it);
}
});
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefix and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with the comment prefix are excluded from the
* results; however, line comments anywhere else &mdash; for example, within
* a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param lineCommentPrefix the prefix that identifies comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
public static String readScript(LineNumberReader lineNumberReader, @Nullable String lineCommentPrefix,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String[] lineCommentPrefixes = (lineCommentPrefix != null) ? new String[] { lineCommentPrefix } : null;
return readScript(lineNumberReader, lineCommentPrefixes, separator, blockCommentEndDelimiter);
}
/**
* Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefixes and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with one of the comment prefixes are excluded
* from the results; however, line comments anywhere else &mdash; for example,
* within a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @param lineCommentPrefixes the prefixes that identify comments in the SQL script
* (typically "--")
* @param separator the statement separator in the SQL script (typically ";")
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @return a {@code String} containing the script lines
* @throws IOException in case of I/O errors
*/
public static String readScript(LineNumberReader lineNumberReader, @Nullable String[] lineCommentPrefixes,
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
String currentStatement = lineNumberReader.readLine();
StringBuilder scriptBuilder = new StringBuilder();
while (currentStatement != null) {
if ((blockCommentEndDelimiter != null && currentStatement.contains(blockCommentEndDelimiter)) ||
(lineCommentPrefixes != null && !startsWithAny(currentStatement, lineCommentPrefixes, 0))) {
if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n');
}
scriptBuilder.append(currentStatement);
}
currentStatement = lineNumberReader.readLine();
}
appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
return scriptBuilder.toString();
}
private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, @Nullable String separator) {
if (separator == null) {
return;
}
String trimmed = separator.trim();
if (trimmed.length() == separator.length()) {
return;
}
// separator ends in whitespace, so we might want to see if the script is trying
// to end the same way
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
scriptBuilder.append(separator.substring(trimmed.length()));
}
}
private static boolean startsWithAny(String script, String[] prefixes, int offset) {
@@ -547,6 +431,204 @@ public abstract class ScriptUtils {
return false;
}
/**
* Does the provided SQL script contain the specified delimiter?
* @param script the SQL script
* @param delim the string delimiting each statement - typically a ';' character
*/
public static boolean containsSqlScriptDelimiters(String script, String delim) {
boolean inLiteral = false;
boolean inEscape = false;
for (int i = 0; i < script.length(); i++) {
char c = script.charAt(i);
if (inEscape) {
inEscape = false;
continue;
}
// MySQL style escapes
if (c == '\\') {
inEscape = true;
continue;
}
if (c == '\'') {
inLiteral = !inLiteral;
}
if (!inLiteral && script.startsWith(delim, i)) {
return true;
}
}
return false;
}
/**
* Execute the given SQL script using default settings for statement
* separators, comment delimiters, and exception handling flags.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource to load the SQL script from; encoded with the
* current platform's default encoding
* @throws ScriptException if an error occurred while executing the SQL script
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String[], String, String, String)
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #DEFAULT_COMMENT_PREFIX
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, Resource resource) throws ScriptException {
return executeSqlScript(connection, new EncodedResource(resource));
}
/**
* Execute the given SQL script using default settings for statement
* separators, comment delimiters, and exception handling flags.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @throws ScriptException if an error occurred while executing the SQL script
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String[], String, String, String)
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #DEFAULT_COMMENT_PREFIX
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException {
return executeSqlScript(connection, resource, DefaultDataBufferFactory.sharedInstance, false, false,
DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
/**
* Execute the given SQL script.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @param continueOnError whether or not to continue without throwing an exception
* in the event of an error
* @param ignoreFailedDrops whether or not to continue in the event of specifically
* an error on a {@code DROP} statement
* @param commentPrefix the prefix that identifies single-line comments in the
* SQL script (typically "--")
* @param separator the script statement separator; defaults to
* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to
* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a
* single statement without a separator
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @throws ScriptException if an error occurred while executing the SQL script
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #FALLBACK_STATEMENT_SEPARATOR
* @see #EOF_STATEMENT_SEPARATOR
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String commentPrefix, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
return executeSqlScript(connection, resource, dataBufferFactory, continueOnError,
ignoreFailedDrops, new String[] { commentPrefix }, separator,
blockCommentStartDelimiter, blockCommentEndDelimiter);
}
/**
* Execute the given SQL script.
* <p>Statement separators and comments will be removed before executing
* individual statements within the supplied script.
* <p><strong>Warning</strong>: this method does <em>not</em> release the
* provided {@link Connection}.
* @param connection the R2DBC connection to use to execute the script; already
* configured and ready to use
* @param resource the resource (potentially associated with a specific encoding)
* to load the SQL script from
* @param continueOnError whether or not to continue without throwing an exception
* in the event of an error
* @param ignoreFailedDrops whether or not to continue in the event of specifically
* an error on a {@code DROP} statement
* @param commentPrefixes the prefixes that identify single-line comments in the
* SQL script (typically "--")
* @param separator the script statement separator; defaults to
* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to
* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a
* single statement without a separator
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter
* @throws ScriptException if an error occurred while executing the SQL script
* @see #DEFAULT_STATEMENT_SEPARATOR
* @see #FALLBACK_STATEMENT_SEPARATOR
* @see #EOF_STATEMENT_SEPARATOR
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String[] commentPrefixes, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL script from " + resource);
}
long startTime = System.currentTimeMillis();
Mono<String> script = readScript(resource, dataBufferFactory, commentPrefixes, separator, blockCommentEndDelimiter)
.onErrorMap(IOException.class, ex -> new CannotReadScriptException(resource, ex));
AtomicInteger statementNumber = new AtomicInteger();
Flux<Void> executeScript = script.flatMapIterable(statement -> {
List<String> statements = new ArrayList<>();
String separatorToUse = separator;
if (separatorToUse == null) {
separatorToUse = DEFAULT_STATEMENT_SEPARATOR;
}
if (!EOF_STATEMENT_SEPARATOR.equals(separatorToUse) && !containsSqlScriptDelimiters(statement, separatorToUse)) {
separatorToUse = FALLBACK_STATEMENT_SEPARATOR;
}
splitSqlScript(resource, statement, separatorToUse, commentPrefixes, blockCommentStartDelimiter,
blockCommentEndDelimiter, statements);
return statements;
}).concatMap(statement -> {
statementNumber.incrementAndGet();
return runStatement(statement, connection, resource, continueOnError, ignoreFailedDrops, statementNumber);
});
if (logger.isDebugEnabled()) {
executeScript = executeScript.doOnComplete(() -> {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
});
}
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
ex -> new UncategorizedScriptException(
"Failed to execute database script from resource [" + resource + "]", ex))
.then();
}
private static Publisher<? extends Void> runStatement(String statement, Connection connection,
EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, AtomicInteger statementNumber) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,11 +17,10 @@
package org.springframework.r2dbc.connection.init;
/**
* Thrown when we cannot determine anything more specific than "something went wrong
* while processing an SQL script": for example, an {@link io.r2dbc.spi.R2dbcException}
* from R2DBC that we cannot pinpoint more precisely.
* Thrown when we cannot determine anything more specific than "something went wrong while
* processing an SQL script": for example, a {@link io.r2dbc.spi.R2dbcException} from
* R2DBC that we cannot pinpoint more precisely.
*
* @author Sam Brannen
* @author Mark Paluch
* @since 5.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -23,7 +23,6 @@ import kotlinx.coroutines.reactive.awaitSingleOrNull
*
* @author Sebastien Deleuze
*/
@Suppress("DEPRECATION")
suspend fun DatabaseClient.GenericExecuteSpec.await() {
then().awaitSingleOrNull()
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -25,7 +25,6 @@ import org.springframework.dao.EmptyResultDataAccessException
*
* @author Sebastien Deleuze
*/
@Suppress("DEPRECATION")
suspend fun <T> RowsFetchSpec<T>.awaitOne(): T {
return one().awaitSingleOrNull() ?: throw EmptyResultDataAccessException(1)
}
@@ -35,7 +34,6 @@ suspend fun <T> RowsFetchSpec<T>.awaitOne(): T {
*
* @author Sebastien Deleuze
*/
@Suppress("DEPRECATION")
suspend fun <T> RowsFetchSpec<T>.awaitOneOrNull(): T? =
one().awaitSingleOrNull()
@@ -44,7 +42,6 @@ suspend fun <T> RowsFetchSpec<T>.awaitOneOrNull(): T? =
*
* @author Sebastien Deleuze
*/
@Suppress("DEPRECATION")
suspend fun <T> RowsFetchSpec<T>.awaitSingle(): T {
return first().awaitSingleOrNull() ?: throw EmptyResultDataAccessException(1)
}
@@ -54,7 +51,6 @@ suspend fun <T> RowsFetchSpec<T>.awaitSingle(): T {
*
* @author Sebastien Deleuze
*/
@Suppress("DEPRECATION")
suspend fun <T> RowsFetchSpec<T>.awaitSingleOrNull(): T? =
first().awaitSingleOrNull()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -24,23 +24,22 @@ import org.springframework.core.io.ClassRelativeResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.r2dbc.core.DatabaseClient;
/**
* Abstract test support for {@link DatabasePopulator}.
*
* @author Dave Syer
* @author Sam Brannen
* @author Oliver Gierke
* @author Mark Paluch
*/
abstract class AbstractDatabasePopulatorTests {
public abstract class AbstractDatabaseInitializationTests {
ClassRelativeResourceLoader resourceLoader = new ClassRelativeResourceLoader(getClass());
ClassRelativeResourceLoader resourceLoader = new ClassRelativeResourceLoader(
getClass());
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
@Test
void scriptWithSingleLineCommentsAndFailedDrop() {
public void scriptWithSingleLineCommentsAndFailedDrop() {
databasePopulator.addScript(resource("db-schema-failed-drop-comments.sql"));
databasePopulator.addScript(resource("db-test-data.sql"));
databasePopulator.setIgnoreFailedDrops(true);
@@ -57,7 +56,7 @@ abstract class AbstractDatabasePopulatorTests {
}
@Test
void scriptWithStandardEscapedLiteral() {
public void scriptWithStandardEscapedLiteral() {
databasePopulator.addScript(defaultSchema());
databasePopulator.addScript(resource("db-test-data-escaped-literal.sql"));
@@ -67,7 +66,7 @@ abstract class AbstractDatabasePopulatorTests {
}
@Test
void scriptWithMySqlEscapedLiteral() {
public void scriptWithMySqlEscapedLiteral() {
databasePopulator.addScript(defaultSchema());
databasePopulator.addScript(resource("db-test-data-mysql-escaped-literal.sql"));
@@ -77,7 +76,7 @@ abstract class AbstractDatabasePopulatorTests {
}
@Test
void scriptWithMultipleStatements() {
public void scriptWithMultipleStatements() {
databasePopulator.addScript(defaultSchema());
databasePopulator.addScript(resource("db-test-data-multiple.sql"));
@@ -87,7 +86,7 @@ abstract class AbstractDatabasePopulatorTests {
}
@Test
void scriptWithMultipleStatementsAndLongSeparator() {
public void scriptWithMultipleStatementsAndLongSeparator() {
databasePopulator.addScript(defaultSchema());
databasePopulator.addScript(resource("db-test-data-endings.sql"));
databasePopulator.setSeparator("@@");
@@ -121,13 +120,15 @@ abstract class AbstractDatabasePopulatorTests {
DatabaseClient client = DatabaseClient.create(connectionFactory);
for (String lastName : lastNames) {
client.sql("select count(0) from users where last_name = :name") //
.bind("name", lastName) //
.map((row, metadata) -> row.get(0)) //
.first() //
.map(number -> ((Number) number).intValue()) //
.as(StepVerifier::create) //
.expectNext(1).as("Did not find user with last name [" + lastName + "].") //
.expectNext(1).as(
"Did not find user with last name [" + lastName + "].") //
.verifyComplete();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -33,11 +33,9 @@ import static org.mockito.BDDMockito.when;
/**
* Unit tests for {@link CompositeDatabasePopulator}.
*
* @author Kazuki Shimizu
* @author Juergen Hoeller
* @author Mark Paluch
*/
class CompositeDatabasePopulatorTests {
public class CompositeDatabasePopulatorTests {
Connection mockedConnection = mock(Connection.class);
@@ -47,13 +45,15 @@ class CompositeDatabasePopulatorTests {
@BeforeEach
void before() {
when(mockedDatabasePopulator1.populate(mockedConnection)).thenReturn(Mono.empty());
when(mockedDatabasePopulator2.populate(mockedConnection)).thenReturn(Mono.empty());
public void before() {
when(mockedDatabasePopulator1.populate(mockedConnection)).thenReturn(
Mono.empty());
when(mockedDatabasePopulator2.populate(mockedConnection)).thenReturn(
Mono.empty());
}
@Test
void addPopulators() {
public void addPopulators() {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2);
@@ -64,7 +64,7 @@ class CompositeDatabasePopulatorTests {
}
@Test
void setPopulatorsWithMultiple() {
public void setPopulatorsWithMultiple() {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.setPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2); // multiple
@@ -75,7 +75,7 @@ class CompositeDatabasePopulatorTests {
}
@Test
void setPopulatorsForOverride() {
public void setPopulatorsForOverride() {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.setPopulators(mockedDatabasePopulator1);
populator.setPopulators(mockedDatabasePopulator2); // override
@@ -87,9 +87,9 @@ class CompositeDatabasePopulatorTests {
}
@Test
void constructWithVarargs() {
CompositeDatabasePopulator populator =
new CompositeDatabasePopulator(mockedDatabasePopulator1, mockedDatabasePopulator2);
public void constructWithVarargs() {
CompositeDatabasePopulator populator = new CompositeDatabasePopulator(
mockedDatabasePopulator1, mockedDatabasePopulator2);
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
@@ -98,7 +98,7 @@ class CompositeDatabasePopulatorTests {
}
@Test
void constructWithCollection() {
public void constructWithCollection() {
Set<DatabasePopulator> populators = new LinkedHashSet<>();
populators.add(mockedDatabasePopulator1);
populators.add(mockedDatabasePopulator2);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -32,7 +32,7 @@ import static org.mockito.BDDMockito.when;
*
* @author Mark Paluch
*/
class ConnectionFactoryInitializerUnitTests {
public class ConnectionFactoryInitializerUnitTests {
AtomicBoolean called = new AtomicBoolean();
@@ -40,11 +40,12 @@ class ConnectionFactoryInitializerUnitTests {
MockConnection connection = MockConnection.builder().build();
MockConnectionFactory connectionFactory = MockConnectionFactory.builder().connection(connection).build();
MockConnectionFactory connectionFactory = MockConnectionFactory.builder().connection(
connection).build();
@Test
void shouldInitializeConnectionFactory() {
public void shouldInitializeConnectionFactory() {
when(populator.populate(connectionFactory)).thenReturn(
Mono.<Void> empty().doOnSubscribe(subscription -> called.set(true)));
@@ -58,7 +59,7 @@ class ConnectionFactoryInitializerUnitTests {
}
@Test
void shouldCleanConnectionFactory() {
public void shouldCleanConnectionFactory() {
when(populator.populate(connectionFactory)).thenReturn(
Mono.<Void> empty().doOnSubscribe(subscription -> called.set(true)));
@@ -66,6 +67,7 @@ class ConnectionFactoryInitializerUnitTests {
initializer.setConnectionFactory(connectionFactory);
initializer.setDatabaseCleaner(populator);
initializer.afterPropertiesSet();
initializer.destroy();
assertThat(called).isTrue();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -20,13 +20,16 @@ import java.util.UUID;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
/**
* Integration tests for {@link DatabasePopulator} using H2.
*
* @author Mark Paluch
*/
class H2DatabasePopulatorIntegrationTests extends AbstractDatabasePopulatorTests {
public class H2DatabasePopulatorIntegrationTests
extends AbstractDatabaseInitializationTests {
UUID databaseName = UUID.randomUUID();
@@ -39,4 +42,20 @@ class H2DatabasePopulatorIntegrationTests extends AbstractDatabasePopulatorTests
return this.connectionFactory;
}
@Test
public void shouldRunScript() {
databasePopulator.addScript(usersSchema());
databasePopulator.addScript(resource("db-test-data-h2.sql"));
// Set statement separator to double newline so that ";" is not
// considered a statement separator within the source code of the
// aliased function 'REVERSE'.
databasePopulator.setSeparator("\n\n");
databasePopulator.populate(connectionFactory).as(
StepVerifier::create).verifyComplete();
assertUsersDatabaseCreated(connectionFactory, "White");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -27,43 +27,47 @@ import static org.mockito.BDDMockito.mock;
/**
* Unit tests for {@link ResourceDatabasePopulator}.
*
* @author Sam Brannen
* @author Mark Paluch
*/
class ResourceDatabasePopulatorUnitTests {
public class ResourceDatabasePopulatorUnitTests {
private static final Resource script1 = mock(Resource.class);
private static final Resource script2 = mock(Resource.class);
private static final Resource script3 = mock(Resource.class);
@Test
void constructWithNullResource() {
assertThatIllegalArgumentException().isThrownBy(() ->
new ResourceDatabasePopulator((Resource) null));
public void constructWithNullResource() {
assertThatIllegalArgumentException().isThrownBy(
() -> new ResourceDatabasePopulator((Resource) null));
}
@Test
void constructWithNullResourceArray() {
assertThatIllegalArgumentException().isThrownBy(() ->
new ResourceDatabasePopulator((Resource[]) null));
public void constructWithNullResourceArray() {
assertThatIllegalArgumentException().isThrownBy(
() -> new ResourceDatabasePopulator((Resource[]) null));
}
@Test
void constructWithResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1);
public void constructWithResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(
script1);
assertThat(databasePopulator.scripts).hasSize(1);
}
@Test
void constructWithMultipleResources() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
public void constructWithMultipleResources() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(
script1, script2);
assertThat(databasePopulator.scripts).hasSize(2);
}
@Test
void constructWithMultipleResourcesAndThenAddScript() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
public void constructWithMultipleResourcesAndThenAddScript() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(
script1, script2);
assertThat(databasePopulator.scripts).hasSize(2);
databasePopulator.addScript(script3);
@@ -71,35 +75,35 @@ class ResourceDatabasePopulatorUnitTests {
}
@Test
void addScriptsWithNullResource() {
public void addScriptsWithNullResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.addScripts((Resource) null));
assertThatIllegalArgumentException().isThrownBy(
() -> databasePopulator.addScripts((Resource) null));
}
@Test
void addScriptsWithNullResourceArray() {
public void addScriptsWithNullResourceArray() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.addScripts((Resource[]) null));
assertThatIllegalArgumentException().isThrownBy(
() -> databasePopulator.addScripts((Resource[]) null));
}
@Test
void setScriptsWithNullResource() {
public void setScriptsWithNullResource() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.setScripts((Resource) null));
assertThatIllegalArgumentException().isThrownBy(
() -> databasePopulator.setScripts((Resource) null));
}
@Test
void setScriptsWithNullResourceArray() {
public void setScriptsWithNullResourceArray() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThatIllegalArgumentException().isThrownBy(() ->
databasePopulator.setScripts((Resource[]) null));
assertThatIllegalArgumentException().isThrownBy(
() -> databasePopulator.setScripts((Resource[]) null));
}
@Test
void setScriptsAndThenAddScript() {
public void setScriptsAndThenAddScript() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
assertThat(databasePopulator.scripts).isEmpty();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,22 +16,17 @@
package org.springframework.r2dbc.connection.init;
import java.util.ArrayList;
import java.util.List;
import org.assertj.core.util.Strings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.support.EncodedResource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.r2dbc.connection.init.ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
import static org.springframework.r2dbc.connection.init.ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
import static org.springframework.r2dbc.connection.init.ScriptUtils.DEFAULT_COMMENT_PREFIXES;
import static org.springframework.r2dbc.connection.init.ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
/**
* Unit tests for {@link ScriptUtils}.
@@ -42,8 +37,6 @@ import static org.springframework.r2dbc.connection.init.ScriptUtils.DEFAULT_STAT
* @author Chris Baldwin
* @author Nicolas Debeissat
* @author Mark Paluch
* @since 5.3
* @see ScriptUtilsIntegrationTests
*/
public class ScriptUtilsUnitTests {
@@ -56,12 +49,14 @@ public class ScriptUtilsUnitTests {
String rawStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String cleanedStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String delimiter = ";";
String script = Strings.join(rawStatement1, rawStatement2, rawStatement3).with(delimiter);
String script = Strings.join(rawStatement1, rawStatement2, rawStatement3).with(
";");
List<String> statements = splitSqlScript(script, delimiter);
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ";", statements);
assertThat(statements).containsExactly(cleanedStatement1, cleanedStatement2, cleanedStatement3);
assertThat(statements).hasSize(3).containsSequence(cleanedStatement1,
cleanedStatement2, cleanedStatement3);
}
@Test
@@ -70,12 +65,13 @@ public class ScriptUtilsUnitTests {
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String delimiter = "\n";
String script = Strings.join(statement1, statement2, statement3).with(delimiter);
String script = Strings.join(statement1, statement2, statement3).with("\n");
List<String> statements = splitSqlScript(script, delimiter);
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, "\n", statements);
assertThat(statements).containsExactly(statement1, statement2, statement3);
assertThat(statements).hasSize(3).containsSequence(statement1, statement2,
statement3);
}
@Test
@@ -83,149 +79,141 @@ public class ScriptUtilsUnitTests {
String statement1 = "do something";
String statement2 = "do something else";
String script = Strings.join(statement1, statement2).with("\n");
char delim = '\n';
String script = statement1 + delim + statement2 + delim;
List<String> statements = splitSqlScript(script, DEFAULT_STATEMENT_SEPARATOR);
List<String> statements = new ArrayList<>();
assertThat(statements).as("stripped but not split statements").containsExactly(script.replace('\n', ' '));
ScriptUtils.splitSqlScript(script, ScriptUtils.DEFAULT_STATEMENT_SEPARATOR,
statements);
assertThat(statements).hasSize(1).contains(script.replace('\n', ' '));
}
@Test // SPR-13218
@Test
public void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() {
String statement1 = "select '1' as \"Dogbert's owner's\" from dual";
String statement2 = "select '2' as \"Dilbert's\" from dual";
String delimiter = ";";
String script = Strings.join(statement1, statement2).with(delimiter);
char delim = ';';
String script = statement1 + delim + statement2 + delim;
List<String> statements = splitSqlScript(script, delimiter);
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ';', statements);
assertThat(statements).containsExactly(statement1, statement2);
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
}
@Test // SPR-11560
public void readAndSplitScriptWithMultipleNewlinesAsSeparator() throws Exception {
@Test
public void readAndSplitScriptWithMultipleNewlinesAsSeparator() {
String script = readScript("db-test-data-multi-newline.sql");
List<String> statements = splitSqlScript(script, "\n\n");
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, "\n\n", statements);
String statement1 = "insert into T_TEST (NAME) values ('Keith')";
String statement2 = "insert into T_TEST (NAME) values ('Dave')";
String statement1 = "insert into users (last_name) values ('Walter')";
String statement2 = "insert into users (last_name) values ('Jesse')";
assertThat(statements).containsExactly(statement1, statement2);
assertThat(statements.size()).as("wrong number of statements").isEqualTo(2);
assertThat(statements.get(0)).as("statement 1 not split correctly").isEqualTo(
statement1);
assertThat(statements.get(1)).as("statement 2 not split correctly").isEqualTo(
statement2);
}
@Test
public void readAndSplitScriptContainingComments() throws Exception {
public void readAndSplitScriptContainingComments() {
String script = readScript("test-data-with-comments.sql");
splitScriptContainingComments(script, DEFAULT_COMMENT_PREFIXES);
splitScriptContainingComments(script);
}
@Test
public void readAndSplitScriptContainingCommentsWithWindowsLineEnding() throws Exception {
String script = readScript("test-data-with-comments.sql").replaceAll("\n", "\r\n");
splitScriptContainingComments(script, DEFAULT_COMMENT_PREFIXES);
public void readAndSplitScriptContainingCommentsWithWindowsLineEnding() {
String script = readScript("test-data-with-comments.sql").replaceAll("\n",
"\r\n");
splitScriptContainingComments(script);
}
@Test
public void readAndSplitScriptContainingCommentsWithMultiplePrefixes() throws Exception {
String script = readScript("test-data-with-multi-prefix-comments.sql");
splitScriptContainingComments(script, "--", "#", "^");
}
private void splitScriptContainingComments(String script, String... commentPrefixes) {
List<String> statements = ScriptUtils.splitSqlScript(null, script, ";", commentPrefixes, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
private void splitScriptContainingComments(String script) {
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ';', statements);
String statement1 = "insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
// Statement 4 addresses the error described in SPR-9982.
String statement4 = "INSERT INTO persons( person_id , name) VALUES( 1 , 'Name' )";
assertThat(statements).containsExactly(statement1, statement2, statement3, statement4);
}
@Test // SPR-10330
public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Exception {
String script = readScript("test-data-with-comments-and-leading-tabs.sql");
List<String> statements = splitSqlScript(script, ";");
String statement1 = "insert into customer (id, name) values (1, 'Sam Brannen')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2013-06-08', 1)";
String statement3 = "insert into orders(id, order_date, customer_id) values (2, '2013-06-08', 1)";
assertThat(statements).containsExactly(statement1, statement2, statement3);
}
@Test // SPR-9531
public void readAndSplitScriptContainingMultiLineComments() throws Exception {
String script = readScript("test-data-with-multi-line-comments.sql");
List<String> statements = splitSqlScript(script, ";");
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )";
assertThat(statements).containsExactly(statement1, statement2);
assertThat(statements).hasSize(4).containsSequence(statement1, statement2,
statement3, statement4);
}
@Test
public void readAndSplitScriptContainingMultiLineNestedComments() throws Exception {
public void readAndSplitScriptContainingCommentsWithLeadingTabs() {
String script = readScript("test-data-with-comments-and-leading-tabs.sql");
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ';', statements);
String statement1 = "insert into customer (id, name) values (1, 'Walter White')";
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2013-06-08', 1)";
String statement3 = "insert into orders(id, order_date, customer_id) values (2, '2013-06-08', 1)";
assertThat(statements).hasSize(3).containsSequence(statement1, statement2,
statement3);
}
@Test
public void readAndSplitScriptContainingMultiLineComments() {
String script = readScript("test-data-with-multi-line-comments.sql");
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ';', statements);
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Walter', 'White')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Jesse' , 'Pinkman' )";
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
}
@Test
public void readAndSplitScriptContainingMultiLineNestedComments() {
String script = readScript("test-data-with-multi-line-nested-comments.sql");
List<String> statements = splitSqlScript(script, ";");
List<String> statements = new ArrayList<>();
ScriptUtils.splitSqlScript(script, ';', statements);
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )";
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Walter', 'White')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Jesse' , 'Pinkman' )";
assertThat(statements).containsExactly(statement1, statement2);
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
}
@ParameterizedTest
@CsvSource(delimiter = '#', value = {
// semicolon
"'select 1\n select '';''' # ; # false",
"'select 1\n select \";\"' # ; # false",
"'select 1; select 2' # ; # true",
// newline
"'select 1; select ''\n''' # '\n' # false",
"'select 1; select \"\n\"' # '\n' # false",
"'select 1\n select 2' # '\n' # true",
// double newline
"'select 1\n select 2' # '\n\n' # false",
"'select 1\n\n select 2' # '\n\n' # true",
// semicolon with MySQL style escapes '\\'
"'insert into users(first, last)\nvalues(''a\\\\'', ''b;'')' # ; # false",
"'insert into users(first, last)\nvalues(''Charles'', ''d\\''Artagnan''); select 1' # ; # true",
// semicolon inside comments
"'-- a;b;c\ninsert into colors(color_num) values(42);' # ; # true",
"'/* a;b;c */\ninsert into colors(color_num) values(42);' # ; # true",
"'-- a;b;c\ninsert into colors(color_num) values(42)' # ; # false",
"'/* a;b;c */\ninsert into colors(color_num) values(42)' # ; # false",
// single quotes inside comments
"'-- What\\''s your favorite color?\ninsert into colors(color_num) values(42);' # ; # true",
"'-- What''s your favorite color?\ninsert into colors(color_num) values(42);' # ; # true",
"'/* What\\''s your favorite color? */\ninsert into colors(color_num) values(42);' # ; # true",
"'/* What''s your favorite color? */\ninsert into colors(color_num) values(42);' # ; # true",
// double quotes inside comments
"'-- double \" quotes\ninsert into colors(color_num) values(42);' # ; # true",
"'-- double \\\" quotes\ninsert into colors(color_num) values(42);' # ; # true",
"'/* double \" quotes */\ninsert into colors(color_num) values(42);' # ; # true",
"'/* double \\\" quotes */\ninsert into colors(color_num) values(42);' # ; # true"
})
public void containsStatementSeparator(String script, String delimiter, boolean expected) {
boolean contains = ScriptUtils.containsStatementSeparator(null, script, delimiter, DEFAULT_COMMENT_PREFIXES,
DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
@Test
public void containsDelimiters() {
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select ';'",
";")).isFalse();
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1; select 2",
";")).isTrue();
assertThat(contains).isEqualTo(expected);
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1; select '\\n\n';",
"\n")).isFalse();
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2",
"\n")).isTrue();
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2",
"\n\n")).isFalse();
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n\n select 2",
"\n\n")).isTrue();
// MySQL style escapes '\\'
assertThat(ScriptUtils.containsSqlScriptDelimiters(
"insert into users(first_name, last_name)\nvalues('a\\\\', 'b;')",
";")).isFalse();
assertThat(ScriptUtils.containsSqlScriptDelimiters(
"insert into users(first_name, last_name)\nvalues('Charles', 'd\\'Artagnan'); select 1;",
";")).isTrue();
}
private String readScript(String path) throws Exception {
EncodedResource resource = new EncodedResource(new ClassPathResource(path, getClass()));
return ScriptUtils.readScript(resource, DefaultDataBufferFactory.sharedInstance, DEFAULT_STATEMENT_SEPARATOR).block();
}
private static List<String> splitSqlScript(String script, String separator) throws ScriptException {
return ScriptUtils.splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIXES, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
private String readScript(String path) {
EncodedResource resource = new EncodedResource(
new ClassPathResource(path, getClass()));
return ScriptUtils.readScript(resource, DefaultDataBufferFactory.sharedInstance).block();
}
}
@@ -0,0 +1 @@
INSERT INTO users(first_name, last_name) values('Walter', 'White');
@@ -1,5 +1,5 @@
insert into T_TEST (NAME)
values ('Keith')
insert into users (last_name)
values ('Walter')
insert into T_TEST (NAME)
values ('Dave')
insert into users (last_name)
values ('Jesse')
@@ -2,7 +2,7 @@
-- x, y, z...
insert into customer (id, name)
values (1, 'Sam Brannen');
values (1, 'Walter White');
-- This is also a comment with a leading tab.
insert into orders(id, order_date, customer_id) values (1, '2013-06-08', 1);
-- This is also a comment with a leading tab, a space, and a tab.
@@ -1,17 +1,17 @@
/* This is a multi line comment
* The next comment line has no text
* The next comment line has no text
* The next comment line starts with a space.
* x, y, z...
* x, y, z...
*/
INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller');
INSERT INTO users(first_name, last_name) VALUES('Walter', 'White');
-- This is also a comment.
/*
/*
* Let's add another comment
* that covers multiple lines
*/INSERT INTO
users(first_name, last_name)
VALUES( 'Sam' -- first_name
, 'Brannen' -- last_name
);--
VALUES( 'Jesse' -- first_name
, 'Pinkman' -- last_name
);--
@@ -5,22 +5,19 @@
* x, y, z...
*/
-- This is a single line comment containing single (') and double quotes (").
INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller');
INSERT INTO users(first_name, last_name) VALUES('Walter', 'White');
-- This is also a comment.
/*-------------------------------------------
-- A fancy multi-line comment that puts
-- A fancy multi-line comments that puts
-- single line comments inside of a multi-line
-- comment block.
Moreover, the block comment end delimiter
appears on a line that can potentially also
be a single-line comment if we weren't
already inside a multi-line comment run.
And here's a line containing single and double quotes (").
-------------------------------------------*/
INSERT INTO
users(first_name, last_name) -- This is a single line comment containing the block-end-comment sequence here */ but it's still a single-line comment
VALUES( 'Sam' -- first_name
, 'Brannen' -- last_name
);--
VALUES( 'Jesse' -- first_name
, 'Pinkman' -- last_name
);--
@@ -1,18 +0,0 @@
-- The next comment line has no text after the '--' prefix.
--
-- The next comment line starts with a space.
-- x, y, z...
insert into customer (id, name)
values (1, 'Rod; Johnson'), (2, 'Adrian Collier');
-- This is also a comment.
insert into orders(id, order_date, customer_id)
values (1, '2008-01-02', 2);
# A comment with a different prefix
insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2);
INSERT INTO persons( person_id--
, name)
^ A comment with yet another different prefix
VALUES( 1 -- person_id
, 'Name' --name
);--
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -130,7 +130,7 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
@Nullable
private PathPatternParser patternParser;
private boolean useSuffixPatternMatch = false;
private boolean useSuffixPatternMatch = true;
private boolean useTrailingSlashPatternMatch = true;
@@ -324,7 +324,7 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p>The default value is {@code false}.
* <p>The default value is {@code true}.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -20,23 +20,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.core.annotation.AliasFor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.I_AM_A_TEAPOT;
import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
/**
@@ -44,34 +41,30 @@ import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.StatusAssertionTests}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
*/
@TestInstance(PER_CLASS)
class StatusAssertionTests {
public class StatusAssertionTests {
private final WebTestClient testClient =
MockMvcWebTestClient.bindToController(new StatusController()).build();
@Test
void statusInt() {
testClient.get().uri("/teaPot").exchange().expectStatus().isEqualTo(I_AM_A_TEAPOT.value());
testClient.get().uri("/created").exchange().expectStatus().isEqualTo(CREATED.value());
testClient.get().uri("/createdWithComposedAnnotation").exchange().expectStatus().isEqualTo(CREATED.value());
testClient.get().uri("/badRequest").exchange().expectStatus().isEqualTo(BAD_REQUEST.value());
testClient.get().uri("/throwsException").exchange().expectStatus().isEqualTo(I_AM_A_TEAPOT.value());
public void testStatusInt() {
testClient.get().uri("/created").exchange().expectStatus().isEqualTo(201);
testClient.get().uri("/createdWithComposedAnnotation").exchange().expectStatus().isEqualTo(201);
testClient.get().uri("/badRequest").exchange().expectStatus().isEqualTo(400);
}
@Test
void httpStatus() {
public void testHttpStatus() {
testClient.get().uri("/created").exchange().expectStatus().isCreated();
testClient.get().uri("/createdWithComposedAnnotation").exchange().expectStatus().isCreated();
testClient.get().uri("/badRequest").exchange().expectStatus().isBadRequest();
}
@Test
void matcher() {
testClient.get().uri("/badRequest").exchange().expectStatus().value(equalTo(BAD_REQUEST.value()));
public void testMatcher() {
testClient.get().uri("/badRequest").exchange().expectStatus().value(equalTo(400));
}
@@ -87,41 +80,26 @@ class StatusAssertionTests {
HttpStatus status() default INTERNAL_SERVER_ERROR;
}
@RestController
@ResponseStatus(I_AM_A_TEAPOT)
@Controller
private static class StatusController {
@RequestMapping("/teaPot")
void teaPot() {
}
@RequestMapping("/created")
@ResponseStatus(CREATED)
void created(){
public @ResponseBody void created(){
}
@Get(path = "/createdWithComposedAnnotation", status = CREATED)
void createdWithComposedAnnotation() {
public @ResponseBody void createdWithComposedAnnotation() {
}
@RequestMapping("/badRequest")
@ResponseStatus(code = BAD_REQUEST, reason = "Expired token")
void badRequest(){
public @ResponseBody void badRequest(){
}
@RequestMapping("/notImplemented")
@ResponseStatus(NOT_IMPLEMENTED)
void notImplemented(){
}
@RequestMapping("/throwsException")
@ResponseStatus(NOT_IMPLEMENTED)
void throwsException() {
throw new IllegalStateException();
}
@ExceptionHandler
void exceptionHandler(IllegalStateException ex) {
public @ResponseBody void notImplemented(){
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -20,23 +20,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.core.annotation.AliasFor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.I_AM_A_TEAPOT;
import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -48,40 +45,39 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal
* @author Rossen Stoyanchev
* @author Sam Brannen
*/
@TestInstance(PER_CLASS)
class StatusAssertionTests {
public class StatusAssertionTests {
private final MockMvc mockMvc = standaloneSetup(new StatusController()).build();
@Test
void httpStatus() throws Exception {
public void testStatusInt() throws Exception {
this.mockMvc.perform(get("/created")).andExpect(status().is(201));
this.mockMvc.perform(get("/createdWithComposedAnnotation")).andExpect(status().is(201));
this.mockMvc.perform(get("/badRequest")).andExpect(status().is(400));
}
@Test
public void testHttpStatus() throws Exception {
this.mockMvc.perform(get("/created")).andExpect(status().isCreated());
this.mockMvc.perform(get("/createdWithComposedAnnotation")).andExpect(status().isCreated());
this.mockMvc.perform(get("/badRequest")).andExpect(status().isBadRequest());
}
@Test
void statusCode() throws Exception {
this.mockMvc.perform(get("/teaPot")).andExpect(status().is(I_AM_A_TEAPOT.value()));
this.mockMvc.perform(get("/created")).andExpect(status().is(CREATED.value()));
this.mockMvc.perform(get("/createdWithComposedAnnotation")).andExpect(status().is(CREATED.value()));
this.mockMvc.perform(get("/badRequest")).andExpect(status().is(BAD_REQUEST.value()));
this.mockMvc.perform(get("/throwsException")).andExpect(status().is(I_AM_A_TEAPOT.value()));
public void testMatcher() throws Exception {
this.mockMvc.perform(get("/badRequest")).andExpect(status().is(equalTo(400)));
}
@Test
void statusCodeWithMatcher() throws Exception {
this.mockMvc.perform(get("/badRequest")).andExpect(status().is(equalTo(BAD_REQUEST.value())));
}
@Test
void reason() throws Exception {
public void testReasonEqualTo() throws Exception {
this.mockMvc.perform(get("/badRequest")).andExpect(status().reason("Expired token"));
// Hamcrest matchers...
this.mockMvc.perform(get("/badRequest")).andExpect(status().reason(equalTo("Expired token")));
}
@Test
void reasonWithMatcher() throws Exception {
this.mockMvc.perform(get("/badRequest")).andExpect(status().reason(equalTo("Expired token")));
public void testReasonMatcher() throws Exception {
this.mockMvc.perform(get("/badRequest")).andExpect(status().reason(endsWith("token")));
}
@@ -98,41 +94,26 @@ class StatusAssertionTests {
HttpStatus status() default INTERNAL_SERVER_ERROR;
}
@RestController
@ResponseStatus(I_AM_A_TEAPOT)
@Controller
private static class StatusController {
@RequestMapping("/teaPot")
void teaPot() {
}
@RequestMapping("/created")
@ResponseStatus(CREATED)
void created() {
public @ResponseBody void created(){
}
@Get(path = "/createdWithComposedAnnotation", status = CREATED)
void createdWithComposedAnnotation() {
public @ResponseBody void createdWithComposedAnnotation() {
}
@RequestMapping("/badRequest")
@ResponseStatus(code = BAD_REQUEST, reason = "Expired token")
void badRequest() {
public @ResponseBody void badRequest(){
}
@RequestMapping("/notImplemented")
@ResponseStatus(NOT_IMPLEMENTED)
void notImplemented() {
}
@RequestMapping("/throwsException")
@ResponseStatus(NOT_IMPLEMENTED)
void throwsException() {
throw new IllegalStateException();
}
@ExceptionHandler
void exceptionHandler(IllegalStateException ex) {
public @ResponseBody void notImplemented(){
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -29,13 +29,10 @@ import org.springframework.transaction.TransactionDefinition;
/**
* Describes a transaction attribute on an individual method or on a class.
*
* <p>When this annotation is declared at the class level, it applies as a default
* to all methods of the declaring class and its subclasses. Note that it does not
* apply to ancestor classes up the class hierarchy; inherited methods need to be
* locally redeclared in order to participate in a subclass-level annotation. For
* details on method visibility constraints, consult the
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction">Transaction Management</a>
* section of the reference manual.
* <p>At the class level, this annotation applies as a default to all methods of
* the declaring class and its subclasses. Note that it does not apply to ancestor
* classes up the class hierarchy; methods need to be locally redeclared in order
* to participate in a subclass-level annotation.
*
* <p>This annotation type is generally directly comparable to Spring's
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
@@ -49,14 +46,14 @@ import org.springframework.transaction.TransactionDefinition;
* consult the {@link org.springframework.transaction.TransactionDefinition} and
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
*
* <p>This annotation commonly works with thread-bound transactions managed by a
* <p>This annotation commonly works with thread-bound transactions managed by
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
* transaction to all data access operations within the current execution thread.
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
*
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
* by a {@link org.springframework.transaction.ReactiveTransactionManager} which
* uses the Reactor context instead of thread-local variables. As a consequence,
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
* uses the Reactor context instead of thread-local attributes. As a consequence,
* all participating data access operations need to execute within the same
* Reactor context in the same reactive pipeline.
*
@@ -97,12 +94,12 @@ public @interface Transactional {
String transactionManager() default "";
/**
* Defines zero (0) or more transaction labels.
* <p>Labels may be used to describe a transaction, and they can be evaluated
* by individual transaction managers. Labels may serve a solely descriptive
* purpose or map to pre-defined transaction manager-specific options.
* <p>See the documentation of the actual transaction manager implementation
* for details on how it evaluates transaction labels.
* Defines zero (0) or more transaction labels. Labels may be used to
* describe a transaction and they can be evaluated by individual transaction
* manager. Labels may serve a solely descriptive purpose or map to
* pre-defined transaction manager-specific options.
* <p>See the description of the actual transaction manager implementation
* how it evaluates transaction labels.
* @since 5.3
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -21,11 +21,10 @@ import java.util.function.Consumer;
import org.springframework.transaction.support.TransactionSynchronization;
/**
* The phase in which a transactional event listener applies.
* The phase at which a transactional event listener applies.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.2
* @see TransactionalEventListener#phase()
* @see TransactionalApplicationListener#getTransactionPhase()
@@ -34,44 +33,35 @@ import org.springframework.transaction.support.TransactionSynchronization;
public enum TransactionPhase {
/**
* Handle the event before transaction commit.
* Fire the event before transaction commit.
* @see TransactionSynchronization#beforeCommit(boolean)
*/
BEFORE_COMMIT,
/**
* Handle the event after the commit has completed successfully.
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and therefore
* executes in the same sequence of events as {@code AFTER_COMPLETION}
* Fire the event after the commit has completed successfully.
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and
* therefore executes in the same after-completion sequence of events,
* (and not in {@link TransactionSynchronization#afterCommit()}).
* <p>Interactions with the underlying transactional resource will not be
* committed in this phase. See
* {@link TransactionSynchronization#afterCompletion(int)} for details.
* @see TransactionSynchronization#afterCompletion(int)
* @see TransactionSynchronization#STATUS_COMMITTED
*/
AFTER_COMMIT,
/**
* Handle the event if the transaction has rolled back.
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and therefore
* executes in the same sequence of events as {@code AFTER_COMPLETION}.
* <p>Interactions with the underlying transactional resource will not be
* committed in this phase. See
* {@link TransactionSynchronization#afterCompletion(int)} for details.
* Fire the event if the transaction has rolled back.
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and
* therefore executes in the same after-completion sequence of events.
* @see TransactionSynchronization#afterCompletion(int)
* @see TransactionSynchronization#STATUS_ROLLED_BACK
*/
AFTER_ROLLBACK,
/**
* Handle the event after the transaction has completed.
* Fire the event after the transaction has completed.
* <p>For more fine-grained events, use {@link #AFTER_COMMIT} or
* {@link #AFTER_ROLLBACK} to intercept transaction commit
* or rollback, respectively.
* <p>Interactions with the underlying transactional resource will not be
* committed in this phase. See
* {@link TransactionSynchronization#afterCompletion(int)} for details.
* @see TransactionSynchronization#afterCompletion(int)
*/
AFTER_COMPLETION
@@ -33,10 +33,9 @@ import org.springframework.lang.Nullable;
* transaction completion.
*
* <p><b>NOTE: Transactional event listeners only work with thread-bound transactions
* managed by a {@link org.springframework.transaction.PlatformTransactionManager
* PlatformTransactionManager}.</b> A reactive transaction managed by a
* {@link org.springframework.transaction.ReactiveTransactionManager ReactiveTransactionManager}
* uses the Reactor context instead of thread-local variables, so from the perspective of
* managed by {@link org.springframework.transaction.PlatformTransactionManager}.</b>
* A reactive transaction managed by {@link org.springframework.transaction.ReactiveTransactionManager}
* uses the Reactor context instead of thread-local attributes, so from the perspective of
* an event listener, there is no compatible active transaction that it can participate in.
*
* @author Juergen Hoeller
@@ -27,34 +27,22 @@ import org.springframework.core.annotation.AliasFor;
/**
* An {@link EventListener} that is invoked according to a {@link TransactionPhase}.
* This is an annotation-based equivalent of {@link TransactionalApplicationListener}.
* This is an an annotation-based equivalent of {@link TransactionalApplicationListener}.
*
* <p>If the event is not published within an active transaction, the event is discarded
* unless the {@link #fallbackExecution} flag is explicitly set. If a transaction is
* running, the event is handled according to its {@code TransactionPhase}.
* running, the event is processed according to its {@code TransactionPhase}.
*
* <p>Adding {@link org.springframework.core.annotation.Order @Order} to your annotated
* method allows you to prioritize that listener amongst other listeners running before
* or after transaction completion.
*
* <p><b>NOTE: Transactional event listeners only work with thread-bound transactions
* managed by a {@link org.springframework.transaction.PlatformTransactionManager
* PlatformTransactionManager}.</b> A reactive transaction managed by a
* {@link org.springframework.transaction.ReactiveTransactionManager ReactiveTransactionManager}
* uses the Reactor context instead of thread-local variables, so from the perspective of
* managed by {@link org.springframework.transaction.PlatformTransactionManager}.</b>
* A reactive transaction managed by {@link org.springframework.transaction.ReactiveTransactionManager}
* uses the Reactor context instead of thread-local attributes, so from the perspective of
* an event listener, there is no compatible active transaction that it can participate in.
*
* <p><strong>WARNING:</strong> if the {@code TransactionPhase} is set to
* {@link TransactionPhase#AFTER_COMMIT AFTER_COMMIT} (the default),
* {@link TransactionPhase#AFTER_ROLLBACK AFTER_ROLLBACK}, or
* {@link TransactionPhase#AFTER_COMPLETION AFTER_COMPLETION}, the transaction will
* have been committed or rolled back already, but the transactional resources might
* still be active and accessible. As a consequence, any data access code triggered
* at this point will still "participate" in the original transaction, but changes
* will not be committed to the transactional resource. See
* {@link org.springframework.transaction.support.TransactionSynchronization#afterCompletion(int)
* TransactionSynchronization.afterCompletion(int)} for details.
*
* @author Stephane Nicoll
* @author Sam Brannen
* @author Oliver Drotbohm
@@ -77,7 +65,7 @@ public @interface TransactionalEventListener {
TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
/**
* Whether the event should be handled if no transaction is running.
* Whether the event should be processed if no transaction is running.
*/
boolean fallbackExecution() default false;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -878,7 +878,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
return ReactiveFlowKt.asFlow(publisher);
}
@SuppressWarnings({"unchecked", "deprecation"})
@SuppressWarnings("unchecked")
@Nullable
private static Object awaitSingleOrNull(Publisher<?> publisher, Object continuation) {
return AwaitKt.awaitSingleOrNull(publisher, (Continuation<Object>) continuation);
@@ -557,7 +557,7 @@ public enum HttpStatus {
*/
@Nullable
public static HttpStatus resolve(int statusCode) {
// Use cached VALUES instead of values() to prevent array allocation.
// used cached VALUES instead of values() to prevent array allocation
for (HttpStatus status : VALUES) {
if (status.value == statusCode) {
return status;
@@ -317,16 +317,6 @@ public class ResponseEntity<T> extends HttpEntity<T> {
return status(HttpStatus.UNPROCESSABLE_ENTITY);
}
/**
* Create a builder with an
* {@linkplain HttpStatus#INTERNAL_SERVER_ERROR INTERNAL_SERVER_ERROR} status.
* @return the created builder
* @since 5.3.8
*/
public static BodyBuilder internalServerError() {
return status(HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Defines a builder that adds headers to the response entity.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -131,12 +131,7 @@ public final class MultipartBodyBuilder {
Part partObject = (Part) part;
PartBuilder builder = asyncPart(name, partObject.content(), DataBuffer.class);
if (!partObject.headers().isEmpty()) {
builder.headers(headers -> {
headers.putAll(partObject.headers());
String filename = headers.getContentDisposition().getFilename();
// reset to parameter name
headers.setContentDispositionFormData(name, filename);
});
builder.headers(headers -> headers.putAll(partObject.headers()));
}
if (contentType != null) {
builder.contentType(contentType);
@@ -16,8 +16,6 @@
package org.springframework.http.client.reactive;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.function.BiFunction;
@@ -50,7 +48,7 @@ import org.springframework.util.Assert;
* @since 5.3
* @see <a href="https://hc.apache.org/index.html">Apache HttpComponents</a>
*/
public class HttpComponentsClientHttpConnector implements ClientHttpConnector, Closeable {
public class HttpComponentsClientHttpConnector implements ClientHttpConnector {
private final CloseableHttpAsyncClient client;
@@ -128,10 +126,6 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
});
}
@Override
public void close() throws IOException {
this.client.close();
}
private static class MonoFutureCallbackAdapter
implements FutureCallback<Message<HttpResponse, Publisher<ByteBuffer>>> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 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.
@@ -119,7 +119,7 @@ public final class ServerSentEvent<T> {
}
/**
* Return a builder for a {@code SseEvent}, populated with the given {@linkplain #data() data}.
* Return a builder for a {@code SseEvent}, populated with the give {@linkplain #data() data}.
* @param <T> the type of data that this event contains
* @return the builder
*/
@@ -59,8 +59,8 @@ abstract class FileStorage {
}
/**
* Create a new {@code FileStorage} based on a temporary directory.
* @param scheduler the scheduler to use for blocking operations
* Create a new {@code FileStorage} based a on a temporary directory.
* @param scheduler scheduler to use for blocking operations
*/
public static FileStorage tempDirectory(Supplier<Scheduler> scheduler) {
return new TempFileStorage(scheduler);
@@ -470,7 +470,6 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
}
@Override
@SuppressWarnings("resource")
public Flux<DataBuffer> content() {
return DataBufferUtils.readInputStream(
getStorage()::getInputStream, DefaultDataBufferFactory.sharedInstance, 4096);
@@ -221,7 +221,6 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
public void jackson2JsonEncoder(Encoder<?> encoder) {
this.jackson2JsonEncoder = encoder;
initObjectWriters();
initTypedWriters();
}
@Override
@@ -234,7 +233,6 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
public void jackson2SmileEncoder(Encoder<?> encoder) {
this.jackson2SmileEncoder = encoder;
initObjectWriters();
initTypedWriters();
}
@Override
@@ -105,6 +105,8 @@ import org.springframework.util.xml.StaxUtils;
*/
public class Jackson2ObjectMapperBuilder {
private static volatile boolean kotlinWarningLogged;
private final Log logger = HttpLogging.forLogName(getClass());
private final Map<Class<?>, Class<?>> mixIns = new LinkedHashMap<>();
@@ -861,7 +863,11 @@ public class Jackson2ObjectMapperBuilder {
modulesToRegister.set(kotlinModule.getTypeId(), kotlinModule);
}
catch (ClassNotFoundException ex) {
// jackson-module-kotlin not available
if (!kotlinWarningLogged) {
kotlinWarningLogged = true;
logger.warn("For Jackson Kotlin classes support please add " +
"\"com.fasterxml.jackson.module:jackson-module-kotlin\" to the classpath");
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2016 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.
@@ -25,16 +25,11 @@ import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* Annotation to indicate that a method parameter is bound to an HTTP cookie.
* Annotation which indicates that a method parameter should be bound to an HTTP cookie.
*
* <p>The method parameter may be declared as type {@link javax.servlet.http.Cookie}
* or as cookie value type (String, int, etc.).
*
* <p>Note that with spring-webmvc 5.3.x and earlier, the cookie value is URL
* decoded. This will be changed in 6.0 but in the meantime, applications can
* also declare parameters of type {@link javax.servlet.http.Cookie} to access
* the raw value.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -80,16 +80,16 @@ public @interface CrossOrigin {
/**
* A list of origins for which cross-origin requests are allowed. Please,
* see {@link CorsConfiguration#setAllowedOrigins(List)} for details.
* <p>By default all origins are allowed unless {@link #originPatterns} is
* <p>By default all origins are allowed unless {@code originPatterns} is
* also set in which case {@code originPatterns} is used instead.
*/
@AliasFor("value")
String[] origins() default {};
/**
* Alternative to {@link #origins} that supports more flexible origin
* patterns. Please, see {@link CorsConfiguration#setAllowedOriginPatterns(List)}
* for details.
* Alternative to {@link #origins()} that supports origins declared via
* wildcard patterns. Please, see
* @link CorsConfiguration#setAllowedOriginPatterns(List)} for details.
* <p>By default this is not set.
* @since 5.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2017 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.
@@ -45,9 +45,8 @@ import org.springframework.http.HttpStatus;
* a return type and avoid the use of {@code @ResponseStatus} altogether.
*
* <p>Note that a controller class may also be annotated with
* {@code @ResponseStatus} which is then inherited by all {@code @RequestMapping}
* and {@code @ExceptionHandler} methods in that class and its subclasses unless
* overridden by a local {@code @ResponseStatus} declaration on the method.
* {@code @ResponseStatus} and is then inherited by all {@code @RequestMapping}
* methods.
*
* @author Arjen Poutsma
* @author Sam Brannen
@@ -79,8 +78,6 @@ public @interface ResponseStatus {
/**
* The <em>reason</em> to be used for the response.
* <p>Defaults to an empty string which will be ignored. Set the reason to a
* non-empty value to have it used for the response.
* @see javax.servlet.http.HttpServletResponse#sendError(int, String)
*/
String reason() default "";
@@ -88,7 +88,7 @@ public abstract class SpringBeanAutowiringSupport {
if (logger.isWarnEnabled()) {
logger.warn("Current WebApplicationContext is not available for processing of " +
ClassUtils.getShortName(target.getClass()) + ": " +
"Make sure this class gets constructed in a Spring web application after the " +
"Make sure this class gets constructed in a Spring web application after the" +
"Spring WebApplicationContext has been initialized. Proceeding without injection.");
}
}
@@ -22,9 +22,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -57,13 +55,16 @@ public class CorsConfiguration {
/** Wildcard representing <em>all</em> origins, methods, or headers. */
public static final String ALL = "*";
private static final List<String> ALL_LIST = Collections.singletonList(ALL);
private static final List<String> ALL_LIST = Collections.unmodifiableList(
Collections.singletonList(ALL));
private static final OriginPattern ALL_PATTERN = new OriginPattern("*");
private static final List<OriginPattern> ALL_PATTERN_LIST = Collections.singletonList(ALL_PATTERN);
private static final List<OriginPattern> ALL_PATTERN_LIST = Collections.unmodifiableList(
Collections.singletonList(ALL_PATTERN));
private static final List<String> DEFAULT_PERMIT_ALL = Collections.singletonList(ALL);
private static final List<String> DEFAULT_PERMIT_ALL = Collections.unmodifiableList(
Collections.singletonList(ALL));
private static final List<HttpMethod> DEFAULT_METHODS = Collections.unmodifiableList(
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD));
@@ -136,13 +137,13 @@ public class CorsConfiguration {
* However an instance of this class is often initialized further, e.g. for
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
*/
public void setAllowedOrigins(@Nullable List<String> origins) {
this.allowedOrigins = (origins == null ? null :
origins.stream().filter(Objects::nonNull).map(this::trimTrailingSlash).collect(Collectors.toList()));
public void setAllowedOrigins(@Nullable List<String> allowedOrigins) {
this.allowedOrigins = (allowedOrigins != null ?
allowedOrigins.stream().map(this::trimTrailingSlash).collect(Collectors.toList()) : null);
}
private String trimTrailingSlash(String origin) {
return (origin.endsWith("/") ? origin.substring(0, origin.length() - 1) : origin);
return origin.endsWith("/") ? origin.substring(0, origin.length() - 1) : origin;
}
/**
@@ -156,10 +157,7 @@ public class CorsConfiguration {
/**
* Variant of {@link #setAllowedOrigins} for adding one origin at a time.
*/
public void addAllowedOrigin(@Nullable String origin) {
if (origin == null) {
return;
}
public void addAllowedOrigin(String origin) {
if (this.allowedOrigins == null) {
this.allowedOrigins = new ArrayList<>(4);
}
@@ -171,22 +169,14 @@ public class CorsConfiguration {
}
/**
* Alternative to {@link #setAllowedOrigins} that supports more flexible
* origins patterns with "*" anywhere in the host name in addition to port
* lists. Examples:
* <ul>
* <li>{@literal https://*.domain1.com} -- domains ending with domain1.com
* <li>{@literal https://*.domain1.com:[8080,8081]} -- domains ending with
* domain1.com on port 8080 or port 8081
* <li>{@literal https://*.domain1.com:[*]} -- domains ending with
* domain1.com on any port, including the default port
* </ul>
* <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
* only supports "*" and cannot be used with {@code allowCredentials}, when
* an allowedOriginPattern is matched, the {@code Access-Control-Allow-Origin}
* response header is set to the matched origin and not to {@code "*"} nor
* to the pattern. Therefore allowedOriginPatterns can be used in combination
* with {@link #setAllowCredentials} set to {@code true}.
* Alternative to {@link #setAllowedOrigins} that supports origins declared
* via wildcard patterns. In contrast to {@link #setAllowedOrigins allowedOrigins}
* which does support the special value {@code "*"}, this property allows
* more flexible patterns, e.g. {@code "https://*.domain1.com"}. Furthermore
* it always sets the {@code Access-Control-Allow-Origin} response header to
* the matched origin and never to {@code "*"}, nor to any other pattern, and
* therefore can be used in combination with {@link #setAllowCredentials}
* set to {@code true}.
* <p>By default this is not set.
* @since 5.3
*/
@@ -221,10 +211,7 @@ public class CorsConfiguration {
* Variant of {@link #setAllowedOriginPatterns} for adding one origin at a time.
* @since 5.3
*/
public void addAllowedOriginPattern(@Nullable String originPattern) {
if (originPattern == null) {
return;
}
public void addAllowedOriginPattern(String originPattern) {
if (this.allowedOriginPatterns == null) {
this.allowedOriginPatterns = new ArrayList<>(4);
}
@@ -660,32 +647,18 @@ public class CorsConfiguration {
*/
private static class OriginPattern {
private static final Pattern PORTS_PATTERN = Pattern.compile("(.*):\\[(\\*|\\d+(,\\d+)*)]");
private final String declaredPattern;
private final Pattern pattern;
OriginPattern(String declaredPattern) {
this.declaredPattern = declaredPattern;
this.pattern = initPattern(declaredPattern);
this.pattern = toPattern(declaredPattern);
}
private static Pattern initPattern(String patternValue) {
String portList = null;
Matcher matcher = PORTS_PATTERN.matcher(patternValue);
if (matcher.matches()) {
patternValue = matcher.group(1);
portList = matcher.group(2);
}
private static Pattern toPattern(String patternValue) {
patternValue = "\\Q" + patternValue + "\\E";
patternValue = patternValue.replace("*", "\\E.*\\Q");
if (portList != null) {
patternValue += (portList.equals(ALL) ? "(:\\d+)?" : ":(" + portList.replace(',', '|') + ")");
}
return Pattern.compile(patternValue);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 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.
@@ -118,8 +118,7 @@ public class InMemoryWebSessionStore implements WebSessionStore {
this.expiredSessionChecker.checkIfNecessary(now);
return Mono.<WebSession>fromSupplier(() -> new InMemoryWebSession(now))
.subscribeOn(Schedulers.boundedElastic())
.publishOn(Schedulers.parallel());
.subscribeOn(Schedulers.boundedElastic());
}
@Override
@@ -85,7 +85,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
private static final String PORT_PATTERN = "(\\{[^}]+}?|[^/?#]*)";
private static final String PORT_PATTERN = "(.[^/?#]*(?:\\{[^/]+?})?)";
private static final String PATH_PATTERN = "([^?#]*)";

Some files were not shown because too many files have changed in this diff Show More