DATAKV-115 - Make derived finder execution thread-safe.

We now make sure separate SpelExpression and EvaluationContext for each and every derived finder execution to assert the expression context cannot be accidentally overridden.

Original pull request: #16.
This commit is contained in:
Christoph Strobl
2015-11-02 13:26:24 +01:00
committed by Oliver Gierke
parent 3c52aff26c
commit e22ee22e58
6 changed files with 175 additions and 37 deletions

View File

@@ -16,7 +16,6 @@
package org.springframework.data.keyvalue.core;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
@@ -33,14 +32,12 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
@@ -77,8 +74,8 @@ public class SpelQueryEngineUnitTests {
doReturn(people).when(adapter).getAllOf(anyString());
assertThat((Collection<Person>) engine.execute(createQueryForMethodWithArgs("findByFirstname", "bob"), null, -1, -1,
anyString()), contains(BOB_WITH_FIRSTNAME));
assertThat((Collection<Person>) engine.execute(createQueryForMethodWithArgs("findByFirstname", "bob"), null, -1,
-1, anyString()), contains(BOB_WITH_FIRSTNAME));
}
/**
@@ -92,7 +89,7 @@ public class SpelQueryEngineUnitTests {
assertThat(engine.count(createQueryForMethodWithArgs("findByFirstname", "bob"), anyString()), is(1L));
}
private static SpelExpression createQueryForMethodWithArgs(String methodName, Object... args) throws Exception {
private static SpelCriteria createQueryForMethodWithArgs(String methodName, Object... args) throws Exception {
List<Class<?>> types = new ArrayList<Class<?>>(args.length);
@@ -105,13 +102,10 @@ public class SpelQueryEngineUnitTests {
doReturn(method.getReturnType()).when(metadata).getReturnedDomainClass(method);
PartTree partTree = new PartTree(method.getName(), method.getReturnType());
SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor(
new QueryMethod(method, metadata, new SpelAwareProxyProjectionFactory()).getParameters(), args));
SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor(new QueryMethod(method,
metadata, new SpelAwareProxyProjectionFactory()).getParameters(), args));
KeyValueQuery<SpelExpression> query = creator.createQuery();
query.getCritieria().setEvaluationContext(new StandardEvaluationContext(args));
return query.getCritieria();
return new SpelCriteria(creator.createQuery().getCritieria(), new StandardEvaluationContext(args));
}
static interface PersonRepository {

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2015-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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.repository.query;
import static org.hamcrest.core.IsNot.*;
import static org.hamcrest.core.IsSame.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.keyvalue.Person;
import org.springframework.data.keyvalue.core.KeyValueOperations;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
import org.springframework.data.repository.query.QueryMethod;
/**
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class KeyValuePartTreeQueryUnitTests {
@Mock KeyValueOperations kvOpsMock;
@Mock RepositoryMetadata metadataMock;
@Mock ProjectionFactory projectionFactoryMock;
/**
* @see DATAKV-115
*/
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void spelExpressionAndContextShouldNotBeReused() throws NoSuchMethodException, SecurityException {
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
when(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);
QueryMethod qm = new QueryMethod(Repo.class.getMethod("findByFirstname", String.class), metadataMock,
projectionFactoryMock);
KeyValuePartTreeQuery query = new KeyValuePartTreeQuery(qm, DefaultEvaluationContextProvider.INSTANCE, kvOpsMock,
SpelQueryCreator.class);
Object[] args = new Object[] { "foo" };
assertThat(query.prepareQuery(args).getCritieria(), not(sameInstance(query.prepareQuery(args).getCritieria())));
}
static interface Repo {
List<Person> findByFirstname(String firstname);
}
}