diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java index db36e7c643..ae11bf8870 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java @@ -19,8 +19,12 @@ package org.springframework.cloud.contract.stubrunner.spring; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; @@ -56,9 +60,6 @@ public class StubRunnerConfiguration { static final String STUBRUNNER_PREFIX = "stubrunner.runningstubs"; - @Autowired(required = false) - private MessageVerifier contractVerifierMessaging; - private StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider(); @Autowired @@ -71,10 +72,11 @@ public class StubRunnerConfiguration { * Bean that initializes stub runners, runs them and on shutdown closes them. Upon its * instantiation JAR with stubs is downloaded and unpacked to a temporary folder and * WireMock server are started for each of those stubs + * @param beanFactory bean factory * @return the batch stub runner bean */ @Bean - public BatchStubRunner batchStubRunner() { + public BatchStubRunner batchStubRunner(BeanFactory beanFactory) { StubRunnerOptionsBuilder builder = builder(); if (this.props.getProxyHost() != null) { builder.withProxy(this.props.getProxyHost(), this.props.getProxyPort()); @@ -82,8 +84,7 @@ public class StubRunnerConfiguration { StubRunnerOptions stubRunnerOptions = stubRunnerOptions(builder); BatchStubRunner batchStubRunner = new BatchStubRunnerFactory(stubRunnerOptions, this.provider.get(stubRunnerOptions), - this.contractVerifierMessaging != null ? this.contractVerifierMessaging - : new NoOpStubMessages()).buildBatchStubRunner(); + new LazyMessageVerifier(beanFactory)).buildBatchStubRunner(); // TODO: Consider running it in a separate thread RunningStubs runningStubs = batchStubRunner.runStubs(); registerPort(runningStubs); @@ -94,6 +95,12 @@ public class StubRunnerConfiguration { return builder.build(); } + @Bean + public BeanPostProcessor batchStubRunnerBeanPostProcessor(BatchStubRunner runner) { + return new BeanPostProcessor() { + }; + } + private StubRunnerOptionsBuilder builder() { return new StubRunnerOptionsBuilder() .withMinMaxPort( @@ -162,3 +169,47 @@ public class StubRunnerConfiguration { } } + +class LazyMessageVerifier implements MessageVerifier { + + private MessageVerifier messageVerifier; + + private final BeanFactory beanFactory; + + LazyMessageVerifier(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + private MessageVerifier messageVerifier() { + if (this.messageVerifier == null) { + try { + this.messageVerifier = this.beanFactory.getBean(MessageVerifier.class); + } + catch (BeansException ex) { + this.messageVerifier = new NoOpStubMessages(); + } + } + return this.messageVerifier; + } + + @Override + public void send(Object message, String destination) { + messageVerifier().send(message, destination); + } + + @Override + public Object receive(String destination, long timeout, TimeUnit timeUnit) { + return messageVerifier().receive(destination, timeout, timeUnit); + } + + @Override + public Object receive(String destination) { + return messageVerifier().receive(destination); + } + + @Override + public void send(Object payload, Map headers, String destination) { + messageVerifier().send(payload, headers, destination); + } + +} diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/issue1225/Issue1225Tests.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/issue1225/Issue1225Tests.java new file mode 100644 index 0000000000..ac9a6fade3 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/issue1225/Issue1225Tests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * https://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.cloud.contract.stubrunner.issue1225; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerPort; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests that stub runner specific auto-configuration can be loaded up in combination with + * other slice tests + * + * @author Biju Kunjummen + */ +@RunWith(SpringRunner.class) +@SpringBootTest(properties = { + "ping.url=http://localhost:${stubrunner.runningstubs.loanIssuance.port}" }) +@AutoConfigureStubRunner(ids = { + "org.springframework.cloud.contract.verifier.stubs:loanIssuance:+:stubs", + "org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer:+:stubs" }) +@ActiveProfiles("test") +public class Issue1225Tests { + + @StubRunnerPort("loanIssuance") + private int stubRunnerLoanIssuancePort; + + @Autowired + private PingProxyController pingProxyController; + + @Test + public void shouldInjectTheStubPortsAsEarlyAsPossible() { + assertThat(this.stubRunnerLoanIssuancePort).isPositive(); + assertThat(this.pingProxyController.pingUrl) + .contains(":" + this.stubRunnerLoanIssuancePort); + } + + @ComponentScan + @SpringBootConfiguration + static class Config { + + } + +} + +@RestController +class PingProxyController { + + String pingUrl; + + PingProxyController(@Value("${ping.url}") String pingUrl) { + this.pingUrl = pingUrl; + } + +} diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/FailFastLoanApplicationServiceTests.java b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/FailFastLoanApplicationServiceTests.java index 0e83d7ddec..98fdf5d154 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/FailFastLoanApplicationServiceTests.java +++ b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/FailFastLoanApplicationServiceTests.java @@ -46,9 +46,8 @@ public class FailFastLoanApplicationServiceTests { // Then assertThat(throwable).isInstanceOf(BeanCreationException.class); - assertThat(throwable.getCause()).isInstanceOf(BeanInstantiationException.class); assertThat(throwable.getCause().getCause()) - .isInstanceOf(IllegalArgumentException.class).hasMessageContaining( + .isInstanceOf(BeanInstantiationException.class).hasMessageContaining( "No stubs or contracts were found for [org.springframework.cloud.contract.verifier.stubs:should-not-be-found:+:stubs] and the switch to fail on no stubs was set."); } @@ -65,9 +64,8 @@ public class FailFastLoanApplicationServiceTests { // Then assertThat(throwable).isInstanceOf(BeanCreationException.class); - assertThat(throwable.getCause()).isInstanceOf(BeanInstantiationException.class); assertThat(throwable.getCause().getCause()) - .isInstanceOf(IllegalStateException.class) + .isInstanceOf(BeanInstantiationException.class) .hasMessageContaining("No stubs were found on classpath "); }