Make api with jaxb

Change-Id: Ic0ac5ce212fac17583699868709b67a701231755
This commit is contained in:
Dominik Adam Przybysz 2015-08-29 14:53:54 +02:00
parent 5545b67ebd
commit 82434f46a3
38 changed files with 583 additions and 422 deletions

20
mockserver-api/pom.xml Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>http-mock-server</artifactId>
<groupId>pl.touk.mockserver</groupId>
<version>1.1.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mockserver-api</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,33 @@
package pl.touk.mockserver.api.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class AddMock extends MockServerRequest {
@XmlElement(required = true)
private String name;
@XmlElement(required = true)
private String path;
@XmlElement(required = true)
private int port;
private String predicate;
private String response;
private Boolean soap;
private Integer statusCode;
private Method method;
private String responseHeaders;
}

View file

@ -0,0 +1,15 @@
package pl.touk.mockserver.api.request;
import javax.xml.bind.annotation.XmlEnum;
@XmlEnum
public enum Method {
POST,
GET,
DELETE,
PUT,
TRACE,
HEAD,
OPTIONS,
PATCH;
}

View file

@ -0,0 +1,4 @@
package pl.touk.mockserver.api.request;
public abstract class MockServerRequest {
}

View file

@ -0,0 +1,15 @@
package pl.touk.mockserver.api.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class PeekMock extends MockServerRequest{
@XmlElement(required = true)
private String name;
}

View file

@ -0,0 +1,17 @@
package pl.touk.mockserver.api.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class RemoveMock extends MockServerRequest {
@XmlElement(required = true)
private String name;
private Boolean skipReport;
}

View file

@ -0,0 +1,4 @@
@XmlAccessorType(XmlAccessType.FIELD) package pl.touk.mockserver.api.request;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

View file

@ -0,0 +1,13 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
@XmlRootElement
@Data
public class ExceptionOccured {
@XmlValue
private String message;
}

View file

@ -0,0 +1,12 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class MockAdded extends MockServerResponse {
}

View file

@ -0,0 +1,14 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import javax.xml.bind.annotation.XmlElement;
@Data
public class MockEventReport {
@XmlElement(required = true)
private MockRequestReport request;
@XmlElement(required = true)
private MockResponseReport response;
}

View file

@ -0,0 +1,16 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement(name = "mocks")
@Data
@EqualsAndHashCode(callSuper = false)
public class MockListing extends MockServerResponse {
@XmlElement(name = "mock")
private List<MockReport> mocks;
}

View file

@ -0,0 +1,16 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class MockPeeked extends MockServerResponse {
@XmlElement(name = "mockEvent")
private List<MockEventReport> mockEvents;
}

View file

@ -0,0 +1,16 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement
@Data
@EqualsAndHashCode(callSuper = false)
public class MockRemoved extends MockServerResponse {
@XmlElement(name = "mockEvent")
private List<MockEventReport> mockEvents;
}

View file

@ -0,0 +1,27 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.xml.bind.annotation.XmlElement;
@Data
public class MockReport {
@XmlElement(required = true)
private String name;
@XmlElement(required = true)
private String path;
@XmlElement(required = true)
private int port;
@XmlElement(required = true)
private String predicate;
@XmlElement(required = true)
private String response;
@XmlElement(required = true)
private String responseHeaders;
}

View file

@ -0,0 +1,24 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
@Data
public class MockRequestReport {
private String text;
@XmlElementWrapper(name = "headers")
@XmlElement(name = "param")
private List<Parameter> headers;
@XmlElementWrapper(name = "query")
@XmlElement(name = "param")
private List<Parameter> queryParams;
@XmlElementWrapper(name = "path")
@XmlElement(name = "elem")
private List<String> paths;
}

View file

@ -0,0 +1,19 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
@Data
public class MockResponseReport {
@XmlElement(required = true)
private int statusCode;
private String text;
@XmlElementWrapper(name = "headers")
@XmlElement(name = "param")
private List<Parameter> headers;
}

View file

@ -0,0 +1,4 @@
package pl.touk.mockserver.api.response;
public abstract class MockServerResponse {
}

View file

@ -0,0 +1,15 @@
package pl.touk.mockserver.api.response;
import lombok.Data;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
@Data
public class Parameter {
@XmlAttribute(required = true)
private String name;
@XmlValue
private String value;
}

View file

@ -0,0 +1,4 @@
@XmlAccessorType(XmlAccessType.FIELD) package pl.touk.mockserver.api.response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

View file

@ -0,0 +1,4 @@
AddMock
Method
PeekMock
RemoveMock

View file

