DATACMNS-1233 - Allow CDI Repositories to be composed of an arbitrary number of implementation classes.

We now support repository fragments for repositories exported through CDI.

Original pull request: #272.
This commit is contained in:
Mark Paluch
2018-02-07 14:36:21 +01:00
committed by Oliver Gierke
parent 919090bf20
commit 0a3b71f0a1
22 changed files with 1006 additions and 214 deletions

View File

@@ -0,0 +1,26 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
public interface AnotherFragmentInterface {
int getPriority();
int getShadowed();
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
class AnotherFragmentInterfaceImpl implements AnotherFragmentInterface {
@Override
public int getPriority() {
return 2;
}
@Override
public int getShadowed() {
return 2;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 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.
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository.cdi;
public class AnotherRepositoryImpl implements AnotherRepositoryCustom {
/**
* @author Mark Paluch
*/
class AnotherRepositoryImpl implements AnotherRepositoryCustom {
@Override
public int returnZero() {

View File

@@ -0,0 +1,58 @@
/*
* Copyright 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.
* 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.repository.cdi;
import static org.assertj.core.api.Assertions.*;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.repository.cdi.isolated.IsolatedComposedRepository;
/**
* CDI integration tests for {@link CdiRepositoryConfiguration}.
*
* @author Mark Paluch
*/
public class CdiConfigurationIntegrationTests {
private static SeContainer container;
@BeforeClass
public static void setUp() {
container = SeContainerInitializer.newInstance() //
.disableDiscovery() //
.addPackages(IsolatedComposedRepository.class) //
.initialize();
}
@Test // DATACMNS-1233
public void shouldApplyImplementationPostfix() {
IsolatedComposedRepository repository = container.select(IsolatedComposedRepository.class).get();
assertThat(repository.getPriority()).isEqualTo(42);
}
@AfterClass
public static void tearDown() {
container.close();
}
}

View File

@@ -155,14 +155,14 @@ public class CdiRepositoryBeanUnitTests {
verify(detector).detectCustomImplementation( //
eq("CdiRepositoryBeanUnitTests.SampleRepositoryImpl"), //
eq("namedRepositoryImpl"), //
eq("CdiRepositoryBeanUnitTests.SampleRepositoryImpl"), //
anySet(), //
anySet(), //
Mockito.any(Function.class) //
);
}
@Test // DATACMNS-1255
@Test // DATACMNS-1233
public void appliesRepositoryConfiguration() {
DummyCdiRepositoryBean<SampleRepository> bean = new DummyCdiRepositoryBean<SampleRepository>(NO_ANNOTATIONS,

View File

@@ -0,0 +1,31 @@
/*
* Copyright 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.
* 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.repository.cdi;
import java.io.Serializable;
import org.springframework.data.repository.Repository;
/**
* @author Mark Paluch
*/
public interface ComposedRepository
extends Repository<Object, Serializable>, FragmentInterface, AnotherFragmentInterface {
// duplicate method shadowed by AnotherFragmentInterfaceImpl. The legacy custom implementation comes last, after all
// other fragments.
int getShadowed();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
public interface ComposedRepositoryCustom {
int returnFourtyTwo();
// duplicate method shadowed by AnotherFragmentInterfaceImpl. The legacy custom implementation comes last, after all
// other
// fragments.
int getShadowed();
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
class ComposedRepositoryImpl implements ComposedRepositoryCustom {
@Override
public int returnFourtyTwo() {
return 42;
}
@Override
public int getShadowed() {
return 1;
}
}

View File

@@ -24,16 +24,15 @@ import java.util.Optional;
import java.util.Set;
import javax.enterprise.context.NormalScope;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.BeanManager;
import org.apache.webbeans.context.AbstractContext;
import org.apache.webbeans.context.creational.BeanInstanceBag;
import org.mockito.Mockito;
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
import org.springframework.data.repository.core.support.DummyRepositoryFactory;
/**
* Dummy extension of {@link CdiRepositoryExtensionSupport} to allow integration tests. Will create mocks for repository
@@ -74,9 +73,12 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport {
}
@Override
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType,
Optional<Object> customImplementation) {
return Mockito.mock(repositoryType);
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
T mock = Mockito.mock(repositoryType);
DummyRepositoryFactory repositoryFactory = new DummyRepositoryFactory(mock);
return create(repositoryFactory, repositoryType, getRepositoryFragments(repositoryType));
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
public interface FragmentInterface {
int getPriority();
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 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.
* 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.repository.cdi;
/**
* @author Mark Paluch
*/
class FragmentInterfaceImpl implements FragmentInterface {
@Override
public int getPriority() {
return 1;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 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.
* 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.repository.cdi;
import static org.assertj.core.api.Assertions.*;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* CDI integration tests for composed repositories.
*
* @author Mark Paluch
*/
public class RepositoryFragmentsIntegrationTests {
private static SeContainer container;
@BeforeClass
public static void setUp() {
container = SeContainerInitializer.newInstance() //
.disableDiscovery() //
.addPackages(ComposedRepository.class) //
.initialize();
}
@Test // DATACMNS-1233
public void shouldInvokeCustomImplementationLast() {
ComposedRepository repository = getBean(ComposedRepository.class);
ComposedRepositoryImpl customImplementation = getBean(ComposedRepositoryImpl.class);
AnotherFragmentInterfaceImpl shadowed = getBean(AnotherFragmentInterfaceImpl.class);
assertThat(repository.getShadowed()).isEqualTo(2);
assertThat(customImplementation.getShadowed()).isEqualTo(1);
assertThat(shadowed.getShadowed()).isEqualTo(2);
}
@Test // DATACMNS-1233
public void shouldRespectInterfaceOrder() {
ComposedRepository repository = getBean(ComposedRepository.class);
FragmentInterfaceImpl fragment = getBean(FragmentInterfaceImpl.class);
AnotherFragmentInterfaceImpl shadowed = getBean(AnotherFragmentInterfaceImpl.class);
assertThat(repository.getPriority()).isEqualTo(1);
assertThat(fragment.getPriority()).isEqualTo(1);
assertThat(shadowed.getPriority()).isEqualTo(2);
}
protected <T> T getBean(Class<T> type) {
return container.select(type).get();
}
@AfterClass
public static void tearDown() {
container.close();
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 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.
* 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.repository.cdi.isolated;
/**
* @author Mark Paluch
*/
public interface FragmentInterface {
int getPriority();
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 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.
* 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.repository.cdi.isolated;
/**
* @author Mark Paluch
*/
class FragmentInterfaceFoo implements FragmentInterface {
@Override
public int getPriority() {
return 42;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 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.
* 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.repository.cdi.isolated;
import java.io.Serializable;
import org.springframework.data.repository.Repository;
/**
* @author Mark Paluch
*/
public interface IsolatedComposedRepository extends Repository<Object, Serializable>, FragmentInterface {}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 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.
* 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.repository.cdi.isolated;
import org.springframework.data.repository.cdi.CdiRepositoryConfiguration;
/**
* @author Mark Paluch
*/
public class MyCdiConfiguration implements CdiRepositoryConfiguration {
@Override
public String getRepositoryImplementationPostfix() {
return "Foo";
}
}