Convert stub-runner base to Java
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.messaging.Message
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Configuration
|
||||
@Import(StubRunnerConfiguration)
|
||||
@CompileStatic
|
||||
class StubRunnerIntegrationConfiguration {
|
||||
|
||||
|
||||
@@ -100,9 +100,6 @@
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>addSources</goal>
|
||||
<goal>addTestSources</goal>
|
||||
<goal>compile</goal>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
|
||||
@@ -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<RemoteRepository> newRepositories(List<String> 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")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<RemoteRepository> 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<RemoteRepository> 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<RemoteRepository> 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<StubConfiguration, File> 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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> T tryToExecuteWithFreePort(Closure<T> 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> T executeLogicForAvailablePort(int portToScan, Closure<T> 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]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<StubRunner> stubRunners
|
||||
|
||||
BatchStubRunner(Iterable<StubRunner> stubRunners) {
|
||||
this.stubRunners = stubRunners
|
||||
}
|
||||
|
||||
@Override
|
||||
RunningStubs runStubs() {
|
||||
Map<StubConfiguration, Integer> appsAndPorts = stubRunners.inject([:]) { Map<StubConfiguration, Integer> acc, StubRunner value ->
|
||||
RunningStubs runningStubs = value.runStubs()
|
||||
acc.putAll(runningStubs.validNamesAndPorts())
|
||||
return acc
|
||||
} as Map<StubConfiguration, Integer>
|
||||
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<StubConfiguration, Collection<Contract>> getContracts() {
|
||||
return stubRunners.inject([:]) { Map<StubConfiguration, Collection<Contract>> map, StubRunner stubRunner ->
|
||||
map.putAll(stubRunner.contracts ?: [:])
|
||||
return map
|
||||
} as Map<StubConfiguration, Collection<Contract>>
|
||||
}
|
||||
|
||||
@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<String, Collection<String>> labels() {
|
||||
return stubRunners.inject([:]) { Map<String, Collection<String>> map, StubRunner stubRunner ->
|
||||
if (stubRunner) {
|
||||
map.putAll(stubRunner.labels() ?: [:])
|
||||
}
|
||||
return map
|
||||
} as Map<String, Collection<String>>
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
stubRunners.each {
|
||||
it.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<StubConfiguration, Integer> namesAndPorts = [:]
|
||||
|
||||
RunningStubs(Map<StubConfiguration, Integer> map) {
|
||||
this.namesAndPorts.putAll(map)
|
||||
}
|
||||
|
||||
RunningStubs(Collection<RunningStubs> runningStubs) {
|
||||
runningStubs.each {
|
||||
this.namesAndPorts.putAll(it.namesAndPorts)
|
||||
}
|
||||
}
|
||||
|
||||
Integer getPort(String artifactId) {
|
||||
return getEntry(artifactId)?.value
|
||||
}
|
||||
|
||||
Map.Entry<StubConfiguration, Integer> 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<StubConfiguration> getAllServices() {
|
||||
return namesAndPorts.keySet()
|
||||
}
|
||||
|
||||
Set<String> getAllServicesNames() {
|
||||
return namesAndPorts.keySet().collect { it.artifactId } as Set
|
||||
}
|
||||
|
||||
Map<String, Integer> toIvyToPortMapping() {
|
||||
return namesAndPorts.collectEntries { [(it.key.toColonSeparatedDependencyNotation()) : it.value] } as Map<String, Integer>
|
||||
}
|
||||
|
||||
Map<StubConfiguration, Integer> 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")
|
||||
}
|
||||
}
|
||||
@@ -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<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 [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()
|
||||
}
|
||||
}
|
||||
@@ -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<Contract> contracts
|
||||
}
|
||||
@@ -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<WiremockMappingDescriptor> projectDescriptors
|
||||
final Collection<Contract> 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<Contract> contracts() {
|
||||
List<Contract> contracts = []
|
||||
contracts.addAll(contractDescriptors())
|
||||
return contracts
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of WireMock JSON files wrapped in {@link WiremockMappingDescriptor}
|
||||
*/
|
||||
private List<WiremockMappingDescriptor> projectDescriptors() {
|
||||
List<WiremockMappingDescriptor> mappingDescriptors = []
|
||||
mappingDescriptors.addAll(contextDescriptors())
|
||||
return mappingDescriptors
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> contextDescriptors() {
|
||||
return (path.exists() ? collectMappingDescriptors(path) : []) as List<WiremockMappingDescriptor>
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> collectMappingDescriptors(File descriptorsDirectory) {
|
||||
List<WiremockMappingDescriptor> mappingDescriptors = []
|
||||
descriptorsDirectory.eachFileRecurse { File file ->
|
||||
if (isMappingDescriptor(file)) {
|
||||
mappingDescriptors << new WiremockMappingDescriptor(file)
|
||||
}
|
||||
}
|
||||
return mappingDescriptors
|
||||
}
|
||||
|
||||
private Collection<Contract> contractDescriptors() {
|
||||
return (path.exists() ? collectContractDescriptors(path) : []) as Collection<Contract>
|
||||
}
|
||||
|
||||
private Collection<Contract> collectContractDescriptors(File descriptorsDirectory) {
|
||||
List<Contract> 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')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<StubConfiguration, Collection<Contract>> 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<String, Collection<String>> labels() {
|
||||
return localStubRunner.labels()
|
||||
}
|
||||
|
||||
private void registerShutdownHook() {
|
||||
Runnable stopAllServers = { this.close() }
|
||||
Runtime.runtime.addShutdownHook(new Thread(stopAllServers))
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
localStubRunner?.shutdown()
|
||||
}
|
||||
}
|
||||
@@ -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<StubConfiguration, Collection<Contract>> getContracts() {
|
||||
return [(stubServer.stubConfiguration): stubServer.contracts]
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean trigger(String ivyNotationAsString, String labelName) {
|
||||
Collection<Contract> matchingContracts = getContracts().findAll {
|
||||
it.key.groupIdAndArtifactMatches(ivyNotationAsString)
|
||||
}.values().flatten() as Collection<Contract>
|
||||
return triggerForDsls(matchingContracts, labelName)
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean trigger(String labelName) {
|
||||
return triggerForDsls(getContracts().values().flatten() as Collection<Contract>, labelName)
|
||||
}
|
||||
|
||||
private boolean triggerForDsls(Collection<Contract> dsls, String labelName) {
|
||||
Collection<Contract> 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<Contract>).each { Contract groovyDsl ->
|
||||
sendMessageIfApplicable(groovyDsl)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<String, Collection<String>> labels() {
|
||||
return getContracts().collectEntries {
|
||||
[(it.key.toColonSeparatedDependencyNotation()) : it.value.collect { it.label }]
|
||||
} as Map<String, Collection<String>>
|
||||
}
|
||||
|
||||
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<WiremockMappingDescriptor> mappings = repository.getProjectDescriptors()
|
||||
Collection<Contract> 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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<StubRunner> createStubsFromServiceConfiguration() {
|
||||
return stubRunnerOptions.getDependencies().collect { StubConfiguration stubsConfiguration ->
|
||||
Map.Entry<StubConfiguration, File> 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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<StubConfiguration> dependencies
|
||||
|
||||
/**
|
||||
* colon separated list of ids to the desired port
|
||||
*/
|
||||
final Map<StubConfiguration, Integer> stubIdsToPortMapping
|
||||
|
||||
@PackageScope
|
||||
StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot,
|
||||
boolean workOffline, String stubsClassifier, Collection<StubConfiguration> dependencies, Map<StubConfiguration, Integer> 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> stubs = new LinkedList<>()
|
||||
private Map<StubConfiguration, Integer> 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<String> 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<StubConfiguration> buildDependencies() {
|
||||
return StubsParser.fromString(stubs, stubsClassifier)
|
||||
}
|
||||
|
||||
private static List<String> stubsToList(String stubIdsToPortMapping) {
|
||||
return stubIdsToPortMapping.split(',').collect { it } as List<String>
|
||||
}
|
||||
|
||||
private void addStub(List<String> 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<StubConfiguration, Integer> stubIdsToPortMapping) {
|
||||
this.stubIdsToPortMapping.putAll(stubIdsToPortMapping)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<WiremockMappingDescriptor> mappings
|
||||
final Collection<Contract> contracts
|
||||
|
||||
StubServer(int port, StubConfiguration stubConfiguration, Collection<WiremockMappingDescriptor> mappings,
|
||||
Collection<Contract> 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<WiremockMappingDescriptor> 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)))
|
||||
}
|
||||
}
|
||||
@@ -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'))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
* <ul>
|
||||
* <li>groupid:artifactid:classifier</li>
|
||||
* <li>groupid:artifactid</li>
|
||||
* </ul>
|
||||
*
|
||||
* In the latter case the provided default stub classifier will be passed.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* "a:b,c:d:e"
|
||||
*/
|
||||
static Set<StubConfiguration> fromString(String list, String defaultClassifier) {
|
||||
def splitList = list.split(',').findAll { it }
|
||||
return fromString(splitList, defaultClassifier)
|
||||
}
|
||||
|
||||
static Set<StubConfiguration> fromString(Collection<String> 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<StubConfiguration, Integer> 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()
|
||||
}
|
||||
}
|
||||
@@ -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 <a href="https://github.com/timyates/groovy-common-extensions">https://github.com/timyates/groovy-common-extensions</a>.
|
||||
*
|
||||
* 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 <tt>destination</tt>
|
||||
* directory is not provided, it will fall back to this file's parent directory.
|
||||
*
|
||||
* @param self
|
||||
* @param destination (optional), the destination directory where this file's content will be unzipped to.
|
||||
* @return a {@link Collection} of unzipped {@link File} objects.
|
||||
*/
|
||||
static Collection<File> 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<File> 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.")
|
||||
}
|
||||
}
|
||||
@@ -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<RemoteRepository> newRepositories(List<String> repositories) {
|
||||
List<RemoteRepository> 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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<RemoteRepository> 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<RemoteRepository> 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<RemoteRepository> 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<StubConfiguration, File> 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<StubConfiguration, File>(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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> T tryToExecuteWithFreePort(PortCallback<T> 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> T executeLogicForAvailablePort(int portToScan, PortCallback<T> 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> {
|
||||
T call(int port) throws IOException;
|
||||
}
|
||||
}
|
||||
@@ -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<StubRunner> stubRunners;
|
||||
|
||||
public BatchStubRunner(Iterable<StubRunner> stubRunners) {
|
||||
this.stubRunners = stubRunners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RunningStubs runStubs() {
|
||||
Map<StubConfiguration, Integer> 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<RunningStubs> running = new LinkedHashSet<>();
|
||||
for (StubRunner stubRunner : stubRunners) {
|
||||
running.add(stubRunner.findAllRunningStubs());
|
||||
}
|
||||
return new RunningStubs(running);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<StubConfiguration, Collection<Contract>> getContracts() {
|
||||
Map<StubConfiguration, Collection<Contract>> map = new LinkedHashMap<>();
|
||||
for (StubRunner stubRunner : stubRunners) {
|
||||
for (Entry<StubConfiguration, Collection<Contract>> 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<String, Collection<String>> 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<String, Collection<String>> labels() {
|
||||
Map<String, Collection<String>> map = new LinkedHashMap<>();
|
||||
for (StubRunner stubRunner : stubRunners) {
|
||||
for (Entry<String, Collection<String>> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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<StubConfiguration, Integer> namesAndPorts = new LinkedHashMap<>();
|
||||
|
||||
public RunningStubs(Map<StubConfiguration, Integer> map) {
|
||||
this.namesAndPorts.putAll(map);
|
||||
}
|
||||
|
||||
public RunningStubs(Collection<RunningStubs> 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<StubConfiguration, Integer> getEntry(String artifactId) {
|
||||
for (Entry<StubConfiguration, Integer> it : namesAndPorts.entrySet()) {
|
||||
if (it.getKey().matchesIvyNotation(artifactId)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getPort(String groupId, String artifactId) {
|
||||
for (Entry<StubConfiguration, Integer> 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<StubConfiguration> getAllServices() {
|
||||
return namesAndPorts.keySet();
|
||||
}
|
||||
|
||||
public Set<String> getAllServicesNames() {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
for (Entry<StubConfiguration, Integer> it : namesAndPorts.entrySet()) {
|
||||
result.add(it.getKey().artifactId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, Integer> toIvyToPortMapping() {
|
||||
Map<String, Integer> result = new LinkedHashMap<>();
|
||||
for (Entry<StubConfiguration, Integer> it : namesAndPorts.entrySet()) {
|
||||
result.put(it.getKey().toColonSeparatedDependencyNotation(), it.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<StubConfiguration, Integer> validNamesAndPorts() {
|
||||
Map<StubConfiguration, Integer> result = new LinkedHashMap<>();
|
||||
for (Entry<StubConfiguration, Integer> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<Contract> contracts;
|
||||
|
||||
public StubData(Integer port, List<Contract> contracts) {
|
||||
this.port = port;
|
||||
this.contracts = contracts;
|
||||
}
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
public List<Contract> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<StubConfiguration,File> downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration)
|
||||
Map.Entry<StubConfiguration,File> downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration);
|
||||
|
||||
|
||||
}
|
||||
@@ -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<StubConfiguration, Collection<Contract>> getContracts()
|
||||
Map<StubConfiguration, Collection<Contract>> getContracts();
|
||||
}
|
||||
@@ -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<WiremockMappingDescriptor> projectDescriptors;
|
||||
final Collection<Contract> 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<WiremockMappingDescriptor> getProjectDescriptors() {
|
||||
return projectDescriptors;
|
||||
}
|
||||
|
||||
public Collection<Contract> getContracts() {
|
||||
return contracts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Contract}
|
||||
*/
|
||||
private Collection<Contract> contracts() {
|
||||
List<Contract> contracts = new ArrayList<>();
|
||||
contracts.addAll(contractDescriptors());
|
||||
return contracts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of WireMock JSON files wrapped in
|
||||
* {@link WiremockMappingDescriptor}
|
||||
*/
|
||||
private List<WiremockMappingDescriptor> projectDescriptors() {
|
||||
List<WiremockMappingDescriptor> mappingDescriptors = new ArrayList<>();
|
||||
mappingDescriptors.addAll(contextDescriptors());
|
||||
return mappingDescriptors;
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> contextDescriptors() {
|
||||
return path.exists() ? collectMappingDescriptors(path)
|
||||
: Collections.<WiremockMappingDescriptor>emptyList();
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> collectMappingDescriptors(
|
||||
File descriptorsDirectory) {
|
||||
final List<WiremockMappingDescriptor> mappingDescriptors = new ArrayList<>();
|
||||
try {
|
||||
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
|
||||
new SimpleFileVisitor<Path>() {
|
||||
@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<Contract> contractDescriptors() {
|
||||
return (path.exists() ? collectContractDescriptors(path)
|
||||
: Collections.<Contract>emptySet());
|
||||
}
|
||||
|
||||
private Collection<Contract> collectContractDescriptors(File descriptorsDirectory) {
|
||||
final List<Contract> mappingDescriptors = new ArrayList<>();
|
||||
try {
|
||||
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
|
||||
new SimpleFileVisitor<Path>() {
|
||||
@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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Object>) 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<StubConfiguration, Collection<Contract>> 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<String, Collection<String>> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> contractVerifierMessaging;
|
||||
private StubServer stubServer;
|
||||
|
||||
public StubRunnerExecutor(AvailablePortScanner portScanner,
|
||||
ContractVerifierMessaging<String, Object> 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<StubConfiguration, Collection<Contract>> getContracts() {
|
||||
return Collections.singletonMap(stubServer.stubConfiguration,
|
||||
stubServer.getContracts());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trigger(String ivyNotationAsString, String labelName) {
|
||||
Collection<Contract> matchingContracts = new ArrayList<>();
|
||||
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts()
|
||||
.entrySet()) {
|
||||
if (it.getKey().groupIdAndArtifactMatches(ivyNotationAsString)) {
|
||||
matchingContracts.addAll(it.getValue());
|
||||
}
|
||||
}
|
||||
;
|
||||
return triggerForDsls(matchingContracts, labelName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trigger(String labelName) {
|
||||
Collection<Contract> matchingContracts = new ArrayList<>();
|
||||
for (Collection<Contract> it : getContracts().values()) {
|
||||
matchingContracts.addAll(it);
|
||||
}
|
||||
return triggerForDsls(matchingContracts, labelName);
|
||||
}
|
||||
|
||||
private boolean triggerForDsls(Collection<Contract> dsls, String labelName) {
|
||||
Collection<Contract> 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<Contract> matchingContracts = new ArrayList<>();
|
||||
for (Collection<Contract> it : getContracts().values()) {
|
||||
matchingContracts.addAll(it);
|
||||
}
|
||||
for (Contract contract : matchingContracts) {
|
||||
sendMessageIfApplicable(contract);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<String>> labels() {
|
||||
Map<String, Collection<String>> labels = new LinkedHashMap<>();
|
||||
for (Entry<StubConfiguration, Collection<Contract>> it : getContracts()
|
||||
.entrySet()) {
|
||||
Collection<String> 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<String, Object> 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<WiremockMappingDescriptor> mappings = repository
|
||||
.getProjectDescriptors();
|
||||
final Collection<Contract> 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<StubServer>() {
|
||||
|
||||
@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<Contract> contracts) {
|
||||
for (Contract contract : contracts) {
|
||||
if (contract.getRequest() != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<StubRunner> createStubsFromServiceConfiguration() {
|
||||
Collection<StubRunner> result = new ArrayList<>();
|
||||
for (StubConfiguration stubsConfiguration : stubRunnerOptions.getDependencies()) {
|
||||
Map.Entry<StubConfiguration, File> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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<StubConfiguration> dependencies;
|
||||
|
||||
/**
|
||||
* colon separated list of ids to the desired port
|
||||
*/
|
||||
final Map<StubConfiguration, Integer> stubIdsToPortMapping;
|
||||
|
||||
public StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot,
|
||||
boolean workOffline, String stubsClassifier, Collection<StubConfiguration> dependencies, Map<StubConfiguration, Integer> 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<StubConfiguration> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public Map<StubConfiguration, Integer> getStubIdsToPortMapping() {
|
||||
return stubIdsToPortMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StubRunnerOptions [minPortValue=" + minPortValue + ", maxPortValue="
|
||||
+ maxPortValue + ", stubRepositoryRoot=" + stubRepositoryRoot
|
||||
+ ", workOffline=" + workOffline + ", stubsClassifier=" + stubsClassifier
|
||||
+ ", dependencies=" + dependencies + ", stubIdsToPortMapping="
|
||||
+ stubIdsToPortMapping + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> stubs = new LinkedList<>();
|
||||
private Map<StubConfiguration, Integer> 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<String> 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<StubConfiguration> buildDependencies() {
|
||||
return StubsParser.fromString(stubs, stubsClassifier);
|
||||
}
|
||||
|
||||
private static List<String> stubsToList(String stubIdsToPortMapping) {
|
||||
return Arrays.asList(stubIdsToPortMapping.split(","));
|
||||
}
|
||||
|
||||
private void addStub(List<String> 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<StubConfiguration, Integer> stubIdsToPortMapping) {
|
||||
this.stubIdsToPortMapping.putAll(stubIdsToPortMapping);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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<WiremockMappingDescriptor> mappings;
|
||||
final Collection<Contract> contracts;
|
||||
|
||||
public StubServer(int port, StubConfiguration stubConfiguration, Collection<WiremockMappingDescriptor> mappings,
|
||||
Collection<Contract> 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<WiremockMappingDescriptor> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Collection<Contract> 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<WiremockMappingDescriptor> 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)));
|
||||
}
|
||||
}
|
||||
@@ -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<String, Collection<String>> labels()
|
||||
Map<String, Collection<String>> labels();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Checks if a String is empty ("") or null.</p>
|
||||
* <p>
|
||||
* Checks if a String is empty ("") or null.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.isEmpty(null) = true
|
||||
@@ -38,9 +40,10 @@ class StringUtils {
|
||||
* StringUtils.isEmpty(" bob ") = false
|
||||
* </pre>
|
||||
*
|
||||
* <p>NOTE: This method changed in Lang version 2.0.
|
||||
* It no longer trims the String.
|
||||
* That functionality is available in isBlank().</p>
|
||||
* <p>
|
||||
* NOTE: This method changed in Lang version 2.0. It no longer trims the String. That
|
||||
* functionality is available in isBlank().
|
||||
* </p>
|
||||
*
|
||||
* @param str the String to check, may be null
|
||||
* @return <code>true</code> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the substring before the last occurrence of a separator.
|
||||
* The separator is not returned.</p>
|
||||
* <p>
|
||||
* Gets the substring before the last occurrence of a separator. The separator is not
|
||||
* returned.
|
||||
* </p>
|
||||
*
|
||||
* <p>A <code>null</code> string input will return <code>null</code>.
|
||||
* An empty ("") string input will return the empty string.
|
||||
* An empty or <code>null</code> separator will return the input string.</p>
|
||||
* <p>
|
||||
* A <code>null</code> string input will return <code>null</code>. An empty ("")
|
||||
* string input will return the empty string. An empty or <code>null</code> separator
|
||||
* will return the input string.
|
||||
* </p>
|
||||
*
|
||||
* <p>If nothing is found, the string input is returned.</p>
|
||||
* <p>
|
||||
* If nothing is found, the string input is returned.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 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,
|
||||
* <code>null</code> if null String input
|
||||
* <code>null</code> if null String input
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String substringBeforeLast(String str, String separator) {
|
||||
@@ -96,15 +114,20 @@ class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the substring after the last occurrence of a separator.
|
||||
* The separator is not returned.</p>
|
||||
* <p>
|
||||
* Gets the substring after the last occurrence of a separator. The separator is not
|
||||
* returned.
|
||||
* </p>
|
||||
*
|
||||
* <p>A <code>null</code> string input will return <code>null</code>.
|
||||
* An empty ("") string input will return the empty string.
|
||||
* An empty or <code>null</code> separator will return the empty string if
|
||||
* the input string is not <code>null</code>.</p>
|
||||
* <p>
|
||||
* A <code>null</code> string input will return <code>null</code>. An empty ("")
|
||||
* string input will return the empty string. An empty or <code>null</code> separator
|
||||
* will return the empty string if the input string is not <code>null</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>If nothing is found, the empty string is returned.</p>
|
||||
* <p>
|
||||
* If nothing is found, the empty string is returned.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 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,
|
||||
* <code>null</code> if null String input
|
||||
* @return the substring after the last occurrence of the separator, <code>null</code>
|
||||
* if null String input
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String substringAfterLast(String str, String separator) {
|
||||
@@ -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
|
||||
*
|
||||
* <ul>
|
||||
* <li>groupid:artifactid:classifier</li>
|
||||
* <li>groupid:artifactid</li>
|
||||
* </ul>
|
||||
*
|
||||
* In the latter case the provided default stub classifier will be passed.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* "a:b,c:d:e"
|
||||
*/
|
||||
public static Set<StubConfiguration> fromString(String list, String defaultClassifier) {
|
||||
List<String> splitList = Arrays.asList(list.split(","));
|
||||
return fromString(splitList, defaultClassifier);
|
||||
}
|
||||
|
||||
public static Set<StubConfiguration> fromString(Collection<String> collection, String defaultClassifier) {
|
||||
Set<StubConfiguration> stubs = new LinkedHashSet<>();
|
||||
for (String config : collection) {
|
||||
if (StringUtils.hasText(config)) {
|
||||
stubs.add(StubSpecification.parse(config, defaultClassifier).stub);
|
||||
}
|
||||
}
|
||||
return stubs;
|
||||
}
|
||||
|
||||
public static Map<StubConfiguration, Integer> 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
* <a href="https://github.com/timyates/groovy-common-extensions">https://github.com/
|
||||
* timyates/groovy-common-extensions</a>.
|
||||
*
|
||||
* 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 <tt>destination</tt> directory is not provided, it will
|
||||
* fall back to this file's parent directory.
|
||||
*
|
||||
* @param self
|
||||
* @param destination (optional), the destination directory where this file's content
|
||||
* will be unzipped to.
|
||||
* @return a {@link Collection} of unzipped {@link File} objects.
|
||||
*/
|
||||
public static Collection<File> 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<File> 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.");
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,6 @@ class StubRepositorySpec extends Specification {
|
||||
when:
|
||||
new StubRepository(new File('src/test/resources/nonexistingrepo'))
|
||||
then:
|
||||
thrown(FileNotFoundException)
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
|
||||
@Bean
|
||||
public ContractVerifierMessaging<?, ?> contractVerifierMessaging(
|
||||
public ContractVerifierMessaging<T, Message<T>> contractVerifierMessaging(
|
||||
ApplicationContext applicationContext,
|
||||
ContractVerifierMessageBuilder<?,?> contractVerifierMessageBuilder) {
|
||||
return new ContractVerifierIntegrationMessaging<>(applicationContext,
|
||||
ContractVerifierMessageBuilder<T, Message<T>> contractVerifierMessageBuilder) {
|
||||
return new ContractVerifierIntegrationMessaging<T>(applicationContext,
|
||||
contractVerifierMessageBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ContractVerifierMessageBuilder<?, ?> contractVerifierMessageBuilder() {
|
||||
public ContractVerifierMessageBuilder<T, Message<T>> contractVerifierMessageBuilder() {
|
||||
return new ContractVerifierIntegrationMessageBuilder<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,17 +43,15 @@ public class ContractVerifierIntegrationMessaging<T> implements
|
||||
ContractVerifierIntegrationMessaging.class);
|
||||
|
||||
private final ApplicationContext context;
|
||||
private final ContractVerifierMessageBuilder builder;
|
||||
private final ContractVerifierMessageBuilder<T, Message<T>> builder;
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("unchecked")
|
||||
public ContractVerifierIntegrationMessaging(ApplicationContext context, ContractVerifierMessageBuilder contractVerifierMessageBuilder) {
|
||||
public ContractVerifierIntegrationMessaging(ApplicationContext context, ContractVerifierMessageBuilder<T, Message<T>> contractVerifierMessageBuilder) {
|
||||
this.context = context;
|
||||
this.builder = contractVerifierMessageBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void send(T payload, Map<String, Object> headers, String destination) {
|
||||
send(builder.create(payload, headers), destination);
|
||||
}
|
||||
@@ -75,7 +73,7 @@ public class ContractVerifierIntegrationMessaging<T> implements
|
||||
public ContractVerifierMessage<T, Message<T>> 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<T>) 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<T> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ContractVerifierMessage<T, Message<T>> create(T t, Map<String, Object> headers) {
|
||||
return builder.create(t, headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ContractVerifierMessage<T, Message<T>> create(Message<T> message) {
|
||||
return builder.create(message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user