@ -0,0 +1,11 @@
ExceptionOccured
MockAdded
MockEventReport
MockListing
MockPeeked
MockRemoved
MockReport
MockRequestReport
MockResponseReport
MockServerResponse
Parameter

View file

@ -25,5 +25,9 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -1,32 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
import org.apache.commons.lang3.StringEscapeUtils
@CompileStatic
@TypeChecked
class AddMockRequestData {
String name
String path
Integer port
String predicate
String response
Boolean soap
Integer statusCode
Method method
String responseHeaders
void setPredicate(String predicate) {
this.predicate = StringEscapeUtils.escapeXml11(predicate)
}
void setResponse(String response) {
this.response = StringEscapeUtils.escapeXml11(response)
}
void setResponseHeaders(String responseHeaders) {
this.responseHeaders = StringEscapeUtils.escapeXml11(responseHeaders)
}
}

View file

@ -1,12 +0,0 @@
package pl.touk.mockserver.client
enum Method {
POST,
GET,
DELETE,
PUT,
TRACE,
HEAD,
OPTIONS,
PATCH
}

View file

@ -1,18 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.TypeChecked
@EqualsAndHashCode
@CompileStatic
@TypeChecked
class MockEvent {
final MockRequest request
final MockResponse response
MockEvent(MockRequest request, MockResponse response) {
this.request = request
this.response = response
}
}

View file

@ -1,22 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
@EqualsAndHashCode
class MockRequest {
final String text
final Map<String, String> headers
final Map<String, String> query
final List<String> path
MockRequest(String text, Map<String, String> headers, Map<String, String> query, List<String> path) {
this.text = text
this.headers = headers
this.query = query
this.path = path
}
}

View file

@ -1,20 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
@EqualsAndHashCode
class MockResponse {
final int statusCode
final String text
final Map<String, String> headers
MockResponse(int statusCode, String text, Map<String, String> headers) {
this.statusCode = statusCode
this.text = text
this.headers = headers
}
}

View file

@ -1,10 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class PeekMockRequestData {
String name
}

View file

@ -1,28 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
@EqualsAndHashCode
@ToString
class RegisteredMock {
final String name
final String path
final int port
final String predicate
final String response
final String responseHeaders
RegisteredMock(String name, String path, int port, String predicate, String response, String responseHeaders) {
this.name = name
this.path = path
this.port = port
this.predicate = predicate
this.response = response
this.responseHeaders = responseHeaders
}
}

View file

