/* * Copyright 2012-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. * 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.boot.diagnostics.analyzer; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.diagnostics.FailureAnalyzer; import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanThreeConfiguration; import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanTwoConfiguration; import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration; import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration.InnerInnerConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; /** * Tests for {@link BeanCurrentlyInCreationFailureAnalyzer}. * * @author Andy Wilkinson */ public class BeanCurrentlyInCreationFailureAnalyzerTests { private final FailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer(); @Test public void cyclicBeanMethods() throws IOException { FailureAnalysis analysis = performAnalysis(CyclicBeanMethodsConfiguration.class); List<String> lines = readDescriptionLines(analysis); assertThat(lines).hasSize(9); assertThat(lines.get(0)).isEqualTo( "The dependencies of some of the beans in the application context form a cycle:"); assertThat(lines.get(1)).isEqualTo(""); assertThat(lines.get(2)).isEqualTo("┌─────┐"); assertThat(lines.get(3)).startsWith( "| one defined in " + InnerInnerConfiguration.class.getName()); assertThat(lines.get(4)).isEqualTo("↑ ↓"); assertThat(lines.get(5)) .startsWith("| two defined in " + InnerConfiguration.class.getName()); assertThat(lines.get(6)).isEqualTo("↑ ↓"); assertThat(lines.get(7)).startsWith( "| three defined in " + CyclicBeanMethodsConfiguration.class.getName()); assertThat(lines.get(8)).isEqualTo("└─────┘"); } @Test public void cycleWithAutowiredFields() throws IOException { FailureAnalysis analysis = performAnalysis(CycleWithAutowiredFields.class); assertThat(analysis.getDescription()).startsWith( "The dependencies of some of the beans in the application context form a cycle:"); List<String> lines = readDescriptionLines(analysis); assertThat(lines).hasSize(9); assertThat(lines.get(0)).isEqualTo( "The dependencies of some of the beans in the application context form a cycle:"); assertThat(lines.get(1)).isEqualTo(""); assertThat(lines.get(2)).isEqualTo("┌─────┐"); assertThat(lines.get(3)).startsWith( "| three defined in " + BeanThreeConfiguration.class.getName()); assertThat(lines.get(4)).isEqualTo("↑ ↓"); assertThat(lines.get(5)).startsWith( "| one defined in " + CycleWithAutowiredFields.class.getName()); assertThat(lines.get(6)).isEqualTo("↑ ↓"); assertThat(lines.get(7)).startsWith("| " + BeanTwoConfiguration.class.getName() + " (field private " + BeanThree.class.getName()); assertThat(lines.get(8)).isEqualTo("└─────┘"); } @Test public void cycleReferencedViaOtherBeans() throws IOException { FailureAnalysis analysis = performAnalysis( CycleReferencedViaOtherBeansConfiguration.class); List<String> lines = readDescriptionLines(analysis); assertThat(lines).hasSize(12); assertThat(lines.get(0)).isEqualTo( "The dependencies of some of the beans in the application context form a cycle:"); assertThat(lines.get(1)).isEqualTo(""); assertThat(lines.get(2)) .contains("refererOne " + "(field " + RefererTwo.class.getName()); assertThat(lines.get(3)).isEqualTo(" ↓"); assertThat(lines.get(4)) .contains("refererTwo " + "(field " + BeanOne.class.getName()); assertThat(lines.get(5)).isEqualTo("┌─────┐"); assertThat(lines.get(6)).startsWith("| one defined in " + CycleReferencedViaOtherBeansConfiguration.class.getName()); assertThat(lines.get(7)).isEqualTo("↑ ↓"); assertThat(lines.get(8)).startsWith("| two defined in " + CycleReferencedViaOtherBeansConfiguration.class.getName()); assertThat(lines.get(9)).isEqualTo("↑ ↓"); assertThat(lines.get(10)).startsWith("| three defined in " + CycleReferencedViaOtherBeansConfiguration.class.getName()); assertThat(lines.get(11)).isEqualTo("└─────┘"); } private List<String> readDescriptionLines(FailureAnalysis analysis) throws IOException { BufferedReader lineReader = new BufferedReader( new StringReader(analysis.getDescription())); try { List<String> lines = new ArrayList<>(); String line; while ((line = lineReader.readLine()) != null) { lines.add(line); } return lines; } finally { lineReader.close(); } } private FailureAnalysis performAnalysis(Class<?> configuration) { FailureAnalysis analysis = this.analyzer.analyze(createFailure(configuration)); assertThat(analysis).isNotNull(); return analysis; } private Exception createFailure(Class<?> configuration) { ConfigurableApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(configuration); } catch (Exception ex) { ex.printStackTrace(); return ex; } finally { if (context != null) { context.close(); } } fail("Expected failure did not occur"); return null; } @org.springframework.context.annotation.Configuration static class CyclicBeanMethodsConfiguration { @Bean public BeanThree three(BeanOne one) { return new BeanThree(); } @org.springframework.context.annotation.Configuration static class InnerConfiguration { @Bean public BeanTwo two(BeanThree three) { return new BeanTwo(); } @Configuration static class InnerInnerConfiguration { @Bean public BeanOne one(BeanTwo two) { return new BeanOne(); } } } } @Configuration static class CycleReferencedViaOtherBeansConfiguration { @Bean public BeanOne one(BeanTwo two) { return new BeanOne(); } @Bean public BeanTwo two(BeanThree three) { return new BeanTwo(); } @Bean public BeanThree three(BeanOne beanOne) { return new BeanThree(); } @Configuration static class InnerConfiguration { @Bean public RefererTwo refererTwo() { return new RefererTwo(); } @Configuration static class InnerInnerConfiguration { @Bean public RefererOne refererOne() { return new RefererOne(); } } } } @org.springframework.context.annotation.Configuration public static class CycleWithAutowiredFields { @Bean public BeanOne one(BeanTwo two) { return new BeanOne(); } public static class BeanTwoConfiguration { @SuppressWarnings("unused") @Autowired private BeanThree three; @Bean public BeanTwo two() { return new BeanTwo(); } } public static class BeanThreeConfiguration { @Bean public BeanThree three(BeanOne one) { return new BeanThree(); } } } static class RefererOne { @Autowired RefererTwo refererTwo; } static class RefererTwo { @Autowired BeanOne beanOne; } static class BeanOne { } static class BeanTwo { } static class BeanThree { } }