From 3a85b60408583e469c3d8aeb056fe5bda79178d2 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 15 Jul 2016 17:10:40 +0100 Subject: [PATCH] Convert stub-runner base to Java --- .../loan/LoanApplicationServiceSpec.groovy | 2 + .../StubRunnerIntegrationConfiguration.groovy | 1 - .../spring-cloud-contract-stub-runner/pom.xml | 3 - .../stubrunner/AetherFactories.groovy | 68 ----- .../stubrunner/AetherStubDownloader.groovy | 155 ------------ .../contract/stubrunner/Arguments.groovy | 43 ---- .../stubrunner/AvailablePortScanner.groovy | 89 ------- .../stubrunner/BatchStubRunner.groovy | 142 ----------- .../contract/stubrunner/RunningStubs.groovy | 88 ------- .../stubrunner/StubConfiguration.groovy | 156 ------------ .../cloud/contract/stubrunner/StubData.groovy | 31 --- .../contract/stubrunner/StubRepository.groovy | 105 -------- .../contract/stubrunner/StubRunner.groovy | 122 --------- .../stubrunner/StubRunnerExecutor.groovy | 164 ------------ .../stubrunner/StubRunnerFactory.groovy | 63 ----- .../contract/stubrunner/StubRunnerMain.groovy | 94 ------- .../stubrunner/StubRunnerOptions.groovy | 86 ------- .../StubRunnerOptionsBuilder.groovy | 135 ---------- .../contract/stubrunner/StubServer.groovy | 104 -------- .../WiremockMappingDescriptor.groovy | 43 ---- .../stubrunner/util/StubsParser.groovy | 81 ------ .../stubrunner/util/ZipCategory.groovy | 72 ------ .../contract/stubrunner/AetherFactories.java | 75 ++++++ .../stubrunner/AetherStubDownloader.java | 210 ++++++++++++++++ .../cloud/contract/stubrunner/Arguments.java | 57 +++++ .../stubrunner/AvailablePortScanner.java | 108 ++++++++ .../contract/stubrunner/BatchStubRunner.java | 181 ++++++++++++++ .../stubrunner/BatchStubRunnerFactory.java} | 42 ++-- .../stubrunner/GroovyDslWrapper.java} | 14 +- .../MessageNotMatchingException.java} | 6 +- .../contract/stubrunner/RunningStubs.java | 136 ++++++++++ .../stubrunner/StubConfiguration.java | 186 ++++++++++++++ .../cloud/contract/stubrunner/StubData.java | 75 ++++++ .../contract/stubrunner/StubDownloader.java} | 9 +- .../contract/stubrunner/StubFinder.java} | 18 +- .../contract/stubrunner/StubRepository.java | 155 ++++++++++++ .../cloud/contract/stubrunner/StubRunner.java | 136 ++++++++++ .../stubrunner/StubRunnerExecutor.java | 234 ++++++++++++++++++ .../stubrunner/StubRunnerFactory.java | 71 ++++++ .../contract/stubrunner/StubRunnerMain.java | 92 +++++++ .../StubRunnerMessagingTrigger.java} | 14 +- .../stubrunner/StubRunnerOptions.java | 120 +++++++++ .../stubrunner/StubRunnerOptionsBuilder.java | 144 +++++++++++ .../contract/stubrunner/StubRunning.java} | 8 +- .../cloud/contract/stubrunner/StubServer.java | 131 ++++++++++ .../contract/stubrunner/StubTrigger.java} | 15 +- .../stubrunner/WiremockMappingDescriptor.java | 80 ++++++ .../stubrunner/util/StringUtils.java} | 81 +++--- .../contract/stubrunner/util/StubsParser.java | 116 +++++++++ .../contract/stubrunner/util/ZipCategory.java | 89 +++++++ .../stubrunner/StubRepositorySpec.groovy | 2 +- .../ContractVerifierCamelConfiguration.java | 14 +- ...tractVerifierIntegrationConfiguration.java | 11 +- .../ContractVerifierIntegrationMessaging.java | 10 +- 54 files changed, 2532 insertions(+), 1955 deletions(-) delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherFactories.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/Arguments.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunner.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/RunningStubs.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubConfiguration.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubData.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRepository.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunner.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMain.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubServer.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StubsParser.groovy delete mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/ZipCategory.groovy create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunner.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.groovy => java/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.java} (54%) rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.groovy => java/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.java} (71%) rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.groovy => java/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.java} (86%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/RunningStubs.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubData.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/StubDownloader.groovy => java/org/springframework/cloud/contract/stubrunner/StubDownloader.java} (82%) rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/StubFinder.groovy => java/org/springframework/cloud/contract/stubrunner/StubFinder.java} (74%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.groovy => java/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.java} (68%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/StubRunning.groovy => java/org/springframework/cloud/contract/stubrunner/StubRunning.java} (80%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/StubTrigger.groovy => java/org/springframework/cloud/contract/stubrunner/StubTrigger.java} (81%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java rename spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/{groovy/org/springframework/cloud/contract/stubrunner/util/StringUtils.groovy => java/org/springframework/cloud/contract/stubrunner/util/StringUtils.java} (64%) create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StubsParser.java create mode 100644 spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/ZipCategory.java diff --git a/samples/samples-standalone/http-client/src/test/groovy/com/example/loan/LoanApplicationServiceSpec.groovy b/samples/samples-standalone/http-client/src/test/groovy/com/example/loan/LoanApplicationServiceSpec.groovy index 64404dd02b..bc9e3a9010 100644 --- a/samples/samples-standalone/http-client/src/test/groovy/com/example/loan/LoanApplicationServiceSpec.groovy +++ b/samples/samples-standalone/http-client/src/test/groovy/com/example/loan/LoanApplicationServiceSpec.groovy @@ -1,6 +1,7 @@ package com.example.loan import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.context.SpringBootContextLoader import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.test.context.ContextConfiguration @@ -14,6 +15,7 @@ import com.example.loan.model.LoanApplicationStatus @ContextConfiguration(loader = SpringBootContextLoader, classes = Application) @AutoConfigureStubRunner +@IntegrationTest("debug=true") class LoanApplicationServiceSpec extends Specification { @Autowired diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner-messaging/spring-cloud-contract-stub-runner-integration/src/main/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner-messaging/spring-cloud-contract-stub-runner-integration/src/main/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.groovy index 70f082d629..62442dd699 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner-messaging/spring-cloud-contract-stub-runner-integration/src/main/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner-messaging/spring-cloud-contract-stub-runner-integration/src/main/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationConfiguration.groovy @@ -39,7 +39,6 @@ import org.springframework.messaging.Message * @author Marcin Grzejszczak */ @Configuration -@Import(StubRunnerConfiguration) @CompileStatic class StubRunnerIntegrationConfiguration { diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/pom.xml index ee67f35838..cee0572e9c 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/pom.xml @@ -100,9 +100,6 @@ - addSources - addTestSources - compile testCompile diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherFactories.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherFactories.groovy deleted file mode 100644 index df67a84c25..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherFactories.groovy +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import org.apache.maven.repository.internal.MavenRepositorySystemUtils -import org.eclipse.aether.DefaultRepositorySystemSession -import org.eclipse.aether.RepositorySystem -import org.eclipse.aether.RepositorySystemSession -import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory -import org.eclipse.aether.impl.DefaultServiceLocator -import org.eclipse.aether.repository.LocalRepository -import org.eclipse.aether.repository.RemoteRepository -import org.eclipse.aether.repository.RepositoryPolicy -import org.eclipse.aether.spi.connector.RepositoryConnectorFactory -import org.eclipse.aether.spi.connector.transport.TransporterFactory -import org.eclipse.aether.transport.file.FileTransporterFactory -import org.eclipse.aether.transport.http.HttpTransporterFactory - -class AetherFactories { - - private static final String MAVEN_LOCAL_REPOSITORY_LOCATION = 'maven.repo.local' - - static RepositorySystem newRepositorySystem() { - DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator() - locator.addService(RepositoryConnectorFactory, BasicRepositoryConnectorFactory) - locator.addService(TransporterFactory, FileTransporterFactory) - locator.addService(TransporterFactory, HttpTransporterFactory) - return locator.getService(RepositorySystem) - } - - static RepositorySystemSession newSession(RepositorySystem system, boolean workOffline) { - DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession() - session.setOffline(workOffline) - if (!workOffline) { - session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS) - } - LocalRepository localRepo = new LocalRepository(localRepositoryDirectory()) - session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)) - return session - } - - static List newRepositories(List repositories) { - return repositories.withIndex() - .findAll { String repo, int index -> repo } - .collect { String repo, int index -> - new RemoteRepository.Builder('remote' + index, 'default', repo).build() - } - } - - static String localRepositoryDirectory() { - System.getProperty(MAVEN_LOCAL_REPOSITORY_LOCATION, "${System.getProperty("user.home")}/.m2/repository") - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.groovy deleted file mode 100644 index 588e26c0b4..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.groovy +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j -import org.eclipse.aether.RepositorySystem -import org.eclipse.aether.RepositorySystemSession -import org.eclipse.aether.artifact.Artifact -import org.eclipse.aether.artifact.DefaultArtifact -import org.eclipse.aether.repository.RemoteRepository -import org.eclipse.aether.resolution.ArtifactRequest -import org.eclipse.aether.resolution.ArtifactResult -import org.eclipse.aether.resolution.VersionRangeRequest -import org.eclipse.aether.resolution.VersionRangeResult -import org.eclipse.aether.resolution.VersionRequest -import org.eclipse.aether.resolution.VersionResult - -import static AetherFactories.newRepositories -import static AetherFactories.newRepositorySystem -import static AetherFactories.newSession -import static org.springframework.cloud.contract.stubrunner.util.ZipCategory.unzipTo -import static java.nio.file.Files.createTempDirectory - -/** - * @author Mariusz Smykula - */ -@CompileStatic -@Slf4j -class AetherStubDownloader implements StubDownloader { - - 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 = '+' - - private final List remoteRepos - private final RepositorySystem repositorySystem - private final RepositorySystemSession session - - AetherStubDownloader(StubRunnerOptions stubRunnerOptions) { - this.remoteRepos = remoteRepositories(stubRunnerOptions) - if (!remoteRepos) { - log.error('Remote repositories for stubs are not specified!') - } - this.repositorySystem = newRepositorySystem() - this.session = newSession(this.repositorySystem, stubRunnerOptions.workOffline) - } - - /** - * Used by the Maven Plugin - * - * @param repositorySystem - * @param remoteRepositories - remote artifact repositories - * @param session - * @param workOffline - */ - AetherStubDownloader(RepositorySystem repositorySystem, - List remoteRepositories, - RepositorySystemSession session) { - this.remoteRepos = remoteRepositories - this.repositorySystem = repositorySystem - this.session = session - if (!remoteRepos) { - log.error('Remote remoteRepositories for stubs are not specified!') - } - } - - private List remoteRepositories(StubRunnerOptions stubRunnerOptions) { - return newRepositories(stubRunnerOptions.stubRepositoryRoot.split(',').toList()) - } - - private File unpackedJar(String resolvedVersion, String stubsGroup, String stubsModule, String classifier) { - log.info("Resolved version is [$resolvedVersion]") - if (!resolvedVersion) { - log.warn("Stub for group [$stubsGroup] module [$stubsModule] and classifier [$classifier] not found in $remoteRepos") - return null - } - Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, ARTIFACT_EXTENSION, resolvedVersion) - ArtifactRequest request = new ArtifactRequest(artifact: artifact, repositories: remoteRepos) - log.info("Resolving artifact '$artifact' using remote repositories $remoteRepos.url") - try { - ArtifactResult result = repositorySystem.resolveArtifact(session, request) - log.info("Resolved artifact $artifact to ${result.artifact.file}") - File temporaryFile = unpackStubJarToATemporaryFolder(result.artifact.file.toURI()) - log.info("Unpacked file to [$temporaryFile]") - return temporaryFile - } catch (Exception e) { - log.warn("Exception occured while trying to download a stub for group [$stubsGroup] module [$stubsModule] and classifier [$classifier] in $remoteRepos", e) - return null - } - - } - - private String getVersion(String stubsGroup, String stubsModule, String version, String classifier) { - if (!version || LATEST_VERSION_IN_IVY == version) { - log.info("Desired version is [$version] - will try to resolve the latest version") - return resolveHighestArtifactVersion(stubsGroup, stubsModule, classifier) - } - log.info("Will try to resolve version [$version]") - return resolveArtifactVersion(stubsGroup, stubsModule, version, classifier) - } - - @Override - Map.Entry downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration) { - String version = getVersion(stubConfiguration.groupId, stubConfiguration.artifactId, stubConfiguration.version, stubConfiguration.classifier) - File unpackedJar = unpackedJar(version, stubConfiguration.groupId, stubConfiguration.artifactId, - stubConfiguration.classifier) - if (!unpackedJar) { - return null - } - return new AbstractMap.SimpleEntry(new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId, version, stubConfiguration.classifier), - unpackedJar) - } - - private String resolveHighestArtifactVersion(String stubsGroup, String stubsModule, String classifier) { - Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, ARTIFACT_EXTENSION, LATEST_ARTIFACT_VERSION) - VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, remoteRepos, null) - VersionRangeResult rangeResult = repositorySystem.resolveVersionRange(session, versionRangeRequest) - if (!rangeResult.highestVersion) { - log.error("Version was not resolved!") - } - return rangeResult.highestVersion ?: '' - } - - private String resolveArtifactVersion(String stubsGroup, String stubsModule, String version, String classifier) { - Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, ARTIFACT_EXTENSION, version) - VersionRequest versionRequest = new VersionRequest(artifact, remoteRepos, null) - VersionResult versionResult = repositorySystem.resolveVersion(session, versionRequest) - return versionResult.version ?: '' - } - - private static File unpackStubJarToATemporaryFolder(URI stubJarUri) { - File tmpDirWhereStubsWillBeUnzipped = createTempDirectory(TEMP_DIR_PREFIX).toFile() - tmpDirWhereStubsWillBeUnzipped.deleteOnExit() - log.info("Unpacking stub from JAR [URI: ${stubJarUri}]") - unzipTo(new File(stubJarUri), tmpDirWhereStubsWillBeUnzipped) - return tmpDirWhereStubsWillBeUnzipped - } - -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/Arguments.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/Arguments.groovy deleted file mode 100644 index 254bc0634b..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/Arguments.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.transform.ToString - -/** - * Arguments passed to the {@link StubRunner} application - * - * @see StubRunner - */ -@CompileStatic -@ToString(includeNames = true) -@PackageScope -class Arguments { - final StubRunnerOptions stubRunnerOptions - final String context - final String repositoryPath - final StubConfiguration stub - - Arguments(StubRunnerOptions stubRunnerOptions, String repositoryPath = "", StubConfiguration stub = null) { - this.stubRunnerOptions = stubRunnerOptions - this.context = context - this.repositoryPath = repositoryPath - this.stub = stub - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.groovy deleted file mode 100644 index d493199106..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.util.logging.Slf4j - -/** - * Tries to execute a closure with an available port from the given range - */ -@CompileStatic -@Slf4j -@PackageScope -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, int maxRetryCount = MAX_RETRY_COUNT) { - checkPortRanges(minPortNumber, maxPortNumber) - this.minPortNumber = minPortNumber - this.maxPortNumber = maxPortNumber - this.maxRetryCount = maxRetryCount - } - - private void checkPortRanges(int minPortNumber, int maxPortNumber) { - if (minPortNumber > maxPortNumber) { - throw new InvalidPortRange(minPortNumber, maxPortNumber) - } - } - - public T tryToExecuteWithFreePort(Closure closure) { - for (i in (1..maxRetryCount)) { - try { - int numberOfPortsToBind = maxPortNumber - minPortNumber + 1 - int portToScan = new Random().nextInt(numberOfPortsToBind) + minPortNumber - checkIfPortIsAvailable(portToScan) - return executeLogicForAvailablePort(portToScan, closure) - } catch (BindException exception) { - log.debug("Failed to execute closure (try: $i/$maxRetryCount)", exception) - } - } - throw new NoPortAvailableException(minPortNumber, maxPortNumber) - } - - private T executeLogicForAvailablePort(int portToScan, Closure closure) { - log.debug("Trying to execute closure with port [$portToScan]") - return closure(portToScan) - } - - private void checkIfPortIsAvailable(int portToScan) { - ServerSocket socket = null - try { - socket = new ServerSocket(portToScan) - } finally { - socket?.close() - } - } - - static class NoPortAvailableException extends RuntimeException { - protected NoPortAvailableException(int lowerBound, int upperBound) { - super("Could not find available port in range $lowerBound:$upperBound") - } - } - - static class InvalidPortRange extends RuntimeException { - protected InvalidPortRange(int lowerBound, int upperBound) { - super("Invalid bounds exceptions, min port [$lowerBound] is greater to max port [$upperBound]") - } - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunner.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunner.groovy deleted file mode 100644 index b15eacc95b..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunner.groovy +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import org.springframework.cloud.contract.spec.Contract -/** - * Manages lifecycle of multiple {@link StubRunner} instances. - * - * @see StubRunner - */ -@CompileStatic -class BatchStubRunner implements StubRunning { - - private final Iterable stubRunners - - BatchStubRunner(Iterable stubRunners) { - this.stubRunners = stubRunners - } - - @Override - RunningStubs runStubs() { - Map appsAndPorts = stubRunners.inject([:]) { Map acc, StubRunner value -> - RunningStubs runningStubs = value.runStubs() - acc.putAll(runningStubs.validNamesAndPorts()) - return acc - } as Map - return new RunningStubs(appsAndPorts) - } - - @Override - URL findStubUrl(String groupId, String artifactId) { - return stubRunners.findResult(null) { StubRunner stubRunner -> - return stubRunner.findStubUrl(groupId, artifactId) - } as URL - } - - @Override - URL findStubUrl(String ivyNotation) { - String[] splitString = ivyNotation.split(":") - if (splitString.length > 3) { - throw new IllegalArgumentException("$ivyNotation is invalid") - } else if (splitString.length == 2) { - return findStubUrl(splitString[0], splitString[1]) - } - return findStubUrl(null, splitString[0]) - } - - @Override - RunningStubs findAllRunningStubs() { - return new RunningStubs(stubRunners.collect { StubRunner runner -> runner.findAllRunningStubs() }) - } - - @Override - Map> getContracts() { - return stubRunners.inject([:]) { Map> map, StubRunner stubRunner -> - map.putAll(stubRunner.contracts ?: [:]) - return map - } as Map> - } - - @Override - boolean trigger(String ivyNotation, String labelName) { - boolean triggered = stubRunners.inject(false) { boolean acc, StubRunner stubRunner -> - boolean success = stubRunner.trigger(ivyNotation, labelName) - if (acc) { - return true - } - return success - } - if (!triggered) { - throw new IllegalArgumentException("No label with name [$labelName] for " + - "dependency [$ivyNotation] was found. Here you have the list of dependencies " + - "and their labels [${ivyToLabels()}]") - } - return triggered - } - - private String ivyToLabels() { - return labels().entrySet().collect { - "Dependency [${it.key}] has labels ${it.value}]" - }.join('\n') - } - - @Override - boolean trigger(String labelName) { - boolean triggered = stubRunners.inject(false) { boolean acc, StubRunner stubRunner -> - boolean success = stubRunner.trigger(labelName) - if (acc) { - return true - } - return success - } - if (!triggered) { - throw new IllegalArgumentException("No label with name [$labelName] was found. " + - "Here you have the list of dependencies and their labels [${ivyToLabels()}") - } - return triggered - } - - @Override - boolean trigger() { - return stubRunners.inject(false) { boolean acc, StubRunner stubRunner -> - boolean success = stubRunner.trigger() - if (acc) { - return true - } - return success - } - } - - @Override - Map> labels() { - return stubRunners.inject([:]) { Map> map, StubRunner stubRunner -> - if (stubRunner) { - map.putAll(stubRunner.labels() ?: [:]) - } - return map - } as Map> - } - - @Override - void close() throws IOException { - stubRunners.each { - it.close() - } - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/RunningStubs.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/RunningStubs.groovy deleted file mode 100644 index 41ca16dea2..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/RunningStubs.groovy +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -/** - * Structure representing executed stubs. Contains the configuration of each stub - * together with the port on which its executed. - */ -@EqualsAndHashCode -@CompileStatic -class RunningStubs { - final Map namesAndPorts = [:] - - RunningStubs(Map map) { - this.namesAndPorts.putAll(map) - } - - RunningStubs(Collection runningStubs) { - runningStubs.each { - this.namesAndPorts.putAll(it.namesAndPorts) - } - } - - Integer getPort(String artifactId) { - return getEntry(artifactId)?.value - } - - Map.Entry getEntry(String artifactId) { - return namesAndPorts.entrySet().find { - it.key.matchesIvyNotation(artifactId) - } - } - - Integer getPort(String groupId, String artifactId) { - return namesAndPorts.entrySet().find { - it.key.matchesIvyNotation("$groupId:$artifactId") - }?.value - } - - boolean isPresent(String artifactId) { - return getEntry(artifactId) - } - - boolean isPresent(String groupId, String artifactId) { - return namesAndPorts.entrySet().find { - it.key.matchesIvyNotation("$groupId:$artifactId") - } - } - - Set getAllServices() { - return namesAndPorts.keySet() - } - - Set getAllServicesNames() { - return namesAndPorts.keySet().collect { it.artifactId } as Set - } - - Map toIvyToPortMapping() { - return namesAndPorts.collectEntries { [(it.key.toColonSeparatedDependencyNotation()) : it.value] } as Map - } - - Map validNamesAndPorts() { - return namesAndPorts.findAll { StubConfiguration key, Integer value -> value != -1 } - } - - @Override - String toString() { - return namesAndPorts.collect { - "Stub [${it.key.toColonSeparatedDependencyNotation()}] is running on port [${it.value}]" - }.join("\n") - } -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubConfiguration.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubConfiguration.groovy deleted file mode 100644 index 82daa2d0cc..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubConfiguration.groovy +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import org.springframework.cloud.contract.stubrunner.util.StringUtils - -/** - * Represents a configuration of a single stub. The stub can be described - * by groupId:artifactId:classifier notation - */ -@CompileStatic -@EqualsAndHashCode -public class StubConfiguration { - private static final String STUB_COLON_DELIMITER = ":" - private static final String DEFAULT_VERSION = "+" - private 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) { - this.groupId = groupId - this.artifactId = artifactId - this.version = version - this.classifier = DEFAULT_CLASSIFIER - } - - public StubConfiguration(String groupId, String artifactId, String version, String classifier) { - this.groupId = groupId - this.artifactId = artifactId - this.version = version - this.classifier = classifier - } - - public StubConfiguration(String stubPath, String defaultClassifier) { - String[] parsedPath = parsedPathEmptyByDefault(stubPath, STUB_COLON_DELIMITER, defaultClassifier) - this.groupId = parsedPath[0] - this.artifactId = parsedPath[1] - this.version = parsedPath[2] - this.classifier = parsedPath[3] - } - - public StubConfiguration(String stubPath) { - String[] parsedPath = parsedPathEmptyByDefault(stubPath, STUB_COLON_DELIMITER, DEFAULT_CLASSIFIER) - this.groupId = parsedPath[0] - this.artifactId = parsedPath[1] - this.version = parsedPath[2] - this.classifier = parsedPath[3] - } - - private List parsedPathEmptyByDefault(String path, String delimiter, String defaultClassifier) { - String[] splitPath = path.split(delimiter) - String stubsGroupId = "" - String stubsArtifactId = "" - String stubsVersion = "" - String stubsClassifier = "" - if (splitPath.length >= 2) { - stubsGroupId = splitPath[0] - stubsArtifactId = splitPath[1] - stubsVersion = splitPath.length >= 3 ? splitPath[2] : DEFAULT_VERSION - stubsClassifier = splitPath.length == 4 ? splitPath[3] : defaultClassifier - } - return [stubsGroupId, stubsArtifactId, stubsVersion, stubsClassifier] - } - - private boolean isDefined() { - return StringUtils.hasText(groupId) && StringUtils.hasText(this.artifactId) - } - - String toColonSeparatedDependencyNotation() { - if(!isDefined()) { - return "" - } - return [groupId, artifactId, version, classifier].join(STUB_COLON_DELIMITER) - } - - @CompileDynamic - boolean groupIdAndArtifactMatches(String ivyNotationAsString) { - def (String groupId, String artifactId) = ivyNotationFrom(ivyNotationAsString) - if (!groupId) { - return this.artifactId == artifactId - } - return this.groupId == groupId && this.artifactId == artifactId - } - - boolean equals(o) { - if (this.is(o)) return true - if (getClass() != o.class) return false - - StubConfiguration that = (StubConfiguration) o - - if (artifactId != that.artifactId) return false - if (groupId != that.groupId) return false - - return true - } - - int hashCode() { - int result - result = (groupId != null ? groupId.hashCode() : 0) - result = 31 * result + (artifactId != null ? artifactId.hashCode() : 0) - return result - } - - boolean matchesIvyNotation(String ivyNotationAsString) { - def strings = ivyNotationAsString.split(':') - if (strings.length == 1) { - return artifactId == ivyNotationAsString - } else if(strings.length == 2) { - return groupId == strings[0] && - artifactId == strings[1] - } else if(strings.length == 3) { - return groupId == strings[0] && - artifactId == strings[1] && - (strings[2] == DEFAULT_VERSION || version == strings[2]) - } - return groupId == strings[0] && - artifactId == strings[1] && - (strings[2] == DEFAULT_VERSION || version == strings[2]) && - classifier == strings[3] - } - - private String[] ivyNotationFrom(String ivyNotation) { - String[] splitString = ivyNotation.split(":") - if (splitString.length == 1) { - // assuming that ivy notation represents artifactId only - return [null, splitString[0]] as String[] - } - return [splitString[0], splitString[1]] as String[] - } - - - @Override - public String toString() { - return toColonSeparatedDependencyNotation() - } -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubData.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubData.groovy deleted file mode 100644 index e66fbb3217..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubData.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import org.springframework.cloud.contract.spec.Contract - -/** - * @author Marcin Grzejszczak - */ -@CompileStatic -@EqualsAndHashCode -class StubData { - final Integer port - final List contracts -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRepository.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRepository.groovy deleted file mode 100644 index c471e63555..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRepository.groovy +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.util.logging.Slf4j -import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter - -/** - * Wraps the folder with WireMock mappings. - */ -@CompileStatic -@PackageScope -@Slf4j -class StubRepository { - - private final File path - final List projectDescriptors - final Collection contracts - - StubRepository(File repository) { - if (!repository.isDirectory()) { - throw new FileNotFoundException("Missing descriptor repository under path [$path]") - } - this.path = repository - this.projectDescriptors = projectDescriptors() - this.contracts = contracts() - } - - /** - * Returns a list of {@link Contract} - */ - private Collection contracts() { - List contracts = [] - contracts.addAll(contractDescriptors()) - return contracts - } - - /** - * Returns the list of WireMock JSON files wrapped in {@link WiremockMappingDescriptor} - */ - private List projectDescriptors() { - List mappingDescriptors = [] - mappingDescriptors.addAll(contextDescriptors()) - return mappingDescriptors - } - - private List contextDescriptors() { - return (path.exists() ? collectMappingDescriptors(path) : []) as List - } - - private List collectMappingDescriptors(File descriptorsDirectory) { - List mappingDescriptors = [] - descriptorsDirectory.eachFileRecurse { File file -> - if (isMappingDescriptor(file)) { - mappingDescriptors << new WiremockMappingDescriptor(file) - } - } - return mappingDescriptors - } - - private Collection contractDescriptors() { - return (path.exists() ? collectContractDescriptors(path) : []) as Collection - } - - private Collection collectContractDescriptors(File descriptorsDirectory) { - List mappingDescriptors = [] - descriptorsDirectory.eachFileRecurse { File file -> - if (isContractDescriptor(file)) { - try { - mappingDescriptors << ContractVerifierDslConverter.convert(file) - } catch (Exception e) { - log.warn("Exception occurred while trying to parse file [$file]", e) - } - } - } - return mappingDescriptors - } - - private static boolean isMappingDescriptor(File file) { - return file.isFile() && file.name.endsWith('.json') - } - - private static boolean isContractDescriptor(File file) { - //TODO: Consider script injections implications... - return file.isFile() && file.name.endsWith('.groovy') - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunner.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunner.groovy deleted file mode 100644 index 7b460ebce7..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunner.groovy +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j -import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging -import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging - -/** - * Represents a single instance of ready-to-run stubs. - * Can run the stubs and then will return the name of the collaborator together with - * its URI. - * Can also be queried if the current groupid and artifactid are matching the - * corresponding running stub. - */ -@Slf4j -@CompileStatic -class StubRunner implements StubRunning { - - private final StubRepository stubRepository - private final StubConfiguration stubsConfiguration - private final StubRunnerOptions stubRunnerOptions - private final StubRunnerExecutor localStubRunner - private final ContractVerifierMessaging contractVerifierMessaging - - @Deprecated - StubRunner(Arguments arguments) { - this(arguments.stubRunnerOptions, arguments.repositoryPath, arguments.stub) - } - - StubRunner(StubRunnerOptions stubRunnerOptions, String repositoryPath, StubConfiguration stubsConfiguration) { - this(stubRunnerOptions, repositoryPath, stubsConfiguration, new NoOpContractVerifierMessaging()) - } - - StubRunner(StubRunnerOptions stubRunnerOptions, String repositoryPath, StubConfiguration stubsConfiguration, - ContractVerifierMessaging contractVerifierMessaging) { - this.stubsConfiguration = stubsConfiguration - this.stubRunnerOptions = stubRunnerOptions - this.stubRepository = new StubRepository(new File(repositoryPath)) - AvailablePortScanner portScanner = new AvailablePortScanner(stubRunnerOptions.minPortValue, - stubRunnerOptions.maxPortValue) - this.contractVerifierMessaging = contractVerifierMessaging - this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging) - } - - @Override - RunningStubs runStubs() { - registerShutdownHook() - return localStubRunner.runStubs(stubRunnerOptions,stubRepository, stubsConfiguration) - } - - @Override - URL findStubUrl(String groupId, String artifactId) { - return localStubRunner.findStubUrl(groupId, artifactId) - } - - @Override - URL findStubUrl(String ivyNotation) { - String[] splitString = ivyNotation.split(":") - if (splitString.length == 1) { - // assuming that ivy notation represents artifactId only - return findStubUrl(null, splitString[0]) - } - return findStubUrl(splitString[0], splitString[1]) - } - - @Override - RunningStubs findAllRunningStubs() { - return localStubRunner.findAllRunningStubs() - } - - @Override - Map> getContracts() { - return localStubRunner.getContracts() - } - - @Override - boolean trigger(String ivyNotation, String labelName) { - return localStubRunner.trigger(ivyNotation, labelName) - } - - @Override - boolean trigger(String labelName) { - return localStubRunner.trigger(labelName) - } - - @Override - boolean trigger() { - return localStubRunner.trigger() - } - - @Override - Map> labels() { - return localStubRunner.labels() - } - - private void registerShutdownHook() { - Runnable stopAllServers = { this.close() } - Runtime.runtime.addShutdownHook(new Thread(stopAllServers)) - } - - @Override - void close() throws IOException { - localStubRunner?.shutdown() - } -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.groovy deleted file mode 100644 index abdbf36f91..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.groovy +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.json.JsonOutput -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j -import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessage -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging -import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging -import org.springframework.cloud.contract.verifier.util.BodyExtractor - -/** - * Runs stubs for a particular {@link StubServer} - */ -@CompileStatic -@Slf4j -class StubRunnerExecutor implements StubFinder { - - private final AvailablePortScanner portScanner - private final ContractVerifierMessaging contractVerifierMessaging - private StubServer stubServer - - StubRunnerExecutor(AvailablePortScanner portScanner, ContractVerifierMessaging contractVerifierMessaging) { - this.portScanner = portScanner - this.contractVerifierMessaging = contractVerifierMessaging - } - - StubRunnerExecutor(AvailablePortScanner portScanner) { - this.portScanner = portScanner - this.contractVerifierMessaging = new NoOpContractVerifierMessaging() - } - - RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, StubRepository repository, StubConfiguration stubConfiguration) { - startStubServers(stubRunnerOptions, stubConfiguration, repository) - RunningStubs runningCollaborators = - new RunningStubs([(stubServer.stubConfiguration): stubServer.port]) - log.info("All stubs are now running [${runningCollaborators.toString()}") - return runningCollaborators - } - - void shutdown() { - stubServer?.stop() - } - - @Override - URL findStubUrl(String groupId, String artifactId) { - if (!groupId) { - return returnStubUrlIfMatches(stubServer.stubConfiguration.artifactId == artifactId) - } - return returnStubUrlIfMatches(stubServer.stubConfiguration.artifactId == artifactId && - stubServer.stubConfiguration.groupId == groupId) - } - - @Override - URL findStubUrl(String ivyNotation) { - String[] splitString = ivyNotation.split(":") - if (splitString.length == 1) { - throw new IllegalArgumentException("$ivyNotation is invalid") - } - return findStubUrl(splitString[0], splitString[1]) - } - - @Override - RunningStubs findAllRunningStubs() { - return new RunningStubs([(stubServer.stubConfiguration) : stubServer.port]) - } - - @Override - Map> getContracts() { - return [(stubServer.stubConfiguration): stubServer.contracts] - } - - @Override - boolean trigger(String ivyNotationAsString, String labelName) { - Collection matchingContracts = getContracts().findAll { - it.key.groupIdAndArtifactMatches(ivyNotationAsString) - }.values().flatten() as Collection - return triggerForDsls(matchingContracts, labelName) - } - - @Override - boolean trigger(String labelName) { - return triggerForDsls(getContracts().values().flatten() as Collection, labelName) - } - - private boolean triggerForDsls(Collection dsls, String labelName) { - Collection matchingDsls = dsls.findAll { it.label == labelName } - if (matchingDsls.empty) { - return false - } - matchingDsls.each { - sendMessageIfApplicable(it) - } - return true - } - - @Override - boolean trigger() { - (getContracts().values().flatten() as Collection).each { Contract groovyDsl -> - sendMessageIfApplicable(groovyDsl) - } - return true - } - - @Override - Map> labels() { - return getContracts().collectEntries { - [(it.key.toColonSeparatedDependencyNotation()) : it.value.collect { it.label }] - } as Map> - } - - private void sendMessageIfApplicable(Contract groovyDsl) { - if (!groovyDsl.outputMessage) { - return - } - ContractVerifierMessage message = contractVerifierMessaging.create( - new JsonOutput().toJson(BodyExtractor.extractClientValueFromBody(groovyDsl.outputMessage?.body?.clientValue)), - groovyDsl.outputMessage?.headers?.asStubSideMap()) - contractVerifierMessaging.send(message, groovyDsl.outputMessage.sentTo.clientValue) - } - - private URL returnStubUrlIfMatches(boolean condition) { - return condition ? stubServer.stubUrl : null - } - - private void startStubServers(StubRunnerOptions stubRunnerOptions, StubConfiguration stubConfiguration, StubRepository repository) { - List mappings = repository.getProjectDescriptors() - Collection contracts = repository.contracts - Integer port = stubRunnerOptions.port(stubConfiguration) - if (port) { - stubServer = new StubServer(port, stubConfiguration, mappings, contracts) - } else { - stubServer = portScanner.tryToExecuteWithFreePort { int availablePort -> - return new StubServer(availablePort, stubConfiguration, mappings, contracts) - } - } - if (!contracts.empty && !contracts.any { it.request }) { - log.debug("There are no HTTP related contracts. Won't start any servers") - return - } - if (contracts.empty) { - log.warn("There are no contracts in the published JAR. This is an unusual situation " + - "that's why will start the server - maybe you know what you're doing...") - } - stubServer = stubServer.start() - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.groovy deleted file mode 100644 index 6980b01e77..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.groovy +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging - -/** - * Factory of StubRunners. Basing on the options and passed collaborators - * downloads the stubs and returns a list of corresponding stub runners. - */ -@Slf4j -@CompileStatic -class StubRunnerFactory { - - private final StubRunnerOptions stubRunnerOptions - private final StubDownloader stubDownloader - private final ContractVerifierMessaging contractVerifierMessaging - - StubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader, ContractVerifierMessaging contractVerifierMessaging) { - this.stubRunnerOptions = stubRunnerOptions - this.stubDownloader = stubDownloader - this.contractVerifierMessaging = contractVerifierMessaging - } - - Collection createStubsFromServiceConfiguration() { - return stubRunnerOptions.getDependencies().collect { StubConfiguration stubsConfiguration -> - Map.Entry entry = stubDownloader.downloadAndUnpackStubJar(stubRunnerOptions, stubsConfiguration) - if (!entry) { - return (StubRunner) null - } - return createStubRunner(entry.key, entry.value) - }.findAll { it != null } - } - - private StubRunner createStubRunner(StubConfiguration stubsConfiguration, File unzipedStubDir) { - if (!unzipedStubDir) { - return null - } - return createStubRunner(unzipedStubDir, stubsConfiguration, stubRunnerOptions) - } - - private StubRunner createStubRunner(File unzippedStubsDir, StubConfiguration stubsConfiguration, - StubRunnerOptions stubRunnerOptions) { - return new StubRunner(stubRunnerOptions, unzippedStubsDir.path, stubsConfiguration, contractVerifierMessaging) - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMain.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMain.groovy deleted file mode 100644 index 3fc959780a..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMain.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j -import org.kohsuke.args4j.CmdLineException -import org.kohsuke.args4j.CmdLineParser -import org.kohsuke.args4j.Option - -import static org.kohsuke.args4j.OptionHandlerFilter.ALL - -@Slf4j -@CompileStatic -class StubRunnerMain { - - @Option(name = "-sr", aliases = ['--stubRepositoryRoot'], usage = "Location of a Jar containing server where you keep your stubs (e.g. http://nexus.net/content/repositories/repository)", required = true) - private String stubRepositoryRoot - - @Option(name = "-ss", aliases = ['--stubsSuffix'], usage = "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'") - private String stubsSuffix = 'stubs' - - @Option(name = "-minp", aliases = ['--minPort'], usage = "Minimal port value to be assigned to the WireMock instance. Defaults to 10000") - private Integer minPortValue = 10000 - - @Option(name = "-maxp", aliases = ['--maxPort'], usage = "Maximum port value to be assigned to the WireMock instance. Defaults to 15000") - private Integer maxPortValue = 15000 - - @Option(name = "-wo", aliases = ['--workOffline'], usage = "Switch to work offline. Defaults to 'false'") - private Boolean workOffline = Boolean.FALSE - - @Option(name = "-s", aliases = ['--stubs'], usage = 'Comma separated list of Ivy representation of jars with stubs. Eg. groupid:artifactid1,groupid2:artifactid2:classifier') - private String stubs - - private final Arguments arguments - - StubRunnerMain(String[] args) { - CmdLineParser parser = new CmdLineParser(this) - try { - parser.parseArgument(args) - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withMinMaxPort(minPortValue, maxPortValue) - .withStubRepositoryRoot(stubRepositoryRoot) - .withWorkOffline(workOffline) - .withStubsClassifier(stubsSuffix) - .withStubs(stubs) - .build() - this.arguments = new Arguments(stubRunnerOptions) - } catch (CmdLineException e) { - printErrorMessage(e, parser) - throw e - } - } - - private void printErrorMessage(CmdLineException e, CmdLineParser parser) { - System.err.println(e.getMessage()) - System.err.println("java -jar stub-runner.jar [options...] ") - parser.printUsage(System.err) - System.err.println() - System.err.println("Example: java -jar stub-runner.jar ${parser.printExample(ALL)}") - } - - static void main(String[] args) { - new StubRunnerMain(args).execute() - } - - private void execute() { - try { - log.debug("Launching StubRunner with args: $arguments") - // TODO: Pass StubsToRun either from String or File - BatchStubRunner stubRunner = new BatchStubRunnerFactory(arguments.stubRunnerOptions).buildBatchStubRunner() - RunningStubs runningCollaborators = stubRunner.runStubs() - log.info(runningCollaborators.toString()) - } catch (Exception e) { - log.error("An exception occurred while trying to execute the stubs", e) - throw e - } - } - -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.groovy deleted file mode 100644 index d93f8ea034..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.groovy +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.transform.ToString - -/** - * Technical options related to running StubRunner - * - * Use {@class StubRunnerOptionsBuilder} to build this object. - * - * @see StubRunnerOptionsBuilder - */ -@ToString(includeNames = true) -@CompileStatic -class StubRunnerOptions { - - /** - * min port value of the WireMock instance for the given collaborator - */ - final Integer minPortValue - - /** - * max port value of the WireMock instance for the given collaborator - */ - final Integer maxPortValue - - /** - * root URL from where the JAR with stub mappings will be downloaded - */ - final String stubRepositoryRoot - - /** - * avoids local repository in dependency resolution - */ - final boolean workOffline - - /** - * stub definition classifier - */ - final String stubsClassifier - - final Collection dependencies - - /** - * colon separated list of ids to the desired port - */ - final Map stubIdsToPortMapping - - @PackageScope - StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot, - boolean workOffline, String stubsClassifier, Collection dependencies, Map stubIdsToPortMapping) { - this.minPortValue = minPortValue - this.maxPortValue = maxPortValue - this.stubRepositoryRoot = stubRepositoryRoot - this.workOffline = workOffline - this.stubsClassifier = stubsClassifier - this.dependencies = dependencies - this.stubIdsToPortMapping = stubIdsToPortMapping - } - - Integer port(StubConfiguration stubConfiguration) { - if (stubIdsToPortMapping) { - return stubIdsToPortMapping[stubConfiguration] - } else { - return null - } - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.groovy deleted file mode 100644 index 4f67205b9c..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.groovy +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import org.springframework.cloud.contract.stubrunner.util.StubsParser - -@CompileStatic -class StubRunnerOptionsBuilder { - private static final String DELIMITER = ':' - private LinkedList stubs = new LinkedList<>() - private Map stubIdsToPortMapping = [:] - - private Integer minPortValue = 10000 - private Integer maxPortValue = 15000 - private String stubRepositoryRoot - private boolean workOffline = false - private String stubsClassifier = 'stubs' - - StubRunnerOptionsBuilder() { - } - - StubRunnerOptionsBuilder(StubRunnerOptions options) { - withOptions(options) - } - - StubRunnerOptionsBuilder withStubs(String stubs) { - addStub(stubsToList(stubs)) - return this - } - - StubRunnerOptionsBuilder withStubs(List stubs) { - for (String stub : stubs) { - withStubs(stub) - } - return this - } - - StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue, Integer maxPortValue) { - this.minPortValue = minPortValue - this.maxPortValue = maxPortValue - return this - } - - StubRunnerOptionsBuilder withMinPort(int minPortValue) { - this.minPortValue = minPortValue - return this - } - - StubRunnerOptionsBuilder withMaxPort(int maxPortValue) { - this.maxPortValue = maxPortValue - return this - } - - StubRunnerOptionsBuilder withStubRepositoryRoot(String stubRepositoryRoot) { - this.stubRepositoryRoot = stubRepositoryRoot - return this - } - - StubRunnerOptionsBuilder withWorkOffline(boolean workOffline) { - this.workOffline = workOffline - return this - } - - StubRunnerOptionsBuilder withStubsClassifier(String stubsClassifier) { - this.stubsClassifier = stubsClassifier - return this - } - - StubRunnerOptionsBuilder withPort(Integer port) { - String lastStub = stubs.peekLast() - println "PORT $lastStub -> $port" - addPort(lastStub + DELIMITER + port) - return this - } - - StubRunnerOptionsBuilder withOptions(StubRunnerOptions options) { - this.minPortValue = options.minPortValue - this.maxPortValue = options.maxPortValue - this.stubRepositoryRoot = options.stubRepositoryRoot - this.workOffline = options.workOffline - this.stubsClassifier = options.stubsClassifier - return this - } - - StubRunnerOptions build() { - return new StubRunnerOptions(minPortValue, maxPortValue, stubRepositoryRoot, workOffline, stubsClassifier, buildDependencies(), stubIdsToPortMapping) - } - - private Collection buildDependencies() { - return StubsParser.fromString(stubs, stubsClassifier) - } - - private static List stubsToList(String stubIdsToPortMapping) { - return stubIdsToPortMapping.split(',').collect { it } as List - } - - private void addStub(List notations) { - for (String notation : notations) { - addStub(notation) - } - } - - private void addStub(String notation) { - if (StubsParser.hasPort(notation)) { - addPort(notation) - stubs.add(StubsParser.ivyFromStringWithPort(notation)) - } else { - stubs.add(notation) - } - } - - private void addPort(String notation) { - putStubIdsToPortMapping(StubsParser.fromStringWithPort(notation)) - } - - private void putStubIdsToPortMapping(Map stubIdsToPortMapping) { - this.stubIdsToPortMapping.putAll(stubIdsToPortMapping) - } - -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubServer.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubServer.groovy deleted file mode 100644 index fe62853634..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubServer.groovy +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.util.logging.Slf4j - -import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.wiremock.WireMockSpring -import org.springframework.util.ClassUtils - -import com.github.tomakehurst.wiremock.WireMockServer -import com.github.tomakehurst.wiremock.client.WireMock -import com.github.tomakehurst.wiremock.core.WireMockConfiguration - -@CompileStatic -@Slf4j -@PackageScope -class StubServer { - private WireMockServer wireMockServer - final StubConfiguration stubConfiguration - final Collection mappings - final Collection contracts - - StubServer(int port, StubConfiguration stubConfiguration, Collection mappings, - Collection contracts) { - this.stubConfiguration = stubConfiguration - this.mappings = mappings - this.wireMockServer = new WireMockServer(config().port(port)) - this.contracts = contracts - } - - private WireMockConfiguration config() { - if (ClassUtils.isPresent('org.springframework.cloud.contract.wiremock.WireMockSpring', null)) { - return WireMockSpring.options() - } - return new WireMockConfiguration() - } - - StubServer start() { - wireMockServer.start() - log.info("Started stub server for project ${stubConfiguration.toColonSeparatedDependencyNotation()} on port ${wireMockServer.port()}") - registerStubMappings() - return this - } - - void stop() { - wireMockServer.stop() - } - - int getPort() { - if (wireMockServer.isRunning()) { - return wireMockServer.port() - } - log.debug("The HTTP Server stub is not running... That means that the " + - "artifact is running a messaging module. Returning back -1 value of the port.") - return -1 - } - - URL getStubUrl() { - return new URL("http://localhost:$port") - } - - private void registerStubMappings() { - WireMock wireMock = new WireMock('localhost', wireMockServer.port()) - registerDefaultHealthChecks(wireMock) - registerStubs(mappings, wireMock) - } - - private void registerDefaultHealthChecks(WireMock wireMock) { - registerHealthCheck(wireMock, '/ping') - registerHealthCheck(wireMock, '/health') - } - - private void registerStubs(Collection sortedMappings, WireMock wireMock) { - sortedMappings.each { WiremockMappingDescriptor mappingDescriptor -> - try { - wireMock.register(mappingDescriptor.mapping) - log.debug("Registered stub mappings from $mappingDescriptor.descriptor") - } catch (Exception e) { - log.warn("Failed to register the stub mapping [$mappingDescriptor]", e) - } - } - } - - private void registerHealthCheck(WireMock wireMock, String url, String body = 'OK') { - wireMock.register(WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200))) - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.groovy deleted file mode 100644 index e3bdba3f9b..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner - -import com.github.tomakehurst.wiremock.stubbing.StubMapping -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.PackageScope -import groovy.transform.ToString - -/** - * Represents a single JSON file that was found in the folder with - * potential WireMock stubs - */ -@CompileStatic -@EqualsAndHashCode -@ToString(includePackage = false) -@PackageScope -class WiremockMappingDescriptor { - final File descriptor - - WiremockMappingDescriptor(File mappingDescriptor) { - this.descriptor = mappingDescriptor - } - - StubMapping getMapping() { - return StubMapping.buildFrom(descriptor.getText('UTF-8')) - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StubsParser.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StubsParser.groovy deleted file mode 100644 index bb45d160fe..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StubsParser.groovy +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.util - -import groovy.transform.CompileStatic -import org.springframework.cloud.contract.stubrunner.StubConfiguration - -/** - * Utility to parse string into a list of configuration of stubs - */ -@CompileStatic -class StubsParser { - - /** - * The string is expected to be a map with entry called "stubs" - * that contains a list of Strings in the format - * - *
    - *
  • groupid:artifactid:classifier
  • - *
  • groupid:artifactid
  • - *
- * - * In the latter case the provided default stub classifier will be passed. - * - * Example: - * - * "a:b,c:d:e" - */ - static Set fromString(String list, String defaultClassifier) { - def splitList = list.split(',').findAll { it } - return fromString(splitList, defaultClassifier) - } - - static Set fromString(Collection collection, String defaultClassifier) { - return collection.findAll { it }.collect { String entry -> - def splitEntry = entry.split(':') - if (splitEntry.last().isInteger()) { - String id = entry - ":${splitEntry.last()}" - new StubConfiguration(id, defaultClassifier) - } - new StubConfiguration(entry, defaultClassifier) - } as Set - } - - static Map fromStringWithPort(String notation) { - def splitEntry = notation.split(':') - if (!splitEntry.last().isInteger()) { - return [:] - } - Integer port = splitEntry.last().toInteger() - String id = notation - ":${splitEntry.last()}" - return [(new StubConfiguration(id)): port] - } - - static String ivyFromStringWithPort(String notation) { - def splitEntry = notation.split(':') - if (!splitEntry.last().isInteger()) { - return '' - } - return notation - ":${splitEntry.last()}" - } - - static boolean hasPort(String notation) { - def splitEntry = notation.split(':') - return splitEntry.last().isInteger() - } -} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/ZipCategory.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/ZipCategory.groovy deleted file mode 100644 index 105298b558..0000000000 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/ZipCategory.groovy +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.stubrunner.util - -import groovy.transform.CompileStatic - -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream - -/** - * Based on https://github.com/timyates/groovy-common-extensions. - * - * Category for {@link File} that adds a method that allows you to unzip - * a given file to a specified location - * - */ -@CompileStatic -class ZipCategory { - - /** - * Unzips this file. If the destination - * 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. - * @return a {@link Collection} of unzipped {@link File} objects. - */ - static Collection unzipTo(File self, File destination) { - checkUnzipDestination(destination) - // if destination directory is not given, we'll fall back to the parent directory of 'self' - if (destination == null) destination = new File(self.parent) - List unzippedFiles = [] - final ZipInputStream zipInput = new ZipInputStream(new FileInputStream(self)) - zipInput.withStream { - ZipEntry entry - while (entry = zipInput.nextEntry) { - if (!entry.isDirectory()) { - final File file = new File(destination, entry.name) - file.parentFile?.mkdirs() - FileOutputStream output = new FileOutputStream(file) - output.withStream { - output << zipInput - } - unzippedFiles << file - } else { - final File dir = new File(destination, entry.name) - dir.mkdirs() - unzippedFiles << dir - } - } - } - return unzippedFiles - } - - private static void checkUnzipDestination(File file) { - if (file && !file.isDirectory()) throw new IllegalArgumentException("'destination' has to be a directory.") - } -} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java new file mode 100644 index 0000000000..2e55c1a00d --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; +import org.eclipse.aether.impl.DefaultServiceLocator; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryPolicy; +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.transport.file.FileTransporterFactory; +import org.eclipse.aether.transport.http.HttpTransporterFactory; +import org.springframework.util.StringUtils; + +public class AetherFactories { + + private static final String MAVEN_LOCAL_REPOSITORY_LOCATION = "maven.repo.local"; + + public static RepositorySystem newRepositorySystem() { + DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); + 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) { + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + session.setOffline(workOffline); + if (!workOffline) { + session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS); + } + LocalRepository localRepo = new LocalRepository(localRepositoryDirectory()); + session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); + return session; + } + + public static List newRepositories(List repositories) { + List result = new ArrayList<>(); + for (int index = 0; index < repositories.size(); index++) { + String repo = repositories.get(index); + if (StringUtils.hasText(repo)) { + result.add(new RemoteRepository.Builder("remote" + index, "default", repo).build()); + } + } + return result; + } + + public static String localRepositoryDirectory() { + return System.getProperty(MAVEN_LOCAL_REPOSITORY_LOCATION, System.getProperty("user.home") + "/.m2/repository"); + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java new file mode 100644 index 0000000000..338fc2e7c3 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java @@ -0,0 +1,210 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import static java.nio.file.Files.createTempDirectory; +import static org.springframework.cloud.contract.stubrunner.AetherFactories.newRepositories; +import static org.springframework.cloud.contract.stubrunner.AetherFactories.newRepositorySystem; +import static org.springframework.cloud.contract.stubrunner.AetherFactories.newSession; +import static org.springframework.cloud.contract.stubrunner.util.ZipCategory.unzipTo; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.VersionRangeRequest; +import org.eclipse.aether.resolution.VersionRangeResolutionException; +import org.eclipse.aether.resolution.VersionRangeResult; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResolutionException; +import org.eclipse.aether.resolution.VersionResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +/** + * @author Mariusz Smykula + */ +public class AetherStubDownloader implements StubDownloader { + + private static final Logger log = LoggerFactory.getLogger(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 = "+"; + + private final List remoteRepos; + private final RepositorySystem repositorySystem; + private final RepositorySystemSession session; + + public AetherStubDownloader(StubRunnerOptions stubRunnerOptions) { + this.remoteRepos = remoteRepositories(stubRunnerOptions); + if (remoteRepos == null || remoteRepos.isEmpty()) { + log.error("Remote repositories for stubs are not specified!"); + } + this.repositorySystem = newRepositorySystem(); + this.session = newSession(this.repositorySystem, stubRunnerOptions.workOffline); + } + + /** + * Used by the Maven Plugin + * + * @param repositorySystem + * @param remoteRepositories - remote artifact repositories + * @param session + * @param workOffline + */ + public AetherStubDownloader(RepositorySystem repositorySystem, + List remoteRepositories, RepositorySystemSession session) { + this.remoteRepos = remoteRepositories; + this.repositorySystem = repositorySystem; + this.session = session; + if (remoteRepos == null || remoteRepos.isEmpty()) { + log.error("Remote remoteRepositories for stubs are not specified!"); + } + } + + private List remoteRepositories( + StubRunnerOptions stubRunnerOptions) { + return newRepositories( + Arrays.asList(stubRunnerOptions.stubRepositoryRoot.split(","))); + } + + private File unpackedJar(String resolvedVersion, String stubsGroup, + String stubsModule, String classifier) { + log.info("Resolved version is" + resolvedVersion); + if (!StringUtils.hasText(resolvedVersion)) { + log.warn("Stub for group [" + stubsGroup + "] module [" + stubsModule + + "] and classifier [" + classifier + "] not found in " + + remoteRepos); + return null; + } + Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, + ARTIFACT_EXTENSION, resolvedVersion); + ArtifactRequest request = new ArtifactRequest(artifact, remoteRepos, null); + log.info("Resolving artifact " + artifact + + " using remote repositories " + remoteRepos); + try { + ArtifactResult result = repositorySystem.resolveArtifact(session, request); + log.info("Resolved artifact " + artifact + "to " + + result.getArtifact().getFile()); + File temporaryFile = unpackStubJarToATemporaryFolder( + result.getArtifact().getFile().toURI()); + log.info("Unpacked file to [" + temporaryFile + "]"); + return temporaryFile; + } + catch (Exception e) { + log.warn( + "Exception occured while trying to download a stub for group [" + + stubsGroup + "] module [" + stubsModule + + "] and classifier [" + classifier + "] in " + remoteRepos, + e); + return null; + } + + } + + private String getVersion(String stubsGroup, String stubsModule, String version, + String classifier) { + if (!StringUtils.hasText(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); + } + log.info("Will try to resolve version " + version); + return resolveArtifactVersion(stubsGroup, stubsModule, version, classifier); + } + + @Override + public Map.Entry downloadAndUnpackStubJar( + StubRunnerOptions options, StubConfiguration stubConfiguration) { + String version = getVersion(stubConfiguration.groupId, + stubConfiguration.artifactId, stubConfiguration.version, + stubConfiguration.classifier); + File unpackedJar = unpackedJar(version, stubConfiguration.groupId, + stubConfiguration.artifactId, stubConfiguration.classifier); + if (unpackedJar == null) { + return null; + } + return new AbstractMap.SimpleEntry(new StubConfiguration( + stubConfiguration.groupId, stubConfiguration.artifactId, version, + stubConfiguration.classifier), unpackedJar); + } + + private String resolveHighestArtifactVersion(String stubsGroup, String stubsModule, + String classifier) { + Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, + ARTIFACT_EXTENSION, LATEST_ARTIFACT_VERSION); + VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, + remoteRepos, null); + VersionRangeResult rangeResult; + try { + rangeResult = repositorySystem.resolveVersionRange(session, + versionRangeRequest); + } + catch (VersionRangeResolutionException e) { + throw new IllegalStateException("Cannot resolve version range", e); + } + if (rangeResult.getHighestVersion() == null) { + log.error("Version was not resolved!"); + } + return rangeResult.getHighestVersion() == null ? null : rangeResult.getHighestVersion().toString(); + } + + private String resolveArtifactVersion(String stubsGroup, String stubsModule, + String version, String classifier) { + Artifact artifact = new DefaultArtifact(stubsGroup, stubsModule, classifier, + ARTIFACT_EXTENSION, version); + VersionRequest versionRequest = new VersionRequest(artifact, remoteRepos, null); + VersionResult versionResult; + try { + versionResult = repositorySystem.resolveVersion(session, versionRequest); + } + catch (VersionResolutionException e) { + throw new IllegalStateException("Cannot resolve version", e); + } + return versionResult.getVersion() == null ? null : versionResult.getVersion(); + } + + private static File unpackStubJarToATemporaryFolder(URI stubJarUri) { + File tmpDirWhereStubsWillBeUnzipped; + try { + tmpDirWhereStubsWillBeUnzipped = createTempDirectory(TEMP_DIR_PREFIX) + .toFile(); + } + catch (IOException e) { + throw new IllegalStateException("Cannot create tmp dir with prefix: [" + TEMP_DIR_PREFIX + "]", e); + } + tmpDirWhereStubsWillBeUnzipped.deleteOnExit(); + log.info("Unpacking stub from JAR [URI: " + stubJarUri + "]"); + unzipTo(new File(stubJarUri), tmpDirWhereStubsWillBeUnzipped); + return tmpDirWhereStubsWillBeUnzipped; + } + +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java new file mode 100644 index 0000000000..676f38b41c --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +/** + * Arguments passed to the {@link StubRunner} application + * + * @see StubRunner + */ +public class Arguments { + final private StubRunnerOptions stubRunnerOptions; + final private String context; + final private String repositoryPath; + final private StubConfiguration stub; + + Arguments(StubRunnerOptions stubRunnerOptions) { + this(stubRunnerOptions, "", null); + } + + Arguments(StubRunnerOptions stubRunnerOptions, String repositoryPath, + StubConfiguration stub) { + this.stubRunnerOptions = stubRunnerOptions; + this.repositoryPath = repositoryPath == null ? "" : repositoryPath; + this.context = null; // eh? + this.stub = stub; + } + + public StubRunnerOptions getStubRunnerOptions() { + return stubRunnerOptions; + } + + public String getContext() { + return context; + } + + public String getRepositoryPath() { + return repositoryPath; + } + + public StubConfiguration getStub() { + return stub; + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.java new file mode 100644 index 0000000000..9facd4f7e3 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AvailablePortScanner.java @@ -0,0 +1,108 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Random; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tries to execute a closure with an available port from the given range + */ +public class AvailablePortScanner { + + private static final Logger log = LoggerFactory.getLogger(AvailablePortScanner.class); + + 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) { + this(minPortNumber, maxPortNumber, MAX_RETRY_COUNT); + } + + AvailablePortScanner(int minPortNumber, int maxPortNumber, int maxRetryCount) { + checkPortRanges(minPortNumber, maxPortNumber); + this.minPortNumber = minPortNumber; + this.maxPortNumber = maxPortNumber; + this.maxRetryCount = maxRetryCount; + } + + private void checkPortRanges(int minPortNumber, int maxPortNumber) { + if (minPortNumber > maxPortNumber) { + throw new InvalidPortRange(minPortNumber, maxPortNumber); + } + } + + public T tryToExecuteWithFreePort(PortCallback closure) { + for (int i = 0; i < maxRetryCount; i++) { + try { + int numberOfPortsToBind = maxPortNumber - minPortNumber + 1; + int portToScan = new Random().nextInt(numberOfPortsToBind) + + minPortNumber; + checkIfPortIsAvailable(portToScan); + return executeLogicForAvailablePort(portToScan, closure); + } + catch (IOException exception) { + log.debug("Failed to execute callback (try: " + i + "/" + maxRetryCount + + ")", exception); + } + } + throw new NoPortAvailableException(minPortNumber, maxPortNumber); + } + + private T executeLogicForAvailablePort(int portToScan, PortCallback closure) throws IOException { + log.debug("Trying to execute closure with port [$portToScan]"); + return closure.call(portToScan); + } + + private void checkIfPortIsAvailable(int portToScan) throws IOException { + ServerSocket socket = null; + try { + socket = new ServerSocket(portToScan); + } + finally { + if (socket != null) { + socket.close(); + } + } + } + + @SuppressWarnings("serial") + static class NoPortAvailableException extends RuntimeException { + protected NoPortAvailableException(int lowerBound, int upperBound) { + super("Could not find available port in range " + lowerBound + ":" + upperBound); + } + } + + @SuppressWarnings("serial") + static class InvalidPortRange extends RuntimeException { + protected InvalidPortRange(int lowerBound, int upperBound) { + super("Invalid bounds exceptions, min port [" + lowerBound + + "] is greater to max port [" + upperBound + "]"); + } + } + + public static interface PortCallback { + T call(int port) throws IOException; + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunner.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunner.java new file mode 100644 index 0000000000..f34b262a1d --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunner.java @@ -0,0 +1,181 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.cloud.contract.spec.Contract; + +/** + * Manages lifecycle of multiple {@link StubRunner} instances. + * + * @see StubRunner + */ +public class BatchStubRunner implements StubRunning { + + private final Iterable stubRunners; + + public BatchStubRunner(Iterable stubRunners) { + this.stubRunners = stubRunners; + } + + @Override + public RunningStubs runStubs() { + Map map = new LinkedHashMap<>(); + for (StubRunner value : stubRunners) { + RunningStubs runningStubs = value.runStubs(); + map.putAll(runningStubs.validNamesAndPorts()); + } + return new RunningStubs(map); + } + + @Override + public URL findStubUrl(String groupId, String artifactId) { + for (StubRunner stubRunner : stubRunners) { + URL url = stubRunner.findStubUrl(groupId, artifactId); + if (url != null) { + return url; + } + } + return null; + } + + @Override + public URL findStubUrl(String ivyNotation) { + String[] splitString = ivyNotation.split(":"); + if (splitString.length > 3) { + throw new IllegalArgumentException(ivyNotation + " is invalid"); + } + else if (splitString.length == 2) { + return findStubUrl(splitString[0], splitString[1]); + } + return findStubUrl(null, splitString[0]); + } + + @Override + public RunningStubs findAllRunningStubs() { + Collection running = new LinkedHashSet<>(); + for (StubRunner stubRunner : stubRunners) { + running.add(stubRunner.findAllRunningStubs()); + } + return new RunningStubs(running); + } + + @Override + public Map> getContracts() { + Map> map = new LinkedHashMap<>(); + for (StubRunner stubRunner : stubRunners) { + for (Entry> entry : stubRunner + .getContracts().entrySet()) { + if (map.containsKey(entry.getKey())) { + map.get(entry.getKey()).addAll(entry.getValue()); + } + else { + map.put(entry.getKey(), new LinkedHashSet<>(entry.getValue())); + } + } + } + return map; + } + + @Override + public boolean trigger(String ivyNotation, String labelName) { + boolean success = false; + for (StubRunner stubRunner : stubRunners) { + if (stubRunner.trigger(ivyNotation, labelName)) { + success = true; + } + } + if (!success) { + throw new IllegalArgumentException("No label with name [" + labelName + + "] for " + "dependency [" + ivyNotation + + "] was found. Here you have the list of dependencies " + + "and their labels [" + ivyToLabels() + "]"); + } + return success; + } + + private String ivyToLabels() { + StringBuilder builder = new StringBuilder(); + for (Entry> entry : labels().entrySet()) { + if (builder.length() > 0) { + builder.append("\n"); + } + builder.append("Dependency [").append(entry.getKey()).append("] has labels ") + .append(entry.getValue()); + } + return builder.toString(); + } + + @Override + public boolean trigger(String labelName) { + boolean success = false; + for (StubRunner stubRunner : stubRunners) { + if (stubRunner.trigger(labelName)) { + success = true; + } + } + if (!success) { + throw new IllegalArgumentException( + "No label with name [" + labelName + "] was found. " + + "Here you have the list of dependencies and their labels [" + + ivyToLabels() + "]"); + } + return success; + } + + @Override + public boolean trigger() { + boolean success = false; + for (StubRunner stubRunner : stubRunners) { + if (stubRunner.trigger()) { + success = true; + } + } + return success; + } + + @Override + public Map> labels() { + Map> map = new LinkedHashMap<>(); + for (StubRunner stubRunner : stubRunners) { + for (Entry> entry : stubRunner.labels() + .entrySet()) { + if (map.containsKey(entry.getKey())) { + map.get(entry.getKey()).addAll(entry.getValue()); + } + else { + map.put(entry.getKey(), new LinkedHashSet<>(entry.getValue())); + } + } + } + return map; + } + + @Override + public void close() throws IOException { + for (StubRunner stubRunner : stubRunners) { + stubRunner.close(); + } + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.java similarity index 54% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.java index 6261afe289..a984157ceb 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/BatchStubRunnerFactory.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -import groovy.transform.CompileStatic -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging -import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; +import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging; /** * Manages lifecycle of multiple {@link StubRunner} instances. @@ -26,34 +25,33 @@ import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVe * @see StubRunner * @see BatchStubRunner */ -@CompileStatic -class BatchStubRunnerFactory { +public class BatchStubRunnerFactory { - private final StubRunnerOptions stubRunnerOptions - private final StubDownloader stubDownloader - private final ContractVerifierMessaging contractVerifierMessaging + private final StubRunnerOptions stubRunnerOptions; + private final StubDownloader stubDownloader; + private final ContractVerifierMessaging contractVerifierMessaging; - BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions) { - this(stubRunnerOptions, new AetherStubDownloader(stubRunnerOptions), new NoOpContractVerifierMessaging()) + public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions) { + this(stubRunnerOptions, new AetherStubDownloader(stubRunnerOptions), new NoOpContractVerifierMessaging()); } - BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, ContractVerifierMessaging contractVerifierMessaging) { - this(stubRunnerOptions, new AetherStubDownloader(stubRunnerOptions), contractVerifierMessaging) + public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, ContractVerifierMessaging contractVerifierMessaging) { + this(stubRunnerOptions, new AetherStubDownloader(stubRunnerOptions), contractVerifierMessaging); } - BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader) { - this(stubRunnerOptions, stubDownloader, new NoOpContractVerifierMessaging()) + public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader) { + this(stubRunnerOptions, stubDownloader, new NoOpContractVerifierMessaging()); } - BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader, ContractVerifierMessaging contractVerifierMessaging) { - this.stubRunnerOptions = stubRunnerOptions - this.stubDownloader = stubDownloader - this.contractVerifierMessaging = contractVerifierMessaging + public BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, StubDownloader stubDownloader, ContractVerifierMessaging contractVerifierMessaging) { + this.stubRunnerOptions = stubRunnerOptions; + this.stubDownloader = stubDownloader; + this.contractVerifierMessaging = contractVerifierMessaging; } - BatchStubRunner buildBatchStubRunner() { - StubRunnerFactory stubRunnerFactory = new StubRunnerFactory(stubRunnerOptions, stubDownloader, contractVerifierMessaging) - return new BatchStubRunner(stubRunnerFactory.createStubsFromServiceConfiguration()) + public BatchStubRunner buildBatchStubRunner() { + StubRunnerFactory stubRunnerFactory = new StubRunnerFactory(stubRunnerOptions, stubDownloader, contractVerifierMessaging); + return new BatchStubRunner(stubRunnerFactory.createStubsFromServiceConfiguration()); } } diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.java similarity index 71% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.java index 2f2ca4f0b0..c0df21b717 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/GroovyDslWrapper.java @@ -14,26 +14,22 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.spec.Contract; /** * @author Marcin Grzejszczak */ -@PackageScope -@CompileStatic class GroovyDslWrapper { - @Delegate final Contract groovyDsl + final Contract groovyDsl; GroovyDslWrapper(Contract groovyDsl) { - this.groovyDsl = groovyDsl + this.groovyDsl = groovyDsl; } boolean hasHttpPart() { - return groovyDsl.request + return groovyDsl.getRequest() != null; } } diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.java similarity index 86% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.java index d99d288ee0..3e739219a9 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/MessageNotMatchingException.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner - -import groovy.transform.InheritConstructors +package org.springframework.cloud.contract.stubrunner; /** * Exception thrown when message is not matched * * @author Marcin Grzejszczak */ -@InheritConstructors +@SuppressWarnings("serial") class MessageNotMatchingException extends RuntimeException { } diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/RunningStubs.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/RunningStubs.java new file mode 100644 index 0000000000..b84c8d7ca2 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/RunningStubs.java @@ -0,0 +1,136 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Structure representing executed stubs. Contains the configuration of each stub together + * with the port on which its executed. + */ +public class RunningStubs { + + final private Map namesAndPorts = new LinkedHashMap<>(); + + public RunningStubs(Map map) { + this.namesAndPorts.putAll(map); + } + + public RunningStubs(Collection runningStubs) { + for (RunningStubs it : runningStubs) { + this.namesAndPorts.putAll(it.namesAndPorts); + } + } + + public Integer getPort(String artifactId) { + return getEntry(artifactId) == null ? null : getEntry(artifactId).getValue(); + } + + public Map.Entry getEntry(String artifactId) { + for (Entry it : namesAndPorts.entrySet()) { + if (it.getKey().matchesIvyNotation(artifactId)) { + return it; + } + } + return null; + } + + public Integer getPort(String groupId, String artifactId) { + for (Entry it : namesAndPorts.entrySet()) { + if (it.getKey().matchesIvyNotation(groupId + ":" + artifactId)) { + return it.getValue(); + } + } + return null; + } + + public boolean isPresent(String artifactId) { + return getEntry(artifactId) != null; + } + + public boolean isPresent(String groupId, String artifactId) { + return getPort(groupId, artifactId) != null; + } + + public Set getAllServices() { + return namesAndPorts.keySet(); + } + + public Set getAllServicesNames() { + Set result = new LinkedHashSet<>(); + for (Entry it : namesAndPorts.entrySet()) { + result.add(it.getKey().artifactId); + } + return result; + } + + public Map toIvyToPortMapping() { + Map result = new LinkedHashMap<>(); + for (Entry it : namesAndPorts.entrySet()) { + result.put(it.getKey().toColonSeparatedDependencyNotation(), it.getValue()); + } + return result; + } + + public Map validNamesAndPorts() { + Map result = new LinkedHashMap<>(); + for (Entry it : namesAndPorts.entrySet()) { + if (it.getValue() != null && it.getValue() >= 0) { + result.put(it.getKey(), it.getValue()); + } + } + return result; + } + + @Override + public String toString() { + return "RunningStubs [namesAndPorts=" + namesAndPorts + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((namesAndPorts == null) ? 0 : namesAndPorts.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RunningStubs other = (RunningStubs) obj; + if (namesAndPorts == null) { + if (other.namesAndPorts != null) + return false; + } + else if (!namesAndPorts.equals(other.namesAndPorts)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java new file mode 100644 index 0000000000..098ee6b49c --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.util.StringUtils; + +/** + * Represents a configuration of a single stub. The stub can be described by + * groupId:artifactId:classifier notation + */ +public class StubConfiguration { + private static final String STUB_COLON_DELIMITER = ":"; + private 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) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = DEFAULT_CLASSIFIER; + } + + public StubConfiguration(String groupId, String artifactId, String version, + String classifier) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = classifier; + } + + public StubConfiguration(String stubPath, String defaultClassifier) { + String[] parsedPath = parsedPathEmptyByDefault(stubPath, STUB_COLON_DELIMITER, + defaultClassifier); + this.groupId = parsedPath[0]; + this.artifactId = parsedPath[1]; + this.version = parsedPath[2]; + this.classifier = parsedPath[3]; + } + + public StubConfiguration(String stubPath) { + String[] parsedPath = parsedPathEmptyByDefault(stubPath, STUB_COLON_DELIMITER, + DEFAULT_CLASSIFIER); + this.groupId = parsedPath[0]; + this.artifactId = parsedPath[1]; + this.version = parsedPath[2]; + this.classifier = parsedPath[3]; + } + + private String[] parsedPathEmptyByDefault(String path, String delimiter, + String defaultClassifier) { + String[] splitPath = path.split(delimiter); + String stubsGroupId = ""; + String stubsArtifactId = ""; + String stubsVersion = ""; + String stubsClassifier = ""; + if (splitPath.length >= 2) { + stubsGroupId = splitPath[0]; + stubsArtifactId = splitPath[1]; + stubsVersion = splitPath.length >= 3 ? splitPath[2] : DEFAULT_VERSION; + stubsClassifier = splitPath.length == 4 ? splitPath[3] : defaultClassifier; + } + return new String[] { stubsGroupId, stubsArtifactId, stubsVersion, + stubsClassifier }; + } + + private boolean isDefined() { + return StringUtils.hasText(groupId) && StringUtils.hasText(this.artifactId); + } + + public String toColonSeparatedDependencyNotation() { + if (!isDefined()) { + return ""; + } + return StringUtils.arrayToDelimitedString( + new String[] { groupId, artifactId, version, classifier }, + STUB_COLON_DELIMITER); + } + + public boolean groupIdAndArtifactMatches(String ivyNotationAsString) { + String[] parts = ivyNotationFrom(ivyNotationAsString); + String groupId = parts[0]; + String artifactId = parts[1]; + if (groupId == null) { + return this.artifactId.equals(artifactId); + } + return this.groupId.equals(groupId) && this.artifactId.equals(artifactId); + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getClassifier() { + return classifier; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode()); + result = prime * result + ((groupId == null) ? 0 : groupId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StubConfiguration other = (StubConfiguration) obj; + if (artifactId == null) { + if (other.artifactId != null) + return false; + } + else if (!artifactId.equals(other.artifactId)) + return false; + if (groupId == null) { + if (other.groupId != null) + return false; + } + else if (!groupId.equals(other.groupId)) + return false; + return true; + } + + public boolean matchesIvyNotation(String ivyNotationAsString) { + String[] strings = ivyNotationAsString.split(":"); + if (strings.length == 1) { + return artifactId.equals(ivyNotationAsString); + } + else if (strings.length == 2) { + return groupId.equals(strings[0]) && artifactId.equals(strings[1]); + } + else if (strings.length == 3) { + return groupId.equals(strings[0]) && artifactId.equals(strings[1]) + && (strings[2].equals(DEFAULT_VERSION) || version.equals(strings[2])); + } + return groupId.equals(strings[0]) && artifactId.equals(strings[1]) + && (strings[2].equals(DEFAULT_VERSION) || version.equals(strings[2])) + && classifier.equals(strings[3]); + } + + private String[] ivyNotationFrom(String ivyNotation) { + String[] splitString = ivyNotation.split(":"); + if (splitString.length == 1) { + // assuming that ivy notation represents artifactId only + return new String[] { null, splitString[0] }; + } + return new String[] { splitString[0], splitString[1] }; + } + + @Override + public String toString() { + return toColonSeparatedDependencyNotation(); + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubData.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubData.java new file mode 100644 index 0000000000..8f796ba444 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubData.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.List; + +import org.springframework.cloud.contract.spec.Contract; + +/** + * @author Marcin Grzejszczak + */ +public class StubData { + final Integer port; + final List contracts; + + public StubData(Integer port, List contracts) { + this.port = port; + this.contracts = contracts; + } + public Integer getPort() { + return port; + } + public List getContracts() { + return contracts; + } + @Override + public String toString() { + return "StubData [port=" + port + ", contracts=" + contracts + "]"; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((contracts == null) ? 0 : contracts.hashCode()); + result = prime * result + ((port == null) ? 0 : port.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StubData other = (StubData) obj; + if (contracts == null) { + if (other.contracts != null) + return false; + } + else if (!contracts.equals(other.contracts)) + return false; + if (port == null) { + if (other.port != null) + return false; + } + else if (!port.equals(other.port)) + return false; + return true; + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubDownloader.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java similarity index 82% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubDownloader.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java index fba3a207a1..87c22347e5 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubDownloader.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java @@ -14,15 +14,18 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -interface StubDownloader { +import java.io.File; +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}. */ - Map.Entry downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration) + Map.Entry downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration); } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubFinder.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java similarity index 74% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubFinder.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java index d2ba01caa9..1fb77f8403 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubFinder.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -import org.springframework.cloud.contract.spec.Contract +import java.net.URL; +import java.util.Collection; +import java.util.Map; -interface StubFinder extends StubTrigger { +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. @@ -26,7 +30,7 @@ interface StubFinder extends StubTrigger { * @param groupId - might be null. In that case a search only via artifactId takes place * @return URL of a running stub or null if not found */ - URL findStubUrl(String groupId, String artifactId) + URL findStubUrl(String groupId, String artifactId); /** * For the given Ivy notation {@code groupId:artifactId} tries to find the matching @@ -35,15 +39,15 @@ interface StubFinder extends StubTrigger { * @param ivyNotation - Ivy representation of the Maven artifact * @return URL of a running stub or null if not found */ - URL findStubUrl(String ivyNotation) + URL findStubUrl(String ivyNotation); /** * Returns all running stubs */ - RunningStubs findAllRunningStubs() + RunningStubs findAllRunningStubs(); /** * Returns the list of Contracts */ - Map> getContracts() + Map> getContracts(); } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java new file mode 100644 index 0000000000..de1a0ed28f --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java @@ -0,0 +1,155 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter; + +/** + * Wraps the folder with WireMock mappings. + */ +public class StubRepository { + + private static final Logger log = LoggerFactory.getLogger(StubRepository.class); + + private final File path; + final List projectDescriptors; + final Collection contracts; + + public StubRepository(File repository) { + if (!repository.isDirectory()) { + throw new IllegalArgumentException( + "Missing descriptor repository under path [" + repository + "]"); + } + this.path = repository; + this.projectDescriptors = projectDescriptors(); + this.contracts = contracts(); + } + + public File getPath() { + return path; + } + + public List getProjectDescriptors() { + return projectDescriptors; + } + + public Collection getContracts() { + return contracts; + } + + /** + * Returns a list of {@link Contract} + */ + private Collection contracts() { + List contracts = new ArrayList<>(); + contracts.addAll(contractDescriptors()); + return contracts; + } + + /** + * Returns the list of WireMock JSON files wrapped in + * {@link WiremockMappingDescriptor} + */ + private List projectDescriptors() { + List mappingDescriptors = new ArrayList<>(); + mappingDescriptors.addAll(contextDescriptors()); + return mappingDescriptors; + } + + private List contextDescriptors() { + return path.exists() ? collectMappingDescriptors(path) + : Collections.emptyList(); + } + + private List collectMappingDescriptors( + File descriptorsDirectory) { + final List mappingDescriptors = new ArrayList<>(); + try { + Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()), + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, + BasicFileAttributes attrs) throws IOException { + File file = path.toFile(); + if (isMappingDescriptor(file)) { + mappingDescriptors + .add(new WiremockMappingDescriptor(file)); + } + return super.visitFile(path, attrs); + } + }); + } + catch (IOException e) { + log.warn("Exception occurred while trying to parse file", e); + } + return mappingDescriptors; + } + + private Collection contractDescriptors() { + return (path.exists() ? collectContractDescriptors(path) + : Collections.emptySet()); + } + + private Collection collectContractDescriptors(File descriptorsDirectory) { + final List mappingDescriptors = new ArrayList<>(); + try { + Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()), + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, + BasicFileAttributes attrs) throws IOException { + File file = path.toFile(); + if (isContractDescriptor(file)) { + mappingDescriptors + .add(ContractVerifierDslConverter.convert(file)); + } + return super.visitFile(path, attrs); + } + }); + } + catch (IOException e) { + log.warn("Exception occurred while trying to parse file", e); + } + return mappingDescriptors; + } + + private static boolean isMappingDescriptor(File file) { + return file.isFile() && file.getName().endsWith(".json"); + } + + private static boolean isContractDescriptor(File file) { + // TODO: Consider script injections implications... + return file.isFile() && file.getName().endsWith(".groovy"); + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java new file mode 100644 index 0000000000..bfae9200a9 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java @@ -0,0 +1,136 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.Map; + +import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; +import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging; + +/** + * Represents a single instance of ready-to-run stubs. Can run the stubs and then will + * return the name of the collaborator together with its URI. Can also be queried if the + * current groupid and artifactid are matching the corresponding running stub. + */ +public class StubRunner implements StubRunning { + + private final StubRepository stubRepository; + private final StubConfiguration stubsConfiguration; + private final StubRunnerOptions stubRunnerOptions; + private final StubRunnerExecutor localStubRunner; + + @Deprecated + StubRunner(Arguments arguments) { + this(arguments.getStubRunnerOptions(), arguments.getRepositoryPath(), + arguments.getStub()); + } + + public StubRunner(StubRunnerOptions stubRunnerOptions, String repositoryPath, + StubConfiguration stubsConfiguration) { + this(stubRunnerOptions, repositoryPath, stubsConfiguration, + new NoOpContractVerifierMessaging()); + } + + public StubRunner(StubRunnerOptions stubRunnerOptions, String repositoryPath, + StubConfiguration stubsConfiguration, + ContractVerifierMessaging contractVerifierMessaging) { + this.stubsConfiguration = stubsConfiguration; + this.stubRunnerOptions = stubRunnerOptions; + this.stubRepository = new StubRepository(new File(repositoryPath)); + AvailablePortScanner portScanner = new AvailablePortScanner( + stubRunnerOptions.getMinPortValue(), stubRunnerOptions.getMaxPortValue()); + this.localStubRunner = new StubRunnerExecutor(portScanner, (ContractVerifierMessaging) contractVerifierMessaging); + } + + @Override + public RunningStubs runStubs() { + registerShutdownHook(); + return localStubRunner.runStubs(stubRunnerOptions, stubRepository, + stubsConfiguration); + } + + @Override + public URL findStubUrl(String groupId, String artifactId) { + return localStubRunner.findStubUrl(groupId, artifactId); + } + + @Override + public URL findStubUrl(String ivyNotation) { + String[] splitString = ivyNotation.split(":"); + if (splitString.length == 1) { + // assuming that ivy notation represents artifactId only + return findStubUrl(null, splitString[0]); + } + return findStubUrl(splitString[0], splitString[1]); + } + + @Override + public RunningStubs findAllRunningStubs() { + return localStubRunner.findAllRunningStubs(); + } + + @Override + public Map> getContracts() { + return localStubRunner.getContracts(); + } + + @Override + public boolean trigger(String ivyNotation, String labelName) { + return localStubRunner.trigger(ivyNotation, labelName); + } + + @Override + public boolean trigger(String labelName) { + return localStubRunner.trigger(labelName); + } + + @Override + public boolean trigger() { + return localStubRunner.trigger(); + } + + @Override + public Map> labels() { + return localStubRunner.labels(); + } + + private void registerShutdownHook() { + Runnable stopAllServers = new Runnable() { + @Override + public void run() { + try { + close(); + } + catch (IOException e) { + } + } + }; + Runtime.getRuntime().addShutdownHook(new Thread(stopAllServers)); + } + + @Override + public void close() throws IOException { + if (localStubRunner != null) { + localStubRunner.shutdown(); + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java new file mode 100644 index 0000000000..f82fbaaefa --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java @@ -0,0 +1,234 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.spec.internal.DslProperty; +import org.springframework.cloud.contract.spec.internal.Headers; +import org.springframework.cloud.contract.spec.internal.OutputMessage; +import org.springframework.cloud.contract.stubrunner.AvailablePortScanner.PortCallback; +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessage; +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; +import org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierMessaging; +import org.springframework.cloud.contract.verifier.util.BodyExtractor; + +import groovy.json.JsonOutput; + +/** + * Runs stubs for a particular {@link StubServer} + */ +public class StubRunnerExecutor implements StubFinder { + + private static final Logger log = LoggerFactory.getLogger(StubRunnerExecutor.class); + private final AvailablePortScanner portScanner; + private final ContractVerifierMessaging contractVerifierMessaging; + private StubServer stubServer; + + public StubRunnerExecutor(AvailablePortScanner portScanner, + ContractVerifierMessaging contractVerifierMessaging) { + this.portScanner = portScanner; + this.contractVerifierMessaging = contractVerifierMessaging; + } + + public StubRunnerExecutor(AvailablePortScanner portScanner) { + this(portScanner, new NoOpContractVerifierMessaging()); + } + + public RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, + StubRepository repository, StubConfiguration stubConfiguration) { + startStubServers(stubRunnerOptions, stubConfiguration, repository); + RunningStubs runningCollaborators = new RunningStubs(Collections + .singletonMap(stubServer.getStubConfiguration(), stubServer.getPort())); + log.info("All stubs are now running " + runningCollaborators.toString()); + return runningCollaborators; + } + + public void shutdown() { + if (stubServer != null) { + stubServer.stop(); + } + } + + @Override + public URL findStubUrl(String groupId, String artifactId) { + if (groupId == null) { + return returnStubUrlIfMatches( + artifactId.equals(stubServer.stubConfiguration.artifactId)); + } + return returnStubUrlIfMatches( + artifactId.equals(stubServer.stubConfiguration.artifactId) + && groupId.equals(stubServer.stubConfiguration.groupId)); + } + + @Override + public URL findStubUrl(String ivyNotation) { + String[] splitString = ivyNotation.split(":"); + if (splitString.length == 1) { + throw new IllegalArgumentException("$ivyNotation is invalid"); + } + return findStubUrl(splitString[0], splitString[1]); + } + + @Override + public RunningStubs findAllRunningStubs() { + return new RunningStubs(Collections.singletonMap(stubServer.stubConfiguration, + stubServer.getPort())); + } + + @Override + public Map> getContracts() { + return Collections.singletonMap(stubServer.stubConfiguration, + stubServer.getContracts()); + } + + @Override + public boolean trigger(String ivyNotationAsString, String labelName) { + Collection matchingContracts = new ArrayList<>(); + for (Entry> it : getContracts() + .entrySet()) { + if (it.getKey().groupIdAndArtifactMatches(ivyNotationAsString)) { + matchingContracts.addAll(it.getValue()); + } + } + ; + return triggerForDsls(matchingContracts, labelName); + } + + @Override + public boolean trigger(String labelName) { + Collection matchingContracts = new ArrayList<>(); + for (Collection it : getContracts().values()) { + matchingContracts.addAll(it); + } + return triggerForDsls(matchingContracts, labelName); + } + + private boolean triggerForDsls(Collection dsls, String labelName) { + Collection matchingDsls = new ArrayList<>(); + for (Contract contract : dsls) { + if (labelName.equals(contract.getLabel())) { + matchingDsls.add(contract); + } + } + if (matchingDsls.isEmpty()) { + return false; + } + for (Contract contract : matchingDsls) { + sendMessageIfApplicable(contract); + } + return true; + } + + @Override + public boolean trigger() { + Collection matchingContracts = new ArrayList<>(); + for (Collection it : getContracts().values()) { + matchingContracts.addAll(it); + } + for (Contract contract : matchingContracts) { + sendMessageIfApplicable(contract); + } + return true; + } + + @Override + public Map> labels() { + Map> labels = new LinkedHashMap<>(); + for (Entry> it : getContracts() + .entrySet()) { + Collection values = new ArrayList<>(); + for (Contract contract : it.getValue()) { + values.add(contract.getLabel()); + } + labels.put(it.getKey().toColonSeparatedDependencyNotation(), values); + } + return labels; + } + + private void sendMessageIfApplicable(Contract groovyDsl) { + OutputMessage outputMessage = groovyDsl.getOutputMessage(); + if (outputMessage == null) { + return; + } + DslProperty body = outputMessage == null ? null : outputMessage.getBody(); + Headers headers = outputMessage == null ? null : outputMessage.getHeaders(); + ContractVerifierMessage message = contractVerifierMessaging + .create(JsonOutput.toJson(BodyExtractor.extractClientValueFromBody( + body == null ? null : body.getClientValue())), + headers == null ? null : headers.asStubSideMap()); + contractVerifierMessaging.send(message, + outputMessage.getSentTo().getClientValue()); + } + + private URL returnStubUrlIfMatches(boolean condition) { + return condition ? stubServer.getStubUrl() : null; + } + + private void startStubServers(StubRunnerOptions stubRunnerOptions, + final StubConfiguration stubConfiguration, StubRepository repository) { + final List mappings = repository + .getProjectDescriptors(); + final Collection contracts = repository.contracts; + Integer port = stubRunnerOptions.port(stubConfiguration); + if (port != null && port >= 0) { + stubServer = new StubServer(port, stubConfiguration, mappings, contracts); + } + else { + stubServer = portScanner + .tryToExecuteWithFreePort(new PortCallback() { + + @Override + public StubServer call(int availablePort) { + return new StubServer(availablePort, stubConfiguration, + mappings, contracts); + } + + }); + } + if (!contracts.isEmpty() && !hasRequest(contracts)) { + log.debug("There are no HTTP related contracts. Won't start any servers"); + return; + } + if (contracts.isEmpty()) { + log.warn( + "There are no contracts in the published JAR. This is an unusual situation " + + "that's why will start the server - maybe you know what you're doing..."); + } + stubServer = stubServer.start(); + } + + private boolean hasRequest(Collection contracts) { + for (Contract contract : contracts) { + if (contract.getRequest() != null) { + return true; + } + } + return false; + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java new file mode 100644 index 0000000000..704a96b9e1 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; + +/** + * Factory of StubRunners. Basing on the options and passed collaborators downloads the + * stubs and returns a list of corresponding stub runners. + */ +public class StubRunnerFactory { + + private final StubRunnerOptions stubRunnerOptions; + private final StubDownloader stubDownloader; + private final ContractVerifierMessaging contractVerifierMessaging; + + public StubRunnerFactory(StubRunnerOptions stubRunnerOptions, + StubDownloader stubDownloader, + ContractVerifierMessaging contractVerifierMessaging) { + this.stubRunnerOptions = stubRunnerOptions; + this.stubDownloader = stubDownloader; + this.contractVerifierMessaging = contractVerifierMessaging; + } + + public Collection createStubsFromServiceConfiguration() { + Collection result = new ArrayList<>(); + for (StubConfiguration stubsConfiguration : stubRunnerOptions.getDependencies()) { + Map.Entry entry = stubDownloader + .downloadAndUnpackStubJar(stubRunnerOptions, stubsConfiguration); + if (entry != null) { + result.add(createStubRunner(entry.getKey(), entry.getValue())); + } + + } + return result; + } + + private StubRunner createStubRunner(StubConfiguration stubsConfiguration, + File unzipedStubDir) { + if (unzipedStubDir == null) { + return null; + } + return createStubRunner(unzipedStubDir, stubsConfiguration, stubRunnerOptions); + } + + private StubRunner createStubRunner(File unzippedStubsDir, + StubConfiguration stubsConfiguration, StubRunnerOptions stubRunnerOptions) { + return new StubRunner(stubRunnerOptions, unzippedStubsDir.getPath(), + stubsConfiguration, contractVerifierMessaging); + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java new file mode 100644 index 0000000000..efe3a11bb9 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java @@ -0,0 +1,92 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StubRunnerMain { + + private static final Logger log = LoggerFactory.getLogger(StubRunnerMain.class); + + @Option(name = "-sr", aliases = "--stubRepositoryRoot", usage = "Location of a Jar containing server where you keep your stubs (e.g. http://nexus.net/content/repositories/repository)", required = true) + private String stubRepositoryRoot; + + @Option(name = "-ss", aliases = "--stubsSuffix", usage = "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'") + private String stubsSuffix = "stubs"; + + @Option(name = "-minp", aliases = "--minPort", usage = "Minimal port value to be assigned to the WireMock instance. Defaults to 10000") + private Integer minPortValue = 10000; + + @Option(name = "-maxp", aliases = "--maxPort", usage = "Maximum port value to be assigned to the WireMock instance. Defaults to 15000") + private Integer maxPortValue = 15000; + + @Option(name = "-wo", aliases = "--workOffline", usage = "Switch to work offline. Defaults to 'false'") + private Boolean workOffline = Boolean.FALSE; + + @Option(name = "-s", aliases = "--stubs", usage = "Comma separated list of Ivy representation of jars with stubs. Eg. groupid:artifactid1,groupid2:artifactid2:classifier") + private String stubs; + + private final Arguments arguments; + + private StubRunnerMain(String[] args) throws CmdLineException { + CmdLineParser parser = new CmdLineParser(this); + try { + parser.parseArgument(args); + StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() + .withMinMaxPort(minPortValue, maxPortValue) + .withStubRepositoryRoot(stubRepositoryRoot) + .withWorkOffline(workOffline) + .withStubsClassifier(stubsSuffix) + .withStubs(stubs) + .build(); + this.arguments = new Arguments(stubRunnerOptions); + } catch (CmdLineException e) { + printErrorMessage(e, parser); + throw e; + } + } + + private void printErrorMessage(CmdLineException e, CmdLineParser parser) { + System.err.println(e.getMessage()); + System.err.println("java -jar stub-runner.jar [options...] "); + parser.printUsage(System.err); + System.err.println(); + System.err.println("Example: java -jar stub-runner.jar ${parser.printExample(ALL)}"); + } + + static void main(String[] args) throws CmdLineException { + new StubRunnerMain(args).execute(); + } + + private void execute() { + try { + log.debug("Launching StubRunner with args: $arguments"); + // TODO: Pass StubsToRun either from String or File + BatchStubRunner stubRunner = new BatchStubRunnerFactory(arguments.getStubRunnerOptions()).buildBatchStubRunner(); + RunningStubs runningCollaborators = stubRunner.runStubs(); + log.info(runningCollaborators.toString()); + } catch (Exception e) { + log.error("An exception occurred while trying to execute the stubs", e); + throw e; + } + } + +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.java similarity index 68% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.java index 523297c1be..f75a807e52 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMessagingTrigger.java @@ -14,22 +14,20 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -import groovy.transform.PackageScope -import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging +import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; /** * @author Marcin Grzejszczak */ -@PackageScope class StubRunnerMessagingTrigger { - private final ContractVerifierMessaging contractVerifierMessaging + private final ContractVerifierMessaging contractVerifierMessaging; - StubRunnerMessagingTrigger(ContractVerifierMessaging contractVerifierMessaging) { - this.contractVerifierMessaging = contractVerifierMessaging + StubRunnerMessagingTrigger(ContractVerifierMessaging contractVerifierMessaging) { + this.contractVerifierMessaging = contractVerifierMessaging; } - def trigger + // def trigger } diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java new file mode 100644 index 0000000000..8affbff49f --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.Collection; +import java.util.Map; + + +/** + * Technical options related to running StubRunner + * + * Use {@class StubRunnerOptionsBuilder} to build this object. + * + * @see StubRunnerOptionsBuilder + */ +public class StubRunnerOptions { + + /** + * min port value of the WireMock instance for the given collaborator + */ + final Integer minPortValue; + + /** + * max port value of the WireMock instance for the given collaborator + */ + final Integer maxPortValue; + + /** + * root URL from where the JAR with stub mappings will be downloaded + */ + final String stubRepositoryRoot; + + /** + * avoids local repository in dependency resolution + */ + final boolean workOffline; + + /** + * stub definition classifier + */ + final String stubsClassifier; + + final Collection dependencies; + + /** + * colon separated list of ids to the desired port + */ + final Map stubIdsToPortMapping; + + public StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot, + boolean workOffline, String stubsClassifier, Collection dependencies, Map stubIdsToPortMapping) { + this.minPortValue = minPortValue; + this.maxPortValue = maxPortValue; + this.stubRepositoryRoot = stubRepositoryRoot; + this.workOffline = workOffline; + this.stubsClassifier = stubsClassifier; + this.dependencies = dependencies; + this.stubIdsToPortMapping = stubIdsToPortMapping; + } + + public Integer port(StubConfiguration stubConfiguration) { + if (stubIdsToPortMapping!=null) { + return stubIdsToPortMapping.get(stubConfiguration); + } else { + return null; + } + } + + public Integer getMinPortValue() { + return minPortValue; + } + + public Integer getMaxPortValue() { + return maxPortValue; + } + + public String getStubRepositoryRoot() { + return stubRepositoryRoot; + } + + public boolean isWorkOffline() { + return workOffline; + } + + public String getStubsClassifier() { + return stubsClassifier; + } + + public Collection getDependencies() { + return dependencies; + } + + public Map getStubIdsToPortMapping() { + return stubIdsToPortMapping; + } + + @Override + public String toString() { + return "StubRunnerOptions [minPortValue=" + minPortValue + ", maxPortValue=" + + maxPortValue + ", stubRepositoryRoot=" + stubRepositoryRoot + + ", workOffline=" + workOffline + ", stubsClassifier=" + stubsClassifier + + ", dependencies=" + dependencies + ", stubIdsToPortMapping=" + + stubIdsToPortMapping + "]"; + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java new file mode 100644 index 0000000000..6c3c6b0535 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java @@ -0,0 +1,144 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.contract.stubrunner.util.StubsParser; + +public class StubRunnerOptionsBuilder { + + private static final String DELIMITER = ":"; + private LinkedList stubs = new LinkedList<>(); + private Map stubIdsToPortMapping = new LinkedHashMap<>(); + + private Integer minPortValue = 10000; + private Integer maxPortValue = 15000; + private String stubRepositoryRoot; + private boolean workOffline = false; + private String stubsClassifier = "stubs"; + + public StubRunnerOptionsBuilder() { + } + + public StubRunnerOptionsBuilder(StubRunnerOptions options) { + withOptions(options); + } + + public StubRunnerOptionsBuilder withStubs(String stubs) { + addStub(stubsToList(stubs)); + return this; + } + + public StubRunnerOptionsBuilder withStubs(List stubs) { + for (String stub : stubs) { + withStubs(stub); + } + return this; + } + + public StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue, + Integer maxPortValue) { + this.minPortValue = minPortValue; + this.maxPortValue = maxPortValue; + return this; + } + + public StubRunnerOptionsBuilder withMinPort(int minPortValue) { + this.minPortValue = minPortValue; + return this; + } + + public StubRunnerOptionsBuilder withMaxPort(int maxPortValue) { + this.maxPortValue = maxPortValue; + return this; + } + + public StubRunnerOptionsBuilder withStubRepositoryRoot(String stubRepositoryRoot) { + this.stubRepositoryRoot = stubRepositoryRoot; + return this; + } + + public StubRunnerOptionsBuilder withWorkOffline(boolean workOffline) { + this.workOffline = workOffline; + return this; + } + + public StubRunnerOptionsBuilder withStubsClassifier(String stubsClassifier) { + this.stubsClassifier = stubsClassifier; + return this; + } + + public StubRunnerOptionsBuilder withPort(Integer port) { + String lastStub = stubs.peekLast(); + addPort(lastStub + DELIMITER + port); + return this; + } + + public StubRunnerOptionsBuilder withOptions(StubRunnerOptions options) { + this.minPortValue = options.minPortValue; + this.maxPortValue = options.maxPortValue; + this.stubRepositoryRoot = options.stubRepositoryRoot; + this.workOffline = options.workOffline; + this.stubsClassifier = options.stubsClassifier; + return this; + } + + public StubRunnerOptions build() { + return new StubRunnerOptions(minPortValue, maxPortValue, stubRepositoryRoot, + workOffline, stubsClassifier, buildDependencies(), stubIdsToPortMapping); + } + + private Collection buildDependencies() { + return StubsParser.fromString(stubs, stubsClassifier); + } + + private static List stubsToList(String stubIdsToPortMapping) { + return Arrays.asList(stubIdsToPortMapping.split(",")); + } + + private void addStub(List notations) { + for (String notation : notations) { + addStub(notation); + } + } + + private void addStub(String notation) { + if (StubsParser.hasPort(notation)) { + addPort(notation); + stubs.add(StubsParser.ivyFromStringWithPort(notation)); + } + else { + stubs.add(notation); + } + } + + private void addPort(String notation) { + putStubIdsToPortMapping(StubsParser.fromStringWithPort(notation)); + } + + private void putStubIdsToPortMapping( + Map stubIdsToPortMapping) { + this.stubIdsToPortMapping.putAll(stubIdsToPortMapping); + } + +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunning.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunning.java similarity index 80% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunning.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunning.java index 08758b541e..55534b874c 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubRunning.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunning.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -interface StubRunning extends Closeable, StubFinder { +import java.io.Closeable; + +public interface StubRunning extends Closeable, StubFinder { /** * Runs the stubs and returns the {@link RunningStubs} */ - RunningStubs runStubs() + RunningStubs runStubs(); } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java new file mode 100644 index 0000000000..4adb289f80 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java @@ -0,0 +1,131 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.wiremock.WireMockSpring; +import org.springframework.util.ClassUtils; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + +public class StubServer { + + private static final Logger log = LoggerFactory.getLogger(StubServer.class); + + private WireMockServer wireMockServer; + final StubConfiguration stubConfiguration; + final Collection mappings; + final Collection contracts; + + public StubServer(int port, StubConfiguration stubConfiguration, Collection mappings, + Collection contracts) { + this.stubConfiguration = stubConfiguration; + this.mappings = mappings; + this.wireMockServer = new WireMockServer(config().port(port)); + this.contracts = contracts; + } + + private WireMockConfiguration config() { + if (ClassUtils.isPresent("org.springframework.cloud.contract.wiremock.WireMockSpring", null)) { + return WireMockSpring.options(); + } + return new WireMockConfiguration(); + } + + public StubServer start() { + wireMockServer.start(); + log.info("Started stub server for project ${stubConfiguration.toColonSeparatedDependencyNotation()} on port ${wireMockServer.port()}"); + registerStubMappings(); + return this; + } + + public void stop() { + wireMockServer.stop(); + } + + public int getPort() { + if (wireMockServer.isRunning()) { + return wireMockServer.port(); + } + log.debug("The HTTP Server stub is not running... That means that the " + + "artifact is running a messaging module. Returning back -1 value of the port."); + return -1; + } + + public URL getStubUrl() { + try { + return new URL("http://localhost:" + getPort()); + } + catch (MalformedURLException e) { + throw new IllegalStateException("Cannot parse URL", e); + } + } + + public WireMockServer getWireMockServer() { + return wireMockServer; + } + + public StubConfiguration getStubConfiguration() { + return stubConfiguration; + } + + public Collection getMappings() { + return mappings; + } + + public Collection getContracts() { + return contracts; + } + + private void registerStubMappings() { + WireMock wireMock = new WireMock("localhost", wireMockServer.port()); + registerDefaultHealthChecks(wireMock); + registerStubs(mappings, wireMock); + } + + private void registerDefaultHealthChecks(WireMock wireMock) { + registerHealthCheck(wireMock, "/ping"); + registerHealthCheck(wireMock, "/health"); + } + + private void registerStubs(Collection sortedMappings, WireMock wireMock) { + for (WiremockMappingDescriptor mappingDescriptor : sortedMappings) { + try { + wireMock.register(mappingDescriptor.getMapping()); + log.debug("Registered stub mappings from $mappingDescriptor.descriptor"); + } catch (Exception e) { + log.warn("Failed to register the stub mapping ["+ mappingDescriptor + "]", e); + } + } + } + + private void registerHealthCheck(WireMock wireMock, String url) { + registerHealthCheck(wireMock, url, "OK"); + } + + private void registerHealthCheck(WireMock wireMock, String url, String body) { + wireMock.register(WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200))); + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubTrigger.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubTrigger.java similarity index 81% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubTrigger.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubTrigger.java index c9ef71bafa..43402bcf98 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/StubTrigger.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubTrigger.java @@ -14,9 +14,12 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner; -interface StubTrigger { +import java.util.Collection; +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. @@ -25,7 +28,7 @@ interface StubTrigger { * * @return true - if managed to run a trigger */ - boolean trigger(String ivyNotation, String labelName) + boolean trigger(String ivyNotation, String labelName); /** * Triggers an event by a given label. @@ -34,7 +37,7 @@ interface StubTrigger { * * @return true - if managed to run a trigger */ - boolean trigger(String labelName) + boolean trigger(String labelName); /** * Triggers all possible events. @@ -43,12 +46,12 @@ interface StubTrigger { * * @return true - if managed to run a trigger */ - boolean trigger() + boolean trigger(); /** * Returns a mapping of ivy notation of a dependency to all the labels it has. * * Feature related to messaging. */ - Map> labels() + Map> labels(); } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java new file mode 100644 index 0000000000..5458d6e416 --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +import org.springframework.util.StreamUtils; + +import com.github.tomakehurst.wiremock.stubbing.StubMapping; + +/** + * Represents a single JSON file that was found in the folder with potential WireMock + * stubs + */ +public class WiremockMappingDescriptor { + + final File descriptor; + + public WiremockMappingDescriptor(File mappingDescriptor) { + this.descriptor = mappingDescriptor; + } + + public StubMapping getMapping() { + try { + return StubMapping.buildFrom(StreamUtils.copyToString( + new FileInputStream(descriptor), Charset.forName("UTF-8"))); + } + catch (IOException e) { + throw new IllegalStateException("Cannot read file", e); + } + } + + @Override + public String toString() { + return "WiremockMappingDescriptor [descriptor=" + descriptor + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((descriptor == null) ? 0 : descriptor.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + WiremockMappingDescriptor other = (WiremockMappingDescriptor) obj; + if (descriptor == null) { + if (other.descriptor != null) + return false; + } + else if (!descriptor.equals(other.descriptor)) + return false; + return true; + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StringUtils.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StringUtils.java similarity index 64% rename from spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StringUtils.groovy rename to spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StringUtils.java index 5382f8ae57..e21ed2422d 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/groovy/org/springframework/cloud/contract/stubrunner/util/StringUtils.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StringUtils.java @@ -14,21 +14,23 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner.util +package org.springframework.cloud.contract.stubrunner.util; /** * Utils ported from Apache Commons * * @author Marcin Grzejszczak */ -class StringUtils { - public static String EMPTY = "" - private static int INDEX_NOT_FOUND = -1 +public class StringUtils { + public static String EMPTY = ""; + private static int INDEX_NOT_FOUND = -1; // Empty checks - //----------------------------------------------------------------------- + // ----------------------------------------------------------------------- /** - *

Checks if a String is empty ("") or null.

+ *

+ * Checks if a String is empty ("") or null. + *

* *
 	 * StringUtils.isEmpty(null)      = true
@@ -38,9 +40,10 @@ class StringUtils {
 	 * StringUtils.isEmpty("  bob  ") = false
 	 * 
* - *

NOTE: This method changed in Lang version 2.0. - * It no longer trims the String. - * That functionality is available in isBlank().

+ *

+ * NOTE: This method changed in Lang version 2.0. It no longer trims the String. That + * functionality is available in isBlank(). + *

* * @param str the String to check, may be null * @return true if the String is empty or null @@ -49,23 +52,38 @@ class StringUtils { return str == null || str.length() == 0; } - static boolean isNotEmpty(String string) { - return string != null && !string.empty + public static boolean isNotEmpty(String string) { + return string != null && !string.isEmpty(); } - static boolean hasText(String string) { - return isNotEmpty(string) && !string.allWhitespace + public static boolean hasText(String string) { + if (!isNotEmpty(string)) { + return false; + } + int strLen = string.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(string.charAt(i))) { + return true; + } + } + return false; } /** - *

Gets the substring before the last occurrence of a separator. - * The separator is not returned.

+ *

+ * Gets the substring before the last occurrence of a separator. The separator is not + * returned. + *

* - *

A null string input will return null. - * An empty ("") string input will return the empty string. - * An empty or null separator will return the input string.

+ *

+ * A null string input will return null. An empty ("") + * string input will return the empty string. An empty or null separator + * will return the input string. + *

* - *

If nothing is found, the string input is returned.

+ *

+ * If nothing is found, the string input is returned. + *

* *
 	 * StringUtils.substringBeforeLast(null, *)      = null
@@ -81,7 +99,7 @@ class StringUtils {
 	 * @param str the String to get a substring from, may be null
 	 * @param separator the String to search for, may be null
 	 * @return the substring before the last occurrence of the separator,
-	 *  null if null String input
+	 * null if null String input
 	 * @since 2.0
 	 */
 	public static String substringBeforeLast(String str, String separator) {
@@ -96,15 +114,20 @@ class StringUtils {
 	}
 
 	/**
-	 * 

Gets the substring after the last occurrence of a separator. - * The separator is not returned.

+ *

+ * Gets the substring after the last occurrence of a separator. The separator is not + * returned. + *

* - *

A null string input will return null. - * An empty ("") string input will return the empty string. - * An empty or null separator will return the empty string if - * the input string is not null.

+ *

+ * A null string input will return null. An empty ("") + * string input will return the empty string. An empty or null separator + * will return the empty string if the input string is not null. + *

* - *

If nothing is found, the empty string is returned.

+ *

+ * If nothing is found, the empty string is returned. + *

* *
 	 * StringUtils.substringAfterLast(null, *)      = null
@@ -120,8 +143,8 @@ class StringUtils {
 	 *
 	 * @param str the String to get a substring from, may be null
 	 * @param separator the String to search for, may be null
-	 * @return the substring after the last occurrence of the separator,
-	 *  null if null String input
+	 * @return the substring after the last occurrence of the separator, null
+	 * if null String input
 	 * @since 2.0
 	 */
 	public static String substringAfterLast(String str, String separator) {
diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StubsParser.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StubsParser.java
new file mode 100644
index 0000000000..0d61fee480
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/StubsParser.java
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 2013-2016 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.springframework.cloud.contract.stubrunner.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.cloud.contract.stubrunner.StubConfiguration;
+
+/**
+ * Utility to parse string into a list of configuration of stubs
+ */
+public class StubsParser {
+
+	/**
+	 * The string is expected to be a map with entry called "stubs"
+	 * that contains a list of Strings in the format
+	 *
+	 * 
    + *
  • groupid:artifactid:classifier
  • + *
  • groupid:artifactid
  • + *
+ * + * In the latter case the provided default stub classifier will be passed. + * + * Example: + * + * "a:b,c:d:e" + */ + public static Set fromString(String list, String defaultClassifier) { + List splitList = Arrays.asList(list.split(",")); + return fromString(splitList, defaultClassifier); + } + + public static Set fromString(Collection collection, String defaultClassifier) { + Set stubs = new LinkedHashSet<>(); + for (String config : collection) { + if (StringUtils.hasText(config)) { + stubs.add(StubSpecification.parse(config, defaultClassifier).stub); + } + } + return stubs; + } + + public static Map fromStringWithPort(String notation) { + StubSpecification stub = StubSpecification.parse(notation, StubConfiguration.DEFAULT_CLASSIFIER); + if (!stub.hasPort()) { + return Collections.emptyMap(); + } + return Collections.singletonMap(stub.stub, stub.port); + } + + public static String ivyFromStringWithPort(String notation) { + StubSpecification stub = StubSpecification.parse(notation, StubConfiguration.DEFAULT_CLASSIFIER); + if (!stub.hasPort()) { + return ""; + } + return stub.stub.toColonSeparatedDependencyNotation(); + } + + public static boolean hasPort(String id) { + String[] splitEntry = id.split(":"); + try { + Integer.valueOf(splitEntry[splitEntry.length-1]); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + private static class StubSpecification { + + private StubConfiguration stub; + private Integer port; + + public StubSpecification(StubConfiguration stub, Integer port) { + this.stub = stub; + this.port = port; + } + + public boolean hasPort() { + return 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]); + id = id.substring(0, id.lastIndexOf(":")); + } catch (NumberFormatException e) { + } + return new StubSpecification(new StubConfiguration(id, defaultClassifier), port); + } + + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/ZipCategory.java b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/ZipCategory.java new file mode 100644 index 0000000000..85d6a19abe --- /dev/null +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/util/ZipCategory.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.springframework.util.StreamUtils; + +/** + * Based on + * https://github.com/ + * timyates/groovy-common-extensions. + * + * Category for {@link File} that adds a method that allows you to unzip a given file to a + * specified location + * + */ +public class ZipCategory { + + /** + * Unzips this file. If the destination 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. + * @return a {@link Collection} of unzipped {@link File} objects. + */ + public static Collection unzipTo(File self, File destination) { + checkUnzipDestination(destination); + // if destination directory is not given, we'll fall back to the parent directory + // of 'self' + if (destination == null) + destination = new File(self.getParent()); + List unzippedFiles = new ArrayList<>(); + try { + ZipInputStream zipInput = new ZipInputStream(new FileInputStream(self)); + 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) { + file.getParentFile().mkdirs(); + } + try (FileOutputStream output = new FileOutputStream(file)) { + StreamUtils.copy(zipInput, output); + } + unzippedFiles.add(file); + } + else { + final File dir = new File(destination, entry.getName()); + dir.mkdirs(); + unzippedFiles.add(dir); + } + } + } + catch (IOException e) { + throw new IllegalStateException("Cannot unzip archive", e); + } + return unzippedFiles; + } + + private static void checkUnzipDestination(File file) { + if (file != null && !file.isDirectory()) + throw new IllegalArgumentException("'destination' has to be a directory."); + } +} diff --git a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy index bc5614e5a1..722739a0fc 100644 --- a/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy +++ b/spring-cloud-contract-stub-runner/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy @@ -45,6 +45,6 @@ class StubRepositorySpec extends Specification { when: new StubRepository(new File('src/test/resources/nonexistingrepo')) then: - thrown(FileNotFoundException) + thrown(IllegalArgumentException) } } diff --git a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-camel/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-camel/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java index 71280d4700..d66706c20d 100644 --- a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-camel/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java +++ b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-camel/src/main/java/org/springframework/cloud/contract/verifier/messaging/camel/ContractVerifierCamelConfiguration.java @@ -16,22 +16,24 @@ package org.springframework.cloud.contract.verifier.messaging.camel; +import org.apache.camel.CamelContext; import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessageBuilder; import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMessaging; -import org.apache.camel.CamelContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * @author Marcin Grzejszczak */ +@Configuration public class ContractVerifierCamelConfiguration { - @Bean ContractVerifierMessaging contractVerifierMessaging(CamelContext context, - ContractVerifierMessageBuilder builder) { - return new ContractVerifierCamelMessaging(context, builder); + @Bean ContractVerifierMessaging contractVerifierMessaging(CamelContext context, + ContractVerifierMessageBuilder builder) { + return new ContractVerifierCamelMessaging<>(context, builder); } - @Bean ContractVerifierMessageBuilder contractVerifierMessageBuilder() { - return new ContractVerifierCamelMessageBuilder(); + @Bean ContractVerifierMessageBuilder contractVerifierMessageBuilder() { + return new ContractVerifierCamelMessageBuilder<>(); } } diff --git a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java index 7d69bddba5..d4bdde69a0 100644 --- a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java +++ b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationConfiguration.java @@ -21,23 +21,24 @@ import org.springframework.cloud.contract.verifier.messaging.ContractVerifierMes import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; /** * @author Marcin Grzejszczak */ @Configuration -public class ContractVerifierIntegrationConfiguration { +public class ContractVerifierIntegrationConfiguration { @Bean - public ContractVerifierMessaging contractVerifierMessaging( + public ContractVerifierMessaging> contractVerifierMessaging( ApplicationContext applicationContext, - ContractVerifierMessageBuilder contractVerifierMessageBuilder) { - return new ContractVerifierIntegrationMessaging<>(applicationContext, + ContractVerifierMessageBuilder> contractVerifierMessageBuilder) { + return new ContractVerifierIntegrationMessaging(applicationContext, contractVerifierMessageBuilder); } @Bean - public ContractVerifierMessageBuilder contractVerifierMessageBuilder() { + public ContractVerifierMessageBuilder> contractVerifierMessageBuilder() { return new ContractVerifierIntegrationMessageBuilder<>(); } } diff --git a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationMessaging.java b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationMessaging.java index 960e07d520..c7a197042a 100644 --- a/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationMessaging.java +++ b/spring-cloud-contract-verifier/spring-cloud-contract-verifier-messaging/spring-cloud-contract-verifier-integration/src/main/java/org/springframework/cloud/contract/verifier/messaging/integration/ContractVerifierIntegrationMessaging.java @@ -43,17 +43,15 @@ public class ContractVerifierIntegrationMessaging implements ContractVerifierIntegrationMessaging.class); private final ApplicationContext context; - private final ContractVerifierMessageBuilder builder; + private final ContractVerifierMessageBuilder> builder; @Autowired - @SuppressWarnings("unchecked") - public ContractVerifierIntegrationMessaging(ApplicationContext context, ContractVerifierMessageBuilder contractVerifierMessageBuilder) { + public ContractVerifierIntegrationMessaging(ApplicationContext context, ContractVerifierMessageBuilder> contractVerifierMessageBuilder) { this.context = context; this.builder = contractVerifierMessageBuilder; } @Override - @SuppressWarnings("unchecked") public void send(T payload, Map headers, String destination) { send(builder.create(payload, headers), destination); } @@ -75,7 +73,7 @@ public class ContractVerifierIntegrationMessaging implements public ContractVerifierMessage> receiveMessage(String destination, long timeout, TimeUnit timeUnit) { try { PollableChannel messageChannel = context.getBean(destination, PollableChannel.class); - return builder.create(messageChannel.receive(timeUnit.toMillis(timeout))); + return builder.create((Message) messageChannel.receive(timeUnit.toMillis(timeout))); } catch (Exception e) { log.error("Exception occurred while trying to read a message from " + " a channel with name [" + destination + "]", e); @@ -89,13 +87,11 @@ public class ContractVerifierIntegrationMessaging implements } @Override - @SuppressWarnings("unchecked") public ContractVerifierMessage> create(T t, Map headers) { return builder.create(t, headers); } @Override - @SuppressWarnings("unchecked") public ContractVerifierMessage> create(Message message) { return builder.create(message); }