diff --git a/spring/src/main/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessor.java b/spring/src/main/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessor.java index 04184a564d..7e57a522d5 100644 --- a/spring/src/main/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessor.java +++ b/spring/src/main/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessor.java @@ -12,19 +12,17 @@ */ package io.kubernetes.client.spring.extended.controller; -import io.kubernetes.client.extended.controller.Controller; -import io.kubernetes.client.extended.controller.ControllerManager; +import static org.springframework.util.Assert.notNull; + import io.kubernetes.client.extended.controller.reconciler.Reconciler; import io.kubernetes.client.informer.SharedInformerFactory; -import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconciler; import io.kubernetes.client.spring.extended.controller.factory.KubernetesControllerFactory; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import java.util.function.Supplier; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.core.Ordered; /** @@ -34,18 +32,21 @@ *

It will create a {@link io.kubernetes.client.extended.controller.Controller} for every * reconciler instances registered in the spring bean-factory. */ -public class KubernetesReconcilerProcessor implements BeanFactoryPostProcessor, Ordered { +public class KubernetesReconcilerProcessor implements BeanDefinitionRegistryPostProcessor, Ordered { - private static final Logger log = LoggerFactory.getLogger(KubernetesReconcilerProcessor.class); + public static final String DEFAULT_SHARED_INFORMER_FACTORY_BEAN_NAME = "sharedInformerFactory"; - private ControllerManager controllerManager; + private final String sharedInformerFactoryBeanName; - private ExecutorService controllerManagerDaemon = Executors.newSingleThreadExecutor(); + private BeanDefinitionRegistry registry; - private SharedInformerFactory sharedInformerFactory; + public KubernetesReconcilerProcessor() { + this(DEFAULT_SHARED_INFORMER_FACTORY_BEAN_NAME); + } - public KubernetesReconcilerProcessor(SharedInformerFactory sharedInformerFactory) { - this.sharedInformerFactory = sharedInformerFactory; + public KubernetesReconcilerProcessor(String sharedInformerFactoryBeanName) { + notNull(sharedInformerFactoryBeanName, "SharedInformerFactory bean name is required"); + this.sharedInformerFactoryBeanName = sharedInformerFactoryBeanName; } @Override @@ -54,20 +55,27 @@ public int getOrder() { } @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - String[] names = beanFactory.getBeanNamesForType(Reconciler.class); - for (String name : names) { - Reconciler reconciler = (Reconciler) beanFactory.getBean(name); - KubernetesReconciler kubernetesReconciler = - reconciler.getClass().getAnnotation(KubernetesReconciler.class); - String reconcilerName = kubernetesReconciler.value(); + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { + this.registry = registry; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + for (String reconcilerName : beanFactory.getBeanNamesForType(Reconciler.class)) { + + Supplier kubernetesControllerFactorySupplier = + () -> + new KubernetesControllerFactory( + beanFactory.getBean(sharedInformerFactoryBeanName, SharedInformerFactory.class), + beanFactory.getBean(reconcilerName, Reconciler.class)); - KubernetesControllerFactory controllerFactory = - new KubernetesControllerFactory(sharedInformerFactory, reconciler); + BeanDefinition controllerFactoryBeanDefinition = + BeanDefinitionBuilder.genericBeanDefinition( + KubernetesControllerFactory.class, kubernetesControllerFactorySupplier) + .getBeanDefinition(); - Controller controller = controllerFactory.getObject(); - beanFactory.registerSingleton(reconcilerName, controller); + registry.registerBeanDefinition( + reconcilerName + "Controller", controllerFactoryBeanDefinition); } } } diff --git a/spring/src/test/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessorTest.java b/spring/src/test/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessorTest.java new file mode 100644 index 0000000000..db3e4f7df3 --- /dev/null +++ b/spring/src/test/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessorTest.java @@ -0,0 +1,107 @@ +/* +Copyright 2020 The Kubernetes 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 io.kubernetes.client.spring.extended.controller; + +import static org.junit.Assert.assertNotNull; + +import io.kubernetes.client.extended.controller.Controller; +import io.kubernetes.client.extended.controller.reconciler.Reconciler; +import io.kubernetes.client.extended.controller.reconciler.Request; +import io.kubernetes.client.extended.controller.reconciler.Result; +import io.kubernetes.client.informer.SharedInformer; +import io.kubernetes.client.informer.SharedInformerFactory; +import io.kubernetes.client.openapi.models.V1Pod; +import io.kubernetes.client.openapi.models.V1PodList; +import io.kubernetes.client.spring.extended.controller.annotation.GroupVersionResource; +import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformer; +import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformers; +import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconciler; +import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconcilerWatches; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = KubernetesReconcilerProcessorTest.App.class) +public class KubernetesReconcilerProcessorTest { + + @SpringBootApplication + static class App { + + @Bean + KubernetesReconcilerProcessor reconcilerProcessorUnderTesting() { + return new KubernetesReconcilerProcessor(); + } + + @Bean + SharedInformerFactory sharedInformerFactory() { + return new TestSharedInformerFactory(); + } + + @KubernetesInformers({ + @KubernetesInformer( + apiTypeClass = V1Pod.class, + apiListTypeClass = V1PodList.class, + groupVersionResource = @GroupVersionResource(resourcePlural = "pods")) + }) + static class TestSharedInformerFactory extends SharedInformerFactory {} + + @Bean("testReconciler1") + TestReconciler testReconciler1ToBeInjected() { + return new TestReconciler(); + } + + @Bean("testReconciler2") + TestReconciler testReconciler2ToBeInjected() { + return new TestReconciler(); + } + } + + @KubernetesReconciler(watches = @KubernetesReconcilerWatches()) + static class TestReconciler implements Reconciler { + + @Autowired private SharedInformer informerToBeInjected; + + @Override + public Result reconcile(Request request) { + return new Result(false); + } + } + + @Autowired + @Qualifier("testReconciler1Controller") + private Controller controller1CreatedByReconcilerProcessor; + + @Autowired + @Qualifier("testReconciler2Controller") + private Controller controller2CreatedByReconcilerProcessor; + + @Autowired + @Qualifier("testReconciler1") + private TestReconciler testReconciler1ToBeInjected; + + @Autowired + @Qualifier("testReconciler2") + private TestReconciler testReconciler2ToBeInjected; + + @Test + public void testAutowiredFieldsOfReconcilerBeansAreSet() { + assertNotNull(testReconciler1ToBeInjected.informerToBeInjected); + assertNotNull(testReconciler2ToBeInjected.informerToBeInjected); + } +}