@ -1,6 +1,5 @@
package pl.touk.mockserver.client
import groovy.util.slurpersupport.GPathResult
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
@ -8,112 +7,70 @@ import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import pl.touk.mockserver.api.request.AddMock
import pl.touk.mockserver.api.request.MockServerRequest
import pl.touk.mockserver.api.request.PeekMock
import pl.touk.mockserver.api.request.RemoveMock
import pl.touk.mockserver.api.response.*
import javax.xml.bind.JAXBContext
class RemoteMockServer {
private final String address
private final CloseableHttpClient client = HttpClients.createDefault()
private static final JAXBContext requestContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader)
private static
final JAXBContext responseContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader)
RemoteMockServer(String host, int port) {
address = "http://$host:$port/serverControl"
}
void addMock(AddMockRequestData addMockRequestData) {
void addMock(AddMock addMockData) {
HttpPost addMockPost = new HttpPost(address)
addMockPost.entity = buildAddMockRequest(addMockRequestData)
addMockPost.entity = buildAddMockRequest(addMockData)
CloseableHttpResponse response = client.execute(addMockPost)
GPathResult responseXml = Util.extractXmlResponse(response)
if (responseXml.name() != 'mockAdded') {
if (responseXml.text() == 'mock already registered') {
throw new MockAlreadyExists()
}
throw new InvalidMockDefinition(responseXml.text())
}
Util.extractResponse(response)
}
List<MockEvent> removeMock(String name, boolean skipReport = false) {
List<MockEventReport> removeMock(String name, boolean skipReport = false) {
HttpPost removeMockPost = new HttpPost(address)
removeMockPost.entity = buildRemoveMockRequest(new RemoveMockRequestData(name: name, skipReport: skipReport))
removeMockPost.entity = buildRemoveMockRequest(new RemoveMock(name: name, skipReport: skipReport))
CloseableHttpResponse response = client.execute(removeMockPost)
GPathResult responseXml = Util.extractXmlResponse(response)
if (responseXml.name() == 'mockRemoved') {
return responseXml.'mockEvent'.collect {
new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response))
}
}
throw new MockDoesNotExist()
MockRemoved mockRemoved = Util.extractResponse(response) as MockRemoved
return mockRemoved.mockEvents ?: []
}
List<MockEvent> peekMock(String name) {
List<MockEventReport> peekMock(String name) {
HttpPost removeMockPost = new HttpPost(address)
removeMockPost.entity = buildPeekMockRequest(new PeekMockRequestData(name: name))
removeMockPost.entity = buildPeekMockRequest(new PeekMock(name: name))
CloseableHttpResponse response = client.execute(removeMockPost)
GPathResult responseXml = Util.extractXmlResponse(response)
if (responseXml.name() == 'mockPeeked') {
return responseXml.'mockEvent'.collect {
new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response))
}
}
throw new MockDoesNotExist()
MockPeeked mockPeeked = Util.extractResponse(response) as MockPeeked
return mockPeeked.mockEvents ?: []
}
private static MockResponse mockResponseFromXml(GPathResult xml) {
return new MockResponse(xml.statusCode.text() as int, xml.text.text(), xml.headers.param.collectEntries {
[(it.@name.text()): it.text()]
})
private static StringEntity buildRemoveMockRequest(RemoveMock data) {
return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8"))
}
private static MockRequest mockRequestFromXml(GPathResult xml) {
return new MockRequest(
xml.text.text(),
xml.headers.param.collectEntries { [(it.@name.text()): it.text()] },
xml.query.param.collectEntries { [(it.@name.text()): it.text()] },
xml.path.elem*.text()
)
private static String marshallRequest(MockServerRequest data) {
StringWriter sw = new StringWriter()
requestContext.createMarshaller().marshal(data, sw)
return sw.toString()
}
private static StringEntity buildRemoveMockRequest(RemoveMockRequestData data) {
return new StringEntity("""\
<removeMock>
<name>${data.name}</name>
<skipReport>${data.skipReport}</skipReport>
</removeMock>
""", ContentType.create("text/xml", "UTF-8"))
private static StringEntity buildPeekMockRequest(PeekMock peekMock) {
return new StringEntity(marshallRequest(peekMock), ContentType.create("text/xml", "UTF-8"))
}
private static StringEntity buildPeekMockRequest(PeekMockRequestData data) {
return new StringEntity("""\
<peekMock>
<name>${data.name}</name>
</peekMock>
""", ContentType.create("text/xml", "UTF-8"))
private static StringEntity buildAddMockRequest(AddMock data) {
return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8"))
}
private static StringEntity buildAddMockRequest(AddMockRequestData data) {
return new StringEntity("""\
<addMock>
<name>${data.name}</name>
<path>${data.path}</path>
<port>${data.port}</port>
${data.predicate ? "<predicate>${data.predicate}</predicate>" : ''}
${data.response ? "<response>${data.response}</response>" : ''}
${data.soap != null ? "<soap>${data.soap}</soap>" : ''}
${data.statusCode ? "<statusCode>${data.statusCode}</statusCode>" : ''}
${data.method ? "<method>${data.method}</method>" : ''}
${data.responseHeaders ? "<responseHeaders>${data.responseHeaders}</responseHeaders>" : ''}
</addMock>
""", ContentType.create("text/xml", "UTF-8"))
}
List<RegisteredMock> listMocks() {
List<MockReport> listMocks() {
HttpGet get = new HttpGet(address)
CloseableHttpResponse response = client.execute(get)
GPathResult xml = Util.extractXmlResponse(response)
if (xml.name() == 'mocks') {
return xml.mock.collect {
new RegisteredMock(it.name.text(), it.path.text(), it.port.text() as int, it.predicate.text(), it.response.text(), it.responseHeaders.text())
}
}
return []
MockListing mockListing = Util.extractResponse(response) as MockListing
return mockListing.mocks
}
}

View file

@ -1,11 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class RemoveMockRequestData {
String name
boolean skipReport = false
}

View file

@ -7,15 +7,42 @@ import groovy.util.slurpersupport.GPathResult
import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.util.EntityUtils
import pl.touk.mockserver.api.response.ExceptionOccured
import pl.touk.mockserver.api.response.MockAdded
import pl.touk.mockserver.api.response.MockServerResponse
import javax.xml.bind.JAXBContext
@CompileStatic
@TypeChecked
class Util {
private static
final JAXBContext responseContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader)
static GPathResult extractXmlResponse(CloseableHttpResponse response) {
return new XmlSlurper().parseText(extractStringResponse(response))
}
static String extractStringResponse(CloseableHttpResponse response) {
HttpEntity entity = response.entity
GPathResult xml = new XmlSlurper().parseText(EntityUtils.toString(entity, 'UTF-8'))
String responseString = EntityUtils.toString(entity, 'UTF-8')
EntityUtils.consumeQuietly(entity)
return xml
return responseString
}
static MockServerResponse extractResponse(CloseableHttpResponse response) {
String responseString = extractStringResponse(response)
if (response.statusLine.statusCode == 200) {
return responseContext.createUnmarshaller().unmarshal(new StringReader(responseString)) as MockServerResponse
}
ExceptionOccured exceptionOccured = responseContext.createUnmarshaller().unmarshal(new StringReader(responseString)) as ExceptionOccured
String message = exceptionOccured.message
if (message == 'mock already registered') {
throw new MockAlreadyExists()
}
if (message == 'mock not registered') {
throw new MockDoesNotExist()
}
throw new InvalidMockDefinition(message)
}
static String soap(String request) {
@ -26,13 +53,11 @@ class Util {
}
static Object extractJsonResponse(CloseableHttpResponse response) {
HttpEntity entity = response.entity
Object json = new JsonSlurper().parseText(EntityUtils.toString(entity, 'UTF-8'))
EntityUtils.consumeQuietly(entity)
return json
return new JsonSlurper().parseText(extractStringResponse(response))
}
static void consumeResponse(CloseableHttpResponse response) {
EntityUtils.consumeQuietly(response.entity)
}
}

View file

@ -7,6 +7,11 @@ import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
import pl.touk.mockserver.api.request.AddMock
import pl.touk.mockserver.api.request.Method
import pl.touk.mockserver.api.response.MockEventReport
import pl.touk.mockserver.api.response.MockReport
import pl.touk.mockserver.api.response.Parameter
import pl.touk.mockserver.client.*
import pl.touk.mockserver.server.HttpMockServer
import spock.lang.Shared
@ -33,7 +38,7 @@ class MockServerIntegrationTest extends Specification {
def "should add working rest mock on endpoint"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -54,7 +59,7 @@ class MockServerIntegrationTest extends Specification {
def "should add working rest mock on endpoint with utf"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRestUtf',
path: 'testEndpoint',
port: 9999,
@ -76,7 +81,7 @@ class MockServerIntegrationTest extends Specification {
def "should add soap mock on endpoint"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -102,7 +107,7 @@ class MockServerIntegrationTest extends Specification {
then:
thrown(MockDoesNotExist)
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -120,7 +125,7 @@ class MockServerIntegrationTest extends Specification {
def "should not add mock with existing name"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -129,7 +134,7 @@ class MockServerIntegrationTest extends Specification {
soap: true
))
when:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint2',
port: 9998,
@ -143,7 +148,7 @@ class MockServerIntegrationTest extends Specification {
def "should not add mock with empty name"() {
when:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: '',
path: 'testEndpoint2',
port: 9998,
@ -157,7 +162,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock after deleting old mock with the same name"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -168,7 +173,7 @@ class MockServerIntegrationTest extends Specification {
and:
remoteMockServer.removeMock('testSoap') == []
and:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -180,14 +185,14 @@ class MockServerIntegrationTest extends Specification {
def "should add simultaneously working post and rest mocks with the same predicate and endpoint nad port"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
predicate: '''{req -> req.xml.name() == 'request'}''',
response: '''{req -> "<goodResponseRest-${req.xml.name()}/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint',
port: 9999,
@ -215,14 +220,14 @@ class MockServerIntegrationTest extends Specification {
@Unroll
def "should dispatch rest mocks when second on #name"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest1',
path: 'test1',
port: 9999,
predicate: '''{req -> req.xml.name() == 'request1'}''',
response: '''{req -> "<goodResponseRest1/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: secondPath,
port: secondPort,
@ -254,7 +259,7 @@ class MockServerIntegrationTest extends Specification {
@Unroll
def "should dispatch rest mock with response code"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest1',
path: 'test1',
port: 9999,
@ -276,7 +281,7 @@ class MockServerIntegrationTest extends Specification {
def "should return response code 404 and error body the same as request body when mocks does not apply"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest1',
path: 'test1',
port: 9999,
@ -295,7 +300,7 @@ class MockServerIntegrationTest extends Specification {
def "should inform that there was problem during adding mock - invalid port"() {
when:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testSoap',
path: 'testEndpoint2',
port: -1,
@ -309,13 +314,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with get method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -332,13 +337,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with trace method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -355,13 +360,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with head method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -377,13 +382,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with options method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -399,13 +404,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with put method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'test1',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'test1',
port: 9999,
@ -424,13 +429,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with delete method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'test1',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'test1',
port: 9999,
@ -447,13 +452,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with patch method"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'test1',
port: 9999,
response: '''{_ -> "<defaultResponse/>"}'''
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'test1',
port: 9999,
@ -472,7 +477,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that return headers"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -492,7 +497,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when certain request headers exists"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -521,7 +526,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when certain query params exists"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -545,7 +550,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when request has specific body"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -570,7 +575,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock which response json to json"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -595,7 +600,7 @@ class MockServerIntegrationTest extends Specification {
def "should get list mocks"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9998,
@ -603,27 +608,27 @@ class MockServerIntegrationTest extends Specification {
response: '''{ req -> '<response/>' }''',
responseHeaders: '{ _ -> [a: "b"] }'
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest4',
path: 'testEndpoint',
port: 9999
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest3',
path: 'testEndpoint2',
port: 9999
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest5',
path: 'testEndpoint',
port: 9999
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest6',
path: 'testEndpoint2',
port: 9999
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999
@ -631,17 +636,17 @@ class MockServerIntegrationTest extends Specification {
remoteMockServer.removeMock('testRest5')
expect:
remoteMockServer.listMocks() == [
new RegisteredMock('testRest', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'),
new RegisteredMock('testRest2', 'testEndpoint', 9998, '''{ req -> req.xml.name() == 'request1'}''', '''{ req -> '<response/>' }''', '{ _ -> [a: "b"] }'),
new RegisteredMock('testRest3', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'),
new RegisteredMock('testRest4', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'),
new RegisteredMock('testRest6', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }')
new MockReport(name: 'testRest', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new MockReport(name: 'testRest2', path: 'testEndpoint', port: 9998, predicate: '''{ req -> req.xml.name() == 'request1'}''', response: '''{ req -> '<response/>' }''', responseHeaders: '{ _ -> [a: "b"] }'),
new MockReport(name: 'testRest3', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new MockReport(name: 'testRest4', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new MockReport(name: 'testRest6', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }')
]
}
def "should add mock accepts path certain path params"() {
given:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -664,7 +669,7 @@ class MockServerIntegrationTest extends Specification {
def "should get mock report when deleting mock"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -674,7 +679,7 @@ class MockServerIntegrationTest extends Specification {
responseHeaders: '''{req -> ['aaa':'14']}''',
soap: false
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -706,40 +711,40 @@ class MockServerIntegrationTest extends Specification {
GPathResult restPostResponse3 = Util.extractXmlResponse(response3)
restPostResponse3.name() == 'goodResponseRest'
when:
List<MockEvent> mockEvents1 = remoteMockServer.removeMock('testRest')
List<MockEventReport> mockEvents1 = remoteMockServer.removeMock('testRest')
then:
mockEvents1.size() == 2
mockEvents1[0].request.text == '<request/>'
!mockEvents1[0].request.headers?.keySet()?.empty
mockEvents1[0].request.query == [:]
mockEvents1[0].request.path == ['testEndpoint']
!mockEvents1[0].response.headers?.keySet()?.empty
!mockEvents1[0].request.headers?.empty
mockEvents1[0].request.queryParams == []
mockEvents1[0].request.paths == ['testEndpoint']
!mockEvents1[0].response.headers?.empty
mockEvents1[0].response.text == '<goodResponseRest-request/>'
mockEvents1[0].response.statusCode == 201
mockEvents1[1].request.text == '<request15/>'
!mockEvents1[1].request.headers?.keySet()?.empty
mockEvents1[1].request.query == [:]
mockEvents1[1].request.path == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.keySet()?.empty
!mockEvents1[1].request.headers?.empty
mockEvents1[1].request.queryParams == []
mockEvents1[1].request.paths == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.empty
mockEvents1[1].response.text == '<goodResponseRest-request15/>'
mockEvents1[1].response.statusCode == 201
when:
List<MockEvent> mockEvents2 = remoteMockServer.removeMock('testRest2')
List<MockEventReport> mockEvents2 = remoteMockServer.removeMock('testRest2')
then:
mockEvents2.size() == 1
mockEvents2[0].request.text == '<reqXYZ/>'
!mockEvents2[0].request.headers?.keySet()?.empty
mockEvents2[0].request.query == [id: '123']
mockEvents2[0].request.path == ['testEndpoint']
mockEvents2[0].response.headers.aaa == '15'
!mockEvents2[0].request.headers?.empty
mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')]
mockEvents2[0].request.paths == ['testEndpoint']
mockEvents2[0].response.headers.find { it.name == 'aaa' }?.value == '15'
mockEvents2[0].response.text == '<goodResponseRest/>'
mockEvents2[0].response.statusCode == 202
}
def "should get mock report when peeking mock"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,
@ -749,7 +754,7 @@ class MockServerIntegrationTest extends Specification {
responseHeaders: '''{req -> ['aaa':'14']}''',
soap: false
))
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest2',
path: 'testEndpoint',
port: 9999,
@ -781,33 +786,33 @@ class MockServerIntegrationTest extends Specification {
GPathResult restPostResponse3 = Util.extractXmlResponse(response3)
restPostResponse3.name() == 'goodResponseRest'
when:
List<MockEvent> mockEvents1 = remoteMockServer.peekMock('testRest')
List<MockEventReport> mockEvents1 = remoteMockServer.peekMock('testRest')
then:
mockEvents1.size() == 2
mockEvents1[0].request.text == '<request/>'
!mockEvents1[0].request.headers?.keySet()?.empty
mockEvents1[0].request.query == [:]
mockEvents1[0].request.path == ['testEndpoint']
!mockEvents1[0].response.headers?.keySet()?.empty
!mockEvents1[0].request.headers?.empty
mockEvents1[0].request.queryParams == []
mockEvents1[0].request.paths == ['testEndpoint']
!mockEvents1[0].response.headers?.empty
mockEvents1[0].response.text == '<goodResponseRest-request/>'
mockEvents1[0].response.statusCode == 201
mockEvents1[1].request.text == '<request15/>'
!mockEvents1[1].request.headers?.keySet()?.empty
mockEvents1[1].request.query == [:]
mockEvents1[1].request.path == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.keySet()?.empty
!mockEvents1[1].request.headers?.empty
mockEvents1[1].request.queryParams == []
mockEvents1[1].request.paths == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.empty
mockEvents1[1].response.text == '<goodResponseRest-request15/>'
mockEvents1[1].response.statusCode == 201
when:
List<MockEvent> mockEvents2 = remoteMockServer.peekMock('testRest2')
List<MockEventReport> mockEvents2 = remoteMockServer.peekMock('testRest2')
then:
mockEvents2.size() == 1
mockEvents2[0].request.text == '<reqXYZ/>'
!mockEvents2[0].request.headers?.keySet()?.empty
mockEvents2[0].request.query == [id: '123']
mockEvents2[0].request.path == ['testEndpoint']
mockEvents2[0].response.headers.aaa == '15'
!mockEvents2[0].request.headers?.empty
mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')]
mockEvents2[0].request.paths == ['testEndpoint']
mockEvents2[0].response.headers.find {it.name == 'aaa'}?.value == '15'
mockEvents2[0].response.text == '<goodResponseRest/>'
mockEvents2[0].response.statusCode == 202
}
@ -815,7 +820,7 @@ class MockServerIntegrationTest extends Specification {
@Unroll
def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() {
expect:
remoteMockServer.addMock(new AddMockRequestData(
remoteMockServer.addMock(new AddMock(
name: 'testRest',
path: 'testEndpoint',
port: 9999,

View file

@ -1,34 +1,40 @@
package pl.touk.mockserver.tests
import groovy.util.slurpersupport.GPathResult
import org.apache.http.client.HttpClient
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.HttpClients
import pl.touk.mockserver.client.AddMockRequestData
import pl.touk.mockserver.api.request.AddMock
import pl.touk.mockserver.client.RemoteMockServer
import pl.touk.mockserver.client.Util
import pl.touk.mockserver.server.HttpMockServer
import spock.lang.Specification
import spock.lang.Timeout
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class ServerMockPT extends Specification {
@Timeout(value = 60)
def "should handle many request simultaneously"() {
given:
HttpClient client = HttpClients.createDefault()
HttpMockServer httpMockServer = new HttpMockServer()
RemoteMockServer controlServerClient = new RemoteMockServer("localhost", 9999)
HttpClient client = HttpClients.createDefault()
int requestAmount = 1000
GPathResult[] responses = new GPathResult[requestAmount]
Thread[] threads = new Thread[requestAmount]
String[] responses = new String[requestAmount]
ExecutorService executorService = Executors.newFixedThreadPool(20)
for (int i = 0; i < requestAmount; ++i) {
int current = i
threads[i] = new Thread({
executorService.submit {
int endpointNumber = current % 10
int port = 9000 + (current % 7)
controlServerClient.addMock(new AddMockRequestData(
controlServerClient.addMock(new AddMock(
name: "testRest$current",
path: "testEndpoint$endpointNumber",
port: port,
@ -38,15 +44,14 @@ class ServerMockPT extends Specification {
HttpPost restPost = new HttpPost("http://localhost:$port/testEndpoint$endpointNumber")
restPost.entity = new StringEntity("<request$current/>", ContentType.create("text/xml", "UTF-8"))
CloseableHttpResponse response = client.execute(restPost)
responses[current] = Util.extractXmlResponse(response)
assert controlServerClient.removeMock("testRest$current").size() == 1
})
responses[current] = Util.extractStringResponse(response)
assert controlServerClient.removeMock("testRest$current", false).size() == 1
}
}
when:
threads*.start()
Thread.sleep(60000)
executorService.awaitTermination(60, TimeUnit.SECONDS)
then:
responses.eachWithIndex { res, i -> assert res.name() == "goodResponse$i" }
responses.eachWithIndex { res, i -> assert new XmlSlurper().parseText(res).name() == "goodResponse$i" as String }
cleanup:
httpMockServer.stop()
}

View file

@ -26,6 +26,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
</dependency>
</dependencies>
<build>

View file

@ -2,9 +2,13 @@ package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange
import groovy.util.logging.Slf4j
import groovy.util.slurpersupport.GPathResult
import groovy.xml.MarkupBuilder
import pl.touk.mockserver.api.request.AddMock
import pl.touk.mockserver.api.request.MockServerRequest
import pl.touk.mockserver.api.request.PeekMock
import pl.touk.mockserver.api.request.RemoveMock
import pl.touk.mockserver.api.response.*
import javax.xml.bind.JAXBContext
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet
@ -17,6 +21,9 @@ class HttpMockServer {
private final List<HttpServerWraper> childServers = new CopyOnWriteArrayList<>()
private final Set<String> mockNames = new CopyOnWriteArraySet<>()
private static
final JAXBContext requestJaxbContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader)
HttpMockServer(int port = 9999) {
httpServerWraper = new HttpServerWraper(port)
@ -26,12 +33,12 @@ class HttpMockServer {
if (ex.requestMethod == 'GET') {
listMocks(ex)
} else if (ex.requestMethod == 'POST') {
GPathResult request = new XmlSlurper().parse(ex.requestBody)
if (request.name() == 'addMock') {
MockServerRequest request = requestJaxbContext.createUnmarshaller().unmarshal(ex.requestBody) as MockServerRequest
if (request instanceof AddMock) {
addMock(request, ex)
} else if (request.name() == 'removeMock') {
} else if (request instanceof RemoveMock) {
removeMock(request, ex)
} else if (request.name() == 'peekMock') {
} else if (request instanceof PeekMock) {
peekMock(request, ex)
} else {
throw new RuntimeException('Unknown request')
@ -46,29 +53,26 @@ class HttpMockServer {
}
void listMocks(HttpExchange ex) {
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mocks {
listMocks().each {
Mock mock ->
builder.mock {
name mock.name
path mock.path
port mock.port
predicate mock.predicateClosureText
response mock.responseClosureText
responseHeaders mock.responseHeadersClosureText
MockListing mockListing = new MockListing(
mocks: listMocks().collect {
new MockReport(
name: it.name,
path: it.path,
port: it.port,
predicate: it.predicateClosureText,
response: it.responseClosureText,
responseHeaders: it.responseHeadersClosureText
)
}
}
}
createResponse(ex, sw.toString(), 200)
)
createResponse(ex, mockListing, 200)
}
Set<Mock> listMocks() {
return childServers.collect { it.mocks }.flatten() as TreeSet
return childServers.collect { it.mocks }.flatten() as TreeSet<Mock>
}
private void addMock(GPathResult request, HttpExchange ex) {
private void addMock(AddMock request, HttpExchange ex) {
String name = request.name
if (name in mockNames) {
throw new RuntimeException('mock already registered')
@ -77,13 +81,13 @@ class HttpMockServer {
HttpServerWraper child = getOrCreateChildServer(mock.port)
child.addMock(mock)
mockNames << name
createResponse(ex, '<mockAdded/>', 200)
createResponse(ex, new MockAdded(), 200)
}
private static Mock mockFromRequest(GPathResult request) {
private static Mock mockFromRequest(AddMock request) {
String name = request.name
String mockPath = request.path
int mockPort = Integer.valueOf(request.port as String)
int mockPort = request.port
Mock mock = new Mock(name, mockPath, mockPort)
mock.predicate = request.predicate
mock.response = request.response
@ -103,85 +107,62 @@ class HttpMockServer {
return child
}
private void removeMock(GPathResult request, HttpExchange ex) {
private void removeMock(RemoveMock request, HttpExchange ex) {
String name = request.name
boolean skipReport = Boolean.parseBoolean(request.skipReport?.toString() ?: 'false')
boolean skipReport = request.skipReport ?: false
if (!(name in mockNames)) {
throw new RuntimeException('mock not registered')
}
log.info("Removing mock $name")
List<MockEvent> mockEvents = skipReport ? [] : childServers.collect { it.removeMock(name) }.flatten()
List<MockEvent> mockEvents = skipReport ? [] : childServers.collect {
it.removeMock(name)
}.flatten() as List<MockEvent>
mockNames.remove(name)
createResponse(ex, createMockRemovedResponse(mockEvents), 200)
MockRemoved mockRemoved = new MockRemoved(
mockEvents: createMockEventReports(mockEvents)
)
createResponse(ex, mockRemoved, 200)
}
private void peekMock(GPathResult request, HttpExchange ex) {
private static List<MockEventReport> createMockEventReports(List<MockEvent> mockEvents) {
return mockEvents.collect {
new MockEventReport(
request: new MockRequestReport(
text: it.request.text,
headers: it.request.headers.collect {
new Parameter(name: it.key, value: it.value)
},
queryParams: it.request.query.collect {
new Parameter(name: it.key, value: it.value)
},
paths: it.request.path
),
response: new MockResponseReport(
statusCode: it.response.statusCode,
text: it.response.text,
headers: it.response.headers.collect {
new Parameter(name: it.key, value: it.value)
}
)
)
}
}
private void peekMock(PeekMock request, HttpExchange ex) {
String name = request.name
if (!(name in mockNames)) {
throw new RuntimeException('mock not registered')
}
log.trace("Peeking mock $name")
List<MockEvent> mockEvents = childServers.collect { it.peekMock(name) }.flatten()
createResponse(ex, createMockPeekedResponse(mockEvents), 200)
}
private static String createMockRemovedResponse(List<MockEvent> mockEvents) {
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mockRemoved {
mockEventsToXml(mockEvents, builder)
}
return sw.toString()
}
private static String createMockPeekedResponse(List<MockEvent> mockEvents) {
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mockPeeked {
mockEventsToXml(mockEvents, builder)
}
return sw.toString()
}
private static void mockEventsToXml(List<MockEvent> events, MarkupBuilder builder) {
events.each { MockEvent event ->
builder.mockEvent {
builder.request {
text event.request.text
headers {
event.request.headers.each {
builder.param(name: it.key, it.value)
}
}
query {
event.request.query.each {
builder.param(name: it.key, it.value)
}
}
path {
event.request.path.each {
builder.elem it
}
}
}
builder.response {
text event.response.text
headers {
event.response.headers.each {
builder.param(name: it.key, it.value)
}
}
statusCode event.response.statusCode
}
}
}
List<MockEvent> mockEvents = childServers.collect { it.peekMock(name) }.flatten() as List<MockEvent>
MockPeeked mockPeeked = new MockPeeked(
mockEvents: createMockEventReports(mockEvents)
)
createResponse(ex, mockPeeked, 200)
}
private static void createErrorResponse(HttpExchange ex, Exception e) {
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.exceptionOccured e.message
createResponse(ex, sw.toString(), 400)
createResponse(ex, new ExceptionOccured(message: e.message), 400)
}
void stop() {

View file

@ -1,14 +1,32 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange
import pl.touk.mockserver.api.response.MockAdded
import javax.xml.bind.JAXBContext
class Util {
static void createResponse(HttpExchange ex, String response, int statusCode) {
byte[] responseBytes = response ? response.getBytes('UTF-8') : new byte[0]
private static
final JAXBContext responseJaxbContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader)
static void createResponse(HttpExchange ex, Object response, int statusCode) {
String responseString = marshall(response)
createResponse(ex, responseString, statusCode)
}
static void createResponse(HttpExchange ex, String responseString, int statusCode) {
byte[] responseBytes = responseString ? responseString.getBytes('UTF-8') : new byte[0]
ex.sendResponseHeaders(statusCode, responseBytes.length ?: -1)
if (response) {
if (responseString) {
ex.responseBody << responseBytes
ex.responseBody.close()
}
}
private static String marshall(Object response) {
StringWriter sw = new StringWriter()
responseJaxbContext.createMarshaller().marshal(response, sw)
return sw.toString()
}
}

12
pom.xml
View file

@ -10,6 +10,7 @@
<module>mockserver-client</module>
<module>mockserver</module>
<module>mockserver-tests</module>
<module>mockserver-api</module>
</modules>
<properties>
@ -22,6 +23,7 @@
<commons-lang3.version>3.3.2</commons-lang3.version>
<slf4j-api.version>1.7.7</slf4j-api.version>
<logback-classic.version>1.0.13</logback-classic.version>
<lombok.version>1.16.6</lombok.version>
</properties>
<scm>
@ -63,6 +65,16 @@
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>