Checkstyle and jdk11 fix with spock

This commit is contained in:
Marcin Grzejszczak
2018-10-05 10:44:28 +02:00
parent 27652ade37
commit 8da2e4ee57
220 changed files with 3214 additions and 1977 deletions

View File

@@ -21,6 +21,7 @@ public class WiremockTestsApplication {
public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}
}
@RestController
@@ -36,6 +37,7 @@ class Controller {
public String home() {
return this.service.go();
}
}
@Component
@@ -51,7 +53,8 @@ class Service {
}
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
return this.restTemplate.getForEntity(this.base + "/resource", String.class)
.getBody();
}
public String getBase() {
@@ -61,4 +64,5 @@ class Service {
public void setBase(String base) {
this.base = base;
}
}

View File

@@ -19,7 +19,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@ActiveProfiles("classrule")
//tag::wiremock_test1[]
// tag::wiremock_test1[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class WiremockForDocsClassRuleTests {
@@ -29,12 +29,14 @@ public class WiremockForDocsClassRuleTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().dynamicPort());
//end::wiremock_test1[]
// end::wiremock_test1[]
@Before
public void setup() {
this.service.setBase("http://localhost:" + wiremock.port());
}
//tag::wiremock_test2[]
// tag::wiremock_test2[]
// A service that calls out over HTTP to localhost:${wiremock.port}
@Autowired
private Service service;
@@ -43,11 +45,11 @@ public class WiremockForDocsClassRuleTests {
@Test
public void contextLoads() throws Exception {
// Stubbing WireMock
wiremock.stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
wiremock.stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}
//end::wiremock_test2[]
// end::wiremock_test2[]

View File

@@ -33,5 +33,6 @@ public class WiremockForDocsMockServerApplicationTests {
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
}
}
// end::wiremock_test[]

View File

@@ -18,32 +18,37 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("docs")
//tag::wiremock_test1[]
// tag::wiremock_test1[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class WiremockForDocsTests {
//end::wiremock_test1[]
@Autowired Environment environment;
// end::wiremock_test1[]
@Autowired
Environment environment;
@Before
public void setup() {
service.setBase("http://localhost:" + this.environment.getProperty("wiremock.server.port"));
service.setBase("http://localhost:"
+ this.environment.getProperty("wiremock.server.port"));
}
//tag::wiremock_test2[]
// tag::wiremock_test2[]
// A service that calls out over HTTP
@Autowired private Service service;
@Autowired
private Service service;
// Using the WireMock APIs in the normal way:
@Test
public void contextLoads() throws Exception {
// Stubbing WireMock
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}
//end::wiremock_test2[]
// end::wiremock_test2[]

View File

@@ -14,9 +14,8 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6060", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:6060", webEnvironment = WebEnvironment.NONE)
@AutoConfigureWireMock(port = 6060)
public class WiremockImportApplicationTests {
@@ -25,8 +24,8 @@ public class WiremockImportApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -13,7 +13,7 @@ import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockMockServerApplicationTests {
@Autowired

View File

@@ -26,11 +26,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6061", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:6061", webEnvironment = WebEnvironment.NONE)
public class WiremockServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6061));
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().port(6061));
@Rule
public ExpectedException expected = ExpectedException.none();
@@ -40,8 +41,8 @@ public class WiremockServerApplicationTests {
@Test
public void hello() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -20,6 +20,7 @@ public class WiremockTestsApplication {
public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}
}
@RestController
@@ -35,6 +36,7 @@ class Controller {
public String home() {
return this.service.go();
}
}
@Component
@@ -50,6 +52,8 @@ class Service {
}
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
return this.restTemplate.getForEntity(this.base + "/resource", String.class)
.getBody();
}
}

View File

@@ -17,7 +17,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6063", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:6063", webEnvironment = WebEnvironment.NONE)
public class WiremockServerApplicationTests {
@ClassRule
@@ -28,8 +28,8 @@ public class WiremockServerApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -21,6 +21,7 @@ public class WiremockTestsApplication {
public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}
}
@RestController
@@ -36,6 +37,7 @@ class Controller {
public String home() {
return this.service.go();
}
}
@Component
@@ -51,6 +53,8 @@ class Service {
}
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
return this.restTemplate.getForEntity(this.base + "/resource", String.class)
.getBody();
}
}

View File

@@ -17,7 +17,6 @@ import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
@@ -32,8 +31,8 @@ public class WiremockHttpsServerApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -14,9 +14,8 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6065", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:6065", webEnvironment = WebEnvironment.NONE)
@AutoConfigureWireMock(port = 6065)
public class WiremockImportApplicationTests {
@@ -25,8 +24,8 @@ public class WiremockImportApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -25,8 +25,8 @@ public class WiremockImportContextPathApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -13,7 +13,7 @@ import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockMockServerApplicationTests {
@Autowired

View File

@@ -27,11 +27,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6067", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:6067", webEnvironment = WebEnvironment.NONE)
public class WiremockServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6067));
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().port(6067));
@Rule
public ExpectedException expected = ExpectedException.none();
@@ -41,8 +42,8 @@ public class WiremockServerApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -20,6 +20,7 @@ public class WiremockTestsApplication {
public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}
}
@RestController
@@ -35,6 +36,7 @@ class Controller {
public String home() {
return this.service.go();
}
}
@Component
@@ -50,6 +52,8 @@ class Service {
}
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
return this.restTemplate.getForEntity(this.base + "/resource", String.class)
.getBody();
}
}

View File

@@ -18,23 +18,22 @@ import org.springframework.util.SocketUtils;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:7443")
@ActiveProfiles("ssl")
public class WiremockHttpsServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(7443).port(SocketUtils.findAvailableTcpPort()));
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring
.options().httpsPort(7443).port(SocketUtils.findAvailableTcpPort()));
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -20,6 +20,7 @@ public class WiremockTestsApplication {
public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}
}
@RestController
@@ -35,6 +36,7 @@ class Controller {
public String home() {
return this.service.go();
}
}
@Component
@@ -50,6 +52,8 @@ class Service {
}
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
return this.restTemplate.getForEntity(this.base + "/resource", String.class)
.getBody();
}
}

View File

@@ -14,9 +14,8 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:7070", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:7070", webEnvironment = WebEnvironment.NONE)
@AutoConfigureWireMock(port = 7070)
public class WiremockImportApplicationTests {
@@ -25,8 +24,8 @@ public class WiremockImportApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -13,7 +13,7 @@ import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockMockServerApplicationTests {
@Autowired

View File

@@ -18,19 +18,20 @@ import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:7071", webEnvironment=WebEnvironment.NONE)
@SpringBootTest(properties = "app.baseUrl=http://localhost:7071", webEnvironment = WebEnvironment.NONE)
public class WiremockServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(7071));
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().port(7071));
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -49,20 +49,24 @@ class AetherFactories {
private static final Log log = LogFactory.getLog(AetherFactories.class);
private static final String MAVEN_LOCAL_REPOSITORY_LOCATION = "maven.repo.local";
private static final String MAVEN_USER_SETTINGS_LOCATION = "org.apache.maven.user-settings";
private static final String MAVEN_GLOBAL_SETTINGS_LOCATION = "org.apache.maven.global-settings";
private static final Random RANDOM = new Random();
public static RepositorySystem newRepositorySystem() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(RepositoryConnectorFactory.class,
BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
return locator.getService(RepositorySystem.class);
}
public static RepositorySystemSession newSession(RepositorySystem system, boolean workOffline) {
public static RepositorySystemSession newSession(RepositorySystem system,
boolean workOffline) {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setOffline(workOffline);
if (!workOffline) {
@@ -71,16 +75,19 @@ class AetherFactories {
session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN);
String localRepositoryDirectory = localRepositoryDirectory(workOffline);
if (log.isDebugEnabled()) {
log.debug("Local Repository Directory set to [" + localRepositoryDirectory + "]. Work offline: [" + workOffline + "]");
log.debug("Local Repository Directory set to [" + localRepositoryDirectory
+ "]. Work offline: [" + workOffline + "]");
}
LocalRepository localRepo = new LocalRepository(localRepositoryDirectory);
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
session.setLocalRepositoryManager(
system.newLocalRepositoryManager(session, localRepo));
return session;
}
protected static String localRepositoryDirectory(boolean workOffline) {
String localRepoLocationFromSettings = settings().getLocalRepository();
String currentLocalRepo = readPropertyFromSystemProps(localRepoLocationFromSettings);
String currentLocalRepo = readPropertyFromSystemProps(
localRepoLocationFromSettings);
if (workOffline) {
return currentLocalRepo;
}
@@ -93,18 +100,21 @@ class AetherFactories {
}
catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug("Failed to create a new temporary directory, will generate a new one under temp dir");
log.debug(
"Failed to create a new temporary directory, will generate a new one under temp dir");
}
return System.getProperty("java.io.tmpdir") + File.separator + RANDOM.nextInt();
return System.getProperty("java.io.tmpdir") + File.separator
+ RANDOM.nextInt();
}
}
private static String readPropertyFromSystemProps(
String localRepoLocationFromSettings) {
String mavenLocalRepo = fromSystemPropOrEnv(MAVEN_LOCAL_REPOSITORY_LOCATION);
return StringUtils.hasText(mavenLocalRepo) ? mavenLocalRepo :
localRepoLocationFromSettings != null ? localRepoLocationFromSettings
: System.getProperty("user.home") + File.separator + ".m2" + File.separator + "repository";
return StringUtils.hasText(mavenLocalRepo) ? mavenLocalRepo
: localRepoLocationFromSettings != null ? localRepoLocationFromSettings
: System.getProperty("user.home") + File.separator + ".m2"
+ File.separator + "repository";
}
// system prop takes precedence over env var
@@ -136,7 +146,8 @@ class AetherFactories {
SettingsBuildingResult result;
try {
result = builder.build(request);
} catch (SettingsBuildingException ex) {
}
catch (SettingsBuildingException ex) {
throw new IllegalStateException(ex);
}
return result.getEffectiveSettings();

View File

@@ -54,28 +54,38 @@ public class AetherStubDownloader implements StubDownloader {
private static final Log log = LogFactory.getLog(AetherStubDownloader.class);
private static final String TEMP_DIR_PREFIX = "contracts";
private static final String ARTIFACT_EXTENSION = "jar";
private static final String LATEST_ARTIFACT_VERSION = "(,]";
private static final String LATEST_VERSION_IN_IVY = "+";
// Preloading class for the shutdown hook not to throw ClassNotFound
private static final Class CLAZZ = TemporaryFileStorage.class;
private final List<RemoteRepository> remoteRepos;
private final RepositorySystem repositorySystem;
private final RepositorySystemSession session;
private final boolean workOffline;
private final boolean deleteStubsAfterTest;
public AetherStubDownloader(StubRunnerOptions stubRunnerOptions) {
this.deleteStubsAfterTest = stubRunnerOptions.isDeleteStubsAfterTest();
if (log.isDebugEnabled()) {
log.debug("Will be resolving versions for the following options: [" + stubRunnerOptions + "]");
log.debug("Will be resolving versions for the following options: ["
+ stubRunnerOptions + "]");
}
this.remoteRepos = remoteRepositories(stubRunnerOptions);
boolean remoteReposMissing = remoteReposMissing();
switch (stubRunnerOptions.stubsMode) {
case LOCAL:
log.info("Remote repos not passed but the switch to work offline was set. " + "Stubs will be used from your local Maven repository.");
log.info("Remote repos not passed but the switch to work offline was set. "
+ "Stubs will be used from your local Maven repository.");
break;
case REMOTE:
if (remoteReposMissing) {
@@ -99,7 +109,6 @@ public class AetherStubDownloader implements StubDownloader {
/**
* Used by the Maven Plugin
*
* @param repositorySystem
* @param remoteRepositories - remote artifact repositories
* @param session
@@ -111,26 +120,29 @@ public class AetherStubDownloader implements StubDownloader {
this.repositorySystem = repositorySystem;
this.session = session;
if (remoteReposMissing()) {
log.error("Remote repositories for stubs are not specified and work offline flag wasn't passed");
log.error(
"Remote repositories for stubs are not specified and work offline flag wasn't passed");
}
this.workOffline = false;
registerShutdownHook();
}
private List<RemoteRepository> remoteRepositories(StubRunnerOptions stubRunnerOptions) {
private List<RemoteRepository> remoteRepositories(
StubRunnerOptions stubRunnerOptions) {
if (stubRunnerOptions.stubRepositoryRoot == null) {
return new ArrayList<>();
}
final String[] repos = stubRunnerOptions.getStubRepositoryRootAsString().split(",");
final String[] repos = stubRunnerOptions.getStubRepositoryRootAsString()
.split(",");
final List<RemoteRepository> remoteRepos = new ArrayList<>();
for (int i = 0; i < repos.length; i++) {
if(StringUtils.hasText(repos[i])) {
final RemoteRepository.Builder builder = new RemoteRepository.Builder("remote" + i, "default", repos[i])
.setAuthentication(new AuthenticationBuilder()
.addUsername(stubRunnerOptions.username)
.addPassword(stubRunnerOptions.password)
.build());
if(stubRunnerOptions.getProxyOptions() != null) {
if (StringUtils.hasText(repos[i])) {
final RemoteRepository.Builder builder = new RemoteRepository.Builder(
"remote" + i, "default", repos[i])
.setAuthentication(new AuthenticationBuilder()
.addUsername(stubRunnerOptions.username)
.addPassword(stubRunnerOptions.password).build());
if (stubRunnerOptions.getProxyOptions() != null) {
final StubRunnerProxyOptions p = stubRunnerOptions.getProxyOptions();
builder.setProxy(new Proxy(null, p.getProxyHost(), p.getProxyPort()));
}
@@ -155,12 +167,14 @@ public class AetherStubDownloader implements StubDownloader {
}
Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier,
ARTIFACT_EXTENSION, resolvedVersion);
ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepos, null);
ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepos,
null);
if (log.isDebugEnabled()) {
log.debug("Resolving artifact [" + artifact
+ "] using remote repositories " + this.remoteRepos);
}
ArtifactResult result = this.repositorySystem.resolveArtifact(this.session, request);
ArtifactResult result = this.repositorySystem.resolveArtifact(this.session,
request);
log.info("Resolved artifact [" + artifact + "] to "
+ result.getArtifact().getFile());
File temporaryFile = unpackStubJarToATemporaryFolder(
@@ -175,7 +189,8 @@ public class AetherStubDownloader implements StubDownloader {
throw new IllegalStateException(
"Exception occurred while trying to download a stub for group ["
+ stubsGroup + "] module [" + stubsModule
+ "] and classifier [" + classifier + "] in " + this.remoteRepos,
+ "] and classifier [" + classifier + "] in "
+ this.remoteRepos,
e);
}
}
@@ -193,13 +208,16 @@ public class AetherStubDownloader implements StubDownloader {
if (StringUtils.isEmpty(version) || LATEST_VERSION_IN_IVY.equals(version)) {
log.info("Desired version is [" + version
+ "] - will try to resolve the latest version");
return resolveHighestArtifactVersion(stubsGroup, stubsModule, classifier, LATEST_ARTIFACT_VERSION);
return resolveHighestArtifactVersion(stubsGroup, stubsModule, classifier,
LATEST_ARTIFACT_VERSION);
}
return resolveHighestArtifactVersion(stubsGroup, stubsModule, classifier, version);
return resolveHighestArtifactVersion(stubsGroup, stubsModule, classifier,
version);
}
@Override
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(StubConfiguration stubConfiguration) {
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration) {
String version = getVersion(stubConfiguration.groupId,
stubConfiguration.artifactId, stubConfiguration.version,
stubConfiguration.classifier);
@@ -211,9 +229,9 @@ public class AetherStubDownloader implements StubDownloader {
if (unpackedJar == null) {
return null;
}
return new AbstractMap.SimpleEntry<>(
new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId, version,
stubConfiguration.classifier), unpackedJar);
return new AbstractMap.SimpleEntry<>(new StubConfiguration(
stubConfiguration.groupId, stubConfiguration.artifactId, version,
stubConfiguration.classifier), unpackedJar);
}
private String resolveHighestArtifactVersion(String stubsGroup, String stubsModule,
@@ -234,15 +252,19 @@ public class AetherStubDownloader implements StubDownloader {
throw new IllegalStateException("Cannot resolve version range", e);
}
if (rangeResult.getHighestVersion() == null) {
throw new IllegalArgumentException("For groupId [" + stubsGroup + "] artifactId [" + stubsModule + "] "
+ "and classifier [" + classifier + "] the version was not resolved! The following exceptions took place "
throw new IllegalArgumentException("For groupId [" + stubsGroup
+ "] artifactId [" + stubsModule + "] " + "and classifier ["
+ classifier
+ "] the version was not resolved! The following exceptions took place "
+ rangeResult.getExceptions());
}
return rangeResult.getHighestVersion() == null ? null : rangeResult.getHighestVersion().toString();
return rangeResult.getHighestVersion() == null ? null
: rangeResult.getHighestVersion().toString();
}
private static File unpackStubJarToATemporaryFolder(URI stubJarUri) {
File tmpDirWhereStubsWillBeUnzipped = TemporaryFileStorage.createTempDir(TEMP_DIR_PREFIX);
File tmpDirWhereStubsWillBeUnzipped = TemporaryFileStorage
.createTempDir(TEMP_DIR_PREFIX);
log.info("Unpacking stub from JAR [URI: " + stubJarUri + "]");
unzipTo(new File(stubJarUri), tmpDirWhereStubsWillBeUnzipped);
TemporaryFileStorage.add(tmpDirWhereStubsWillBeUnzipped);
@@ -250,8 +272,8 @@ public class AetherStubDownloader implements StubDownloader {
}
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(
() -> TemporaryFileStorage.cleanup(AetherStubDownloader.this.deleteStubsAfterTest)));
Runtime.getRuntime().addShutdownHook(new Thread(() -> TemporaryFileStorage
.cleanup(AetherStubDownloader.this.deleteStubsAfterTest)));
}
}

View File

@@ -9,13 +9,16 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
* @since 2.0.0
*/
public class AetherStubDownloaderBuilder implements StubDownloaderBuilder {
private static final Log log = LogFactory.getLog(AetherStubDownloaderBuilder.class);
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
if (stubRunnerOptions.stubsMode == StubRunnerProperties.StubsMode.CLASSPATH) {
return null;
}
log.info("Will download stubs and contracts via Aether");
return new AetherStubDownloader(stubRunnerOptions);
}
}

View File

@@ -22,8 +22,11 @@ package org.springframework.cloud.contract.stubrunner;
* @see StubRunner
*/
class Arguments {
final private StubRunnerOptions stubRunnerOptions;
final private String repositoryPath;
final private StubConfiguration stub;
Arguments(StubRunnerOptions stubRunnerOptions) {
@@ -49,8 +52,11 @@ class Arguments {
return this.stub;
}
@Override public String toString() {
@Override
public String toString() {
return "Arguments{" + "stubRunnerOptions=" + this.stubRunnerOptions
+ ", repositoryPath='" + this.repositoryPath + '\'' + ", stub=" + this.stub + '}';
+ ", repositoryPath='" + this.repositoryPath + '\'' + ", stub="
+ this.stub + '}';
}
}

View File

@@ -33,7 +33,9 @@ class AvailablePortScanner {
private static final int MAX_RETRY_COUNT = 1000;
private final int minPortNumber;
private final int maxPortNumber;
private final int maxRetryCount;
AvailablePortScanner(int minPortNumber, int maxPortNumber) {
@@ -64,15 +66,16 @@ class AvailablePortScanner {
}
catch (IOException exception) {
if (log.isDebugEnabled()) {
log.debug("Failed to execute callback (try: " + i + "/" + this.maxRetryCount
+ ")", exception);
log.debug("Failed to execute callback (try: " + i + "/"
+ this.maxRetryCount + ")", exception);
}
}
}
throw new NoPortAvailableException(this.minPortNumber, this.maxPortNumber);
}
private <T> T executeLogicForAvailablePort(int portToScan, PortCallback<T> closure) throws IOException {
private <T> T executeLogicForAvailablePort(int portToScan, PortCallback<T> closure)
throws IOException {
if (log.isDebugEnabled()) {
log.debug("Trying to execute closure with port [" + portToScan + "]");
}
@@ -93,20 +96,28 @@ class AvailablePortScanner {
@SuppressWarnings("serial")
static class NoPortAvailableException extends RuntimeException {
NoPortAvailableException(int lowerBound, int upperBound) {
super("Could not find available port in range " + lowerBound + ":" + upperBound);
super("Could not find available port in range " + lowerBound + ":"
+ upperBound);
}
}
@SuppressWarnings("serial")
static class InvalidPortRange extends RuntimeException {
InvalidPortRange(int lowerBound, int upperBound) {
super("Invalid bounds exceptions, min port [" + lowerBound
+ "] is greater to max port [" + upperBound + "]");
}
}
public interface PortCallback<T> {
T call(int port) throws IOException;
}
}

View File

@@ -54,7 +54,9 @@ public class BatchStubRunner implements StubRunning {
for (StubRunner stubRunner : this.stubRunners) {
try {
return stubRunner.findStubUrl(groupId, artifactId);
} catch (StubNotFoundException e) {}
}
catch (StubNotFoundException e) {
}
}
throw new StubNotFoundException(groupId, artifactId);
}
@@ -64,7 +66,9 @@ public class BatchStubRunner implements StubRunning {
for (StubRunner stubRunner : this.stubRunners) {
try {
return stubRunner.findStubUrl(ivyNotation);
} catch (StubNotFoundException e) {}
}
catch (StubNotFoundException e) {
}
}
throw new StubNotFoundException(ivyNotation);
}
@@ -175,4 +179,5 @@ public class BatchStubRunner implements StubRunning {
stubRunner.close();
}
}
}

View File

@@ -28,35 +28,44 @@ import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessag
public class BatchStubRunnerFactory {
private final StubRunnerOptions stubRunnerOptions;
private final StubDownloader stubDownloader;
private final MessageVerifier<?> contractVerifierMessaging;
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions) {
this(stubRunnerOptions, new NoOpStubMessages());
}
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, MessageVerifier verifier) {
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions,
MessageVerifier verifier) {
this(stubRunnerOptions, aetherStubDownloader(stubRunnerOptions), verifier);
}
private static StubDownloader aetherStubDownloader(StubRunnerOptions stubRunnerOptions) {
private static StubDownloader aetherStubDownloader(
StubRunnerOptions stubRunnerOptions) {
StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider();
return provider.get(stubRunnerOptions);
}
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader) {
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions,
StubDownloader stubDownloader) {
this(stubRunnerOptions, stubDownloader, new NoOpStubMessages());
}
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader, MessageVerifier<?> contractVerifierMessaging) {
public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions,
StubDownloader stubDownloader, MessageVerifier<?> contractVerifierMessaging) {
this.stubRunnerOptions = stubRunnerOptions;
this.stubDownloader = stubDownloader;
this.contractVerifierMessaging = contractVerifierMessaging;
}
public BatchStubRunner buildBatchStubRunner() {
StubRunnerFactory stubRunnerFactory = new StubRunnerFactory(this.stubRunnerOptions, this.stubDownloader, this.contractVerifierMessaging);
return new BatchStubRunner(stubRunnerFactory.createStubsFromServiceConfiguration());
StubRunnerFactory stubRunnerFactory = new StubRunnerFactory(
this.stubRunnerOptions, this.stubDownloader,
this.contractVerifierMessaging);
return new BatchStubRunner(
stubRunnerFactory.createStubsFromServiceConfiguration());
}
}

View File

@@ -23,12 +23,13 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
/**
* Stub downloader that picks stubs and contracts from the provided resource.
* If {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#stubsMode} is set
* to {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties.StubsMode#CLASSPATH}
* Stub downloader that picks stubs and contracts from the provided resource. If
* {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#stubsMode}
* is set to
* {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties.StubsMode#CLASSPATH}
* then classpath is searched according to what has been passed in
* {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#ids}. The
* pattern to search for stubs looks like this
* {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#ids}.
* The pattern to search for stubs looks like this
*
* <ul>
* <li>{@code META-INF/group.id/artifactid/ ** /*.* }</li>
@@ -51,10 +52,10 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
*/
public class ClasspathStubProvider implements StubDownloaderBuilder {
private static final Log log = LogFactory
.getLog(ClasspathStubProvider.class);
private static final Log log = LogFactory.getLog(ClasspathStubProvider.class);
private static final int TEMP_DIR_ATTEMPTS = 10000;
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
new DefaultResourceLoader());
@@ -72,35 +73,41 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
List<String> paths = toPaths(repoRoots);
List<Resource> resources = resolveResources(paths);
if (log.isDebugEnabled()) {
log.debug("For paths " + paths + " found following resources " + resources);
log.debug("For paths " + paths + " found following resources "
+ resources);
}
if (resources.isEmpty()) {
throw new IllegalStateException("No stubs were found on classpath for [" + config.getGroupId() + ":" + config.getArtifactId() + "]");
throw new IllegalStateException(
"No stubs were found on classpath for [" + config.getGroupId()
+ ":" + config.getArtifactId() + "]");
}
final File tmp = createTempDir();
if (stubRunnerOptions.isDeleteStubsAfterTest()) {
tmp.deleteOnExit();
}
Pattern groupAndArtifactPattern = Pattern.compile(
"^(.*)(" + config.getGroupId() + "." + config.getArtifactId() + ")(.*)$");
Pattern groupAndArtifactPattern = Pattern.compile("^(.*)("
+ config.getGroupId() + "." + config.getArtifactId() + ")(.*)$");
String version = config.getVersion();
for (Resource resource : resources) {
try {
String relativePath = relativePathPicker(resource, groupAndArtifactPattern);
String relativePath = relativePathPicker(resource,
groupAndArtifactPattern);
if (log.isDebugEnabled()) {
log.debug("Relative path for resource is [" + relativePath + "]");
log.debug("Relative path for resource is [" + relativePath
+ "]");
}
// the relative path is OS agnostic and contains / only
int lastIndexOf = relativePath.lastIndexOf("/");
String relativePathWithoutFile = lastIndexOf > -1 ?
relativePath.substring(0, lastIndexOf) :
relativePath;
String relativePathWithoutFile = lastIndexOf > -1
? relativePath.substring(0, lastIndexOf) : relativePath;
if (log.isDebugEnabled()) {
log.debug("Relative path without file name is [" + relativePathWithoutFile + "]");
log.debug("Relative path without file name is ["
+ relativePathWithoutFile + "]");
}
Path directory = Files.createDirectories(
new File(tmp, relativePathWithoutFile).toPath());
File newFile = new File(directory.toFile(), resource.getFilename());
File newFile = new File(directory.toFile(),
resource.getFilename());
if (!newFile.exists() && !isDirectory(resource)) {
try (InputStream stream = resource.getInputStream()) {
Files.copy(stream, newFile.toPath());
@@ -109,31 +116,38 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
if (log.isDebugEnabled()) {
log.debug("Stored file [" + newFile + "]");
}
} catch (IOException e) {
}
catch (IOException e) {
log.error("Exception occurred while trying to create dirs", e);
throw new IllegalStateException(e);
}
}
log.info("Unpacked files for [" + config.getGroupId() + ":" + config.getArtifactId()
+ ":" + version + "] to folder [" + tmp + "]");
log.info("Unpacked files for [" + config.getGroupId() + ":"
+ config.getArtifactId() + ":" + version + "] to folder [" + tmp
+ "]");
return new AbstractMap.SimpleEntry<>(
new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
config.getClassifier()), tmp);
new StubConfiguration(config.getGroupId(), config.getArtifactId(),
version, config.getClassifier()),
tmp);
}
boolean isDirectory(Resource resource) {
try {
return resource.getFile().isDirectory();
} catch (Exception e) {
}
catch (Exception e) {
if (log.isTraceEnabled()) {
log.trace("Exception occurred while trying to convert path to file for resource [" + resource + "]", e);
log.trace(
"Exception occurred while trying to convert path to file for resource ["
+ resource + "]",
e);
}
return false;
}
}
String relativePathPicker(Resource resource,
Pattern groupAndArtifactPattern) throws IOException {
String relativePathPicker(Resource resource, Pattern groupAndArtifactPattern)
throws IOException {
String uri = resource.getURI().toString();
Matcher groupAndArtifactMatcher = groupAndArtifactPattern.matcher(uri);
if (groupAndArtifactMatcher.matches()) {
@@ -165,7 +179,8 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
resources.addAll(list);
}
catch (IOException e) {
log.error("Exception occurred while trying to fetch resources from [" + path + "]");
log.error("Exception occurred while trying to fetch resources from ["
+ path + "]");
throw new IllegalStateException(e);
}
}
@@ -175,11 +190,12 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
private List<RepoRoot> repoRoot(StubRunnerOptions stubRunnerOptions,
StubConfiguration configuration) {
if (stubRunnerOptions.getStubRepositoryRoot() != null) {
return Collections
.singletonList(new RepoRoot(stubRunnerOptions.getStubRepositoryRootAsString()));
return Collections.singletonList(
new RepoRoot(stubRunnerOptions.getStubRepositoryRootAsString()));
}
else {
String path = "/**/" + configuration.getGroupId() + "/" + configuration.getArtifactId();
String path = "/**/" + configuration.getGroupId() + "/"
+ configuration.getArtifactId();
return Arrays.asList(new RepoRoot("classpath*:/META-INF" + path, "/**/*.*"),
new RepoRoot("classpath*:/contracts" + path, "/**/*.*"),
new RepoRoot("classpath*:/mappings" + path, "/**/*.*"));
@@ -196,14 +212,15 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
return tempDir;
}
}
throw new IllegalStateException(
"Failed to create directory within " + TEMP_DIR_ATTEMPTS
+ " attempts (tried " + baseName + "0 to " + baseName + (
TEMP_DIR_ATTEMPTS - 1) + ")");
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName
+ (TEMP_DIR_ATTEMPTS - 1) + ")");
}
private static class RepoRoot {
final String repoRoot;
final String fullPath;
RepoRoot(String repoRoot) {
@@ -215,5 +232,7 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
this.repoRoot = repoRoot;
this.fullPath = repoRoot + suffix;
}
}
}

