Split project to modules

This commit is contained in:
Dominik Adam Przybysz 2014-12-14 21:25:39 +01:00
parent 1bbaf72084
commit 73b3630e2f
26 changed files with 145 additions and 34 deletions

29
mockserver/pom.xml Normal file
View file

@ -0,0 +1,29 @@
<?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.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mockserver</artifactId>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,70 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange
import groovy.transform.PackageScope
import groovy.util.logging.Slf4j
import java.util.concurrent.CopyOnWriteArrayList
@Slf4j
@PackageScope
class ContextExecutor {
private final HttpServerWraper httpServerWraper
final String path
private final List<Mock> mocks
ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) {
this.httpServerWraper = httpServerWraper
this.path = '/' + initialMock.path
this.mocks = new CopyOnWriteArrayList<>([initialMock])
httpServerWraper.createContext(path, {
HttpExchange ex ->
MockRequest request = new MockRequest(ex.requestBody.text, ex.requestHeaders, ex.requestURI)
log.info("Mock received input")
for (Mock mock : mocks) {
try {
if (mock.match(ex.requestMethod, request)) {
MockResponse httpResponse = mock.apply(request)
fillExchange(ex, httpResponse)
return
}
} catch (Exception e) {
e.printStackTrace()
}
}
Util.createResponse(ex, request.text, 404)
})
}
String getPath() {
return path.substring(1)
}
String getContextPath() {
return path
}
private static void fillExchange(HttpExchange httpExchange, MockResponse response) {
response.headers.each {
httpExchange.responseHeaders.add(it.key, it.value)
}
Util.createResponse(httpExchange, response.text, response.statusCode)
}
List<MockEvent> removeMock(String name) {
Mock mock = mocks.find { it.name == name }
if (mock) {
mocks.remove(mock)
return mock.history
}
return []
}
void addMock(Mock mock) {
mocks << mock
}
List<Mock> getMocks() {
return mocks
}
}

View file

@ -0,0 +1,158 @@
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 java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet
import static pl.touk.mockserver.server.Util.createResponse
@Slf4j
class HttpMockServer {
private final HttpServerWraper httpServerWraper
private final List<HttpServerWraper> childServers = new CopyOnWriteArrayList<>()
private final Set<String> mockNames = new CopyOnWriteArraySet<>()
HttpMockServer(int port = 9999) {
httpServerWraper = new HttpServerWraper(port)
httpServerWraper.createContext('/serverControl', {
HttpExchange ex ->
try {
if (ex.requestMethod == 'GET') {
listMocks(ex)
} else if (ex.requestMethod == 'POST') {
GPathResult request = new XmlSlurper().parse(ex.requestBody)
if (request.name() == 'addMock') {
addMock(request, ex)
} else if (request.name() == 'removeMock') {
removeMock(request, ex)
} else {
throw new RuntimeException('Unknown request')
}
} else {
throw new RuntimeException('Unknown request')
}
} catch (Exception e) {
createErrorResponse(ex, e)
}
})
}
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
}
}
}
createResponse(ex, sw.toString(), 200)
}
Set<Mock> listMocks() {
return childServers.collect { it.mocks }.flatten() as TreeSet
}
private void addMock(GPathResult request, HttpExchange ex) {
String name = request.name
if (name in mockNames) {
throw new RuntimeException('mock already registered')
}
Mock mock = mockFromRequest(request)
HttpServerWraper child = getOrCreateChildServer(mock.port)
child.addMock(mock)
mockNames << name
createResponse(ex, '<mockAdded/>', 200)
}
private static Mock mockFromRequest(GPathResult request) {
String name = request.name
String mockPath = request.path
int mockPort = Integer.valueOf(request.port as String)
Mock mock = new Mock(name, mockPath, mockPort)
mock.predicate = request.predicate
mock.response = request.response
mock.soap = request.soap
mock.statusCode = request.statusCode
mock.method = request.method
mock.responseHeaders = request.responseHeaders
return mock
}
private HttpServerWraper getOrCreateChildServer(int mockPort) {
HttpServerWraper child = childServers.find { it.port == mockPort }
if (!child) {
child = new HttpServerWraper(mockPort)
childServers << child
}
return child
}
private void removeMock(GPathResult request, HttpExchange ex) {
String name = request.name
if (!(name in mockNames)) {
throw new RuntimeException('mock not registered')
}
log.info("Removing mock $name")
List<MockEvent> mockEvents = childServers.collect { it.removeMock(name) }.flatten()
mockNames.remove(name)
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mockRemoved {
mockEvents.each { MockEvent m ->
builder.mockEvent {
builder.request {
text m.request.text
headers {
m.request.headers.each {
builder.param(name: it.key, it.value)
}
}
query {
m.request.query.each {
builder.param(name: it.key, it.value)
}
}
path {
m.request.path.each {
builder.elem it
}
}
}
builder.response {
text m.response.text
headers {
m.response.headers.each {
builder.param(name: it.key, it.value)
}
}
statusCode m.response.statusCode
}
}
}
}
createResponse(ex, sw.toString(), 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)
}
void stop() {
childServers.each { it.stop() }
httpServerWraper.stop()
}
}

View file