View File

@@ -36,12 +36,14 @@ class CompositeStubDownloaderBuilder implements StubDownloaderBuilder {
this.builders = builders;
}
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
if (this.builders == null) {
return null;
}
return new CompositeStubDownloader(this.builders, stubRunnerOptions);
}
}
class CompositeStubDownloader implements StubDownloader {
@@ -49,6 +51,7 @@ class CompositeStubDownloader implements StubDownloader {
private static final Log log = LogFactory.getLog(CompositeStubDownloader.class);
private final List<StubDownloaderBuilder> builders;
private final StubRunnerOptions stubRunnerOptions;
CompositeStubDownloader(List<StubDownloaderBuilder> builders,
@@ -56,14 +59,13 @@ class CompositeStubDownloader implements StubDownloader {
this.builders = builders;
this.stubRunnerOptions = stubRunnerOptions;
if (log.isDebugEnabled()) {
log.debug("Registered following stub downloaders " + this.builders
.stream()
.map(b -> b.getClass().getName())
.collect(Collectors.toList()));
log.debug("Registered following stub downloaders " + this.builders.stream()
.map(b -> b.getClass().getName()).collect(Collectors.toList()));
}
}
@Override public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
@Override
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration) {
for (StubDownloaderBuilder builder : this.builders) {
StubDownloader downloader = builder.build(this.stubRunnerOptions);
@@ -71,21 +73,27 @@ class CompositeStubDownloader implements StubDownloader {
continue;
}
if (log.isDebugEnabled()) {
log.debug("Found a matching stub downloader [" + downloader.getClass().getName() + "]");
log.debug("Found a matching stub downloader ["
+ downloader.getClass().getName() + "]");
}
Map.Entry<StubConfiguration, File> entry = downloader
.downloadAndUnpackStubJar(stubConfiguration);
if (entry != null) {
if (log.isDebugEnabled()) {
log.debug("Found a matching entry [" + entry + "] by stub downloader [" + downloader.getClass().getName() + "]");
log.debug(
"Found a matching entry [" + entry + "] by stub downloader ["
+ downloader.getClass().getName() + "]");
}
return entry;
} else {
}
else {
log.warn("Stub Downloader [" + downloader.getClass().getName() + "] "
+ "failed to find an entry for [" + stubConfiguration.toColonSeparatedDependencyNotation() + "]. "
+ "failed to find an entry for ["
+ stubConfiguration.toColonSeparatedDependencyNotation() + "]. "
+ "Will proceed to the next one");
}
}
return null;
}
}

View File

@@ -30,24 +30,28 @@ import org.springframework.util.StringUtils;
* inclusion patterns
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public class ContractDownloader {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final StubDownloader stubDownloader;
private final StubConfiguration contractsJarStubConfiguration;
private final String contractsPath;
private final String projectGroupId;
private final String projectArtifactId;
private final String projectVersion;
public ContractDownloader(StubDownloader stubDownloader,
StubConfiguration contractsJarStubConfiguration,
String contractsPath, String projectGroupId, String projectArtifactId,
String projectVersion) {
StubConfiguration contractsJarStubConfiguration, String contractsPath,
String projectGroupId, String projectArtifactId, String projectVersion) {
this.stubDownloader = stubDownloader;
this.contractsJarStubConfiguration = contractsJarStubConfiguration;
this.contractsPath = contractsPath;
@@ -58,10 +62,11 @@ public class ContractDownloader {
/**
* Downloads JAR containing all the contracts. Plugin configuration gets updated with
* the inclusion pattern for the downloaded contracts. The JAR with the contracts contains all
* the contracts for all the projects. We're interested only in its subset.
*
* @param config - Plugin configuration that will get updated with the inclusion pattern
* the inclusion pattern for the downloaded contracts. The JAR with the contracts
* contains all the contracts for all the projects. We're interested only in its
* subset.
* @param config - Plugin configuration that will get updated with the inclusion
* pattern
* @return location of the unpacked downloaded stubs
*/
public File unpackedDownloadedContracts(ContractVerifierConfigProperties config) {
@@ -70,15 +75,16 @@ public class ContractDownloader {
return contractsDirectory;
}
public ContractVerifierConfigProperties updatePropertiesWithInclusion(File contractsDirectory,
ContractVerifierConfigProperties config) {
public ContractVerifierConfigProperties updatePropertiesWithInclusion(
File contractsDirectory, ContractVerifierConfigProperties config) {
String pattern;
String includedAntPattern;
if (StringUtils.hasText(this.contractsPath)) {
pattern = patternFromProperty(contractsDirectory);
log.info("Will pick a pattern from the contractPath property");
includedAntPattern = wrapWithAntPattern(contractsPath());
} else {
}
else {
log.info("Will pick a pattern from group id and artifact id");
if (hasGavInPath(contractsDirectory)) {
if (log.isDebugEnabled()) {
@@ -88,12 +94,14 @@ public class ContractDownloader {
// we're already under proper folder (for the given group and artifact)
pattern = fileToPattern(contractsDirectory);
includedAntPattern = "**/";
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("No group & artifact in path");
}
pattern = groupArtifactToPattern(contractsDirectory);
includedAntPattern = wrapWithAntPattern(slashSeparatedGroupId() + "/" + this.projectArtifactId);
includedAntPattern = wrapWithAntPattern(
slashSeparatedGroupId() + "/" + this.projectArtifactId);
}
}
log.info("Pattern to pick contracts equals [" + pattern + "]");
@@ -120,13 +128,11 @@ public class ContractDownloader {
}
private boolean hasVersionInPath(File file) {
return file.getAbsolutePath()
.contains(this.projectVersion);
return file.getAbsolutePath().contains(this.projectVersion);
}
private boolean hasSeparatedGroupInPath(File file, String separator) {
return file.getAbsolutePath()
.contains(groupAndArtifact(separator));
return file.getAbsolutePath().contains(groupAndArtifact(separator));
}
private String groupAndArtifact(String separator) {
@@ -134,9 +140,9 @@ public class ContractDownloader {
}
private String patternFromProperty(File contractsDirectory) {
return ("^" + contractsDirectory.getAbsolutePath() +
"(" + File.separator + ")?" + ".*" +
contractsPath().replace("/", File.separator) + ".*$").replace("\\", "\\\\");
return ("^" + contractsDirectory.getAbsolutePath() + "(" + File.separator + ")?"
+ ".*" + contractsPath().replace("/", File.separator) + ".*$")
.replace("\\", "\\\\");
}
private String contractsPath() {
@@ -144,18 +150,21 @@ public class ContractDownloader {
}
private String surroundWithSeparator(String string) {
String path = string.startsWith(File.separator) ? string : File.separator + string;
String path = string.startsWith(File.separator) ? string
: File.separator + string;
return path.endsWith(File.separator) ? path : path + File.separator;
}
private String wrapWithAntPattern(String path) {
String changedPath = path.replace(File.separator, "/");
return "**" + surroundWithSeparator(changedPath).replace(File.separator, "/") + "**/";
return "**" + surroundWithSeparator(changedPath).replace(File.separator, "/")
+ "**/";
}
private File unpackAndDownloadContracts() {
if (log.isDebugEnabled()) {
log.debug("Will download contracts for [" + this.contractsJarStubConfiguration + "]");
log.debug("Will download contracts for [" + this.contractsJarStubConfiguration
+ "]");
}
Map.Entry<StubConfiguration, File> unpackedContractStubs = this.stubDownloader
.downloadAndUnpackStubJar(this.contractsJarStubConfiguration);
@@ -166,23 +175,17 @@ public class ContractDownloader {
}
private String groupArtifactToPattern(File contractsDirectory) {
return ("^" +
contractsDirectory.getAbsolutePath() +
"(" + File.separator + ")?" + ".*" +
slashSeparatedGroupId() +
File.separator +
this.projectArtifactId
+ File.separator +
".*$").replace("\\", "\\\\");
return ("^" + contractsDirectory.getAbsolutePath() + "(" + File.separator + ")?"
+ ".*" + slashSeparatedGroupId() + File.separator + this.projectArtifactId
+ File.separator + ".*$").replace("\\", "\\\\");
}
private String fileToPattern(File contractsDirectory) {
return ("^" +
contractsDirectory.getAbsolutePath() +
".*$").replace("\\", "\\\\");
return ("^" + contractsDirectory.getAbsolutePath() + ".*$").replace("\\", "\\\\");
}
private String slashSeparatedGroupId() {
return this.projectGroupId.replace(".", File.separator);
}
}

View File

@@ -39,14 +39,20 @@ public class ContractProjectUpdater {
private static final Log log = LogFactory.getLog(ContractProjectUpdater.class);
private static final int DEFAULT_ATTEMPTS_NO = 10;
private static final long DEFAULT_WAIT_BETWEEN_ATTEMPTS = 1000;
// TODO: Add this to the documentation
private static final String DEFAULT_COMMIT_MESSAGE = "Updating project [$project] with stubs";
private static final String GIT_ATTEMPTS_NO_PROP = "git.no-of-attempts";
private static final String GIT_WAIT_BETWEEN_ATTEMPTS = "git.wait-between-attempts";
private static final String GIT_COMMIT_MESSAGE = "git.commit-message";
private final StubRunnerOptions stubRunnerOptions;
private final GitContractsRepo gitContractsRepo;
public ContractProjectUpdater(StubRunnerOptions stubRunnerOptions) {
@@ -66,40 +72,47 @@ public class ContractProjectUpdater {
this.stubRunnerOptions.stubRepositoryRoot, this.stubRunnerOptions);
copyStubs(projectName, rootStubsFolder, clonedRepo);
GitRepo gitRepo = new GitRepo(clonedRepo, properties);
String msg = StubRunnerPropertyUtils.getProperty(this.stubRunnerOptions.getProperties(),
GIT_COMMIT_MESSAGE);
GitRepo.CommitResult commit = gitRepo
.commit(clonedRepo, commitMessage(projectName, msg));
String msg = StubRunnerPropertyUtils
.getProperty(this.stubRunnerOptions.getProperties(), GIT_COMMIT_MESSAGE);
GitRepo.CommitResult commit = gitRepo.commit(clonedRepo,
commitMessage(projectName, msg));
if (commit == GitRepo.CommitResult.EMPTY) {
log.info("There were no changes to commit. Won't push the changes");
return;
}
String attempts = StubRunnerPropertyUtils.getProperty(this.stubRunnerOptions.getProperties(),
GIT_ATTEMPTS_NO_PROP);
int intAttempts = StringUtils.hasText(attempts) ? Integer.parseInt(attempts) : DEFAULT_ATTEMPTS_NO;
String wait = StubRunnerPropertyUtils.getProperty(this.stubRunnerOptions.getProperties(),
GIT_WAIT_BETWEEN_ATTEMPTS);
long longWait = StringUtils.hasText(wait) ? Long.parseLong(wait) : DEFAULT_WAIT_BETWEEN_ATTEMPTS;
String attempts = StubRunnerPropertyUtils.getProperty(
this.stubRunnerOptions.getProperties(), GIT_ATTEMPTS_NO_PROP);
int intAttempts = StringUtils.hasText(attempts) ? Integer.parseInt(attempts)
: DEFAULT_ATTEMPTS_NO;
String wait = StubRunnerPropertyUtils.getProperty(
this.stubRunnerOptions.getProperties(), GIT_WAIT_BETWEEN_ATTEMPTS);
long longWait = StringUtils.hasText(wait) ? Long.parseLong(wait)
: DEFAULT_WAIT_BETWEEN_ATTEMPTS;
tryToPushCurrentBranch(clonedRepo, gitRepo, intAttempts, longWait);
}
private void tryToPushCurrentBranch(File clonedRepo, GitRepo gitRepo, int intAttempts,
long longWait) {
int currentAttempt = 0;
while(currentAttempt < intAttempts) {
log.info("Trying to push changes, attempt " + (currentAttempt + 1) + "/" + intAttempts);
while (currentAttempt < intAttempts) {
log.info("Trying to push changes, attempt " + (currentAttempt + 1) + "/"
+ intAttempts);
gitRepo.pull(clonedRepo);
log.info("Successfully pulled changes from remote for project with contract and stubs");
log.info(
"Successfully pulled changes from remote for project with contract and stubs");
try {
gitRepo.pushCurrentBranch(clonedRepo);
log.info("Successfully pushed changes with current stubs");
break;
} catch (IllegalStateException e) {
}
catch (IllegalStateException e) {
// empty
log.error("Exception occurred while trying to push the changes", e);
currentAttempt++;
if (currentAttempt == intAttempts) {
throw new IllegalStateException("Failed to push changes to the project with contracts and stubs. Exceeded number of retries [" + intAttempts + "]");
throw new IllegalStateException(
"Failed to push changes to the project with contracts and stubs. Exceeded number of retries ["
+ intAttempts + "]");
}
try {
Thread.sleep(longWait);
@@ -112,9 +125,8 @@ public class ContractProjectUpdater {
}
private String commitMessage(String projectName, String msg) {
return StringUtils.hasText(msg) ?
replaceProject(projectName, msg) :
replaceProject(projectName, DEFAULT_COMMIT_MESSAGE);
return StringUtils.hasText(msg) ? replaceProject(projectName, msg)
: replaceProject(projectName, DEFAULT_COMMIT_MESSAGE);
}
private String replaceProject(String projectName, String msg) {
@@ -124,36 +136,44 @@ public class ContractProjectUpdater {
private void copyStubs(String projectName, Path rootStubsFolder, File clonedRepo) {
try {
if (log.isDebugEnabled()) {
log.debug("Copying stubs from [" + rootStubsFolder.toString() + "] to the cloned repo [" + clonedRepo.getAbsolutePath() + "] for project [" + projectName + "]");
log.debug("Copying stubs from [" + rootStubsFolder.toString()
+ "] to the cloned repo [" + clonedRepo.getAbsolutePath()
+ "] for project [" + projectName + "]");
}
Files.walkFileTree(rootStubsFolder,
new DirectoryCopyingVisitor(rootStubsFolder, clonedRepo.toPath()));
if (log.isDebugEnabled()) {
log.debug("Successfully copied stubs to the cloned repo for project [" + projectName + "]");
log.debug("Successfully copied stubs to the cloned repo for project ["
+ projectName + "]");
}
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
class DirectoryCopyingVisitor extends SimpleFileVisitor<Path> {
private static final Log log = LogFactory.getLog(DirectoryCopyingVisitor.class);
private final Path from;
private final Path to;
DirectoryCopyingVisitor(Path from, Path to) {
this.from = from;
this.to = to;
if (log.isDebugEnabled()) {
log.debug("Will copy from [" + from.toString() + "] to [" + to.toString() + "]");
log.debug("Will copy from [" + from.toString() + "] to [" + to.toString()
+ "]");
}
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
Path relativePath = this.from.relativize(dir);
if (".git".equals(relativePath.toString())) {
return FileVisitResult.SKIP_SUBTREE;
@@ -164,7 +184,8 @@ class DirectoryCopyingVisitor extends SimpleFileVisitor<Path> {
log.debug("Created a folder [" + targetPath.toString() + "]");
}
Files.createDirectory(targetPath);
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("Folder [" + targetPath.toString() + "] already exists");
}
@@ -173,12 +194,15 @@ class DirectoryCopyingVisitor extends SimpleFileVisitor<Path> {
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Path relativePath = this.to.resolve(this.from.relativize(file));
Files.copy(file, relativePath, StandardCopyOption.REPLACE_EXISTING);
if (log.isDebugEnabled()) {
log.debug("Copied file from [" + file.toString() + "] to [" + relativePath.toString() + "]");
log.debug("Copied file from [" + file.toString() + "] to ["
+ relativePath.toString() + "]");
}
return FileVisitResult.CONTINUE;
}
}

View File

@@ -60,8 +60,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;
/**
* Abstraction over a Git repo. Can cloned repo from a given location
* and check its branch.
* Abstraction over a Git repo. Can cloned repo from a given location and check its
* branch.
*
* taken from: https://github.com/spring-cloud/spring-cloud-release-tools
*
@@ -101,7 +101,7 @@ class GitRepo {
*/
File cloneProject(URI projectUri) {
try {
log.info("Cloning repo from [" + projectUri + "] to [" + this.basedir + "]");
log.info("Cloning repo from [" + projectUri + "] to [" + this.basedir + "]");
Git git = cloneToBasedir(projectUri, this.basedir);
if (git != null) {
git.close();
@@ -158,29 +158,34 @@ class GitRepo {
* @param message - commit message
*/
CommitResult commit(File project, String message) {
try(Git git = this.gitFactory.open(file(project))) {
try (Git git = this.gitFactory.open(file(project))) {
git.add().addFilepattern(".").call();
git.commit().setAllowEmpty(false).setMessage(message).call();
log.info("Commited successfully with message [" + message + "]");
return CommitResult.SUCCESSFUL;
} catch (EmtpyCommitException e) {
}
catch (EmtpyCommitException e) {
log.info("There were no changes detected. Will not commit an empty commit");
return CommitResult.EMPTY;
} catch (Exception e) {
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
void reset(File project) {
try(Git git = this.gitFactory.open(file(project))) {
try (Git git = this.gitFactory.open(file(project))) {
git.reset().setMode(ResetCommand.ResetType.HARD).call();
} catch (Exception e) {
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
enum CommitResult {
SUCCESSFUL, EMPTY
}
/**
@@ -188,9 +193,10 @@ class GitRepo {
* @param project - Git project
*/
void pushCurrentBranch(File project) {
try(Git git = this.gitFactory.open(file(project))) {
try (Git git = this.gitFactory.open(file(project))) {
this.gitFactory.push(git).call();
} catch (Exception e) {
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
@@ -223,8 +229,7 @@ class GitRepo {
}
}
private Ref checkoutBranch(File projectDir, String branch)
throws GitAPIException {
private Ref checkoutBranch(File projectDir, String branch) throws GitAPIException {
Git git = this.gitFactory.open(projectDir);
CheckoutCommand command = git.checkout().setName(branch);
try {
@@ -236,7 +241,8 @@ class GitRepo {
catch (GitAPIException e) {
deleteBaseDirIfExists();
throw e;
} finally {
}
finally {
git.close();
}
}
@@ -272,8 +278,8 @@ class GitRepo {
return containsBranch(git, label, null);
}
private boolean containsBranch(Git git, String label, ListBranchCommand.ListMode listMode)
throws GitAPIException {
private boolean containsBranch(Git git, String label,
ListBranchCommand.ListMode listMode) throws GitAPIException {
ListBranchCommand command = git.branchList();
if (listMode != null) {
command.setListMode(listMode);
@@ -299,35 +305,42 @@ class GitRepo {
}
/**
* Wraps the static method calls to {@link Git} and
* {@link CloneCommand} allowing for easier unit testing.
* Wraps the static method calls to {@link Git} and {@link CloneCommand} allowing for
* easier unit testing.
*/
static class JGitFactory {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final Logger log = LoggerFactory
.getLogger(MethodHandles.lookup().lookupClass());
private final JschConfigSessionFactory factory = new JschConfigSessionFactory() {
@Override protected void configure(OpenSshConfig.Host host, Session session) {
@Override
protected void configure(OpenSshConfig.Host host, Session session) {
}
@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
Connector connector = null;
try {
if(SSHAgentConnector.isConnectorAvailable()){
if (SSHAgentConnector.isConnectorAvailable()) {
USocketFactory usf = new JNAUSocketFactory();
connector = new SSHAgentConnector(usf);
}
log.info("Successfully connected to an agent");
} catch (AgentProxyException e) {
log.error("Exception occurred while trying to connect to agent. Will create"
+ "the default JSch connection", e);
}
catch (AgentProxyException e) {
log.error(
"Exception occurred while trying to connect to agent. Will create"
+ "the default JSch connection",
e);
return super.createDefaultJSch(fs);
}
final JSch jsch = super.createDefaultJSch(fs);
if (connector != null) {
JSch.setConfig("PreferredAuthentications", "publickey,password");
IdentityRepository identityRepository = new RemoteIdentityRepository(connector);
IdentityRepository identityRepository = new RemoteIdentityRepository(
connector);
jsch.setIdentityRepository(identityRepository);
}
return jsch;
@@ -338,9 +351,11 @@ class GitRepo {
JGitFactory(GitStubDownloaderProperties properties) {
if (org.springframework.util.StringUtils.hasText(properties.username)) {
log.info("Passed username and password - will set a custom credentials provider");
log.info(
"Passed username and password - will set a custom credentials provider");
this.provider = credentialsProvider(properties);
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("No custom credentials provider will be set");
}
@@ -349,8 +364,7 @@ class GitRepo {
}
CredentialsProvider credentialsProvider(GitStubDownloaderProperties properties) {
return new UsernamePasswordCredentialsProvider(
properties.username,
return new UsernamePasswordCredentialsProvider(properties.username,
properties.password);
}
@@ -367,20 +381,17 @@ class GitRepo {
};
CloneCommand getCloneCommandByCloneRepository() {
return Git.cloneRepository()
.setCredentialsProvider(this.provider)
return Git.cloneRepository().setCredentialsProvider(this.provider)
.setTransportConfigCallback(this.callback);
}
PushCommand push(Git git) {
return git.push()
.setCredentialsProvider(this.provider)
return git.push().setCredentialsProvider(this.provider)
.setTransportConfigCallback(this.callback);
}
PullCommand pull(Git git) {
return git.pull()
.setCredentialsProvider(this.provider)
return git.pull().setCredentialsProvider(this.provider)
.setTransportConfigCallback(this.callback);
}
@@ -392,5 +403,7 @@ class GitRepo {
throw new IllegalStateException(e);
}
}
}
}

View File

@@ -10,6 +10,7 @@ import java.util.Collection;
* @since 1.1.0
*/
public interface HttpServerStub {
/**
* Port on which the server is running
*/
@@ -21,14 +22,12 @@ public interface HttpServerStub {
boolean isRunning();
/**
* Starts the server on a random port. Should return itself
* to allow chaining.
* Starts the server on a random port. Should return itself to allow chaining.
*/
HttpServerStub start();
/**
* Starts the server on a given port. Should return itself
* to allow chaining.
* Starts the server on a given port. Should return itself to allow chaining.
*/
HttpServerStub start(int port);
@@ -38,8 +37,8 @@ public interface HttpServerStub {
HttpServerStub stop();
/**
* Registers the stub files in the HTTP server stub. Should return itself
* to allow chaining.
* Registers the stub files in the HTTP server stub. Should return itself to allow
* chaining.
*/
HttpServerStub registerMappings(Collection<File> stubFiles);
@@ -52,4 +51,5 @@ public interface HttpServerStub {
* Returns {@code true} if the file is a valid stub mapping
*/
boolean isAccepted(File file);
}

View File

@@ -23,4 +23,5 @@ package org.springframework.cloud.contract.stubrunner;
*/
@SuppressWarnings("serial")
class MessageNotMatchingException extends RuntimeException {
}

View File

@@ -7,6 +7,7 @@ import java.util.Collection;
* @author Marcin Grzejszczak
*/
class NoOpHttpServerStub implements HttpServerStub {
@Override
public int port() {
return -1;
@@ -37,11 +38,14 @@ class NoOpHttpServerStub implements HttpServerStub {
return this;
}
@Override public String registeredMappings() {
@Override
public String registeredMappings() {
return "";
}
@Override public boolean isAccepted(File file) {
@Override
public boolean isAccepted(File file) {
return true;
}
}

View File

@@ -27,11 +27,11 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Uses {@code META-INF/spring.factories} to read {@link ProtocolResolver} list
* that gets added to {@link DefaultResourceLoader}. Each implementor of a new
* {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}, if
* one uses a new protocol, should register their own {@link ProtocolResolver} so
* that Stub Runner can convert a {@link String} version of a URI to a {@link Resource}.
* Uses {@code META-INF/spring.factories} to read {@link ProtocolResolver} list that gets
* added to {@link DefaultResourceLoader}. Each implementor of a new
* {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}, if one
* uses a new protocol, should register their own {@link ProtocolResolver} so that Stub
* Runner can convert a {@link String} version of a URI to a {@link Resource}.
*
* IMPORTANT! Internal tool. Do not use.
*
@@ -41,14 +41,16 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
public class ResourceResolver {
private static final Log log = LogFactory.getLog(ResourceResolver.class);
private static final List<ProtocolResolver> RESOLVERS = new ArrayList<>();
private static final DefaultResourceLoader LOADER = new DefaultResourceLoader();
static {
RESOLVERS.addAll(
SpringFactoriesLoader.loadFactories(StubDownloaderBuilder.class, null)
);
RESOLVERS.addAll(new StubDownloaderBuilderProvider().defaultStubDownloaderBuilders());
SpringFactoriesLoader.loadFactories(StubDownloaderBuilder.class, null));
RESOLVERS.addAll(
new StubDownloaderBuilderProvider().defaultStubDownloaderBuilders());
for (ProtocolResolver resolver : RESOLVERS) {
LOADER.addProtocolResolver(resolver);
}
@@ -61,9 +63,13 @@ public class ResourceResolver {
public static Resource resource(String url) {
try {
return LOADER.getResource(url);
} catch (Exception e) {
log.error("Exception occurred while trying to read the resource [" + url + "]", e);
}
catch (Exception e) {
log.error(
"Exception occurred while trying to read the resource [" + url + "]",
e);
return null;
}
}
}

View File

@@ -60,9 +60,10 @@ public final class ScmStubDownloaderBuilder implements StubDownloaderBuilder {
return ACCEPTABLE_PROTOCOLS.stream().anyMatch(url::startsWith);
}
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
if (stubRunnerOptions.getStubsMode() == StubRunnerProperties.StubsMode.CLASSPATH ||
stubRunnerOptions.getStubRepositoryRoot() == null) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
if (stubRunnerOptions.getStubsMode() == StubRunnerProperties.StubsMode.CLASSPATH
|| stubRunnerOptions.getStubRepositoryRoot() == null) {
return null;
}
Resource resource = stubRunnerOptions.getStubRepositoryRoot();
@@ -72,14 +73,15 @@ public final class ScmStubDownloaderBuilder implements StubDownloaderBuilder {
return new GitStubDownloader(stubRunnerOptions);
}
@Override public Resource resolve(String location, ResourceLoader resourceLoader) {
@Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
if (StringUtils.isEmpty(location) || !isProtocolAccepted(location)) {
return null;
}
return new GitResource(location);
}
}
}
/**
* Primitive version of a Git {@link Resource}
@@ -92,17 +94,21 @@ class GitResource extends AbstractResource {
this.rawLocation = location;
}
@Override public String getDescription() {
@Override
public String getDescription() {
return this.rawLocation;
}
@Override public InputStream getInputStream() throws IOException {
@Override
public InputStream getInputStream() throws IOException {
return null;
}
@Override public URI getURI() throws IOException {
@Override
public URI getURI() throws IOException {
return URI.create(this.rawLocation);
}
}
class GitContractsRepo {
@@ -120,34 +126,43 @@ class GitContractsRepo {
File clonedRepo(Resource repo) {
File file = CACHED_LOCATIONS.get(repo);
GitStubDownloaderProperties properties = new GitStubDownloaderProperties(repo, this.options);
GitStubDownloaderProperties properties = new GitStubDownloaderProperties(repo,
this.options);
if (file == null) {
File tmpDirWhereStubsWillBeUnzipped = TemporaryFileStorage.createTempDir(TEMP_DIR_PREFIX);
File tmpDirWhereStubsWillBeUnzipped = TemporaryFileStorage
.createTempDir(TEMP_DIR_PREFIX);
GitRepo gitRepo = new GitRepo(tmpDirWhereStubsWillBeUnzipped, properties);
file = gitRepo.cloneProject(properties.url);
gitRepo.checkout(file, properties.branch);
CACHED_LOCATIONS.put(repo, file);
if (log.isDebugEnabled()) {
log.debug("The project hasn't already been cloned. Cloned it to [" + file + "]");
log.debug("The project hasn't already been cloned. Cloned it to [" + file
+ "]");
}
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("The project has already been cloned to [" + file + "]. Will reset any changes.");
log.debug("The project has already been cloned to [" + file
+ "]. Will reset any changes.");
}
new GitRepo(file, properties).reset(file);
}
return file;
}
}
class GitStubDownloader implements StubDownloader {
private static final Log log = LogFactory.getLog(GitStubDownloader.class);
// Preloading class for the shutdown hook not to throw ClassNotFound
private static final Class CLAZZ = TemporaryFileStorage.class;
private final StubRunnerOptions stubRunnerOptions;
private final boolean deleteStubsAfterTest;
private final GitContractsRepo gitContractsRepo;
GitStubDownloader(StubRunnerOptions stubRunnerOptions) {
@@ -157,48 +172,62 @@ class GitStubDownloader implements StubDownloader {
registerShutdownHook();
}
@Override public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
@Override
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration) {
if (StringUtils.isEmpty(stubConfiguration.version) || "+".equals(stubConfiguration.version)) {
throw new IllegalStateException("Concrete version wasn't passed for [" + stubConfiguration.toColonSeparatedDependencyNotation() + "]");
if (StringUtils.isEmpty(stubConfiguration.version)
|| "+".equals(stubConfiguration.version)) {
throw new IllegalStateException("Concrete version wasn't passed for ["
+ stubConfiguration.toColonSeparatedDependencyNotation() + "]");
}
try {
if (log.isDebugEnabled()) {
log.debug("Trying to find a contract for [" + stubConfiguration.toColonSeparatedDependencyNotation() + "]");
log.debug("Trying to find a contract for ["
+ stubConfiguration.toColonSeparatedDependencyNotation() + "]");
}
Resource repo = this.stubRunnerOptions.getStubRepositoryRoot();
File clonedRepo = this.gitContractsRepo.clonedRepo(repo);
FileWalker walker = new FileWalker(stubConfiguration);
Files.walkFileTree(clonedRepo.toPath(), walker);
if (walker.foundFile != null) {
return new AbstractMap.SimpleEntry<>(stubConfiguration, walker.foundFile.toFile());
return new AbstractMap.SimpleEntry<>(stubConfiguration,
walker.foundFile.toFile());
}
}
catch (IOException e) {
throw new IllegalStateException(e);
}
if (log.isDebugEnabled()) {
log.debug("No matching contracts were found in the repo for [" + stubConfiguration.toColonSeparatedDependencyNotation() + "]. Returning null");
log.debug("No matching contracts were found in the repo for ["
+ stubConfiguration.toColonSeparatedDependencyNotation()
+ "]. Returning null");
}
return null;
}
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(
() -> TemporaryFileStorage.cleanup(GitStubDownloader.this.deleteStubsAfterTest)));
Runtime.getRuntime().addShutdownHook(new Thread(() -> TemporaryFileStorage
.cleanup(GitStubDownloader.this.deleteStubsAfterTest)));
}
}
class GitStubDownloaderProperties {
private static final Log log = LogFactory.getLog(GitStubDownloaderProperties.class);
private static final String GIT_BRANCH_PROPERTY = "git.branch";
private static final String GIT_USERNAME_PROPERTY = "git.username";
private static final String GIT_PASSWORD_PROPERTY = "git.password";
final URI url;
final String username;
final String password;
final String branch;
GitStubDownloaderProperties(Resource repo, StubRunnerOptions options) {
@@ -206,24 +235,28 @@ class GitStubDownloaderProperties {
Map<String, String> args = options.getProperties();
try {
repoUrl = schemeSpecificPart(repo.getURI());
} catch (IOException e) {
}
catch (IOException e) {
throw new IllegalStateException(e);
}
// if we had git://https://... we want the part starting from https
// if we had git://git@... we want the full address again
// if the URL starts with git@... and ends with .git, we want to remove it
String modifiedRepo = repoUrl.startsWith("git@") ? modifyUrlForGitRepo(repoUrl) : repoUrl;
String modifiedRepo = repoUrl.startsWith("git@") ? modifyUrlForGitRepo(repoUrl)
: repoUrl;
this.url = URI.create(modifiedRepo);
String username = StubRunnerPropertyUtils.getProperty(args, GIT_USERNAME_PROPERTY);
String username = StubRunnerPropertyUtils.getProperty(args,
GIT_USERNAME_PROPERTY);
this.username = StringUtils.hasText(username) ? username : options.getUsername();
String password = StubRunnerPropertyUtils.getProperty(args, GIT_PASSWORD_PROPERTY);
String password = StubRunnerPropertyUtils.getProperty(args,
GIT_PASSWORD_PROPERTY);
this.password = StringUtils.hasText(password) ? password : options.getPassword();
String branch = StubRunnerPropertyUtils.getProperty(args, GIT_BRANCH_PROPERTY);
this.branch = StringUtils.hasText(branch) ? branch : "master";
if (log.isDebugEnabled()) {
log.debug("Repo url is [" + repoUrl + "], modified url string "
+ "is [" + modifiedRepo + "] URL is [" + this.url + "] and "
+ "branch is [" + this.branch + "]");
log.debug("Repo url is [" + repoUrl + "], modified url string " + "is ["
+ modifiedRepo + "] URL is [" + this.url + "] and " + "branch is ["
+ this.branch + "]");
}
}
@@ -238,12 +271,15 @@ class GitStubDownloaderProperties {
private String modifyUrlForGitRepo(String gitRepo) {
return "git:" + gitRepo;
}
}
class FileWalker extends SimpleFileVisitor<Path> {
private final PathMatcher matcherWithDot;
private final PathMatcher matcherWithoutDot;
Path foundFile;
FileWalker(StubConfiguration stubConfiguration) {
@@ -253,20 +289,21 @@ class FileWalker extends SimpleFileVisitor<Path> {
.getPathMatcher("glob:" + matcherGlob(stubConfiguration, "/"));
}
private String matcherGlob(StubConfiguration stubConfiguration, String groupArtifactSeparator) {
private String matcherGlob(StubConfiguration stubConfiguration,
String groupArtifactSeparator) {
return "**" + stubConfiguration.groupId + groupArtifactSeparator
+ stubConfiguration.artifactId + "/"
+ stubConfiguration.version;
+ stubConfiguration.artifactId + "/" + stubConfiguration.version;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
if (this.matcherWithDot.matches(dir.toAbsolutePath()) ||
this.matcherWithoutDot.matches(dir.toAbsolutePath())) {
if (this.matcherWithDot.matches(dir.toAbsolutePath())
|| this.matcherWithoutDot.matches(dir.toAbsolutePath())) {
this.foundFile = dir;
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
}

View File

@@ -23,13 +23,18 @@ import org.springframework.util.StringUtils;
* groupId:artifactId:version:classifier notation
*/
public class StubConfiguration {
private static final String STUB_COLON_DELIMITER = ":";
static final String DEFAULT_VERSION = "+";
public static final String DEFAULT_CLASSIFIER = "stubs";
final String groupId;
final String artifactId;
final String version;
final String classifier;
public StubConfiguration(String groupId, String artifactId, String version) {
@@ -87,18 +92,16 @@ public class StubConfiguration {
}
/**
* Returns a colon separated representation of the stub configuration
* (e.g. groupid:artifactid:version:classifier)
* Returns a colon separated representation of the stub configuration (e.g.
* groupid:artifactid:version:classifier)
*/
public String toColonSeparatedDependencyNotation() {
if (!isDefined()) {
return "";
}
return StringUtils.arrayToDelimitedString(
new String[] { nullCheck(this.groupId),
nullCheck(this.artifactId),
nullCheck(this.version),
nullCheck(this.classifier) },
new String[] { nullCheck(this.groupId), nullCheck(this.artifactId),
nullCheck(this.version), nullCheck(this.classifier) },
STUB_COLON_DELIMITER);
}
@@ -108,10 +111,9 @@ public class StubConfiguration {
/**
* Checks if ivy notation matches group and artifact ids
*
* @param ivyNotationAsString - e.g. group:artifact:version:classifier
* @return {@code true} if artifact id matches and there's no group id. Or if
* both group id and artifact id are present and matching
* @return {@code true} if artifact id matches and there's no group id. Or if both
* group id and artifact id are present and matching
*/
public boolean groupIdAndArtifactMatches(String ivyNotationAsString) {
String[] parts = ivyNotationFrom(ivyNotationAsString);
@@ -127,8 +129,8 @@ public class StubConfiguration {
* Returns {@code true} for a snapshot or a LATEST (+) version
*/
public boolean isVersionChanging() {
return DEFAULT_VERSION.equals(this.version) ||
this.version.toLowerCase().contains("snapshot");
return DEFAULT_VERSION.equals(this.version)
|| this.version.toLowerCase().contains("snapshot");
}
public String getGroupId() {
@@ -151,7 +153,8 @@ public class StubConfiguration {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.artifactId == null) ? 0 : this.artifactId.hashCode());
result = prime * result
+ ((this.artifactId == null) ? 0 : this.artifactId.hashCode());
result = prime * result + ((this.groupId == null) ? 0 : this.groupId.hashCode());
return result;
}
@@ -185,13 +188,16 @@ public class StubConfiguration {
if (strings.length == 1) {
return this.artifactId.equals(ivyNotationAsString);
}
if (strings.length >= 2 && !(this.groupId.equals(strings[0]) && this.artifactId.equals(strings[1]))) {
if (strings.length >= 2 && !(this.groupId.equals(strings[0])
&& this.artifactId.equals(strings[1]))) {
return false;
}
if (strings.length >= 3 && !(this.version.equals(strings[2]) || DEFAULT_VERSION.equals(strings[2]))) {
if (strings.length >= 3 && !(this.version.equals(strings[2])
|| DEFAULT_VERSION.equals(strings[2]))) {
return false;
}
if (strings.length == 4 && !(this.classifier.equals(strings[3]) || DEFAULT_CLASSIFIER.equals(strings[3]))) {
if (strings.length == 4 && !(this.classifier.equals(strings[3])
|| DEFAULT_CLASSIFIER.equals(strings[3]))) {
return false;
}
return true;
@@ -210,4 +216,5 @@ public class StubConfiguration {
public String toString() {
return toColonSeparatedDependencyNotation();
}
}

View File

@@ -20,11 +20,11 @@ import java.io.File;
import java.util.Map;
/**
* Contract for providing a tuple containing configuration of a downloaded
* and unpacked stub, together with the file location of that extracted artifact.
* Contract for providing a tuple containing configuration of a downloaded and unpacked
* stub, together with the file location of that extracted artifact.
*
* Note: Actually the artifact doesn't have to be a JAR. method name contains
* that suffix for historical reasons.
* Note: Actually the artifact doesn't have to be a JAR. method name contains that suffix
* for historical reasons.
*
* @author Marcin Grzejszczak
* @since 1.0.0
@@ -32,8 +32,11 @@ import java.util.Map;
public interface StubDownloader {
/**
* Returns a mapping of updated StubConfiguration (it will contain the resolved version) and the location of the downloaded JAR.
* If there was no artifact this method will return {@code null}.
* Returns a mapping of updated StubConfiguration (it will contain the resolved
* version) and the location of the downloaded JAR. If there was no artifact this
* method will return {@code null}.
*/
Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(StubConfiguration stubConfiguration);
Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration);
}

View File

@@ -21,14 +21,14 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* Builder for a {@link StubDownloader}. Can't allow direct usage
* of {@link StubDownloader} cause in order to register instances
* of this interface in {@link org.springframework.core.io.support.SpringFactoriesLoader}
* one needs a default constructor whereas the {@link StubDownloader}
* instances need to be constructed from stub related options.
* Builder for a {@link StubDownloader}. Can't allow direct usage of
* {@link StubDownloader} cause in order to register instances of this interface in
* {@link org.springframework.core.io.support.SpringFactoriesLoader} one needs a default
* constructor whereas the {@link StubDownloader} instances need to be constructed from
* stub related options.
*
* Since {@code 2.0.0} extends {@link ProtocolResolver}. Implementations have
* to tell Spring how to parse the repository root String into a resource.
* Since {@code 2.0.0} extends {@link ProtocolResolver}. Implementations have to tell
* Spring how to parse the repository root String into a resource.
*
* @author Marcin Grzejszczak
* @since 1.1.0
@@ -36,11 +36,14 @@ import org.springframework.core.io.ResourceLoader;
public interface StubDownloaderBuilder extends ProtocolResolver {
/**
* @return {@link StubDownloader} instance of {@code null} if current parameters don't allow building the instance
* @return {@link StubDownloader} instance of {@code null} if current parameters don't
* allow building the instance
*/
StubDownloader build(StubRunnerOptions stubRunnerOptions);
@Override default Resource resolve(String location, ResourceLoader resourceLoader) {
@Override
default Resource resolve(String location, ResourceLoader resourceLoader) {
return null;
}
}

View File

@@ -7,8 +7,8 @@ import java.util.List;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Provider for {@link StubDownloaderBuilder}. It can also pick a default
* downloader if none is provided
* Provider for {@link StubDownloaderBuilder}. It can also pick a default downloader if
* none is provided
*
* @author Marcin Grzejszczak
* @since 1.1.0
@@ -28,8 +28,10 @@ public class StubDownloaderBuilderProvider {
/**
* @param stubRunnerOptions
* @param additionalBuilders - optional array of {@link StubDownloaderBuilder}s to append to the list of builders
* @return composite {@link StubDownloader} that iterates over a list of stub downloaders
* @param additionalBuilders - optional array of {@link StubDownloaderBuilder}s to
* append to the list of builders
* @return composite {@link StubDownloader} that iterates over a list of stub
* downloaders
*/
public StubDownloader get(StubRunnerOptions stubRunnerOptions,
StubDownloaderBuilder... additionalBuilders) {
@@ -43,8 +45,8 @@ public class StubDownloaderBuilderProvider {
}
List<StubDownloaderBuilder> defaultStubDownloaderBuilders() {
return Arrays
.asList(new ScmStubDownloaderBuilder(), new ClasspathStubProvider(),
new AetherStubDownloaderBuilder());
return Arrays.asList(new ScmStubDownloaderBuilder(), new ClasspathStubProvider(),
new AetherStubDownloaderBuilder());
}
}

View File

@@ -23,19 +23,20 @@ import java.util.Map;
import org.springframework.cloud.contract.spec.Contract;
public interface StubFinder extends StubTrigger {
/**
* For the given groupId and artifactId tries to find the matching
* URL of the running stub.
*
* @param groupId - might be null. In that case a search only via artifactId takes place
* For the given groupId and artifactId tries to find the matching URL of the running
* stub.
* @param groupId - might be null. In that case a search only via artifactId takes
* place
* @return URL of a running stub or throws exception if not found
*/
URL findStubUrl(String groupId, String artifactId) throws StubNotFoundException;
/**
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]} tries to
* find the matching URL of the running stub. You can also pass only {@code artifactId}.
*
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]}
* tries to find the matching URL of the running stub. You can also pass only
* {@code artifactId}.
* @param ivyNotation - Ivy representation of the Maven artifact
* @return URL of a running stub or throws exception if not found
*/
@@ -50,4 +51,5 @@ public interface StubFinder extends StubTrigger {
* Returns the list of Contracts
*/
Map<StubConfiguration, Collection<Contract>> getContracts();
}

View File

@@ -9,10 +9,12 @@ package org.springframework.cloud.contract.stubrunner;
public class StubNotFoundException extends RuntimeException {
public StubNotFoundException(String groupId, String artifactId) {
super("Stub not found for groupid [" + groupId + "] and artifactid [" + artifactId + "]");
super("Stub not found for groupid [" + groupId + "] and artifactid [" + artifactId
+ "]");
}
public StubNotFoundException(String ivyNotation) {
super("Stub not found for stub with notation [" + ivyNotation + "]");
}
}

View File

@@ -46,10 +46,15 @@ class StubRepository {
private static final Log log = LogFactory.getLog(StubRepository.class);
private final File path;
final List<File> stubs;
final Collection<Contract> contracts;
private final List<ContractConverter> contractConverters;
private final List<HttpServerStub> httpServerStubs;
private final StubRunnerOptions options;
StubRepository(File repository, List<HttpServerStub> httpServerStubs,
@@ -58,9 +63,11 @@ class StubRepository {
throw new IllegalArgumentException(
"Missing descriptor repository under path [" + repository + "]");
}
this.contractConverters = SpringFactoriesLoader.loadFactories(ContractConverter.class, null);
this.contractConverters = SpringFactoriesLoader
.loadFactories(ContractConverter.class, null);
if (log.isTraceEnabled()) {
log.trace("Found the following contract converters " + this.contractConverters);
log.trace(
"Found the following contract converters " + this.contractConverters);
}
this.httpServerStubs = httpServerStubs;
this.path = repository;
@@ -107,8 +114,7 @@ class StubRepository {
: Collections.<File>emptyList();
}
private List<File> collectMappings(
File descriptorsDirectory) {
private List<File> collectMappings(File descriptorsDirectory) {
final List<File> mappingDescriptors = new ArrayList<>();
try {
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
@@ -117,7 +123,8 @@ class StubRepository {
public FileVisitResult visitFile(Path path,
BasicFileAttributes attrs) throws IOException {
File file = path.toFile();
if (httpServerStubAccepts(file) && isStubPerConsumerPathMatching(file)) {
if (httpServerStubAccepts(file)
&& isStubPerConsumerPathMatching(file)) {
mappingDescriptors.add(file);
}
return super.visitFile(path, attrs);
@@ -155,7 +162,8 @@ class StubRepository {
}
@SuppressWarnings("unchecked")
private Collection<Contract> collectContractDescriptors(final File descriptorsDirectory) {
private Collection<Contract> collectContractDescriptors(
final File descriptorsDirectory) {
final List<Contract> contractDescriptors = new ArrayList<>();
try {
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
@@ -168,11 +176,20 @@ class StubRepository {
if (isStubPerConsumerPathMatching(file)) {
if (isContractDescriptor(file)) {
contractDescriptors
.addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file));
} else if (converter != null && converter.isAccepted(file)) {
contractDescriptors.addAll(converter.convertFrom(file));
} else if (YamlContractConverter.INSTANCE.isAccepted(file)) {
contractDescriptors.addAll(YamlContractConverter.INSTANCE.convertFrom(file));
.addAll(ContractVerifierDslConverter
.convertAsCollection(
file.getParentFile(), file));
}
else if (converter != null
&& converter.isAccepted(file)) {
contractDescriptors
.addAll(converter.convertFrom(file));
}
else if (YamlContractConverter.INSTANCE
.isAccepted(file)) {
contractDescriptors
.addAll(YamlContractConverter.INSTANCE
.convertFrom(file));
}
}
return super.visitFile(path, attrs);
@@ -194,7 +211,9 @@ class StubRepository {
String absolutePath = file.getAbsolutePath();
boolean stubPerConsumerMatching = absolutePath.contains(searchedConsumerName);
if (log.isDebugEnabled()) {
log.debug("Absolute path [" + absolutePath + "] contains [" + searchedConsumerName + "] in its path [" + stubPerConsumerMatching + "]");
log.debug("Absolute path [" + absolutePath + "] contains ["
+ searchedConsumerName + "] in its path [" + stubPerConsumerMatching
+ "]");
}
return stubPerConsumerMatching;
}

View File

@@ -44,8 +44,11 @@ public class StubRunner implements StubRunning {
private static final Log log = LogFactory.getLog(StubRunner.class);
private final StubRepository stubRepository;
private final StubConfiguration stubsConfiguration;
private final StubRunnerOptions stubRunnerOptions;
private final StubRunnerExecutor localStubRunner;
public StubRunner(StubRunnerOptions stubRunnerOptions, String repositoryPath,
@@ -59,31 +62,40 @@ public class StubRunner implements StubRunning {
MessageVerifier<?> contractVerifierMessaging) {
this.stubsConfiguration = stubsConfiguration;
this.stubRunnerOptions = stubRunnerOptions;
List<HttpServerStub> serverStubs = SpringFactoriesLoader.loadFactories(HttpServerStub.class, null);
this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs, this.stubRunnerOptions);
List<HttpServerStub> serverStubs = SpringFactoriesLoader
.loadFactories(HttpServerStub.class, null);
this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs,
this.stubRunnerOptions);
AvailablePortScanner portScanner = new AvailablePortScanner(
stubRunnerOptions.getMinPortValue(), stubRunnerOptions.getMaxPortValue());
this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging, serverStubs);
this.localStubRunner = new StubRunnerExecutor(portScanner,
contractVerifierMessaging, serverStubs);
}
@Override
public RunningStubs runStubs() {
registerShutdownHook();
RunningStubs stubs = this.localStubRunner.runStubs(this.stubRunnerOptions, this.stubRepository,
this.stubsConfiguration);
RunningStubs stubs = this.localStubRunner.runStubs(this.stubRunnerOptions,
this.stubRepository, this.stubsConfiguration);
if (this.stubRunnerOptions.hasMappingsOutputFolder()) {
String registeredMappings = this.localStubRunner.registeredMappings();
if (StringUtils.hasText(registeredMappings)) {
File outputMappings = new File(this.stubRunnerOptions.getMappingsOutputFolder(),
File outputMappings = new File(
this.stubRunnerOptions.getMappingsOutputFolder(),
this.stubsConfiguration.artifactId + "_"
+ stubs.getPort(this.stubsConfiguration.toColonSeparatedDependencyNotation()));
+ stubs.getPort(this.stubsConfiguration
.toColonSeparatedDependencyNotation()));
try {
outputMappings.getParentFile().mkdirs();
clearOldFiles(outputMappings.getParentFile(), this.stubsConfiguration.artifactId);
clearOldFiles(outputMappings.getParentFile(),
this.stubsConfiguration.artifactId);
outputMappings.createNewFile();
Files.write(Paths.get(outputMappings.toURI()), registeredMappings.getBytes());
Files.write(Paths.get(outputMappings.toURI()),
registeredMappings.getBytes());
if (log.isDebugEnabled()) {
log.debug("Stored the mappings for artifactid [" + this.stubsConfiguration.artifactId + "] at [" + outputMappings + "] location");
log.debug("Stored the mappings for artifactid ["
+ this.stubsConfiguration.artifactId + "] at ["
+ outputMappings + "] location");
}
}
catch (IOException e) {
@@ -97,20 +109,22 @@ public class StubRunner implements StubRunning {
private void clearOldFiles(File outputFolder, final String filename) {
File[] files = outputFolder.listFiles(new FilenameFilter() {
@Override public boolean accept(final File dir, final String name) {
@Override
public boolean accept(final File dir, final String name) {
return name.startsWith(filename);
}
});
if (files == null) {
if(log.isDebugEnabled()) {
if (log.isDebugEnabled()) {
log.debug("Failed to retrieve any mappings");
}
return;
}
for (final File file : files) {
if (!file.delete()) {
if(log.isDebugEnabled()) {
log.debug("Exception occurred while trying to remove [" + file.getAbsolutePath() + "]");
if (log.isDebugEnabled()) {
log.debug("Exception occurred while trying to remove ["
+ file.getAbsolutePath() + "]");
}
}
}
@@ -176,4 +190,5 @@ public class StubRunner implements StubRunning {
this.localStubRunner.shutdown();
}
}
}

View File

@@ -51,17 +51,23 @@ class StubRunnerExecutor implements StubFinder {
static final Set<StubServer> STUB_SERVERS = new ConcurrentHashSet<>();
private final AvailablePortScanner portScanner;
private final MessageVerifier<?> contractVerifierMessaging;
private StubServer stubServer;
private final List<HttpServerStub> serverStubs;
StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifier<?> contractVerifierMessaging, List<HttpServerStub> serverStubs) {
StubRunnerExecutor(AvailablePortScanner portScanner,
MessageVerifier<?> contractVerifierMessaging,
List<HttpServerStub> serverStubs) {
this.portScanner = portScanner;
this.contractVerifierMessaging = contractVerifierMessaging;
this.serverStubs = serverStubs;
}
StubRunnerExecutor(AvailablePortScanner portScanner, List<HttpServerStub> serverStubs) {
StubRunnerExecutor(AvailablePortScanner portScanner,
List<HttpServerStub> serverStubs) {
this(portScanner, new NoOpStubMessages(), serverStubs);
}
@@ -69,12 +75,12 @@ class StubRunnerExecutor implements StubFinder {
this(portScanner, new NoOpStubMessages(), new ArrayList<HttpServerStub>());
}
public RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, StubRepository repository,
StubConfiguration stubConfiguration) {
public RunningStubs runStubs(StubRunnerOptions stubRunnerOptions,
StubRepository repository, StubConfiguration stubConfiguration) {
if (this.stubServer != null) {
if (log.isDebugEnabled()) {
log.debug("Returning cached version of stubs [" + stubConfiguration.toColonSeparatedDependencyNotation()
+ "]");
log.debug("Returning cached version of stubs ["
+ stubConfiguration.toColonSeparatedDependencyNotation() + "]");
}
return runningStubs();
}
@@ -85,8 +91,8 @@ class StubRunnerExecutor implements StubFinder {
}
private RunningStubs runningStubs() {
return new RunningStubs(
Collections.singletonMap(this.stubServer.getStubConfiguration(), this.stubServer.getPort()));
return new RunningStubs(Collections.singletonMap(
this.stubServer.getStubConfiguration(), this.stubServer.getPort()));
}
public void shutdown() {
@@ -103,11 +109,13 @@ class StubRunnerExecutor implements StubFinder {
public URL findStubUrl(String groupId, String artifactId) {
URL url = null;
if (groupId == null) {
url = findStubUrl(this.stubServer.stubConfiguration.artifactId.equals(artifactId));
url = findStubUrl(
this.stubServer.stubConfiguration.artifactId.equals(artifactId));
}
if (url == null) {
url = findStubUrl(this.stubServer.stubConfiguration.artifactId.equals(artifactId)
&& this.stubServer.stubConfiguration.groupId.equals(groupId));
url = findStubUrl(
this.stubServer.stubConfiguration.artifactId.equals(artifactId)
&& this.stubServer.stubConfiguration.groupId.equals(groupId));
}
if (url == null) {
throw new StubNotFoundException(groupId, artifactId);
@@ -119,8 +127,8 @@ class StubRunnerExecutor implements StubFinder {
public URL findStubUrl(String ivyNotation) {
String[] splitString = ivyNotation.split(":", -1);
if (splitString.length > 4) {
throw new IllegalArgumentException(
"[" + ivyNotation + "] is an invalid notation. Pass [groupId]:artifactId[:version][:classifier].");
throw new IllegalArgumentException("[" + ivyNotation
+ "] is an invalid notation. Pass [groupId]:artifactId[:version][:classifier].");
}
else if (splitString.length == 1) {
return findStubUrl(null, splitString[0]);
@@ -131,7 +139,8 @@ class StubRunnerExecutor implements StubFinder {
else if (splitString.length == 3) {
return findStubUrl(groupIdArtifactVersionMatches(splitString));
}
return findStubUrl(groupIdArtifactVersionMatches(splitString) && classifierMatches(splitString));
return findStubUrl(groupIdArtifactVersionMatches(splitString)
&& classifierMatches(splitString));
}
private boolean classifierMatches(String[] splitString) {
@@ -150,18 +159,21 @@ class StubRunnerExecutor implements StubFinder {
@Override
public RunningStubs findAllRunningStubs() {
return new RunningStubs(Collections.singletonMap(this.stubServer.stubConfiguration, this.stubServer.getPort()));
return new RunningStubs(Collections.singletonMap(
this.stubServer.stubConfiguration, this.stubServer.getPort()));
}
@Override
public Map<StubConfiguration, Collection<Contract>> getContracts() {
return Collections.singletonMap(this.stubServer.stubConfiguration, this.stubServer.getContracts());
return Collections.singletonMap(this.stubServer.stubConfiguration,
this.stubServer.getContracts());
}
@Override
public boolean trigger(String ivyNotationAsString, String labelName) {
Collection<Contract> matchingContracts = new ArrayList<>();
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts().entrySet()) {
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts()
.entrySet()) {
if (it.getKey().groupIdAndArtifactMatches(ivyNotationAsString)) {
matchingContracts.addAll(it.getValue());
}
@@ -181,7 +193,8 @@ class StubRunnerExecutor implements StubFinder {
private boolean triggerForDsls(Collection<Contract> dsls, String labelName) {
Collection<Contract> matchingDsls = new ArrayList<>();
for (Contract contract : dsls) {
if (labelName.equals(contract.getLabel()) && contract.getOutputMessage() != null) {
if (labelName.equals(contract.getLabel())
&& contract.getOutputMessage() != null) {
matchingDsls.add(contract);
}
}
@@ -216,7 +229,8 @@ class StubRunnerExecutor implements StubFinder {
@Override
public Map<String, Collection<String>> labels() {
Map<String, Collection<String>> labels = new LinkedHashMap<>();
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts().entrySet()) {
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts()
.entrySet()) {
Collection<String> values = new ArrayList<>();
for (Contract contract : it.getValue()) {
if (contract.getLabel() != null) {
@@ -233,17 +247,18 @@ class StubRunnerExecutor implements StubFinder {
DslProperty<?> body = outputMessage.getBody();
Headers headers = outputMessage.getHeaders();
this.contractVerifierMessaging.send(
JsonOutput
.toJson(BodyExtractor.extractClientValueFromBody(body == null ? null : body.getClientValue())),
headers == null ? null : headers.asStubSideMap(), outputMessage.getSentTo().getClientValue());
JsonOutput.toJson(BodyExtractor.extractClientValueFromBody(
body == null ? null : body.getClientValue())),
headers == null ? null : headers.asStubSideMap(),
outputMessage.getSentTo().getClientValue());
}
private URL returnStubUrlIfMatches(boolean condition) {
return condition ? this.stubServer.getStubUrl() : null;
}
private void startStubServers(final StubRunnerOptions stubRunnerOptions, final StubConfiguration stubConfiguration,
StubRepository repository) {
private void startStubServers(final StubRunnerOptions stubRunnerOptions,
final StubConfiguration stubConfiguration, StubRepository repository) {
final List<File> mappings = repository.getStubs();
final Collection<Contract> contracts = repository.contracts;
Integer port = stubRunnerOptions.port(stubConfiguration);
@@ -251,20 +266,23 @@ class StubRunnerExecutor implements StubFinder {
if (log.isDebugEnabled()) {
log.debug("There are no HTTP related contracts. Won't start any servers");
}
this.stubServer = new StubServer(stubConfiguration, mappings, contracts, new NoOpHttpServerStub()).start();
this.stubServer = new StubServer(stubConfiguration, mappings, contracts,
new NoOpHttpServerStub()).start();
return;
}
if (port != null && port >= 0) {
this.stubServer = new StubServer(stubConfiguration, mappings, contracts, httpServerStub()).start(port);
this.stubServer = new StubServer(stubConfiguration, mappings, contracts,
httpServerStub()).start(port);
}
else {
this.stubServer = this.portScanner.tryToExecuteWithFreePort(new PortCallback<StubServer>() {
@Override
public StubServer call(int availablePort) {
return new StubServer(stubConfiguration, mappings, contracts,
httpServerStub()).start(availablePort);
}
});
this.stubServer = this.portScanner
.tryToExecuteWithFreePort(new PortCallback<StubServer>() {
@Override
public StubServer call(int availablePort) {
return new StubServer(stubConfiguration, mappings, contracts,
httpServerStub()).start(availablePort);
}
});
}
STUB_SERVERS.add(this.stubServer);
}

View File

@@ -32,15 +32,17 @@ import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
*/
class StubRunnerFactory {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final StubRunnerOptions stubRunnerOptions;
private final StubDownloader stubDownloader;
private final MessageVerifier<?> contractVerifierMessaging;
public StubRunnerFactory(StubRunnerOptions stubRunnerOptions,
StubDownloader stubDownloader,
MessageVerifier<?> contractVerifierMessaging) {
StubDownloader stubDownloader, MessageVerifier<?> contractVerifierMessaging) {
this.stubRunnerOptions = stubRunnerOptions;
this.stubDownloader = stubDownloader;
this.contractVerifierMessaging = contractVerifierMessaging;
@@ -48,18 +50,22 @@ class StubRunnerFactory {
public Collection<StubRunner> createStubsFromServiceConfiguration() {
if (log.isDebugEnabled()) {
log.debug("Will download stubs for dependencies " + this.stubRunnerOptions.getDependencies());
log.debug("Will download stubs for dependencies "
+ this.stubRunnerOptions.getDependencies());
}
if (this.stubRunnerOptions.getDependencies().isEmpty()) {
log.warn("No stubs to download have been passed. Most likely you have forgotten to pass "
+ "them either via annotation or a property");
log.warn(
"No stubs to download have been passed. Most likely you have forgotten to pass "
+ "them either via annotation or a property");
}
Collection<StubRunner> result = new ArrayList<>();
for (StubConfiguration stubsConfiguration : this.stubRunnerOptions.getDependencies()) {
for (StubConfiguration stubsConfiguration : this.stubRunnerOptions
.getDependencies()) {
Map.Entry<StubConfiguration, File> entry = this.stubDownloader
.downloadAndUnpackStubJar(stubsConfiguration);
if (log.isDebugEnabled()) {
log.debug("For stub configuration [" + stubsConfiguration + "] the downloaded entry is [" + entry + "]");
log.debug("For stub configuration [" + stubsConfiguration
+ "] the downloaded entry is [" + entry + "]");
}
if (entry != null) {
result.add(createStubRunner(entry.getKey(), entry.getValue()));
@@ -74,7 +80,8 @@ class StubRunnerFactory {
if (unzipedStubDir == null) {
return null;
}
return createStubRunner(unzipedStubDir, stubsConfiguration, this.stubRunnerOptions);
return createStubRunner(unzipedStubDir, stubsConfiguration,
this.stubRunnerOptions);
}
private StubRunner createStubRunner(File unzippedStubsDir,

View File

@@ -41,49 +41,55 @@ public class StubRunnerMain {
private StubRunnerMain(String[] args) throws Exception {
OptionParser parser = new OptionParser();
try {
ArgumentAcceptingOptionSpec<Integer> minPortValueOpt = parser
.acceptsAll(Arrays.asList("minp", "minPort"),
"Minimum port value to be assigned to the WireMock instance. Defaults to 10000")
ArgumentAcceptingOptionSpec<Integer> minPortValueOpt = parser.acceptsAll(
Arrays.asList("minp", "minPort"),
"Minimum port value to be assigned to the WireMock instance. Defaults to 10000")
.withRequiredArg().ofType(Integer.class).defaultsTo(10000);
ArgumentAcceptingOptionSpec<Integer> maxPortValueOpt = parser
.acceptsAll(Arrays.asList("maxp", "maxPort"),
"Maximum port value to be assigned to the WireMock instance. Defaults to 15000")
ArgumentAcceptingOptionSpec<Integer> maxPortValueOpt = parser.acceptsAll(
Arrays.asList("maxp", "maxPort"),
"Maximum port value to be assigned to the WireMock instance. Defaults to 15000")
.withRequiredArg().ofType(Integer.class).defaultsTo(15000);
ArgumentAcceptingOptionSpec<String> stubsOpt = parser
.acceptsAll(Arrays.asList("s", "stubs"),
"Comma separated list of Ivy representation of jars with stubs. Eg. groupid:artifactid1,groupid2:artifactid2:classifier")
ArgumentAcceptingOptionSpec<String> stubsOpt = parser.acceptsAll(
Arrays.asList("s", "stubs"),
"Comma separated list of Ivy representation of jars with stubs. Eg. groupid:artifactid1,groupid2:artifactid2:classifier")
.withRequiredArg();
ArgumentAcceptingOptionSpec<String> classifierOpt = parser
.acceptsAll(Arrays.asList("c", "classifier"),
"Suffix for the jar containing stubs (e.g. 'stubs' if the stub jar would have a 'stubs' classifier for stubs: foobar-stubs ). Defaults to 'stubs'")
ArgumentAcceptingOptionSpec<String> classifierOpt = parser.acceptsAll(
Arrays.asList("c", "classifier"),
"Suffix for the jar containing stubs (e.g. 'stubs' if the stub jar would have a 'stubs' classifier for stubs: foobar-stubs ). Defaults to 'stubs'")
.withRequiredArg().defaultsTo("stubs");
ArgumentAcceptingOptionSpec<String> rootOpt = parser
.acceptsAll(Arrays.asList("r", "root"),"Location of a Jar containing server where you keep your stubs (e.g. http://nexus.net/content/repositories/repository)")
ArgumentAcceptingOptionSpec<String> rootOpt = parser.acceptsAll(
Arrays.asList("r", "root"),
"Location of a Jar containing server where you keep your stubs (e.g. http://nexus.net/content/repositories/repository)")
.withRequiredArg();
ArgumentAcceptingOptionSpec<String> usernameOpt = parser
.acceptsAll(Arrays.asList("u", "username"),"Username to user when connecting to repository")
.acceptsAll(Arrays.asList("u", "username"),
"Username to user when connecting to repository")
.withOptionalArg();
ArgumentAcceptingOptionSpec<String> passwordOpt = parser
.acceptsAll(Arrays.asList("p", "password"),"Password to user when connecting to repository")
.acceptsAll(Arrays.asList("p", "password"),
"Password to user when connecting to repository")
.withOptionalArg();
ArgumentAcceptingOptionSpec<String> proxyHostOpt = parser
.acceptsAll(Arrays.asList("phost", "proxyHost"),"Proxy host to use for repository requests")
.acceptsAll(Arrays.asList("phost", "proxyHost"),
"Proxy host to use for repository requests")
.withOptionalArg();
ArgumentAcceptingOptionSpec<Integer> proxyPortOpt = parser
.acceptsAll(Arrays.asList("pport", "proxyPort"),"Proxy port to use for repository requests")
.withOptionalArg()
.ofType(Integer.class);
.acceptsAll(Arrays.asList("pport", "proxyPort"),
"Proxy port to use for repository requests")
.withOptionalArg().ofType(Integer.class);
ArgumentAcceptingOptionSpec<String> stubsMode = parser
.acceptsAll(Arrays.asList("sm", "stubsMode"),"Stubs mode to be used. Acceptable values " + Arrays
.toString(StubRunnerProperties.StubsMode.values()))
.withRequiredArg().defaultsTo(StubRunnerProperties.StubsMode.CLASSPATH.toString());
.acceptsAll(Arrays.asList("sm", "stubsMode"),
"Stubs mode to be used. Acceptable values " + Arrays
.toString(StubRunnerProperties.StubsMode.values()))
.withRequiredArg()
.defaultsTo(StubRunnerProperties.StubsMode.CLASSPATH.toString());
OptionSet options = parser.parse(args);
String stubs = options.valueOf(stubsOpt);
StubRunnerProperties.StubsMode stubsModeValue = StubRunnerProperties.StubsMode.valueOf(
options.valueOf(stubsMode));
StubRunnerProperties.StubsMode stubsModeValue = StubRunnerProperties.StubsMode
.valueOf(options.valueOf(stubsMode));
Integer minPortValue = options.valueOf(minPortValueOpt);
Integer maxPortValue = options.valueOf(maxPortValueOpt);
String stubRepositoryRoot= options.valueOf(rootOpt);
String stubRepositoryRoot = options.valueOf(rootOpt);
String stubsSuffix = options.valueOf(classifierOpt);
final String username = options.valueOf(usernameOpt);
final String password = options.valueOf(passwordOpt);
@@ -93,9 +99,7 @@ public class StubRunnerMain {
.withMinMaxPort(minPortValue, maxPortValue)
.withStubRepositoryRoot(stubRepositoryRoot)
.withStubsMode(stubsModeValue).withStubsClassifier(stubsSuffix)
.withUsername(username)
.withPassword(password)
.withStubs(stubs);
.withUsername(username).withPassword(password).withStubs(stubs);
if (proxyHost != null) {
builder.withProxy(proxyHost, proxyPort);
}

View File

@@ -94,35 +94,38 @@ public class StubRunnerOptions {
private String consumerName;
/**
* For debugging purposes you can output the registered mappings to a given folder. Each HTTP server
* stub will have its own subfolder where all the mappings will get stored.
* For debugging purposes you can output the registered mappings to a given folder.
* Each HTTP server stub will have its own subfolder where all the mappings will get
* stored.
*/
private String mappingsOutputFolder;
final StubRunnerProperties.StubsMode stubsMode;
/**
* If set to {@code false} will NOT delete stubs from a temporary
* folder after running tests
* If set to {@code false} will NOT delete stubs from a temporary folder after running
* tests
*/
private boolean deleteStubsAfterTest;
/**
* Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
* Map of properties that can be passed to custom
* {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
*/
private Map<String, String> properties = new HashMap<>();
StubRunnerOptions(Integer minPortValue, Integer maxPortValue,
Resource stubRepositoryRoot, StubRunnerProperties.StubsMode stubsMode, String stubsClassifier,
Collection<StubConfiguration> dependencies,
Map<StubConfiguration, Integer> stubIdsToPortMapping,
String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions,
Resource stubRepositoryRoot, StubRunnerProperties.StubsMode stubsMode,
String stubsClassifier, Collection<StubConfiguration> dependencies,
Map<StubConfiguration, Integer> stubIdsToPortMapping, String username,
String password, final StubRunnerProxyOptions stubRunnerProxyOptions,
boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder,
boolean deleteStubsAfterTest, Map<String, String> properties) {
this.minPortValue = minPortValue;
this.maxPortValue = maxPortValue;
this.stubRepositoryRoot = stubRepositoryRoot;
this.stubsMode = stubsMode != null ? stubsMode : StubRunnerProperties.StubsMode.CLASSPATH;
this.stubsMode = stubsMode != null ? stubsMode
: StubRunnerProperties.StubsMode.CLASSPATH;
this.stubsClassifier = stubsClassifier;
this.dependencies = dependencies;
this.stubIdsToPortMapping = stubIdsToPortMapping;
@@ -147,8 +150,10 @@ public class StubRunnerOptions {
public static StubRunnerOptions fromSystemProps() {
StubRunnerOptionsBuilder builder = new StubRunnerOptionsBuilder()
.withMinPort(Integer.valueOf(System.getProperty("stubrunner.port.range.min", "10000")))
.withMaxPort(Integer.valueOf(System.getProperty("stubrunner.port.range.max", "15000")))
.withMinPort(Integer.valueOf(
System.getProperty("stubrunner.port.range.min", "10000")))
.withMaxPort(Integer.valueOf(
System.getProperty("stubrunner.port.range.max", "15000")))
.withStubRepositoryRoot(ResourceResolver
.resource(System.getProperty("stubrunner.repository.root", "")))
.withStubsMode(System.getProperty("stubrunner.stubs-mode", "LOCAL"))
@@ -156,14 +161,18 @@ public class StubRunnerOptions {
.withStubs(System.getProperty("stubrunner.ids", ""))
.withUsername(System.getProperty("stubrunner.username"))
.withPassword(System.getProperty("stubrunner.password"))
.withStubPerConsumer(Boolean.parseBoolean(System.getProperty("stubrunner.stubs-per-consumer", "false")))
.withStubPerConsumer(Boolean.parseBoolean(
System.getProperty("stubrunner.stubs-per-consumer", "false")))
.withConsumerName(System.getProperty("stubrunner.consumer-name"))
.withMappingsOutputFolder(System.getProperty("stubrunner.mappings-output-folder"))
.withDeleteStubsAfterTest(Boolean.parseBoolean(System.getProperty("stubrunner.delete-stubs-after-test", "true")))
.withMappingsOutputFolder(
System.getProperty("stubrunner.mappings-output-folder"))
.withDeleteStubsAfterTest(Boolean.parseBoolean(
System.getProperty("stubrunner.delete-stubs-after-test", "true")))
.withProperties(stubRunnerProps());
String proxyHost = System.getProperty("stubrunner.proxy.host");
if (proxyHost != null) {
builder.withProxy(proxyHost, Integer.parseInt(System.getProperty("stubrunner.proxy.port")));
builder.withProxy(proxyHost,
Integer.parseInt(System.getProperty("stubrunner.proxy.port")));
}
return builder.build();
}
@@ -172,12 +181,12 @@ public class StubRunnerOptions {
Map<String, String> map = new HashMap<>();
Properties properties = System.getProperties();
Set<String> propertyNames = properties.stringPropertyNames();
propertyNames
.stream()
propertyNames.stream()
// stubrunner.properties.foo.bar=baz
.filter(s -> s.toLowerCase().startsWith("stubrunner.properties"))
// foo.bar=baz
.forEach(s -> map.put(s.substring("stubrunner.properties".length() + 1), System.getProperty(s)));
.forEach(s -> map.put(s.substring("stubrunner.properties".length() + 1),
System.getProperty(s)));
return map;
}
@@ -287,6 +296,7 @@ public class StubRunnerOptions {
public static class StubRunnerProxyOptions {
private final String proxyHost;
private final int proxyPort;
public StubRunnerProxyOptions(final String proxyHost, final int proxyPort) {
@@ -302,25 +312,30 @@ public class StubRunnerOptions {
return this.proxyPort;
}
@Override public String toString() {
@Override
public String toString() {
return "StubRunnerProxyOptions{" + "proxyHost='" + this.proxyHost + '\''
+ ", proxyPort=" + this.proxyPort + '}';
}
}
@Override public String toString() {
return "StubRunnerOptions{" + "minPortValue=" + this.minPortValue + ", maxPortValue="
+ this.maxPortValue + ", stubRepositoryRoot='" + this.stubRepositoryRoot + '\''
+ ", stubsMode='" + this.stubsMode + "', stubsClassifier='" + this.stubsClassifier
+ '\'' + ", dependencies=" + this.dependencies + ", stubIdsToPortMapping="
+ this.stubIdsToPortMapping + ", username='" + obfuscate(this.username) + '\'' + ", password='"
+ obfuscate(this.password) + '\'' + ", stubRunnerProxyOptions='" + this.stubRunnerProxyOptions + "', stubsPerConsumer='"
+ this.stubsPerConsumer
+ '\'' + ", stubsPerConsumer='" + this.stubsPerConsumer + '\''
+ '}';
@Override
public String toString() {
return "StubRunnerOptions{" + "minPortValue=" + this.minPortValue
+ ", maxPortValue=" + this.maxPortValue + ", stubRepositoryRoot='"
+ this.stubRepositoryRoot + '\'' + ", stubsMode='" + this.stubsMode
+ "', stubsClassifier='" + this.stubsClassifier + '\'' + ", dependencies="
+ this.dependencies + ", stubIdsToPortMapping="
+ this.stubIdsToPortMapping + ", username='" + obfuscate(this.username)
+ '\'' + ", password='" + obfuscate(this.password) + '\''
+ ", stubRunnerProxyOptions='" + this.stubRunnerProxyOptions
+ "', stubsPerConsumer='" + this.stubsPerConsumer + '\''
+ ", stubsPerConsumer='" + this.stubsPerConsumer + '\'' + '}';
}
private String obfuscate(String string) {
return StringUtils.hasText(string) ? "****" : "";
}
}

View File

@@ -33,22 +33,37 @@ import org.springframework.util.StringUtils;
public class StubRunnerOptionsBuilder {
private static final String DELIMITER = ":";
private LinkedList<String> stubs = new LinkedList<>();
private Collection<StubConfiguration> stubConfigurations = new ArrayList<>();
private Map<StubConfiguration, Integer> stubIdsToPortMapping = new LinkedHashMap<>();
private Integer minPortValue = 10000;
private Integer maxPortValue = 15000;
private Resource stubRepositoryRoot;
private String stubsClassifier = "stubs";
private String username;
private String password;
private StubRunnerOptions.StubRunnerProxyOptions stubRunnerProxyOptions;
private boolean stubsPerConsumer = false;
private String consumerName;
private String mappingsOutputFolder;
private StubRunnerProperties.StubsMode stubsMode;
private boolean deleteStubsAfterTest = true;
private Map<String, String> properties = new HashMap<>();
public StubRunnerOptionsBuilder() {
@@ -70,7 +85,8 @@ public class StubRunnerOptionsBuilder {
return this;
}
public StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue, Integer maxPortValue) {
public StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue,
Integer maxPortValue) {
this.minPortValue = minPortValue;
this.maxPortValue = maxPortValue;
return this;
@@ -98,7 +114,8 @@ public class StubRunnerOptionsBuilder {
return this;
}
public StubRunnerOptionsBuilder withStubsMode(StubRunnerProperties.StubsMode stubsMode) {
public StubRunnerOptionsBuilder withStubsMode(
StubRunnerProperties.StubsMode stubsMode) {
this.stubsMode = stubsMode;
return this;
}
@@ -131,21 +148,23 @@ public class StubRunnerOptionsBuilder {
this.stubsPerConsumer = options.isStubsPerConsumer();
this.consumerName = options.getConsumerName();
this.mappingsOutputFolder = options.getMappingsOutputFolder();
this.stubConfigurations = options.dependencies != null ?
options.dependencies : new ArrayList<>();
this.stubIdsToPortMapping = options.stubIdsToPortMapping != null ?
options.stubIdsToPortMapping : new LinkedHashMap<>();
this.stubConfigurations = options.dependencies != null ? options.dependencies
: new ArrayList<>();
this.stubIdsToPortMapping = options.stubIdsToPortMapping != null
? options.stubIdsToPortMapping : new LinkedHashMap<>();
this.deleteStubsAfterTest = options.isDeleteStubsAfterTest();
this.properties = options.getProperties();
return this;
}
public StubRunnerOptionsBuilder withMappingsOutputFolder(String mappingsOutputFolder) {
public StubRunnerOptionsBuilder withMappingsOutputFolder(
String mappingsOutputFolder) {
this.mappingsOutputFolder = mappingsOutputFolder;
return this;
}
public StubRunnerOptionsBuilder withDeleteStubsAfterTest(boolean deleteStubsAfterTest) {
public StubRunnerOptionsBuilder withDeleteStubsAfterTest(
boolean deleteStubsAfterTest) {
this.deleteStubsAfterTest = deleteStubsAfterTest;
return this;
}
@@ -156,15 +175,17 @@ public class StubRunnerOptionsBuilder {
}
public StubRunnerOptions build() {
return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot,
this.stubsMode, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping,
this.username, this.password, this.stubRunnerProxyOptions, this.stubsPerConsumer, this.consumerName,
this.mappingsOutputFolder, this.deleteStubsAfterTest, this.properties);
return new StubRunnerOptions(this.minPortValue, this.maxPortValue,
this.stubRepositoryRoot, this.stubsMode, this.stubsClassifier,
buildDependencies(), this.stubIdsToPortMapping, this.username,
this.password, this.stubRunnerProxyOptions, this.stubsPerConsumer,
this.consumerName, this.mappingsOutputFolder, this.deleteStubsAfterTest,
this.properties);
}
private Collection<StubConfiguration> buildDependencies() {
List<StubConfiguration> stubConfigurations = StubsParser
.fromString(this.stubs, this.stubsClassifier);
List<StubConfiguration> stubConfigurations = StubsParser.fromString(this.stubs,
this.stubsClassifier);
this.stubConfigurations.addAll(stubConfigurations);
return this.stubConfigurations;
}
@@ -174,14 +195,17 @@ public class StubRunnerOptionsBuilder {
if (stubIdsToPortMapping.length == 1 && !containsRange(stubIdsToPortMapping[0])) {
list.addAll(StringUtils.commaDelimitedListToSet(stubIdsToPortMapping[0]));
return list;
} else if (stubIdsToPortMapping.length == 1 && containsRange(stubIdsToPortMapping[0])) {
}
else if (stubIdsToPortMapping.length == 1
&& containsRange(stubIdsToPortMapping[0])) {
LinkedList<String> linkedList = new LinkedList<>();
String[] split = stubIdsToPortMapping[0].split(",");
for (String string : split) {
if (containsClosingRange(string)) {
String last = linkedList.pop();
linkedList.push(last + "," + string);
} else {
}
else {
linkedList.push(string);
}
}
@@ -210,7 +234,8 @@ public class StubRunnerOptionsBuilder {
if (StubsParser.hasPort(notation)) {
addPort(notation);
this.stubs.add(StubsParser.ivyFromStringWithPort(notation));
} else {
}
else {
this.stubs.add(notation);
}
}
@@ -234,8 +259,10 @@ public class StubRunnerOptionsBuilder {
return this;
}
public StubRunnerOptionsBuilder withProxy(final String proxyHost, final int proxyPort) {
this.stubRunnerProxyOptions = new StubRunnerOptions.StubRunnerProxyOptions(proxyHost, proxyPort);
public StubRunnerOptionsBuilder withProxy(final String proxyHost,
final int proxyPort) {
this.stubRunnerProxyOptions = new StubRunnerOptions.StubRunnerProxyOptions(
proxyHost, proxyPort);
return this;
}
@@ -248,4 +275,5 @@ public class StubRunnerOptionsBuilder {
this.consumerName = consumerName;
return this;
}
}

View File

@@ -38,8 +38,8 @@ class StubRunnerPropertyUtils {
static PropertyFetcher FETCHER = new PropertyFetcher();
/**
* For Env vars takes the prop name, converts dots to underscores and applies
* upper case
* For Env vars takes the prop name, converts dots to underscores and applies upper
* case
*/
static boolean isPropertySet(String propName) {
String value = getProperty(new HashMap<>(), propName);
@@ -47,8 +47,7 @@ class StubRunnerPropertyUtils {
}
/**
* For options, system props and env vars returns {@code true}
* when property is set
* For options, system props and env vars returns {@code true} when property is set
*/
static boolean hasProperty(Map<String, String> options, String propName) {
String value = getProperty(options, propName);
@@ -56,14 +55,15 @@ class StubRunnerPropertyUtils {
}
/**
* Tries to pick a value from options, for Env vars takes the prop name, converts
* dots to underscores and applies upper case
* Tries to pick a value from options, for Env vars takes the prop name, converts dots
* to underscores and applies upper case
*/
static String getProperty(Map<String, String> options, String propName) {
if (options != null && options.containsKey(propName)) {
String value = options.get(propName);
if (log.isTraceEnabled()) {
log.trace("Options map contains the prop [" + propName + "] with value [" + value + "]");
log.trace("Options map contains the prop [" + propName + "] with value ["
+ value + "]");
}
return value;
}
@@ -81,7 +81,8 @@ class StubRunnerPropertyUtils {
String systemProp = FETCHER.systemProp(stubRunnerProp);
if (StringUtils.hasText(systemProp)) {
if (log.isTraceEnabled()) {
log.trace("System property [" + stubRunnerProp + "] has value [" + systemProp + "]");
log.trace("System property [" + stubRunnerProp + "] has value ["
+ systemProp + "]");
}
return systemProp;
}
@@ -89,17 +90,22 @@ class StubRunnerPropertyUtils {
.replaceAll("-", "_").toUpperCase();
String envVar = FETCHER.envVar(convertedEnvProp);
if (log.isTraceEnabled()) {
log.trace("Environment variable [" + convertedEnvProp + "] has value [" + envVar + "]");
log.trace("Environment variable [" + convertedEnvProp + "] has value ["
+ envVar + "]");
}
return envVar;
}
}
class PropertyFetcher {
String systemProp(String prop) {
return System.getProperty(prop);
}
String envVar(String prop) {
return System.getenv(prop);
}
}

View File

@@ -21,8 +21,8 @@ import java.io.Closeable;
public interface StubRunning extends Closeable, StubFinder {
/**
* Runs the stubs and returns the {@link RunningStubs}. If the stubs were
* already started then a cached version will be returned.
* Runs the stubs and returns the {@link RunningStubs}. If the stubs were already
* started then a cached version will be returned.
*/
RunningStubs runStubs();

View File

@@ -31,8 +31,11 @@ class StubServer {
private static final Log log = LogFactory.getLog(StubServer.class);
private final HttpServerStub httpServerStub;
final StubConfiguration stubConfiguration;
final Collection<File> mappings;
final Collection<Contract> contracts;
StubServer(StubConfiguration stubConfiguration, Collection<File> mappings,
@@ -54,7 +57,8 @@ class StubServer {
}
private StubServer stubServer() {
log.info("Started stub server for project [" + this.stubConfiguration.toColonSeparatedDependencyNotation()
log.info("Started stub server for project ["
+ this.stubConfiguration.toColonSeparatedDependencyNotation()
+ "] on port " + this.httpServerStub.port());
this.httpServerStub.registerMappings(this.mappings);
return this;
@@ -103,22 +107,26 @@ class StubServer {
return this.httpServerStub.registeredMappings();
}
@Override public boolean equals(Object o) {
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
StubServer that = (StubServer) o;
return Objects.equals(this.stubConfiguration, that.stubConfiguration) && Objects
.equals(this.contracts, that.contracts);
return Objects.equals(this.stubConfiguration, that.stubConfiguration)
&& Objects.equals(this.contracts, that.contracts);
}
@Override public int hashCode() {
@Override
public int hashCode() {
return Objects.hash(this.stubConfiguration, this.contracts);
}
@Override public String toString() {
return "StubServer{" + "stubConfiguration=" + this.stubConfiguration + ", mappingsSize="
+ this.mappings.size() + '}';
@Override
public String toString() {
return "StubServer{" + "stubConfiguration=" + this.stubConfiguration
+ ", mappingsSize=" + this.mappings.size() + '}';
}
}

View File

@@ -22,10 +22,10 @@ import java.util.Map;
public interface StubTrigger {
/**
* Triggers an event by a given label for a given {@code groupid:artifactid} notation. You can use only {@code artifactId} too.
* Triggers an event by a given label for a given {@code groupid:artifactid} notation.
* You can use only {@code artifactId} too.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/
boolean trigger(String ivyNotation, String labelName);
@@ -34,7 +34,6 @@ public interface StubTrigger {
* Triggers an event by a given label.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/
boolean trigger(String labelName);
@@ -43,7 +42,6 @@ public interface StubTrigger {
* Triggers all possible events.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/
boolean trigger();
@@ -54,4 +52,5 @@ public interface StubTrigger {
* Feature related to messaging.
*/
Map<String, Collection<String>> labels();
}

View File

@@ -42,9 +42,9 @@ class TemporaryFileStorage {
private static final Log log = LogFactory.getLog(TemporaryFileStorage.class);
/**
* There are problems with removal of stubs unpacked to a temporary folder.
* That's why we're creating a bounded in-memory storage of unpacked files
* and later we register a shutdown hook to remove all these files.
* There are problems with removal of stubs unpacked to a temporary folder. That's why
* we're creating a bounded in-memory storage of unpacked files and later we register
* a shutdown hook to remove all these files.
*/
private static final Queue<File> TEMP_FILES_LOG = new LinkedBlockingQueue<>(1000);
@@ -66,8 +66,8 @@ class TemporaryFileStorage {
if (file.isDirectory()) {
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws
IOException {
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
if (log.isTraceEnabled()) {
log.trace("Removing file [" + file + "]");
}
@@ -76,7 +76,8 @@ class TemporaryFileStorage {
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
public FileVisitResult postVisitDirectory(Path dir,
IOException exc) throws IOException {
if (log.isTraceEnabled()) {
log.trace("Removing dir [" + dir + "]");
}
@@ -84,11 +85,13 @@ class TemporaryFileStorage {
return FileVisitResult.CONTINUE;
}
});
} else {
}
else {
Files.delete(file.toPath());
}
}
} catch (NoClassDefFoundError | IOException e) {
}
catch (NoClassDefFoundError | IOException e) {
// Added NoClassDefFoundError cause sometimes it's visible in the builds
// this error is completely harmless
if (log.isTraceEnabled()) {
@@ -99,12 +102,12 @@ class TemporaryFileStorage {
static File createTempDir(String tempDirPrefix) {
try {
return createTempDirectory(tempDirPrefix)
.toFile();
return createTempDirectory(tempDirPrefix).toFile();
}
catch (IOException e) {
throw new IllegalStateException(
"Cannot create tmp dir with prefix: [" + tempDirPrefix + "]", e);
}
}
}

View File

@@ -43,12 +43,18 @@ import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
* @author Marcin Grzejszczak
*/
public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptions {
private static final String DELIMITER = ":";
private static final String LATEST_VERSION = "+";
StubRunnerOptionsBuilder stubRunnerOptionsBuilder = new StubRunnerOptionsBuilder(StubRunnerOptions.fromSystemProps());
StubRunnerOptionsBuilder stubRunnerOptionsBuilder = new StubRunnerOptionsBuilder(
StubRunnerOptions.fromSystemProps());
BatchStubRunner stubFinder;
MessageVerifier verifier = new ExceptionThrowingMessageVerifier();
StubRunnerRule delegate = this;
public StubRunnerRule() {
@@ -65,102 +71,122 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio
}
private void before() {
stubFinder(new BatchStubRunnerFactory(builder().build(), verifier()).buildBatchStubRunner());
stubFinder(new BatchStubRunnerFactory(builder().build(), verifier())
.buildBatchStubRunner());
StubRunnerRule.this.stubFinder().runStubs();
}
};
}
@Override public StubRunnerRule messageVerifier(MessageVerifier messageVerifier) {
@Override
public StubRunnerRule messageVerifier(MessageVerifier messageVerifier) {
verifier(messageVerifier);
return this.delegate;
}
@Override public StubRunnerRule options(StubRunnerOptions stubRunnerOptions) {
@Override
public StubRunnerRule options(StubRunnerOptions stubRunnerOptions) {
builder().withOptions(stubRunnerOptions);
return this.delegate;
}
@Override public StubRunnerRule minPort(int minPort) {
@Override
public StubRunnerRule minPort(int minPort) {
builder().withMinPort(minPort);
return this.delegate;
}
@Override public StubRunnerRule maxPort(int maxPort) {
@Override
public StubRunnerRule maxPort(int maxPort) {
builder().withMaxPort(maxPort);
return this.delegate;
}
@Override public StubRunnerRule repoRoot(String repoRoot) {
@Override
public StubRunnerRule repoRoot(String repoRoot) {
builder().withStubRepositoryRoot(repoRoot);
return this.delegate;
}
@Override public StubRunnerRule stubsMode(StubRunnerProperties.StubsMode stubsMode) {
@Override
public StubRunnerRule stubsMode(StubRunnerProperties.StubsMode stubsMode) {
builder().withStubsMode(stubsMode);
return this.delegate;
}
@Override public PortStubRunnerRule downloadStub(String groupId, String artifactId,
@Override
public PortStubRunnerRule downloadStub(String groupId, String artifactId,
String version, String classifier) {
builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version + DELIMITER + classifier);
builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version
+ DELIMITER + classifier);
return new PortStubRunnerRule(this.delegate);
}
@Override public PortStubRunnerRule downloadLatestStub(String groupId, String artifactId,
@Override
public PortStubRunnerRule downloadLatestStub(String groupId, String artifactId,
String classifier) {
builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + LATEST_VERSION + DELIMITER + classifier);
builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + LATEST_VERSION
+ DELIMITER + classifier);
return new PortStubRunnerRule(this.delegate);
}
@Override public PortStubRunnerRule downloadStub(String groupId, String artifactId,
@Override
public PortStubRunnerRule downloadStub(String groupId, String artifactId,
String version) {
builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version);
return new PortStubRunnerRule(this.delegate);
}
@Override public PortStubRunnerRule downloadStub(String groupId, String artifactId) {
@Override
public PortStubRunnerRule downloadStub(String groupId, String artifactId) {
builder().withStubs(groupId + DELIMITER + artifactId);
return new PortStubRunnerRule(this.delegate);
}
@Override public PortStubRunnerRule downloadStub(String ivyNotation) {
@Override
public PortStubRunnerRule downloadStub(String ivyNotation) {
builder().withStubs(ivyNotation);
return new PortStubRunnerRule(this.delegate);
}
@Override public StubRunnerRule downloadStubs(String... ivyNotations) {
@Override
public StubRunnerRule downloadStubs(String... ivyNotations) {
builder().withStubs(Arrays.asList(ivyNotations));
return new PortStubRunnerRule(this.delegate);
}
@Override public StubRunnerRule downloadStubs(List<String> ivyNotations) {
@Override
public StubRunnerRule downloadStubs(List<String> ivyNotations) {
builder().withStubs(ivyNotations);
return new PortStubRunnerRule(this.delegate);
}
@Override public StubRunnerRule withStubPerConsumer(boolean stubPerConsumer) {
@Override
public StubRunnerRule withStubPerConsumer(boolean stubPerConsumer) {
builder().withStubPerConsumer(stubPerConsumer);
return this.delegate;
}
@Override public StubRunnerRule withConsumerName(String consumerName) {
@Override
public StubRunnerRule withConsumerName(String consumerName) {
builder().withConsumerName(consumerName);
return this.delegate;
}
@Override public StubRunnerRule withMappingsOutputFolder(String mappingsOutputFolder) {
@Override
public StubRunnerRule withMappingsOutputFolder(String mappingsOutputFolder) {
builder().withMappingsOutputFolder(mappingsOutputFolder);
return this.delegate;
}
@Override public StubRunnerRule withDeleteStubsAfterTest(
boolean deleteStubsAfterTest) {
@Override
public StubRunnerRule withDeleteStubsAfterTest(boolean deleteStubsAfterTest) {
builder().withDeleteStubsAfterTest(deleteStubsAfterTest);
return this.delegate;
}
@Override public StubRunnerRule withProperties(Map<String, String> properties) {
@Override
public StubRunnerRule withProperties(Map<String, String> properties) {
builder().withProperties(properties);
return this.delegate;
}
@@ -189,7 +215,8 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio
public boolean trigger(String ivyNotation, String labelName) {
boolean result = this.stubFinder().trigger(ivyNotation, labelName);
if (!result) {
throw new IllegalStateException("Failed to trigger a message with notation [" + ivyNotation + "] and label [" + labelName + "]");
throw new IllegalStateException("Failed to trigger a message with notation ["
+ ivyNotation + "] and label [" + labelName + "]");
}
return result;
}
@@ -198,7 +225,8 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio
public boolean trigger(String labelName) {
boolean result = this.stubFinder().trigger(labelName);
if (!result) {
throw new IllegalStateException("Failed to trigger a message with label [" + labelName + "]");
throw new IllegalStateException(
"Failed to trigger a message with label [" + labelName + "]");
}
return result;
}
@@ -245,22 +273,26 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio
private static final String EXCEPTION_MESSAGE = "Please provide a custom MessageVerifier to use this feature";
@Override public void send(Object message, String destination) {
@Override
public void send(Object message, String destination) {
throw new UnsupportedOperationException(EXCEPTION_MESSAGE);
}
@Override public Object receive(String destination, long timeout,
TimeUnit timeUnit) {
@Override
public Object receive(String destination, long timeout, TimeUnit timeUnit) {
throw new UnsupportedOperationException(EXCEPTION_MESSAGE);
}
@Override public Object receive(String destination) {
@Override
public Object receive(String destination) {
throw new UnsupportedOperationException(EXCEPTION_MESSAGE);
}
@Override public void send(Object payload, Map headers, String destination) {
@Override
public void send(Object payload, Map headers, String destination) {
throw new UnsupportedOperationException(EXCEPTION_MESSAGE);
}
}
/**
@@ -268,15 +300,19 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio
*
* @since 1.2.0
*/
public static class PortStubRunnerRule extends StubRunnerRule implements PortStubRunnerRuleOptions {
public static class PortStubRunnerRule extends StubRunnerRule
implements PortStubRunnerRuleOptions {
PortStubRunnerRule(StubRunnerRule delegate) {
super(delegate);
}
@Override public StubRunnerRule withPort(Integer port) {
@Override
public StubRunnerRule withPort(Integer port) {
builder().withPort(port);
return this.delegate;
}
}
}

View File

@@ -8,10 +8,12 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
interface StubRunnerRuleOptions {
/**
* Pass the {@link MessageVerifier} that this rule should use.
* If you don't pass anything a {@link StubRunnerRule.ExceptionThrowingMessageVerifier} will be used.
* That means that an exception will be thrown whenever you try to do sth messaging related.
* Pass the {@link MessageVerifier} that this rule should use. If you don't pass
* anything a {@link StubRunnerRule.ExceptionThrowingMessageVerifier} will be used.
* That means that an exception will be thrown whenever you try to do sth messaging
* related.
*/
StubRunnerRule messageVerifier(MessageVerifier messageVerifier);
@@ -45,11 +47,12 @@ interface StubRunnerRuleOptions {
/**
* Group Id, artifact Id, version and classifier of a single stub to download
*/
PortStubRunnerRuleOptions downloadStub(String groupId, String artifactId, String version,
String classifier);
PortStubRunnerRuleOptions downloadStub(String groupId, String artifactId,
String version, String classifier);
/**
* Group Id, artifact Id and classifier of a single stub to download in the latest version
* Group Id, artifact Id and classifier of a single stub to download in the latest
* version
*/
PortStubRunnerRuleOptions downloadLatestStub(String groupId, String artifactId,
String classifier);
@@ -61,7 +64,8 @@ interface StubRunnerRuleOptions {
String version);
/**
* Group Id, artifact Id of a single stub to download. Default classifier will be picked.
* Group Id, artifact Id of a single stub to download. Default classifier will be
* picked.
*/
PortStubRunnerRuleOptions downloadStub(String groupId, String artifactId);
@@ -96,13 +100,15 @@ interface StubRunnerRuleOptions {
StubRunnerRule withMappingsOutputFolder(String mappingsOutputFolder);
/**
* If set to {@code false} will NOT delete stubs from a temporary
* folder after running tests
* If set to {@code false} will NOT delete stubs from a temporary folder after running
* tests
*/
StubRunnerRule withDeleteStubsAfterTest(boolean deleteStubsAfterTest);
/**
* Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
* Map of properties that can be passed to custom
* {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
*/
StubRunnerRule withProperties(Map<String, String> properties);
}

View File

@@ -12,7 +12,7 @@ import org.springframework.context.annotation.Configuration;
* {@link org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner} by
* loading in AutoConfigurations related to Stream and Integration only if the relevant
* jars are in classpath.
*
*
* @author Biju Kunjummen
*/
@Configuration
@@ -32,4 +32,5 @@ public class StubRunnerStreamsIntegrationAutoConfiguration {
static class IntegrationRelatedAutoConfiguration {
}
}

View File

@@ -46,22 +46,24 @@ import org.springframework.messaging.Message;
*/
@Configuration
@ConditionalOnClass(IntegrationFlowBuilder.class)
@ConditionalOnProperty(name="stubrunner.integration.enabled", havingValue="true", matchIfMissing=true)
@ConditionalOnProperty(name = "stubrunner.integration.enabled", havingValue = "true", matchIfMissing = true)
public class StubRunnerIntegrationConfiguration {
@Bean
@ConditionalOnMissingBean(name="stubFlowRegistrar")
@ConditionalOnMissingBean(name = "stubFlowRegistrar")
public FlowRegistrar stubFlowRegistrar(AutowireCapableBeanFactory beanFactory,
BatchStubRunner batchStubRunner) {
Map<StubConfiguration, Collection<Contract>> contracts = batchStubRunner
.getContracts();
for (Entry<StubConfiguration, Collection<Contract>> entry : contracts.entrySet()) {
for (Entry<StubConfiguration, Collection<Contract>> entry : contracts
.entrySet()) {
String name = entry.getKey().getGroupId() + "_"
+ entry.getKey().getArtifactId();
for (Contract dsl : entry.getValue()) {
if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null
&& dsl.getInput().getMessageFrom().getClientValue() != null) {
final String flowName = name + "_" + dsl.getLabel() + "_" + dsl.hashCode();
final String flowName = name + "_" + dsl.getLabel() + "_"
+ dsl.hashCode();
IntegrationFlowBuilder builder = IntegrationFlows
.from(dsl.getInput().getMessageFrom().getClientValue())
.filter(new StubRunnerIntegrationMessageSelector(dsl),
@@ -90,18 +92,22 @@ public class StubRunnerIntegrationConfiguration {
beanFactory.getBean(flowName + ".filter", Lifecycle.class).start();
beanFactory.getBean(flowName + ".transformer", Lifecycle.class)
.start();
}
}
}
}
return new FlowRegistrar();
}
private static class DummyMessageHandler {
@SuppressWarnings("unused")
public void handle(Message<?> message) {
}
}
static class FlowRegistrar {
}
}

View File

@@ -45,6 +45,7 @@ import com.toomuchcoding.jsonassert.JsonAssertion;
class StubRunnerIntegrationMessageSelector implements MessageSelector {
private final Contract groovyDsl;
private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper();
StubRunnerIntegrationMessageSelector(Contract groovyDsl) {
@@ -58,7 +59,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
}
Object inputMessage = message.getPayload();
BodyMatchers matchers = this.groovyDsl.getInput().getBodyMatchers();
Object dslBody = MapConverter.getStubSideValues(this.groovyDsl.getInput().getMessageBody());
Object dslBody = MapConverter
.getStubSideValues(this.groovyDsl.getInput().getMessageBody());
Object matchingInputMessage = JsonToJsonPathsConverter
.removeMatchingJsonPaths(dslBody, matchers);
JsonPaths jsonPaths = JsonToJsonPathsConverter
@@ -66,7 +68,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
matchingInputMessage);
DocumentContext parsedJson;
try {
parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage));
parsedJson = JsonPath
.parse(this.objectMapper.writeValueAsString(inputMessage));
}
catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize to JSON", e);
@@ -77,7 +80,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
}
if (matchers != null && matchers.hasMatchers()) {
for (BodyMatcher matcher : matchers.jsonPathMatchers()) {
String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody);
String jsonPath = JsonToJsonPathsConverter
.convertJsonPathAndRegexToAJsonPath(matcher, dslBody);
matches &= matchesJsonPath(parsedJson, jsonPath);
}
}
@@ -86,8 +90,7 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
private boolean matchesJsonPath(DocumentContext parsedJson, String jsonPath) {
try {
JsonAssertion.assertThat(parsedJson)
.matchesJsonPath(jsonPath);
JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath);
return true;
}
catch (Exception e) {
@@ -102,10 +105,11 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
String name = it.getName();
Object value = it.getClientValue();
Object valueInHeader = headers.get(name);
matches &= value instanceof Pattern ?
((Pattern) value).matcher(valueInHeader.toString()).matches() :
valueInHeader!=null && valueInHeader.equals(value);
matches &= value instanceof Pattern
? ((Pattern) value).matcher(valueInHeader.toString()).matches()
: valueInHeader != null && valueInHeader.equals(value);
}
return matches;
}
}

View File

@@ -30,7 +30,8 @@ import org.springframework.messaging.support.MessageBuilder;
*
* @author Marcin Grzejszczak
*/
class StubRunnerIntegrationTransformer implements GenericTransformer<Message<?>, Message<?>> {
class StubRunnerIntegrationTransformer
implements GenericTransformer<Message<?>, Message<?>> {
private final Contract groovyDsl;
@@ -43,8 +44,11 @@ class StubRunnerIntegrationTransformer implements GenericTransformer<Message<?>,
if (this.groovyDsl.getOutputMessage() == null) {
return source;
}
String payload = BodyExtractor.extractStubValueFrom(this.groovyDsl.getOutputMessage().getBody());
Map<String, Object> headers = this.groovyDsl.getOutputMessage().getHeaders().asStubSideMap();
String payload = BodyExtractor
.extractStubValueFrom(this.groovyDsl.getOutputMessage().getBody());
Map<String, Object> headers = this.groovyDsl.getOutputMessage().getHeaders()
.asStubSideMap();
return MessageBuilder.createMessage(payload, new MessageHeaders(headers));
}
}

View File

@@ -55,15 +55,15 @@ import org.springframework.util.StringUtils;
* @author Marcin Grzejszczak
*/
@Configuration
@ConditionalOnClass({IntegrationFlows.class, EnableBinding.class})
@ConditionalOnProperty(name="stubrunner.stream.enabled", havingValue="true", matchIfMissing=true)
@ConditionalOnClass({ IntegrationFlows.class, EnableBinding.class })
@ConditionalOnProperty(name = "stubrunner.stream.enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore(StubRunnerIntegrationConfiguration.class)
public class StubRunnerStreamConfiguration {
private static final Log log = LogFactory.getLog(StubRunnerStreamConfiguration.class);
@Bean
@ConditionalOnMissingBean(name="stubFlowRegistrar")
@ConditionalOnMissingBean(name = "stubFlowRegistrar")
@ConditionalOnBean(BindingServiceProperties.class)
public FlowRegistrar stubFlowRegistrar(AutowireCapableBeanFactory beanFactory,
BatchStubRunner batchStubRunner) {
@@ -78,10 +78,9 @@ public class StubRunnerStreamConfiguration {
if (dsl == null) {
continue;
}
if (dsl.getInput() != null
&& dsl.getInput().getMessageFrom() != null
&& StringUtils.hasText(
dsl.getInput().getMessageFrom().getClientValue())) {
if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null
&& StringUtils.hasText(
dsl.getInput().getMessageFrom().getClientValue())) {
final String flowName = name + "_" + dsl.getLabel() + "_"
+ dsl.hashCode();
String from = resolvedDestination(beanFactory,
@@ -111,16 +110,18 @@ public class StubRunnerStreamConfiguration {
builder = builder.handle(new DummyMessageHandler(), "handle");
}
beanFactory.initializeBean(builder.get(), flowName);
beanFactory.getBean(flowName + ".filter", Lifecycle.class)
.start();
beanFactory.getBean(flowName + ".filter", Lifecycle.class).start();
beanFactory.getBean(flowName + ".transformer", Lifecycle.class)
.start();
} else if (dsl.getOutputMessage() != null
}
else if (dsl.getOutputMessage() != null
&& dsl.getOutputMessage().getSentTo() != null
&& StringUtils.hasText(
dsl.getOutputMessage().getSentTo().getClientValue())) {
BinderAwareChannelResolver resolver = beanFactory.getBean(BinderAwareChannelResolver.class);
resolver.resolveDestination(dsl.getOutputMessage().getSentTo().getClientValue());
dsl.getOutputMessage().getSentTo().getClientValue())) {
BinderAwareChannelResolver resolver = beanFactory
.getBean(BinderAwareChannelResolver.class);
resolver.resolveDestination(
dsl.getOutputMessage().getSentTo().getClientValue());
}
}
}
@@ -133,26 +134,33 @@ public class StubRunnerStreamConfiguration {
for (Map.Entry<String, BindingProperties> entry : bindings.entrySet()) {
if (destination.equals(entry.getValue().getDestination())) {
if (log.isDebugEnabled()) {
log.debug("Found a channel named [" + entry.getKey() + "] with destination [" + destination + "]");
log.debug("Found a channel named [" + entry.getKey()
+ "] with destination [" + destination + "]");
}
return entry.getKey();
}
}
if (log.isDebugEnabled()) {
log.debug(
"No destination named [" + destination + "] was found. Assuming that the destination equals the channel name");
log.debug("No destination named [" + destination
+ "] was found. Assuming that the destination equals the channel name");
}
return destination;
}
private Map<String, BindingProperties> bindingProperties(AutowireCapableBeanFactory context) {
private Map<String, BindingProperties> bindingProperties(
AutowireCapableBeanFactory context) {
return context.getBean(BindingServiceProperties.class).getBindings();
}
private static class DummyMessageHandler {
public void handle(Message<?> message) {}
public void handle(Message<?> message) {
}
}
static class FlowRegistrar {
}
}

View File

@@ -48,9 +48,11 @@ import com.toomuchcoding.jsonassert.JsonAssertion;
*/
class StubRunnerStreamMessageSelector implements MessageSelector {
private static final Log log = LogFactory.getLog(StubRunnerStreamMessageSelector.class);
private static final Log log = LogFactory
.getLog(StubRunnerStreamMessageSelector.class);
private final Contract groovyDsl;
private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper();
StubRunnerStreamMessageSelector(Contract groovyDsl) {
@@ -62,13 +64,15 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
List<String> unmatchedHeaders = headersMatch(message);
if (!unmatchedHeaders.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("Contract [" + this.groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders);
log.debug("Contract [" + this.groovyDsl
+ "] hasn't matched the following headers " + unmatchedHeaders);
}
return false;
}
Object inputMessage = message.getPayload();
BodyMatchers matchers = this.groovyDsl.getInput().getBodyMatchers();
Object dslBody = MapConverter.getStubSideValues(this.groovyDsl.getInput().getMessageBody());
Object dslBody = MapConverter
.getStubSideValues(this.groovyDsl.getInput().getMessageBody());
Object matchingInputMessage = JsonToJsonPathsConverter
.removeMatchingJsonPaths(dslBody, matchers);
JsonPaths jsonPaths = JsonToJsonPathsConverter
@@ -76,7 +80,8 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
matchingInputMessage);
DocumentContext parsedJson;
try {
parsedJson = JsonPath.parse(this.objectMapper.writeValueAsString(inputMessage));
parsedJson = JsonPath
.parse(this.objectMapper.writeValueAsString(inputMessage));
}
catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize to JSON", e);
@@ -88,23 +93,27 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
}
if (matchers != null && matchers.hasMatchers()) {
for (BodyMatcher matcher : matchers.jsonPathMatchers()) {
String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody);
String jsonPath = JsonToJsonPathsConverter
.convertJsonPathAndRegexToAJsonPath(matcher, dslBody);
matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath);
}
}
if (!unmatchedJsonPath.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("Contract [" + this.groovyDsl + "] didn't much the body due to " + unmatchedJsonPath);
log.debug("Contract [" + this.groovyDsl + "] didn't much the body due to "
+ unmatchedJsonPath);
}
}
return matches;
}
private boolean matchesJsonPath(List<String> unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) {
private boolean matchesJsonPath(List<String> unmatchedJsonPath,
DocumentContext parsedJson, String jsonPath) {
try {
JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath);
return true;
} catch (Exception e) {
}
catch (Exception e) {
unmatchedJsonPath.add(e.getLocalizedMessage());
return false;
}
@@ -121,20 +130,25 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
if (value instanceof Pattern) {
Pattern pattern = (Pattern) value;
matches = pattern.matcher(valueInHeader.toString()).matches();
} else {
matches = valueInHeader != null && valueInHeader.toString().equals(value.toString());
}
else {
matches = valueInHeader != null
&& valueInHeader.toString().equals(value.toString());
}
if (!matches) {
unmatchedHeaders.add("Header with name [" + name + "] was supposed to " +
unmatchedText(value) + " but the value is [" + (valueInHeader != null ?
valueInHeader.toString() : "null") + "]");
unmatchedHeaders.add("Header with name [" + name + "] was supposed to "
+ unmatchedText(value) + " but the value is ["
+ (valueInHeader != null ? valueInHeader.toString() : "null")
+ "]");
}
}
return unmatchedHeaders;
}
private String unmatchedText(Object expectedValue) {
return expectedValue instanceof Pattern ? "match pattern [" + ((Pattern) expectedValue).pattern() + "]" :
"be equal to [" + expectedValue + "]";
return expectedValue instanceof Pattern
? "match pattern [" + ((Pattern) expectedValue).pattern() + "]"
: "be equal to [" + expectedValue + "]";
}
}

View File

@@ -40,11 +40,15 @@ class StubRunnerStreamTransformer implements GenericTransformer<Message<?>, Mess
@Override
public Message<?> transform(Message<?> source) {
if (this.groovyDsl.getOutputMessage()==null) {
if (this.groovyDsl.getOutputMessage() == null) {
return source;
}
String payload = BodyExtractor.extractStubValueFrom(this.groovyDsl.getOutputMessage().getBody());
Map<String, Object> headers = this.groovyDsl.getOutputMessage().getHeaders().asStubSideMap();
return MessageBuilder.createMessage(payload.getBytes(), new MessageHeaders(headers));
String payload = BodyExtractor
.extractStubValueFrom(this.groovyDsl.getOutputMessage().getBody());
Map<String, Object> headers = this.groovyDsl.getOutputMessage().getHeaders()
.asStubSideMap();
return MessageBuilder.createMessage(payload.getBytes(),
new MessageHeaders(headers));
}
}

View File

@@ -20,46 +20,59 @@ import org.springframework.web.client.RestTemplate;
* @author Marcin Grzejszczak
* @since 1.2.6
*/
public final class StubRunnerWireMockTestExecutionListener extends AbstractTestExecutionListener {
public final class StubRunnerWireMockTestExecutionListener
extends AbstractTestExecutionListener {
private static final Log log = LogFactory.getLog(StubRunnerWireMockTestExecutionListener.class);
private static final Log log = LogFactory
.getLog(StubRunnerWireMockTestExecutionListener.class);
private static Map<ApplicationContext, Map<WireMockHttpServerStub, PortAndMappings>> STUBS = new ConcurrentHashMap<>();
@Override public void beforeTestClass(TestContext testContext) {
@Override
public void beforeTestClass(TestContext testContext) {
Map<WireMockHttpServerStub, PortAndMappings> stubs = STUBS
.get(testContext.getApplicationContext());
if (stubs != null) {
if (log.isDebugEnabled()) {
log.debug("Found a matching application context from [" + testContext.getTestClass().getName() + "]");
log.debug("Found a matching application context from ["
+ testContext.getTestClass().getName() + "]");
}
for (Map.Entry<WireMockHttpServerStub, PortAndMappings> entry : stubs.entrySet()) {
for (Map.Entry<WireMockHttpServerStub, PortAndMappings> entry : stubs
.entrySet()) {
while (entry.getKey().isRunning()) {
entry.getKey().stop();
}
List<StubMapping> mappings = entry.getValue().mappings;
if (log.isDebugEnabled()) {
log.debug("Stopped a running WireMock instance at "
+ "port [" + entry.getValue().port + "] with stub mappings "
+ "size [" + mappings.size() + "]. Restarting the stub.");
log.debug("Stopped a running WireMock instance at " + "port ["
+ entry.getValue().port + "] with stub mappings " + "size ["
+ mappings.size() + "]. Restarting the stub.");
}
entry.getKey().start(entry.getValue().port);
entry.getKey().registerDescriptors(mappings);
/*
Thanks to Tom Akehurst:
I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a
previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it
isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet.
Calling the admin endpoint just happened to remove the dead connection from the pool.
This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false");
* Thanks to Tom Akehurst: I looked at tcpdump while running the failing
* test. HttpUrlConnection is doing something weird - it's creating a
* connection in a previous test case, which works fine, then the usual
* fin -> fin ack etc. etc. ending handshake happens. But it seems it
* isn't discarded, but reused after that. Because the server thinks
* (rightly) that the connection is closed, it just sends a RST packet.
* Calling the admin endpoint just happened to remove the dead connection
* from the pool. This also fixes the problem (which using the Java HTTP
* client): System.setProperty("http.keepAlive", "false");
*/
Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + entry.getValue().port + "/__admin/mappings", String.class)
.getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible");
Assert.isTrue(
new RestTemplate()
.getForEntity("http://localhost:" + entry.getValue().port
+ "/__admin/mappings", String.class)
.getStatusCode().is2xxSuccessful(),
"__admin/mappings endpoint wasn't accessible");
}
}
}
@Override public void afterTestClass(TestContext testContext) {
@Override
public void afterTestClass(TestContext testContext) {
STUBS.put(testContext.getApplicationContext(), WireMockHttpServerStub.SERVERS);
if (log.isDebugEnabled()) {
log.debug("Stopping servers " + WireMockHttpServerStub.SERVERS);
@@ -68,4 +81,5 @@ public final class StubRunnerWireMockTestExecutionListener extends AbstractTestE
serverStub.stop();
}
}
}

View File

@@ -50,9 +50,9 @@ public class WireMockHttpServerStub implements HttpServerStub {
private WireMockServer wireMockServer;
private WireMockConfiguration config() {
if (ClassUtils.isPresent("org.springframework.cloud.contract.wiremock.WireMockSpring", null)) {
return WireMockSpring.options()
.extensions(responseTransformers());
if (ClassUtils.isPresent(
"org.springframework.cloud.contract.wiremock.WireMockSpring", null)) {
return WireMockSpring.options().extensions(responseTransformers());
}
return new WireMockConfiguration().extensions(responseTransformers());
}
@@ -65,7 +65,8 @@ public class WireMockHttpServerStub implements HttpServerStub {
for (WireMockExtensions wireMockExtension : wireMockExtensions) {
extensions.addAll(wireMockExtension.extensions());
}
} else {
}
else {
extensions.add(new DefaultResponseTransformer(false, helpers()));
}
return extensions.toArray(new Extension[extensions.size()]);
@@ -73,9 +74,8 @@ public class WireMockHttpServerStub implements HttpServerStub {
/**
* Override this if you want to register your own helpers
*
* @deprecated - please use the {@link WireMockExtensions} mechanism and pass
* the helpers in your implementation
* @deprecated - please use the {@link WireMockExtensions} mechanism and pass the
* helpers in your implementation
*/
@Deprecated
protected Map<String, Helper> helpers() {
@@ -108,8 +108,8 @@ public class WireMockHttpServerStub implements HttpServerStub {
@Override
public HttpServerStub start(int port) {
this.wireMockServer = new WireMockServer(config().port(port)
.notifier(new Slf4jNotifier(true)));
this.wireMockServer = new WireMockServer(
config().port(port).notifier(new Slf4jNotifier(true)));
this.wireMockServer.start();
if (log.isDebugEnabled()) {
log.debug("Started WireMock at port [" + port + "]");
@@ -141,7 +141,8 @@ public class WireMockHttpServerStub implements HttpServerStub {
return this;
}
@Override public String registeredMappings() {
@Override
public String registeredMappings() {
Collection<String> mappings = new ArrayList<>();
for (StubMapping stubMapping : this.wireMockServer.getStubMappings()) {
mappings.add(stubMapping.toString());
@@ -189,12 +190,14 @@ public class WireMockHttpServerStub implements HttpServerStub {
try {
stubMappings.add(registerDescriptor(wireMock, mappingDescriptor));
if (log.isDebugEnabled()) {
log.debug("Registered stub mappings from [" + mappingDescriptor + "]");
log.debug(
"Registered stub mappings from [" + mappingDescriptor + "]");
}
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Failed to register the stub mapping [" + mappingDescriptor + "]", e);
log.debug("Failed to register the stub mapping [" + mappingDescriptor
+ "]", e);
}
}
}
@@ -210,7 +213,8 @@ public class WireMockHttpServerStub implements HttpServerStub {
void registerDescriptors(List<StubMapping> stubMappings) {
if (log.isDebugEnabled()) {
log.debug("Registering stub mappings size [" + stubMappings.size() + "] at port [" + port() + "]");
log.debug("Registering stub mappings size [" + stubMappings.size()
+ "] at port [" + port() + "]");
}
for (StubMapping mapping : stubMappings) {
wireMock().register(mapping);
@@ -222,13 +226,16 @@ public class WireMockHttpServerStub implements HttpServerStub {
}
private void registerHealthCheck(WireMock wireMock, String url, String body) {
wireMock.register(
WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200)));
wireMock.register(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.aResponse().withBody(body).withStatus(200)));
}
}
class PortAndMappings {
final Integer port;
final List<StubMapping> mappings;
PortAndMappings(Integer port, List<StubMapping> mappings) {
@@ -236,7 +243,10 @@ class PortAndMappings {
this.mappings = mappings;
}
@Override public String toString() {
return "PortAndMappings{" + "port=" + this.port + ", mappings=" + this.mappings.size() + '}';
@Override
public String toString() {
return "PortAndMappings{" + "port=" + this.port + ", mappings="
+ this.mappings.size() + '}';
}
}

View File

@@ -34,7 +34,8 @@ import org.springframework.context.annotation.Import;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({HttpStubsController.class, TriggerController.class, StubRunnerConfiguration.class})
@Import({ HttpStubsController.class, TriggerController.class,
StubRunnerConfiguration.class })
public @interface EnableStubRunnerServer {
}

View File

@@ -49,9 +49,10 @@ public class HttpStubsController {
@RequestMapping(path = "/{ivy:.*}")
public ResponseEntity<Integer> consumer(@PathVariable String ivy) {
Integer port = this.stubRunning.runStubs().getPort(ivy);
if (port!=null) {
if (port != null) {
return ResponseEntity.ok(port);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

View File

@@ -49,22 +49,32 @@ public class TriggerController {
}
@PostMapping("/{label:.*}")
public ResponseEntity<Map<String, Collection<String>>> trigger(@PathVariable String label) {
public ResponseEntity<Map<String, Collection<String>>> trigger(
@PathVariable String label) {
try {
this.stubFinder.trigger(label);
return ResponseEntity.ok().body(Collections.<String, Collection<String>>emptyMap());
} catch (Exception e) {
throw new RuntimeException("Exception occurred while trying to return [" + label + "] label. \n\nAvailable labels are [" + this.stubFinder.labels() +" ]", e);
return ResponseEntity.ok()
.body(Collections.<String, Collection<String>>emptyMap());
}
catch (Exception e) {
throw new RuntimeException("Exception occurred while trying to return ["
+ label + "] label. \n\nAvailable labels are ["
+ this.stubFinder.labels() + " ]", e);
}
}
@PostMapping("/{ivyNotation:.*}/{label:.*}")
public ResponseEntity<Map<String, Collection<String>>> triggerByArtifact(@PathVariable String ivyNotation, @PathVariable String label) {
public ResponseEntity<Map<String, Collection<String>>> triggerByArtifact(
@PathVariable String ivyNotation, @PathVariable String label) {
try {
this.stubFinder.trigger(ivyNotation, label);
return ResponseEntity.ok().body(Collections.<String, Collection<String>>emptyMap());
} catch (Exception e) {
throw new RuntimeException("Exception occurred while trying to return [" + label + "] label. \n\nAvailable labels are [" + this.stubFinder.labels() +" ]", e);
return ResponseEntity.ok()
.body(Collections.<String, Collection<String>>emptyMap());
}
catch (Exception e) {
throw new RuntimeException("Exception occurred while trying to return ["
+ label + "] label. \n\nAvailable labels are ["
+ this.stubFinder.labels() + " ]", e);
}
}

View File

@@ -55,8 +55,9 @@ public @interface AutoConfigureStubRunner {
String repositoryRoot() default "";
/**
* The ids of the stubs to run in "ivy" notation ([groupId]:artifactId[:version][:classifier][:port]).
* {@code groupId}, {@code version}, {@code classifier} and {@code port} can be optional.
* The ids of the stubs to run in "ivy" notation
* ([groupId]:artifactId[:version][:classifier][:port]). {@code groupId},
* {@code version}, {@code classifier} and {@code port} can be optional.
*/
String[] ids() default {};
@@ -66,39 +67,50 @@ public @interface AutoConfigureStubRunner {
String classifier() default "stubs";
/**
* On the producer side the consumers can have a folder that contains contracts related only to them. By setting the flag to {@code true}
* we no longer register all stubs but only those that correspond to the consumer application's name. In other words
* we'll scan the path of every stub and if it contains the name of the consumer in the path only then will it get registered.
* On the producer side the consumers can have a folder that contains contracts
* related only to them. By setting the flag to {@code true} we no longer register all
* stubs but only those that correspond to the consumer application's name. In other
* words we'll scan the path of every stub and if it contains the name of the consumer
* in the path only then will it get registered.
*
* Let's look at this example. Let's assume
* that we have a producer called {@code foo} and two consumers {@code baz} and {@code bar}. On the {@code foo} producer side the
* Let's look at this example. Let's assume that we have a producer called {@code foo}
* and two consumers {@code baz} and {@code bar}. On the {@code foo} producer side the
* contracts would look like this
* {@code src/test/resources/contracts/baz-service/some/contracts/...} and
* {@code src/test/resources/contracts/bar-service/some/contracts/...}.
*
* Then when the consumer with {@code spring.application.name} or the {@link AutoConfigureStubRunner#consumerName()}
* annotation parameter set to {@code baz-service} will define the test setup as follows
* {@code @AutoConfigureStubRunner(ids = "com.example:foo:+:stubs:8095", stubsPerConsumer=true)} then only the stubs registered
* under {@code src/test/resources/contracts/baz-service/some/contracts/...} will get registered and those under
* {@code src/test/resources/contracts/bar-service/some/contracts/...} will get ignored.
* Then when the consumer with {@code spring.application.name} or the
* {@link AutoConfigureStubRunner#consumerName()} annotation parameter set to
* {@code baz-service} will define the test setup as follows
* {@code @AutoConfigureStubRunner(ids = "com.example:foo:+:stubs:8095", stubsPerConsumer=true)}
* then only the stubs registered under
* {@code src/test/resources/contracts/baz-service/some/contracts/...} will get
* registered and those under
* {@code src/test/resources/contracts/bar-service/some/contracts/...} will get
* ignored.
*
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
* @see <a href=
* "https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
*
*/
boolean stubsPerConsumer() default false;
/**
* You can override the default {@code spring.application.name} of this field by setting a value to this parameter.
* You can override the default {@code spring.application.name} of this field by
* setting a value to this parameter.
*
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
* @see <a href=
* "https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
*/
String consumerName() default "";
/**
* For debugging purposes you can output the registered mappings to a given folder. Each HTTP server
* stub will have its own subfolder where all the mappings will get stored.
* For debugging purposes you can output the registered mappings to a given folder.
* Each HTTP server stub will have its own subfolder where all the mappings will get
* stored.
*
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/355">issue 355</a>
* @see <a href=
* "https://github.com/spring-cloud/spring-cloud-contract/issues/355">issue 355</a>
*/
String mappingsOutputFolder() default "";
@@ -114,4 +126,5 @@ public @interface AutoConfigureStubRunner {
* @return the properties to add
*/
String[] properties() default {};
}

View File

@@ -54,9 +54,12 @@ public class StubRunnerConfiguration {
@Autowired(required = false)
private MessageVerifier<?> contractVerifierMessaging;
private StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider();
@Autowired
private StubRunnerProperties props;
@Autowired
private ConfigurableEnvironment environment;
@@ -84,18 +87,17 @@ public class StubRunnerConfiguration {
private StubRunnerOptionsBuilder builder() throws IOException {
return new StubRunnerOptionsBuilder()
.withMinMaxPort(this.props.getMinPort(), this.props.getMaxPort())
.withStubRepositoryRoot(this.props.getRepositoryRoot())
.withStubsMode(this.props.getStubsMode())
.withStubsClassifier(this.props.getClassifier())
.withStubs(this.props.getIds())
.withUsername(this.props.getUsername())
.withPassword(this.props.getPassword())
.withStubPerConsumer(this.props.isStubsPerConsumer())
.withConsumerName(consumerName())
.withMappingsOutputFolder(this.props.getMappingsOutputFolder())
.withDeleteStubsAfterTest(this.props.isDeleteStubsAfterTest())
.withProperties(this.props.getProperties());
.withMinMaxPort(this.props.getMinPort(), this.props.getMaxPort())
.withStubRepositoryRoot(this.props.getRepositoryRoot())
.withStubsMode(this.props.getStubsMode())
.withStubsClassifier(this.props.getClassifier())
.withStubs(this.props.getIds()).withUsername(this.props.getUsername())
.withPassword(this.props.getPassword())
.withStubPerConsumer(this.props.isStubsPerConsumer())
.withConsumerName(consumerName())
.withMappingsOutputFolder(this.props.getMappingsOutputFolder())
.withDeleteStubsAfterTest(this.props.isDeleteStubsAfterTest())
.withProperties(this.props.getProperties());
}
private String consumerName() {
@@ -108,15 +110,19 @@ public class StubRunnerConfiguration {
private void registerPort(RunningStubs runStubs) {
MutablePropertySources propertySources = this.environment.getPropertySources();
if (!propertySources.contains(STUBRUNNER_PREFIX)) {
propertySources.addFirst(
new MapPropertySource(STUBRUNNER_PREFIX, new HashMap<String, Object>()));
propertySources.addFirst(new MapPropertySource(STUBRUNNER_PREFIX,
new HashMap<String, Object>()));
}
Map<String, Object> source = ((MapPropertySource) propertySources
.get(STUBRUNNER_PREFIX)).getSource();
for (Map.Entry<StubConfiguration, Integer> entry : runStubs.validNamesAndPorts().entrySet()) {
source.put(STUBRUNNER_PREFIX + "." + entry.getKey().getArtifactId() + ".port", entry.getValue());
// there are projects where artifact id is the same, what differs is the group id
source.put(STUBRUNNER_PREFIX + "." + entry.getKey().getGroupId() + "." + entry.getKey().getArtifactId() + ".port", entry.getValue());
for (Map.Entry<StubConfiguration, Integer> entry : runStubs.validNamesAndPorts()
.entrySet()) {
source.put(STUBRUNNER_PREFIX + "." + entry.getKey().getArtifactId() + ".port",
entry.getValue());
// there are projects where artifact id is the same, what differs is the group
// id
source.put(STUBRUNNER_PREFIX + "." + entry.getKey().getGroupId() + "."
+ entry.getKey().getArtifactId() + ".port", entry.getValue());
}
}

View File

@@ -7,13 +7,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The annotated field with this annotation will have the port of a running stub
* injected.
* The annotated field with this annotation will have the port of a running stub injected.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@Target({ElementType.FIELD})
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StubRunnerPort {

View File

@@ -21,7 +21,8 @@ class StubRunnerPortBeanPostProcessor implements BeanPostProcessor {
this.environment = environment;
}
@Override public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
injectStubRunnerPort(bean);
return bean;
@@ -29,15 +30,17 @@ class StubRunnerPortBeanPostProcessor implements BeanPostProcessor {
private void injectStubRunnerPort(Object bean) {
Class<?> clazz = bean.getClass();
ReflectionUtils.FieldCallback fieldCallback =
new StubRunnerPortFieldCallback(this.environment, bean);
ReflectionUtils.FieldCallback fieldCallback = new StubRunnerPortFieldCallback(
this.environment, bean);
ReflectionUtils.doWithFields(clazz, fieldCallback);
}
}
class StubRunnerPortFieldCallback implements ReflectionUtils.FieldCallback {
private final Environment environment;
private final Object bean;
StubRunnerPortFieldCallback(Environment environment, Object bean) {
@@ -45,17 +48,20 @@ class StubRunnerPortFieldCallback implements ReflectionUtils.FieldCallback {
this.bean = bean;
}
@Override public void doWith(Field field)
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
if (!field.isAnnotationPresent(StubRunnerPort.class)) {
return;
}
ReflectionUtils.makeAccessible(field);
String stub = field.getDeclaredAnnotation(StubRunnerPort.class).value();
Integer port = this.environment.getProperty(
StubRunnerConfiguration.STUBRUNNER_PREFIX + "." + stub.replace(":", ".") + ".port", Integer.class);
Integer port = this.environment
.getProperty(StubRunnerConfiguration.STUBRUNNER_PREFIX + "."
+ stub.replace(":", ".") + ".port", Integer.class);
if (port != null) {
field.set(this.bean, port);
}
}
}

View File

@@ -49,8 +49,9 @@ public class StubRunnerProperties {
private Resource repositoryRoot;
/**
* The ids of the stubs to run in "ivy" notation ([groupId]:artifactId:[version]:[classifier][:port]).
* {@code groupId}, {@code classifier}, {@code version} and {@code port} can be optional.
* The ids of the stubs to run in "ivy" notation
* ([groupId]:artifactId:[version]:[classifier][:port]). {@code groupId},
* {@code classifier}, {@code version} and {@code port} can be optional.
*/
private String[] ids = new String[0];
@@ -85,7 +86,8 @@ public class StubRunnerProperties {
private boolean stubsPerConsumer;
/**
* You can override the default {@code spring.application.name} of this field by setting a value to this parameter.
* You can override the default {@code spring.application.name} of this field by
* setting a value to this parameter.
*/
private String consumerName;
@@ -100,13 +102,14 @@ public class StubRunnerProperties {
private StubsMode stubsMode;
/**
* If set to {@code false} will NOT delete stubs from a temporary
* folder after running tests
* If set to {@code false} will NOT delete stubs from a temporary folder after running
* tests
*/
private boolean deleteStubsAfterTest = true;
/**
* Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
* Map of properties that can be passed to custom
* {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
*/
private Map<String, String> properties = new HashMap<>();
@@ -129,6 +132,7 @@ public class StubRunnerProperties {
* Fetch the stubs from a remote location
*/
REMOTE,
}
public int getMinPort() {
@@ -248,8 +252,8 @@ public class StubRunnerProperties {
}
public void setProperties(String[] properties) {
Properties elements = StringUtils
.splitArrayElementsIntoProperties(properties, "=");
Properties elements = StringUtils.splitArrayElementsIntoProperties(properties,
"=");
if (elements == null) {
return;
}
@@ -258,13 +262,14 @@ public class StubRunnerProperties {
}
}
@Override public String toString() {
return "StubRunnerProperties{" + "minPort=" + this.minPort + ", maxPort=" + this.maxPort
+ ", repositoryRoot=" + this.repositoryRoot
+ ", ids=" + Arrays.toString(this.ids) + ", classifier='" + this.classifier + '\''
+ ", setStubsPerConsumer='" + this.stubsPerConsumer + "', consumerName='" + this.consumerName + '\''
+ ", stubsMode='" + this.stubsMode + '\''
+ ", size of properties=" + this.properties.size()
+ '}';
@Override
public String toString() {
return "StubRunnerProperties{" + "minPort=" + this.minPort + ", maxPort="
+ this.maxPort + ", repositoryRoot=" + this.repositoryRoot + ", ids="
+ Arrays.toString(this.ids) + ", classifier='" + this.classifier + '\''
+ ", setStubsPerConsumer='" + this.stubsPerConsumer + "', consumerName='"
+ this.consumerName + '\'' + ", stubsMode='" + this.stubsMode + '\''
+ ", size of properties=" + this.properties.size() + '}';
}
}

View File

@@ -36,4 +36,5 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Documented
@ConditionalOnProperty(value = "stubrunner.cloud.stubbed.discovery.enabled", havingValue = "false")
public @interface ConditionalOnStubbedDiscoveryDisabled {
}

View File

@@ -25,8 +25,8 @@ import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Conditional that checks if the user turned on the stubbed discovery mode.
* The feature is turned on by default.
* Conditional that checks if the user turned on the stubbed discovery mode. The feature
* is turned on by default.
*
* @author Marcin Grzejszczak
*
@@ -37,4 +37,5 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Documented
@ConditionalOnProperty(value = "stubrunner.cloud.stubbed.discovery.enabled", havingValue = "true", matchIfMissing = true)
public @interface ConditionalOnStubbedDiscoveryEnabled {
}

View File

@@ -24,30 +24,27 @@ import org.springframework.cloud.contract.stubrunner.StubConfiguration;
import org.springframework.util.StringUtils;
/**
* Maps Ivy based ids to service Ids. You might want to name the service you're calling
* in another way than artifact id. If that's the case then this class should be used
* to change do the proper mapping.
* Maps Ivy based ids to service Ids. You might want to name the service you're calling in
* another way than artifact id. If that's the case then this class should be used to
* change do the proper mapping.
*
* Just provide in your properties file for example:
*
* stubrunner.idsToServiceIds:
* fraudDetectionServer: someNameThatShouldMapFraudDetectionServer
* stubrunner.idsToServiceIds: fraudDetectionServer:
* someNameThatShouldMapFraudDetectionServer
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
@ConfigurationProperties("stubrunner")
public class StubMapperProperties {
/**
* Mapping of Ivy notation based ids to serviceIds
* inside your application
* Mapping of Ivy notation based ids to serviceIds inside your application
*
* Example
*
* "a:b" -> "myService"
* "artifactId" -> "myOtherService"
* "a:b" -> "myService" "artifactId" -> "myOtherService"
*/
private Map<String, String> idsToServiceIds = new HashMap<>();
@@ -65,8 +62,8 @@ public class StubMapperProperties {
if (StringUtils.hasText(id)) {
return id;
}
String groupAndArtifact = this.idsToServiceIds.get(stubConfiguration.getGroupId() +
":" + stubConfiguration.getArtifactId());
String groupAndArtifact = this.idsToServiceIds.get(
stubConfiguration.getGroupId() + ":" + stubConfiguration.getArtifactId());
if (StringUtils.hasText(groupAndArtifact)) {
return groupAndArtifact;
}
@@ -81,4 +78,5 @@ public class StubMapperProperties {
}
return null;
}
}

View File

@@ -32,27 +32,30 @@ import org.springframework.cloud.contract.stubrunner.StubFinder;
import org.springframework.util.StringUtils;
/**
* Custom version of {@link DiscoveryClient} that tries to find an instance
* in one of the started WireMock servers
* Custom version of {@link DiscoveryClient} that tries to find an instance in one of the
* started WireMock servers
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
class StubRunnerDiscoveryClient implements DiscoveryClient {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final DiscoveryClient delegate;
private final StubFinder stubFinder;
private final StubMapperProperties stubMapperProperties;
public StubRunnerDiscoveryClient(DiscoveryClient delegate, StubFinder stubFinder,
StubMapperProperties stubMapperProperties, String springAppName) {
this.delegate = delegate instanceof StubRunnerDiscoveryClient ?
noOpDiscoveryClient() : delegate;
this.delegate = delegate instanceof StubRunnerDiscoveryClient
? noOpDiscoveryClient() : delegate;
if (log.isDebugEnabled()) {
log.debug("Will delegate calls to discovery service [" + this.delegate + "] if a stub is not found");
log.debug("Will delegate calls to discovery service [" + this.delegate
+ "] if a stub is not found");
}
this.stubFinder = stubFinder;
this.stubMapperProperties = stubMapperProperties;
@@ -62,7 +65,8 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
StubMapperProperties stubMapperProperties, String springAppName) {
this.delegate = noOpDiscoveryClient();
if (log.isDebugEnabled()) {
log.debug("Will delegate calls to discovery service [" + this.delegate + "] if a stub is not found");
log.debug("Will delegate calls to discovery service [" + this.delegate
+ "] if a stub is not found");
}
this.stubFinder = stubFinder;
this.stubMapperProperties = stubMapperProperties;
@@ -76,7 +80,8 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
public String description() {
try {
return this.delegate.description();
} catch (Exception e) {
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Failed to fetch description from delegate", e);
}
@@ -86,23 +91,25 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
@Override
public List<ServiceInstance> getInstances(String serviceId) {
String ivyNotation = this.stubMapperProperties.fromServiceIdToIvyNotation(serviceId);
String ivyNotation = this.stubMapperProperties
.fromServiceIdToIvyNotation(serviceId);
String serviceToFind = StringUtils.hasText(ivyNotation) ? ivyNotation : serviceId;
URL stubUrl = this.stubFinder.findStubUrl(serviceToFind);
log.info("Resolved from ivy [" + ivyNotation + "] service to find [" + serviceToFind + "]. "
+ "Found stub is available under URL [" + stubUrl + "]");
log.info("Resolved from ivy [" + ivyNotation + "] service to find ["
+ serviceToFind + "]. " + "Found stub is available under URL [" + stubUrl
+ "]");
if (stubUrl == null) {
return getInstancesFromDelegate(serviceId);
}
return Collections.<ServiceInstance>singletonList(
new StubRunnerServiceInstance(serviceId, stubUrl.getHost(), stubUrl.getPort(), toUri(stubUrl))
);
return Collections.<ServiceInstance>singletonList(new StubRunnerServiceInstance(
serviceId, stubUrl.getHost(), stubUrl.getPort(), toUri(stubUrl)));
}
private List<ServiceInstance> getInstancesFromDelegate(String serviceId) {
try {
return new ArrayList<>(this.delegate.getInstances(serviceId));
} catch (Exception e) {
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Failed to fetch instances from delegate", e);
}
@@ -113,7 +120,8 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
private URI toUri(URL url) {
try {
return url.toURI();
} catch (Exception e) {
}
catch (Exception e) {
return null;
}
}
@@ -131,7 +139,8 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
private List<String> getServicesFromDelegate() {
try {
return new ArrayList<>(this.delegate.getServices());
} catch (Exception e) {
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Failed to fetch services from delegate", e);
}
@@ -143,6 +152,7 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
public int getOrder() {
return this.delegate.getOrder();
}
}
class StubRunnerNoOpDiscoveryClient implements DiscoveryClient {
@@ -161,4 +171,5 @@ class StubRunnerNoOpDiscoveryClient implements DiscoveryClient {
public List<String> getServices() {
return Collections.emptyList();
}
}

View File

@@ -26,14 +26,16 @@ import org.springframework.cloud.client.ServiceInstance;
* {@link ServiceInstance} with a helpful constructor
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
class StubRunnerServiceInstance implements ServiceInstance {
private final String serviceId;
private final String host;
private final int port;
private final URI uri;
public StubRunnerServiceInstance(String serviceId, String host, int port, URI uri) {
@@ -72,4 +74,5 @@ class StubRunnerServiceInstance implements ServiceInstance {
public Map<String, String> getMetadata() {
return new HashMap<>();
}
}

View File

@@ -32,8 +32,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* Wraps {@link DiscoveryClient} in a Stub Runner implementation that tries to find
* a corresponding WireMock server for a searched dependency
* Wraps {@link DiscoveryClient} in a Stub Runner implementation that tries to find a
* corresponding WireMock server for a searched dependency
*
* @since 1.0.0
*/
@@ -43,7 +43,8 @@ import org.springframework.core.env.Environment;
@ConditionalOnProperty(value = "stubrunner.cloud.enabled", matchIfMissing = true)
public class StubRunnerSpringCloudAutoConfiguration {
@Autowired BeanFactory beanFactory;
@Autowired
BeanFactory beanFactory;
@Bean
public StubRunnerDiscoveryClientWrapper stubRunnerDiscoveryClientWrapper() {
@@ -57,7 +58,8 @@ public class StubRunnerSpringCloudAutoConfiguration {
public DiscoveryClient noOpStubRunnerDiscoveryClient(StubFinder stubFinder,
StubMapperProperties stubMapperProperties,
@Value("${spring.application.name:unknown}") String springAppName) {
return new StubRunnerDiscoveryClient(stubFinder, stubMapperProperties, springAppName);
return new StubRunnerDiscoveryClient(stubFinder, stubMapperProperties,
springAppName);
}
}
@@ -65,33 +67,43 @@ public class StubRunnerSpringCloudAutoConfiguration {
class StubRunnerDiscoveryClientWrapper implements BeanPostProcessor {
private final BeanFactory beanFactory;
DiscoveryClient discoveryClient;
StubFinder stubFinder;
StubMapperProperties stubMapperProperties;
String springAppName;
Boolean stubbedDiscoveryEnabled;
Boolean cloudDelegateEnabled;
StubRunnerDiscoveryClientWrapper(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override public Object postProcessBeforeInitialization(Object bean, String beanName)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof DiscoveryClient && !(bean instanceof StubRunnerDiscoveryClient)) {
if (bean instanceof DiscoveryClient
&& !(bean instanceof StubRunnerDiscoveryClient)) {
if (!isStubbedDiscoveryEnabled()) {
return bean;
}
if (isCloudDelegateEnabled()) {
return new StubRunnerDiscoveryClient((DiscoveryClient) bean,
stubFinder(), stubMapperProperties(), springAppName());
return new StubRunnerDiscoveryClient((DiscoveryClient) bean, stubFinder(),
stubMapperProperties(), springAppName());
}
return new StubRunnerDiscoveryClient(stubFinder(), stubMapperProperties(), springAppName());
return new StubRunnerDiscoveryClient(stubFinder(), stubMapperProperties(),
springAppName());
}
return bean;
}
@@ -105,7 +117,8 @@ class StubRunnerDiscoveryClientWrapper implements BeanPostProcessor {
StubMapperProperties stubMapperProperties() {
if (this.stubMapperProperties == null) {
this.stubMapperProperties = this.beanFactory.getBean(StubMapperProperties.class);
this.stubMapperProperties = this.beanFactory
.getBean(StubMapperProperties.class);
}
return this.stubMapperProperties;
}
@@ -120,21 +133,20 @@ class StubRunnerDiscoveryClientWrapper implements BeanPostProcessor {
boolean isStubbedDiscoveryEnabled() {
if (this.stubbedDiscoveryEnabled == null) {
this.stubbedDiscoveryEnabled = Boolean.valueOf(
this.beanFactory.getBean(Environment.class)
.getProperty("stubrunner.cloud.stubbed.discovery.enabled", "true")
);
this.stubbedDiscoveryEnabled = Boolean
.valueOf(this.beanFactory.getBean(Environment.class).getProperty(
"stubrunner.cloud.stubbed.discovery.enabled", "true"));
}
return this.stubbedDiscoveryEnabled;
}
boolean isCloudDelegateEnabled() {
if (this.cloudDelegateEnabled == null) {
this.cloudDelegateEnabled = Boolean.valueOf(
this.beanFactory.getBean(Environment.class)
.getProperty("stubrunner.cloud.delegate.enabled", "false")
);
this.cloudDelegateEnabled = Boolean
.valueOf(this.beanFactory.getBean(Environment.class)
.getProperty("stubrunner.cloud.delegate.enabled", "false"));
}
return this.cloudDelegateEnabled;
}
}

View File

@@ -4,9 +4,10 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud;
* Contract for registering stubs in a Service Discovery.
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public interface StubsRegistrar extends AutoCloseable {
void registerStubs();
}

View File

@@ -20,24 +20,28 @@ import org.springframework.util.StringUtils;
* Registers all stubs in Zookeeper Service Discovery
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public class ConsulStubsRegistrar implements StubsRegistrar {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final StubRunning stubRunning;
private final ConsulClient consulClient;
private final StubMapperProperties stubMapperProperties;
private final ConsulDiscoveryProperties consulDiscoveryProperties;
private final InetUtils inetUtils;
private final List<NewService> services = new LinkedList<>();
public ConsulStubsRegistrar(StubRunning stubRunning, ConsulClient consulClient,
StubMapperProperties stubMapperProperties,
ConsulDiscoveryProperties consulDiscoveryProperties,
InetUtils inetUtils) {
StubMapperProperties stubMapperProperties,
ConsulDiscoveryProperties consulDiscoveryProperties, InetUtils inetUtils) {
this.stubRunning = stubRunning;
this.consulClient = consulClient;
this.stubMapperProperties = stubMapperProperties;
@@ -45,7 +49,8 @@ public class ConsulStubsRegistrar implements StubsRegistrar {
this.inetUtils = inetUtils;
}
@Override public void registerStubs() {
@Override
public void registerStubs() {
Map<StubConfiguration, Integer> activeStubs = this.stubRunning.runStubs()
.validNamesAndPorts();
for (Map.Entry<StubConfiguration, Integer> entry : activeStubs.entrySet()) {
@@ -54,12 +59,14 @@ public class ConsulStubsRegistrar implements StubsRegistrar {
try {
this.consulClient.agentServiceRegister(newService);
if (log.isDebugEnabled()) {
log.debug("Successfully registered stub [" + entry.getKey().toColonSeparatedDependencyNotation()
log.debug("Successfully registered stub ["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery");
}
}
catch (Exception e) {
log.warn("Exception occurred while trying to register a stub [" + entry.getKey().toColonSeparatedDependencyNotation()
log.warn("Exception occurred while trying to register a stub ["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery", e);
}
}
@@ -67,9 +74,10 @@ public class ConsulStubsRegistrar implements StubsRegistrar {
protected NewService newService(StubConfiguration stubConfiguration, Integer port) {
NewService newService = new NewService();
newService.setAddress(StringUtils.hasText(this.consulDiscoveryProperties.getHostname()) ?
this.consulDiscoveryProperties.getHostname() :
this.inetUtils.findFirstNonLoopbackAddress().getHostName());
newService.setAddress(
StringUtils.hasText(this.consulDiscoveryProperties.getHostname())
? this.consulDiscoveryProperties.getHostname()
: this.inetUtils.findFirstNonLoopbackAddress().getHostName());
newService.setId(stubConfiguration.getArtifactId());
newService.setName(name(stubConfiguration));
newService.setPort(port);
@@ -91,4 +99,5 @@ public class ConsulStubsRegistrar implements StubsRegistrar {
this.consulClient.agentServiceDeregister(service.getId());
}
}
}

View File

@@ -38,19 +38,19 @@ import org.springframework.context.annotation.Configuration;
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(value = {StubRunnerConfiguration.class,
ConsulServiceRegistryAutoConfiguration.class})
@AutoConfigureAfter(value = { StubRunnerConfiguration.class,
ConsulServiceRegistryAutoConfiguration.class })
@ConditionalOnClass(ConsulClient.class)
@ConditionalOnStubbedDiscoveryDisabled
@ConditionalOnProperty(value = "stubrunner.cloud.consul.enabled", matchIfMissing = true)
public class StubRunnerSpringCloudConsulAutoConfiguration {
@Bean(initMethod = "registerStubs")
public StubsRegistrar stubsRegistrar(StubRunning stubRunning, ConsulClient consulClient,
StubMapperProperties stubMapperProperties,
ConsulDiscoveryProperties consulDiscoveryProperties,
InetUtils inetUtils) {
public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
ConsulClient consulClient, StubMapperProperties stubMapperProperties,
ConsulDiscoveryProperties consulDiscoveryProperties, InetUtils inetUtils) {
return new ConsulStubsRegistrar(stubRunning, consulClient, stubMapperProperties,
consulDiscoveryProperties, inetUtils);
consulDiscoveryProperties, inetUtils);
}
}

View File

@@ -36,4 +36,5 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Documented
@ConditionalOnProperty(value = "eureka.client.enabled", havingValue = "true", matchIfMissing = true)
@interface ConditionalOnEurekaEnabled {
}

View File

@@ -32,28 +32,34 @@ import com.netflix.discovery.EurekaClient;
* Registers all stubs in Eureka Service Discovery
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public class EurekaStubsRegistrar implements StubsRegistrar {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final StubRunning stubRunning;
private final StubMapperProperties stubMapperProperties;
private final InetUtils inetUtils;
private final EurekaInstanceConfigBean eurekaInstanceConfigBean;
private final EurekaClientConfigBean eurekaClientConfigBean;
private final List<EurekaRegistration> registrations = new LinkedList<>();
private final ServiceRegistry<EurekaRegistration> serviceRegistry;
private final ApplicationContext context;
public EurekaStubsRegistrar(StubRunning stubRunning,
ServiceRegistry<EurekaRegistration> serviceRegistry,
StubMapperProperties stubMapperProperties, InetUtils inetUtils,
EurekaInstanceConfigBean eurekaInstanceConfigBean,
EurekaClientConfigBean eurekaClientConfigBean,
ApplicationContext context) {
EurekaClientConfigBean eurekaClientConfigBean, ApplicationContext context) {
this.stubRunning = stubRunning;
this.stubMapperProperties = stubMapperProperties;
this.serviceRegistry = serviceRegistry;
@@ -63,7 +69,8 @@ public class EurekaStubsRegistrar implements StubsRegistrar {
this.context = context;
}
@Override public void registerStubs() {
@Override
public void registerStubs() {
Map<StubConfiguration, Integer> activeStubs = this.stubRunning.runStubs()
.validNamesAndPorts();
for (Map.Entry<StubConfiguration, Integer> entry : activeStubs.entrySet()) {
@@ -72,21 +79,23 @@ public class EurekaStubsRegistrar implements StubsRegistrar {
+ instance.getHostname() + ", " + instance.getNonSecurePort() + ", "
+ instance.getInstanceId() + "]");
InstanceInfo instanceInfo = new InstanceInfoFactory().create(instance);
ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(instance, instanceInfo);
ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(
instance, instanceInfo);
AbstractDiscoveryClientOptionalArgs args = args();
EurekaClient client = new CloudEurekaClient(applicationInfoManager, this.eurekaClientConfigBean, args, this.context);
EurekaClient client = new CloudEurekaClient(applicationInfoManager,
this.eurekaClientConfigBean, args, this.context);
EurekaRegistration registration = EurekaRegistration.builder(instance)
.with(this.eurekaClientConfigBean, this.context)
.with(client)
.build();
.with(this.eurekaClientConfigBean, this.context).with(client).build();
this.registrations.add(registration);
try {
this.serviceRegistry.register(registration);
log.info("Successfully registered stub " + "[" + entry.getKey()
.toColonSeparatedDependencyNotation() + "] in Service Discovery");
log.info("Successfully registered stub " + "["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery");
}
catch (Exception e) {
log.warn("Exception occurred while trying to register a stub [" + entry.getKey().toColonSeparatedDependencyNotation()
log.warn("Exception occurred while trying to register a stub ["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery", e);
}
}
@@ -94,27 +103,29 @@ public class EurekaStubsRegistrar implements StubsRegistrar {
private AbstractDiscoveryClientOptionalArgs args() {
try {
return this.context
.getBean(AbstractDiscoveryClientOptionalArgs.class);
} catch (BeansException e) {
return this.context.getBean(AbstractDiscoveryClientOptionalArgs.class);
}
catch (BeansException e) {
return null;
}
}
private EurekaInstanceConfigBean registration(Map.Entry<StubConfiguration, Integer> entry) {
private EurekaInstanceConfigBean registration(
Map.Entry<StubConfiguration, Integer> entry) {
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(this.inetUtils);
String appName = name(entry.getKey());
config.setInstanceEnabledOnit(true);
InetAddress address = this.inetUtils.findFirstNonLoopbackAddress();
config.setIpAddress(address.getHostAddress());
config.setHostname(StringUtils.hasText(hostName(entry)) ?
hostName(entry) : address.getHostName());
config.setHostname(StringUtils.hasText(hostName(entry)) ? hostName(entry)
: address.getHostName());
config.setAppname(appName);
config.setVirtualHostName(appName);
config.setSecureVirtualHostName(appName);
int port = port(entry);
config.setNonSecurePort(port);
config.setInstanceId(address.getHostAddress() + ":" + entry.getKey().getArtifactId() + ":" + port);
config.setInstanceId(address.getHostAddress() + ":"
+ entry.getKey().getArtifactId() + ":" + port);
config.setLeaseRenewalIntervalInSeconds(1);
return config;
}
@@ -142,4 +153,5 @@ public class EurekaStubsRegistrar implements StubsRegistrar {
this.serviceRegistry.deregister(registration);
}
}
}

View File

@@ -47,11 +47,11 @@ import org.springframework.core.env.Environment;
* Autoconfiguration for registering stubs in a Eureka Service discovery
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter({StubRunnerConfiguration.class, EurekaClientAutoConfiguration.class})
@AutoConfigureAfter({ StubRunnerConfiguration.class,
EurekaClientAutoConfiguration.class })
@ConditionalOnClass(CloudEurekaClient.class)
@ConditionalOnStubbedDiscoveryDisabled
@ConditionalOnEurekaEnabled
@@ -61,43 +61,58 @@ public class StubRunnerSpringCloudEurekaAutoConfiguration {
@Profile("!cloud")
@Configuration
protected static class NonCloudConfig {
@Bean(initMethod = "registerStubs")
public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
ServiceRegistry<EurekaRegistration> serviceRegistry, ApplicationContext context,
StubMapperProperties stubMapperProperties, InetUtils inetUtils,
EurekaInstanceConfigBean eurekaInstanceConfigBean, EurekaClientConfigBean eurekaClientConfigBean) {
return new EurekaStubsRegistrar(stubRunning, serviceRegistry, stubMapperProperties, inetUtils,
eurekaInstanceConfigBean, eurekaClientConfigBean, context);
ServiceRegistry<EurekaRegistration> serviceRegistry,
ApplicationContext context, StubMapperProperties stubMapperProperties,
InetUtils inetUtils, EurekaInstanceConfigBean eurekaInstanceConfigBean,
EurekaClientConfigBean eurekaClientConfigBean) {
return new EurekaStubsRegistrar(stubRunning, serviceRegistry,
stubMapperProperties, inetUtils, eurekaInstanceConfigBean,
eurekaClientConfigBean, context);
}
}
@Profile("cloud")
@Configuration
protected static class CloudConfig {
private static final int DEFAULT_PORT = 80;
private static final Log log = LogFactory.getLog(CloudConfig.class);
@Autowired Environment environment;
@Autowired
Environment environment;
@Bean(initMethod = "registerStubs")
public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
ServiceRegistry<EurekaRegistration> serviceRegistry, ApplicationContext context,
StubMapperProperties stubMapperProperties, InetUtils inetUtils,
EurekaInstanceConfigBean eurekaInstanceConfigBean, EurekaClientConfigBean eurekaClientConfigBean) {
return new EurekaStubsRegistrar(stubRunning, serviceRegistry, stubMapperProperties, inetUtils,
eurekaInstanceConfigBean, eurekaClientConfigBean, context) {
@Override protected String hostName(Map.Entry<StubConfiguration, Integer> entry) {
String hostname =
CloudConfig.this.environment.getProperty("application.hostname") +
"-" + entry.getValue() + "." + CloudConfig.this.environment.getProperty("application.domain");
log.info("Registering stub [" + entry.getKey().getArtifactId() + "] with hostname [" + hostname + "]");
ServiceRegistry<EurekaRegistration> serviceRegistry,
ApplicationContext context, StubMapperProperties stubMapperProperties,
InetUtils inetUtils, EurekaInstanceConfigBean eurekaInstanceConfigBean,
EurekaClientConfigBean eurekaClientConfigBean) {
return new EurekaStubsRegistrar(stubRunning, serviceRegistry,
stubMapperProperties, inetUtils, eurekaInstanceConfigBean,
eurekaClientConfigBean, context) {
@Override
protected String hostName(Map.Entry<StubConfiguration, Integer> entry) {
String hostname = CloudConfig.this.environment
.getProperty("application.hostname") + "-" + entry.getValue()
+ "." + CloudConfig.this.environment
.getProperty("application.domain");
log.info("Registering stub [" + entry.getKey().getArtifactId()
+ "] with hostname [" + hostname + "]");
return hostname;
}
@Override protected int port(Map.Entry<StubConfiguration, Integer> entry) {
@Override
protected int port(Map.Entry<StubConfiguration, Integer> entry) {
return DEFAULT_PORT;
}
};
}
}
}

View File

@@ -26,18 +26,20 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ServerList;
/**
* Ribbon AutoConfiguration that manipulates the service id to make the service
* be picked from the list of available WireMock instance if one is available.
* Ribbon AutoConfiguration that manipulates the service id to make the service be picked
* from the list of available WireMock instance if one is available.
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
class StubRunnerRibbonBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
private StubFinder stubFinder;
private StubMapperProperties stubMapperProperties;
private IClientConfig clientConfig;
StubRunnerRibbonBeanPostProcessor(BeanFactory beanFactory) {
@@ -53,7 +55,8 @@ class StubRunnerRibbonBeanPostProcessor implements BeanPostProcessor {
private StubMapperProperties stubMapperProperties() {
if (this.stubMapperProperties == null) {
this.stubMapperProperties = this.beanFactory.getBean(StubMapperProperties.class);
this.stubMapperProperties = this.beanFactory
.getBean(StubMapperProperties.class);
}
return this.stubMapperProperties;
}
@@ -66,15 +69,19 @@ class StubRunnerRibbonBeanPostProcessor implements BeanPostProcessor {
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ServerList && !(bean instanceof StubRunnerRibbonServerList)) {
return new StubRunnerRibbonServerList(stubFinder(), stubMapperProperties(), clientConfig());
return new StubRunnerRibbonServerList(stubFinder(), stubMapperProperties(),
clientConfig());
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}

View File

@@ -32,8 +32,10 @@ import org.springframework.context.annotation.Role;
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
class StubRunnerRibbonConfiguration {
@Bean
static StubRunnerRibbonBeanPostProcessor stubRunnerRibbonBeanPostProcessor(BeanFactory beanFactory) {
static StubRunnerRibbonBeanPostProcessor stubRunnerRibbonBeanPostProcessor(
BeanFactory beanFactory) {
return new StubRunnerRibbonBeanPostProcessor(beanFactory);
}

View File

@@ -36,12 +36,12 @@ import org.springframework.util.StringUtils;
* Stub Runner representation of a server list
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
class StubRunnerRibbonServerList implements ServerList<Server> {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final ServerList<Server> serverList;
@@ -50,10 +50,12 @@ class StubRunnerRibbonServerList implements ServerList<Server> {
final IClientConfig clientConfig) {
String serviceName = clientConfig.getClientName();
String mappedServiceName = StringUtils
.hasText(stubMapperProperties.fromServiceIdToIvyNotation(serviceName)) ?
stubMapperProperties.fromServiceIdToIvyNotation(serviceName) : serviceName;
.hasText(stubMapperProperties.fromServiceIdToIvyNotation(serviceName))
? stubMapperProperties.fromServiceIdToIvyNotation(serviceName)
: serviceName;
RunningStubs runningStubs = stubFinder.findAllRunningStubs();
final Map.Entry<StubConfiguration, Integer> entry = runningStubs.getEntry(mappedServiceName);
final Map.Entry<StubConfiguration, Integer> entry = runningStubs
.getEntry(mappedServiceName);
final List<Server> servers = new ArrayList<>();
if (entry != null) {
servers.add(new Server("localhost", entry.getValue()) {
@@ -62,7 +64,8 @@ class StubRunnerRibbonServerList implements ServerList<Server> {
return new MetaInfo() {
@Override
public String getAppName() {
return stubMapperProperties.fromIvyNotationToId(entry.getKey().toColonSeparatedDependencyNotation());
return stubMapperProperties.fromIvyNotationToId(
entry.getKey().toColonSeparatedDependencyNotation());
}
@Override
@@ -72,12 +75,14 @@ class StubRunnerRibbonServerList implements ServerList<Server> {
@Override
public String getServiceIdForDiscovery() {
return stubMapperProperties.fromIvyNotationToId(entry.getKey().getArtifactId());
return stubMapperProperties
.fromIvyNotationToId(entry.getKey().getArtifactId());
}
@Override
public String getInstanceId() {
return stubMapperProperties.fromIvyNotationToId(entry.getKey().getArtifactId());
return stubMapperProperties
.fromIvyNotationToId(entry.getKey().getArtifactId());
}
};
}
@@ -105,4 +110,5 @@ class StubRunnerRibbonServerList implements ServerList<Server> {
public List<Server> getUpdatedListOfServers() {
return this.serverList.getUpdatedListOfServers();
}
}

View File

@@ -35,11 +35,11 @@ import org.springframework.context.annotation.Configuration;
* Autoconfiguration for registering stubs in a Zookeeper Service discovery
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter({ StubRunnerConfiguration.class, CuratorServiceDiscoveryAutoConfiguration.class })
@AutoConfigureAfter({ StubRunnerConfiguration.class,
CuratorServiceDiscoveryAutoConfiguration.class })
@ConditionalOnClass(org.apache.curator.x.discovery.ServiceInstance.class)
@ConditionalOnStubbedDiscoveryDisabled
@ConditionalOnZookeeperDiscoveryEnabled
@@ -47,8 +47,11 @@ import org.springframework.context.annotation.Configuration;
public class StubRunnerSpringCloudZookeeperAutoConfiguration {
@Bean(initMethod = "registerStubs")
public StubsRegistrar stubsRegistrar(StubRunning stubRunning, CuratorFramework curatorFramework,
StubMapperProperties stubMapperProperties, ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
return new ZookeeperStubsRegistrar(stubRunning, curatorFramework, stubMapperProperties, zookeeperDiscoveryProperties);
public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
CuratorFramework curatorFramework, StubMapperProperties stubMapperProperties,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
return new ZookeeperStubsRegistrar(stubRunning, curatorFramework,
stubMapperProperties, zookeeperDiscoveryProperties);
}
}

View File

@@ -23,21 +23,25 @@ import org.springframework.util.StringUtils;
* Registers all stubs in Zookeeper Service Discovery
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public class ZookeeperStubsRegistrar implements StubsRegistrar {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
private final StubRunning stubRunning;
private final CuratorFramework curatorFramework;
private final StubMapperProperties stubMapperProperties;
private final ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
private final List<ServiceDiscovery> discoveryList = new LinkedList<>();
public ZookeeperStubsRegistrar(StubRunning stubRunning, CuratorFramework curatorFramework,
StubMapperProperties stubMapperProperties,
public ZookeeperStubsRegistrar(StubRunning stubRunning,
CuratorFramework curatorFramework, StubMapperProperties stubMapperProperties,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
this.stubRunning = stubRunning;
this.curatorFramework = curatorFramework;
@@ -45,30 +49,36 @@ public class ZookeeperStubsRegistrar implements StubsRegistrar {
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
}
@Override public void registerStubs() {
@Override
public void registerStubs() {
Map<StubConfiguration, Integer> activeStubs = this.stubRunning.runStubs()
.validNamesAndPorts();
for (Map.Entry<StubConfiguration, Integer> entry : activeStubs.entrySet()) {
ServiceInstance serviceInstance = serviceInstance(entry.getKey(), entry.getValue());
ServiceInstance serviceInstance = serviceInstance(entry.getKey(),
entry.getValue());
ServiceDiscovery serviceDiscovery = serviceDiscovery(serviceInstance);
this.discoveryList.add(serviceDiscovery);
try {
serviceDiscovery.start();
if (log.isDebugEnabled()) {
log.debug("Successfully registered stub [" + entry.getKey().toColonSeparatedDependencyNotation()
log.debug("Successfully registered stub ["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery");
}
}
catch (Exception e) {
log.warn("Exception occurred while trying to register a stub [" + entry.getKey().toColonSeparatedDependencyNotation()
log.warn("Exception occurred while trying to register a stub ["
+ entry.getKey().toColonSeparatedDependencyNotation()
+ "] in Service Discovery", e);
}
}
}
protected ServiceInstance serviceInstance(StubConfiguration stubConfiguration, int port) {
protected ServiceInstance serviceInstance(StubConfiguration stubConfiguration,
int port) {
try {
return ServiceInstance.builder().uriSpec(new UriSpec(this.zookeeperDiscoveryProperties.getUriSpec()))
return ServiceInstance.builder()
.uriSpec(new UriSpec(this.zookeeperDiscoveryProperties.getUriSpec()))
.address("localhost").port(port).name(name(stubConfiguration))
.build();
}
@@ -98,4 +108,5 @@ public class ZookeeperStubsRegistrar implements StubsRegistrar {
discovery.close();
}
}
}

View File

@@ -31,8 +31,8 @@ import org.springframework.util.StringUtils;
public class StubsParser {
/**
* The string is expected to be a map with entry called "stubs"
* that contains a list of Strings in the format
* The string is expected to be a map with entry called "stubs" that contains a list
* of Strings in the format
*
* <ul>
* <li>groupid:artifactid:version:classifier:port</li>
@@ -47,7 +47,8 @@ public class StubsParser {
*
* "a:b,c:d:e"
*/
public static List<StubConfiguration> fromString(Collection<String> collection, String defaultClassifier) {
public static List<StubConfiguration> fromString(Collection<String> collection,
String defaultClassifier) {
List<StubConfiguration> stubs = new ArrayList<>();
for (String config : collection) {
if (StringUtils.hasText(config)) {
@@ -58,7 +59,8 @@ public class StubsParser {
}
public static Map<StubConfiguration, Integer> fromStringWithPort(String notation) {
StubSpecification stub = StubSpecification.parse(notation, StubConfiguration.DEFAULT_CLASSIFIER);
StubSpecification stub = StubSpecification.parse(notation,
StubConfiguration.DEFAULT_CLASSIFIER);
if (!stub.hasPort()) {
return Collections.emptyMap();
}
@@ -66,7 +68,8 @@ public class StubsParser {
}
public static String ivyFromStringWithPort(String notation) {
StubSpecification stub = StubSpecification.parse(notation, StubConfiguration.DEFAULT_CLASSIFIER);
StubSpecification stub = StubSpecification.parse(notation,
StubConfiguration.DEFAULT_CLASSIFIER);
if (!stub.hasPort()) {
return "";
}
@@ -76,16 +79,18 @@ public class StubsParser {
public static boolean hasPort(String id) {
String[] splitEntry = id.split(":");
try {
Integer.valueOf(splitEntry[splitEntry.length-1]);
Integer.valueOf(splitEntry[splitEntry.length - 1]);
return true;
} catch (NumberFormatException e) {
}
catch (NumberFormatException e) {
return false;
}
}
private static class StubSpecification {
private final StubConfiguration stub;
private final Integer port;
public StubSpecification(StubConfiguration stub, Integer port) {
@@ -96,16 +101,20 @@ public class StubsParser {
public boolean hasPort() {
return this.port != null;
}
private static StubSpecification parse(String id, String defaultClassifier) {
String[] splitEntry = id.split(":");
Integer port = null;
try {
port = Integer.valueOf(splitEntry[splitEntry.length-1]);
port = Integer.valueOf(splitEntry[splitEntry.length - 1]);
id = id.substring(0, id.lastIndexOf(":"));
} catch (NumberFormatException e) {}
return new StubSpecification(new StubConfiguration(id, defaultClassifier), port);
}
catch (NumberFormatException e) {
}
return new StubSpecification(new StubConfiguration(id, defaultClassifier),
port);
}
}
}

View File

@@ -43,7 +43,6 @@ public class ZipCategory {
/**
* Unzips this file. If the <tt>destination</tt> directory is not provided, it will
* fall back to this file's parent directory.
*
* @param self
* @param destination (optional), the destination directory where this file's content
* will be unzipped to.
@@ -58,8 +57,8 @@ public class ZipCategory {
List<File> unzippedFiles = new ArrayList<>();
try (InputStream fileInputStream = Files.newInputStream(self.toPath())) {
try (ZipInputStream zipInput = new ZipInputStream(fileInputStream)) {
for (ZipEntry entry = zipInput.getNextEntry(); entry != null; entry = zipInput
.getNextEntry()) {
for (ZipEntry entry = zipInput
.getNextEntry(); entry != null; entry = zipInput.getNextEntry()) {
if (!entry.isDirectory()) {
final File file = new File(destination, entry.getName());
if (file.getParentFile() != null) {
@@ -77,7 +76,8 @@ public class ZipCategory {
}
}
}
} catch (IOException e) {
}
catch (IOException e) {
throw new IllegalStateException("Cannot unzip archive", e);
}
return unzippedFiles;
@@ -87,4 +87,5 @@ public class ZipCategory {
if (file != null && !file.isDirectory())
throw new IllegalArgumentException("'destination' has to be a directory.");
}
}

View File

@@ -39,7 +39,9 @@ import org.junit.rules.TemporaryFolder;
*/
public abstract class AbstractGitTest {
@Rule public TemporaryFolder tmp = new TemporaryFolder();
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
File tmpFolder;
@Before
@@ -53,7 +55,7 @@ public abstract class AbstractGitTest {
try (PrintStream out = new PrintStream(new FileOutputStream(newFile))) {
out.print("foo");
}
try(Git git = openGitProject(project)) {
try (Git git = openGitProject(project)) {
git.add().addFilepattern("newFile").call();
}
return newFile;
@@ -61,7 +63,7 @@ public abstract class AbstractGitTest {
void setOriginOnProjectToTmp(File origin, File project, boolean push)
throws GitAPIException, IOException, URISyntaxException {
try(Git git = openGitProject(project)) {
try (Git git = openGitProject(project)) {
RemoteRemoveCommand remove = git.remoteRemove();
remove.setName("origin");
remove.call();
@@ -72,7 +74,8 @@ public abstract class AbstractGitTest {
command.call();
StoredConfig config = git.getRepository().getConfig();
RemoteConfig originConfig = new RemoteConfig(config, "origin");
originConfig.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
originConfig
.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
originConfig.update(config);
config.save();
}
@@ -87,4 +90,5 @@ public abstract class AbstractGitTest {
projectRepo.cloneProject(projectToClone.toURI());
return baseDir;
}
}

View File

@@ -10,10 +10,13 @@ import static org.assertj.core.api.BDDAssertions.then;
*/
public class ClasspathStubProviderTest {
@Test public void should_return_null_if_stub_mode_is_not_classpath() {
StubDownloader stubDownloader = new ClasspathStubProvider().build(new StubRunnerOptionsBuilder().withStubsMode(
StubRunnerProperties.StubsMode.REMOTE).build());
@Test
public void should_return_null_if_stub_mode_is_not_classpath() {
StubDownloader stubDownloader = new ClasspathStubProvider()
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE).build());
then(stubDownloader).isNull();
}
}

View File

@@ -14,11 +14,14 @@ import org.junit.Test;
*/
public class CompositeStubDownloaderBuilderTests {
@Test public void should_delegate_work_to_other_stub_downloaders() {
@Test
public void should_delegate_work_to_other_stub_downloaders() {
EmptyStubDownloaderBuilder emptyStubDownloaderBuilder = new EmptyStubDownloaderBuilder();
ImpossibleToBuildStubDownloaderBuilder impossible = new ImpossibleToBuildStubDownloaderBuilder();
List<StubDownloaderBuilder> builders = Arrays.asList(emptyStubDownloaderBuilder, impossible, new SomeStubDownloaderBuilder());
CompositeStubDownloaderBuilder builder = new CompositeStubDownloaderBuilder(builders);
List<StubDownloaderBuilder> builders = Arrays.asList(emptyStubDownloaderBuilder,
impossible, new SomeStubDownloaderBuilder());
CompositeStubDownloaderBuilder builder = new CompositeStubDownloaderBuilder(
builders);
StubDownloader downloader = builder.build(new StubRunnerOptionsBuilder().build());
Map.Entry<StubConfiguration, File> entry = downloader
@@ -29,20 +32,23 @@ public class CompositeStubDownloaderBuilderTests {
BDDAssertions.then(impossible.called).isTrue();
}
@Test public void should_return_null_if_no_builders_were_passed() {
@Test
public void should_return_null_if_no_builders_were_passed() {
CompositeStubDownloaderBuilder builder = new CompositeStubDownloaderBuilder(null);
StubDownloader downloader = builder.build(new StubRunnerOptionsBuilder().build());
BDDAssertions.then(downloader).isNull();
}
}
class EmptyStubDownloaderBuilder implements StubDownloaderBuilder {
EmptyStubDownloader emptyStubDownloader;
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
this.emptyStubDownloader = new EmptyStubDownloader();
return this.emptyStubDownloader;
}
@@ -50,13 +56,15 @@ class EmptyStubDownloaderBuilder implements StubDownloaderBuilder {
boolean downloaderCalled() {
return this.emptyStubDownloader.called;
}
}
class ImpossibleToBuildStubDownloaderBuilder implements StubDownloaderBuilder {
boolean called;
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
this.called = true;
return null;
}
@@ -64,25 +72,33 @@ class ImpossibleToBuildStubDownloaderBuilder implements StubDownloaderBuilder {
}
class EmptyStubDownloader implements StubDownloader {
boolean called;
@Override public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
@Override
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration) {
this.called = true;
return null;
}
}
class SomeStubDownloaderBuilder implements StubDownloaderBuilder {
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
@Override
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
return new SomeStubDownloader();
}
}
class SomeStubDownloader implements StubDownloader {
@Override public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
@Override
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
StubConfiguration stubConfiguration) {
return new AbstractMap.SimpleEntry<>(stubConfiguration, new File("."));
}
}

View File

@@ -36,18 +36,25 @@ import static org.assertj.core.api.BDDAssertions.then;
* @author Marcin Grzejszczak
*/
public class ContractProjectUpdaterTest extends AbstractGitTest {
File originalProject;
File project;
ContractProjectUpdater updater;
GitRepo gitRepo;
File origin;
@Rule public OutputCapture outputCapture = new OutputCapture();
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Before
public void setup() throws Exception {
GitContractsRepo.CACHED_LOCATIONS.clear();
this.originalProject = new File(GitRepoTests.class.getResource("/git_samples/contract-git").toURI());
this.originalProject = new File(
GitRepoTests.class.getResource("/git_samples/contract-git").toURI());
TestUtils.prepareLocalRepo();
this.gitRepo = new GitRepo(this.tmpFolder);
this.origin = clonedProject(this.tmp.newFolder(), this.originalProject);
@@ -56,31 +63,36 @@ public class ContractProjectUpdaterTest extends AbstractGitTest {
setOriginOnProjectToTmp(this.origin, this.project, true);
StubRunnerOptions options = new StubRunnerOptionsBuilder()
.withStubRepositoryRoot("file://" + this.project.getAbsolutePath() + "/")
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.build();
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE).build();
this.updater = new ContractProjectUpdater(options);
}
@Test
public void should_push_changes_to_current_branch() throws Exception {
File stubs = new File(GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
File stubs = new File(
GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
this.updater.updateContractProject("hello-world", stubs.toPath());
// project, not origin, cause we're making one more clone of the local copy
try(Git git = openGitProject(this.project)) {
try (Git git = openGitProject(this.project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("Updating project [hello-world] with stubs");
then(revCommit.getShortMessage())
.isEqualTo("Updating project [hello-world] with stubs");
// I have no idea but the file gets deleted after pushing
git.reset().setMode(ResetCommand.ResetType.HARD).call();
}
BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists();
BDDAssertions.then(new File(this.project,
"META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json"))
.exists();
BDDAssertions.then(gitRepo.gitFactory.provider).isNull();
BDDAssertions.then(outputCapture.toString()).contains("No custom credentials provider will be set");
BDDAssertions.then(outputCapture.toString())
.contains("No custom credentials provider will be set");
}
@Test
public void should_push_changes_to_current_branch_using_credentials() throws Exception {
public void should_push_changes_to_current_branch_using_credentials()
throws Exception {
StubRunnerOptions options = new StubRunnerOptionsBuilder()
.withStubRepositoryRoot("file://" + this.project.getAbsolutePath() + "/")
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
@@ -89,56 +101,68 @@ public class ContractProjectUpdaterTest extends AbstractGitTest {
put("git.username", "foo");
put("git.password", "bar");
}
} )
.build();
}).build();
ContractProjectUpdater updater = new ContractProjectUpdater(options);
File stubs = new File(GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
File stubs = new File(
GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
updater.updateContractProject("hello-world", stubs.toPath());
// project, not origin, cause we're making one more clone of the local copy
try(Git git = openGitProject(this.project)) {
try (Git git = openGitProject(this.project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("Updating project [hello-world] with stubs");
then(revCommit.getShortMessage())
.isEqualTo("Updating project [hello-world] with stubs");
// I have no idea but the file gets deleted after pushing
git.reset().setMode(ResetCommand.ResetType.HARD).call();
}
BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists();
BDDAssertions.then(outputCapture.toString()).contains("Passed username and password - will set a custom credentials provider");
BDDAssertions.then(new File(this.project,
"META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json"))
.exists();
BDDAssertions.then(outputCapture.toString()).contains(
"Passed username and password - will set a custom credentials provider");
}
@Test
public void should_push_changes_to_current_branch_using_root_credentials() throws Exception {
public void should_push_changes_to_current_branch_using_root_credentials()
throws Exception {
StubRunnerOptions options = new StubRunnerOptionsBuilder()
.withStubRepositoryRoot("file://" + this.project.getAbsolutePath() + "/")
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withUsername("foo")
.withPassword("bar")
.build();
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE).withUsername("foo")
.withPassword("bar").build();
ContractProjectUpdater updater = new ContractProjectUpdater(options);
File stubs = new File(GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
File stubs = new File(
GitRepoTests.class.getResource("/git_samples/sample_stubs").toURI());
updater.updateContractProject("hello-world", stubs.toPath());
// project, not origin, cause we're making one more clone of the local copy
try(Git git = openGitProject(this.project)) {
try (Git git = openGitProject(this.project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("Updating project [hello-world] with stubs");
then(revCommit.getShortMessage())
.isEqualTo("Updating project [hello-world] with stubs");
// I have no idea but the file gets deleted after pushing
git.reset().setMode(ResetCommand.ResetType.HARD).call();
}
BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).exists();
BDDAssertions.then(outputCapture.toString()).contains("Passed username and password - will set a custom credentials provider");
BDDAssertions.then(new File(this.project,
"META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json"))
.exists();
BDDAssertions.then(outputCapture.toString()).contains(
"Passed username and password - will set a custom credentials provider");
}
@Test
public void should_not_push_changes_to_current_branch_when_no_changes_were_made() throws Exception {
public void should_not_push_changes_to_current_branch_when_no_changes_were_made()
throws Exception {
this.updater.updateContractProject("hello-world", this.origin.toPath());
try(Git git = openGitProject(this.project)) {
try (Git git = openGitProject(this.project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("Initial commit");
}
BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json")).doesNotExist();
BDDAssertions.then(new File(this.project,
"META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json"))
.doesNotExist();
}
}

View File

@@ -31,17 +31,19 @@ import static org.assertj.core.api.BDDAssertions.then;
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
/**
* @author Marcin Grzejszczak
* taken from: https://github.com/spring-cloud/spring-cloud-release-tools
* @author Marcin Grzejszczak taken from:
* https://github.com/spring-cloud/spring-cloud-release-tools
*/
public class GitRepoTests extends AbstractGitTest {
File project;
GitRepo gitRepo;
@Before
public void setup() throws IOException, URISyntaxException {
this.project = new File(GitRepoTests.class.getResource("/git_samples/contract-git").toURI());
this.project = new File(
GitRepoTests.class.getResource("/git_samples/contract-git").toURI());
TestUtils.prepareLocalRepo();
this.gitRepo = new GitRepo(this.tmpFolder);
}
@@ -54,19 +56,22 @@ public class GitRepoTests extends AbstractGitTest {
}
@Test
public void should_throw_exception_when_there_is_no_repo() throws IOException, URISyntaxException {
public void should_throw_exception_when_there_is_no_repo()
throws IOException, URISyntaxException {
thenThrownBy(() -> this.gitRepo
.cloneProject(GitRepoTests.class.getResource("/git_samples/").toURI()))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Exception occurred while cloning repo");
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Exception occurred while cloning repo");
}
@Test
public void should_throw_an_exception_when_failed_to_initialize_the_repo() throws IOException {
thenThrownBy(() -> new GitRepo(this.tmpFolder, new ExceptionThrowingJGitFactory()).cloneProject(this.project.toURI()))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Exception occurred while cloning repo")
.hasCauseInstanceOf(CustomException.class);
public void should_throw_an_exception_when_failed_to_initialize_the_repo()
throws IOException {
thenThrownBy(() -> new GitRepo(this.tmpFolder, new ExceptionThrowingJGitFactory())
.cloneProject(this.project.toURI()))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Exception occurred while cloning repo")
.hasCauseInstanceOf(CustomException.class);
}
@Test
@@ -79,12 +84,14 @@ public class GitRepoTests extends AbstractGitTest {
}
@Test
public void should_throw_an_exception_when_checking_out_nonexisting_branch() throws IOException {
public void should_throw_an_exception_when_checking_out_nonexisting_branch()
throws IOException {
File project = this.gitRepo.cloneProject(this.project.toURI());
try {
this.gitRepo.checkout(project, "nonExistingBranch");
fail("should throw an exception");
} catch (IllegalStateException e) {
}
catch (IllegalStateException e) {
then(e).hasMessageContaining("Ref nonExistingBranch can not be resolved");
}
}
@@ -96,7 +103,7 @@ public class GitRepoTests extends AbstractGitTest {
this.gitRepo.commit(project, "some message");
try(Git git = openGitProject(project)) {
try (Git git = openGitProject(project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("some message");
}
@@ -120,7 +127,7 @@ public class GitRepoTests extends AbstractGitTest {
this.gitRepo.commit(project, "empty commit");
try(Git git = openGitProject(project)) {
try (Git git = openGitProject(project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isNotEqualTo("empty commit");
}
@@ -136,7 +143,7 @@ public class GitRepoTests extends AbstractGitTest {
this.gitRepo.pushCurrentBranch(project);
try(Git git = openGitProject(origin)) {
try (Git git = openGitProject(origin)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("some message");
}
@@ -152,21 +159,27 @@ public class GitRepoTests extends AbstractGitTest {
this.gitRepo.pull(project);
try(Git git = openGitProject(project)) {
try (Git git = openGitProject(project)) {
RevCommit revCommit = git.log().call().iterator().next();
then(revCommit.getShortMessage()).isEqualTo("some message");
}
}
}
class ExceptionThrowingJGitFactory extends GitRepo.JGitFactory {
@Override CloneCommand getCloneCommandByCloneRepository() {
@Override
CloneCommand getCloneCommandByCloneRepository() {
throw new CustomException("foo");
}
}
class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}

View File

@@ -31,7 +31,9 @@ import static org.assertj.core.api.BDDAssertions.then;
public class GitStubDownloaderTests {
@Rule public TemporaryFolder tmp = new TemporaryFolder();
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
File temporaryFolder;
@Before
@@ -45,10 +47,10 @@ public class GitStubDownloaderTests {
public void should_return_a_null_downloader_for_a_classptath_mode() {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.CLASSPATH)
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.CLASSPATH)
.withProperties(props()).build());
then(stubDownloader).isNull();
}
@@ -57,10 +59,10 @@ public class GitStubDownloaderTests {
public void should_return_a_null_downloader_for_a_empty_repo() {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withProperties(props()).build());
then(stubDownloader).isNull();
}
@@ -69,46 +71,54 @@ public class GitStubDownloaderTests {
public void should_return_a_null_downloader_for_a_non_git_repo() {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("http://foo.com")
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("http://foo.com").withProperties(props())
.build());
then(stubDownloader).isNull();
}
@Test
public void should_pick_stubs_for_group_and_artifact_with_version_from_a_git_repo() throws Exception {
public void should_pick_stubs_for_group_and_artifact_with_version_from_a_git_repo()
throws Exception {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://" + file("/git_samples/contract-git/").getAbsolutePath() + "/")
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://"
+ file("/git_samples/contract-git/").getAbsolutePath()
+ "/")
.withProperties(props()).build());
Map.Entry<StubConfiguration, File> entry = stubDownloader
.downloadAndUnpackStubJar(new StubConfiguration("foo.bar:bazService:0.0.1-SNAPSHOT"));
.downloadAndUnpackStubJar(
new StubConfiguration("foo.bar:bazService:0.0.1-SNAPSHOT"));
then(entry).isNotNull();
then(entry.getValue().getAbsolutePath()).contains("foo.bar" + File.separator + "bazService" + File.separator + "0.0.1-SNAPSHOT");
then(entry.getValue().getAbsolutePath()).contains("foo.bar" + File.separator
+ "bazService" + File.separator + "0.0.1-SNAPSHOT");
}
@Test
public void should_fail_to_fetch_stubs_when_latest_version_was_specified()
throws URISyntaxException {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://" + file("/git_samples/contract-git").getAbsolutePath())
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://"
+ file("/git_samples/contract-git").getAbsolutePath())
.withProperties(props()).build());
try {
stubDownloader
.downloadAndUnpackStubJar(new StubConfiguration("foo.bar:bazService:+"));
} catch (IllegalStateException e) {
then(e).hasMessageContaining("Concrete version wasn't passed for [foo.bar:bazService:+:stubs]");
stubDownloader.downloadAndUnpackStubJar(
new StubConfiguration("foo.bar:bazService:+"));
}
catch (IllegalStateException e) {
then(e).hasMessageContaining(
"Concrete version wasn't passed for [foo.bar:bazService:+:stubs]");
}
}
@@ -116,17 +126,20 @@ public class GitStubDownloaderTests {
public void should_fail_to_fetch_stubs_when_concrete_version_was_not_specified()
throws URISyntaxException {
StubDownloaderBuilder stubDownloaderBuilder = new ScmStubDownloaderBuilder();
StubDownloader stubDownloader = stubDownloaderBuilder.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://" + file("/git_samples/contract-git").getAbsolutePath())
.withProperties(props())
.build());
StubDownloader stubDownloader = stubDownloaderBuilder
.build(new StubRunnerOptionsBuilder()
.withStubsMode(StubRunnerProperties.StubsMode.REMOTE)
.withStubRepositoryRoot("git://"
+ file("/git_samples/contract-git").getAbsolutePath())
.withProperties(props()).build());
try {
stubDownloader
.downloadAndUnpackStubJar(new StubConfiguration("foo.bar", "bazService", ""));
} catch (IllegalStateException e) {
then(e).hasMessageContaining("Concrete version wasn't passed for [foo.bar:bazService::stubs]");
stubDownloader.downloadAndUnpackStubJar(
new StubConfiguration("foo.bar", "bazService", ""));
}
catch (IllegalStateException e) {
then(e).hasMessageContaining(
"Concrete version wasn't passed for [foo.bar:bazService::stubs]");
}
}
@@ -139,4 +152,5 @@ public class GitStubDownloaderTests {
private File file(String relativePath) throws URISyntaxException {
return new File(GitStubDownloaderTests.class.getResource(relativePath).toURI());
}
}

Some files were not shown because too many files have changed in this diff Show More