@ -0,0 +1,54 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
import groovy.transform.PackageScope
import groovy.util.logging.Slf4j
import java.util.concurrent.Executors
@Slf4j
@PackageScope
class HttpServerWraper {
private final HttpServer httpServer
final int port
private List<ContextExecutor> executors = []
HttpServerWraper(int port) {
this.port = port
InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port)
httpServer = HttpServer.create(addr, 0)
httpServer.executor = Executors.newCachedThreadPool()
log.info("Http server statrting on port $port...")
httpServer.start()
log.info('Http server is started')
}
void createContext(String context, HttpHandler handler) {
httpServer.createContext(context, handler)
}
void addMock(Mock mock) {
ContextExecutor executor = executors.find { it.path == mock.path }
if (executor) {
executor.addMock(mock)
} else {
executors << new ContextExecutor(this, mock)
}
log.info("Added mock ${mock.name}")
}
void stop() {
executors.each { httpServer.removeContext(it.contextPath) }
httpServer.stop(0)
}
List<MockEvent> removeMock(String name) {
return executors.collect { it.removeMock(name) }.flatten()
}
List<Mock> getMocks() {
return executors.collect { it.mocks }.flatten()
}
}

View file

@ -0,0 +1,20 @@
package pl.touk.mockserver.server
import groovy.util.logging.Slf4j
@Slf4j
class Main {
static void main(String[] args) {
HttpMockServer httpMockServer = new HttpMockServer()
Runtime.runtime.addShutdownHook(new Thread({
log.info('Http server is stopping...')
httpMockServer.stop()
log.info('Http server is stopped')
} as Runnable))
while (true) {
Thread.sleep(10000)
}
}
}

View file

@ -0,0 +1,96 @@
package pl.touk.mockserver.server
import groovy.transform.EqualsAndHashCode
import groovy.transform.PackageScope
import groovy.util.logging.Slf4j
import java.util.concurrent.CopyOnWriteArrayList
@PackageScope
@EqualsAndHashCode(excludes = ["counter"])
@Slf4j
class Mock implements Comparable<Mock> {
final String name
final String path
final int port
Closure predicate = { _ -> true }
Closure response = { _ -> '' }
Closure responseHeaders = { _ -> [:] }
boolean soap = false
int statusCode = 200
String method = 'POST'
int counter = 0
final List<MockEvent> history = new CopyOnWriteArrayList<>()
Mock(String name, String path, int port) {
if (!(name)) {
throw new RuntimeException("Mock name must be given")
}
this.name = name
this.path = path
this.port = port
}
boolean match(String method, MockRequest request) {
return this.method == method && predicate(request)
}
MockResponse apply(MockRequest request) {
log.debug("Mock $name invoked")
++counter
String responseText = response(request)
String response = soap ? wrapSoap(responseText) : responseText
Map<String, String> headers = responseHeaders(request)
MockResponse mockResponse = new MockResponse(statusCode, response, headers)
history << new MockEvent(request, mockResponse)
return mockResponse
}
private static String wrapSoap(String request) {
"""<?xml version='1.0' encoding='UTF-8'?>
<soap-env:Envelope xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/' xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<soap-env:Body>${request}</soap-env:Body>
</soap-env:Envelope>"""
}
void setPredicate(String predicate) {
if (predicate) {
this.predicate = Eval.me(predicate) as Closure
}
}
void setResponse(String response) {
if (response) {
this.response = Eval.me(response) as Closure
}
}
void setSoap(String soap) {
if (soap) {
this.soap = Boolean.valueOf(soap)
}
}
void setStatusCode(String statusCode) {
if (statusCode) {
this.statusCode = Integer.valueOf(statusCode)
}
}
void setMethod(String method) {
if (method) {
this.method = method
}
}
void setResponseHeaders(String responseHeaders) {
if (responseHeaders) {
this.responseHeaders = Eval.me(responseHeaders) as Closure
}
}
@Override
int compareTo(Mock o) {
return name.compareTo(o.name)
}
}

View file

@ -0,0 +1,14 @@
package pl.touk.mockserver.server
import groovy.transform.PackageScope
@PackageScope
class MockEvent {
final MockRequest request
final MockResponse response
MockEvent(MockRequest request, MockResponse response) {
this.request = request
this.response = response
}
}

View file

@ -0,0 +1,72 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.Headers
import groovy.json.JsonSlurper
import groovy.transform.PackageScope
import groovy.util.slurpersupport.GPathResult
@PackageScope
class MockRequest {
final String text
final Map<String, String> headers
final Map<String, String> query
final GPathResult xml
final GPathResult soap
final Object json
final List<String> path
MockRequest(String text, Headers headers, URI uri) {
this.text = text
this.headers = headersToMap(headers)
this.query = queryParamsToMap(uri.query)
this.xml = inputToXml(text)
this.soap = inputToSoap(xml)
this.json = inputToJson(text)
this.path = uri.path.split('/').findAll()
}
private static GPathResult inputToXml(String text) {
try {
return new XmlSlurper().parseText(text)
} catch (Exception _) {
return null
}
}
private static GPathResult inputToSoap(GPathResult xml) {
try {
if (xml.name() == 'Envelope' && xml.Body.size() > 0) {
return getSoapBodyContent(xml)
} else {
return null
}
} catch (Exception _) {
return null
}
}
private static GPathResult getSoapBodyContent(GPathResult xml) {
return xml.Body.'**'[1]
}
private static Object inputToJson(String text) {
try {
return new JsonSlurper().parseText(text)
} catch (Exception _) {
return null
}
}
private static Map<String, String> queryParamsToMap(String query) {
return query?.split('&')?.collectEntries {
String[] keyValue = it.split('='); [(keyValue[0]): keyValue[1]]
} ?: [:]
}
private static Map<String, String> headersToMap(Headers headers) {
return headers.collectEntries {
[it.key.toLowerCase(), it.value.join(',')]
}
}
}

View file

@ -0,0 +1,16 @@
package pl.touk.mockserver.server
import groovy.transform.PackageScope
@PackageScope
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

@ -0,0 +1,13 @@
package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange
class Util {
static void createResponse(HttpExchange ex, String response, int statusCode) {
ex.sendResponseHeaders(statusCode, response ? response.length() : -1)
if (response) {
ex.responseBody << response
ex.responseBody.close()
}
}
}