diff --git a/.gitleaks.toml b/.gitleaks.toml
new file mode 100644
index 00000000000..9f39311824d
--- /dev/null
+++ b/.gitleaks.toml
@@ -0,0 +1,5 @@
+[allowlist]
+ description = "Global Allowlist"
+ paths = [
+ '''junit\/mockwebserver\/src\/main\/resources\/ssl\/fabric8$'''
+ ]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb1762dd85b..8aa3ab991f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
* Fix #5373: Gradle base API based on v8.2.1
#### New Features
+* Fix #5430: Mock Web Server migrated to this repository
#### _**Note**_: Breaking changes
* Fix #5343: Removed `io.fabric8.kubernetes.model.annotation.PrinterColumn`, use `io.fabric8.crd.generator.annotation.PrinterColumn`
diff --git a/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/KubernetesCrudDispatcherHandler.java b/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/KubernetesCrudDispatcherHandler.java
index bb1c41305b0..6cc79611aa1 100644
--- a/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/KubernetesCrudDispatcherHandler.java
+++ b/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/KubernetesCrudDispatcherHandler.java
@@ -52,7 +52,7 @@ default MockResponse handle(RecordedRequest request) throws KubernetesCrudDispat
default void validatePath(AttributeSet query, JsonNode updatedResource) throws KubernetesCrudDispatcherException {
// metadata.name
final String updatedName = updatedResource.path(METADATA).path(NAME).asText();
- final String pathName = query.getAttribute(NAME).getValue().toString();
+ final String pathName = query.getAttribute(NAME).getValues().iterator().next().toString();
if (!updatedName.isEmpty() && !pathName.equals(updatedName)) {
throw new KubernetesCrudDispatcherException(
"the name of the object (" + updatedName + ") does not match the name on the URL (" + pathName + ")",
@@ -62,7 +62,7 @@ default void validatePath(AttributeSet query, JsonNode updatedResource) throws K
// metadata.namespace
if (query.getAttribute(NAMESPACE) != null) {
final String updatedNamespace = updatedResource.path(METADATA).path(NAMESPACE).asText();
- final String pathNamespace = query.getAttribute(NAMESPACE).getValue().toString();
+ final String pathNamespace = query.getAttribute(NAMESPACE).getValues().iterator().next().toString();
if (!updatedNamespace.isEmpty() && !updatedNamespace.equals(pathNamespace)) {
throw new KubernetesCrudDispatcherException(
"the namespace of the object (" + updatedNamespace + ") does not match the namespace on the URL ("
diff --git a/junit/mockwebserver/pom.xml b/junit/mockwebserver/pom.xml
new file mode 100644
index 00000000000..aeb1d584df8
--- /dev/null
+++ b/junit/mockwebserver/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ 4.0.0
+
+ io.fabric8
+ kubernetes-client-project
+ 6.9-SNAPSHOT
+ ../../pom.xml
+
+
+ mockwebserver
+ jar
+
+ Fabric8 :: Mock Web Server
+
+
+
+ com.squareup.okhttp3
+ mockwebserver
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ io.fabric8
+ zjsonpatch
+
+
+ org.spockframework
+ spock-core
+ test
+
+
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ ${gmavenplus-plugin.version}
+
+
+
+ addSources
+ addTestSources
+ compileTests
+
+
+
+
+
+
+
+
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/Context.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/Context.java
new file mode 100644
index 00000000000..1357f13d590
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/Context.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Context {
+
+ private final ObjectMapper mapper;
+
+ public Context() {
+ this(new ObjectMapper());
+ }
+
+ public Context(ObjectMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ public ObjectMapper getMapper() {
+ return mapper;
+ }
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/DefaultMockServer.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/DefaultMockServer.java
new file mode 100644
index 00000000000..ff9c1ad7154
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/DefaultMockServer.java
@@ -0,0 +1,210 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+import io.fabric8.mockwebserver.dsl.MockServerExpectation;
+import io.fabric8.mockwebserver.internal.MockDispatcher;
+import io.fabric8.mockwebserver.internal.MockSSLContextFactory;
+import io.fabric8.mockwebserver.internal.MockServerExpectationImpl;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class DefaultMockServer implements MockServer {
+
+ private final Context context;
+ private final boolean useHttps;
+ private final MockWebServer server;
+ private final Map> responses;
+ private final AtomicInteger lastRequestCount;
+ private final AtomicReference lastRequest;
+
+ private final AtomicBoolean initialized = new AtomicBoolean();
+ private final AtomicBoolean shutdown = new AtomicBoolean();
+
+ public DefaultMockServer() {
+ this(new Context(), new MockWebServer(), new HashMap<>(), false);
+ }
+
+ public DefaultMockServer(boolean useHttps) {
+ this(new Context(), new MockWebServer(), new HashMap<>(), useHttps);
+ }
+
+ public DefaultMockServer(MockWebServer server, Map> responses, boolean useHttps) {
+ this(new Context(), server, responses, useHttps);
+ }
+
+ public DefaultMockServer(Context context, MockWebServer server, Map> responses,
+ boolean useHttps) {
+ this(context, server, responses, new MockDispatcher(responses), useHttps);
+ }
+
+ public DefaultMockServer(Context context, MockWebServer server, Map> responses,
+ Dispatcher dispatcher, boolean useHttps) {
+ this.context = context;
+ this.useHttps = useHttps;
+ this.server = server;
+ this.responses = responses;
+ this.lastRequest = new AtomicReference<>();
+ this.lastRequestCount = new AtomicInteger(0);
+ this.server.setDispatcher(dispatcher);
+ }
+
+ private void startInternal() {
+ if (initialized.compareAndSet(false, true)) {
+ if (useHttps) {
+ server.useHttps(MockSSLContextFactory.create().getSocketFactory(), false);
+ }
+ onStart();
+ }
+ }
+
+ private void shutdownInternal() {
+ if (shutdown.compareAndSet(false, true)) {
+ onShutdown();
+ }
+ }
+
+ public final void start() {
+ try {
+ startInternal();
+ server.start();
+ } catch (IOException e) {
+ throw new MockServerException("Exception when starting DefaultMockServer", e);
+ }
+ }
+
+ public final void start(int port) {
+ try {
+ startInternal();
+ server.start(port);
+ } catch (IOException e) {
+ throw new MockServerException("Exception when starting DefaultMockServer with port", e);
+ }
+ }
+
+ public final void start(InetAddress inetAddress, int port) {
+ try {
+ startInternal();
+ server.start(inetAddress, port);
+ } catch (IOException e) {
+ throw new MockServerException("Exception when starting DefaultMockServer with InetAddress and port", e);
+ }
+ }
+
+ public final void shutdown() {
+ try {
+ server.shutdown();
+ } catch (IOException e) {
+ throw new MockServerException("Exception when stopping DefaultMockServer", e);
+ } finally {
+ shutdownInternal();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String url(String path) {
+ return server.url(path).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getPort() {
+ return server.getPort();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHostName() {
+ return server.getHostName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Proxy toProxyAddress() {
+ return server.toProxyAddress();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MockServerExpectation expect() {
+ return new MockServerExpectationImpl(responses, context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getRequestCount() {
+ return server.getRequestCount();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RecordedRequest takeRequest() throws InterruptedException {
+ return server.takeRequest();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RecordedRequest takeRequest(long timeout, TimeUnit unit) throws InterruptedException {
+ return server.takeRequest(timeout, unit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized RecordedRequest getLastRequest() throws InterruptedException {
+ if (lastRequest.get() != null && getRequestCount() == lastRequestCount.get()) {
+ return lastRequest.get();
+ }
+ int requestCount = getRequestCount() - lastRequestCount.getAndSet(getRequestCount());
+ RecordedRequest latestRequest = null;
+ while (requestCount-- > 0) {
+ latestRequest = takeRequest();
+ }
+ lastRequest.set(latestRequest);
+ return latestRequest;
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServer.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServer.java
new file mode 100644
index 00000000000..ea2d0de4e32
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServer.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+import io.fabric8.mockwebserver.dsl.MockServerExpectation;
+import okhttp3.mockwebserver.RecordedRequest;
+
+import java.net.Proxy;
+import java.util.concurrent.TimeUnit;
+
+public interface MockServer {
+
+ /**
+ * This method is called right before start. Override it to add extra initialization.
+ */
+ default void onStart() {
+ }
+
+ /**
+ * This method is called right after shutdown. Override it to add extra cleanup.
+ */
+ default void onShutdown() {
+ }
+
+ /**
+ * The port for the {@link okhttp3.mockwebserver.MockWebServer}.
+ *
+ * @return the MockWebServer port.
+ */
+ int getPort();
+
+ /**
+ * The host name for the {@link okhttp3.mockwebserver.MockWebServer}.
+ *
+ * @return the MockWebServer host name;
+ */
+ String getHostName();
+
+ /**
+ * Returns a {@link Proxy} for the {@link okhttp3.mockwebserver.MockWebServer} with the current HostName and Port.
+ *
+ * @return a Proxy for the MockWebServer.
+ */
+ Proxy toProxyAddress();
+
+ /**
+ * Returns a String URL for connecting to this server.
+ *
+ * @param path the request path, such as "/".
+ */
+ String url(String path);
+
+ /**
+ * Returns a {@link MockServerExpectation} to set the expectations.
+ *
+ * @return the MockServerExpectation builder.
+ */
+ MockServerExpectation expect();
+
+ /**
+ * Returns the number of HTTP requests received thus far by this server. This may exceed the
+ * number of HTTP connections when connection reuse is in practice.
+ */
+ int getRequestCount();
+
+ /**
+ * Awaits the next HTTP request, removes it, and returns it. Callers should use this to verify the
+ * request was sent as intended. This method will block until the request is available, possibly
+ * forever.
+ *
+ * @return the head of the request queue
+ */
+ RecordedRequest takeRequest() throws InterruptedException;
+
+ /**
+ * Awaits the next HTTP request (waiting up to the specified wait time if necessary), removes it,
+ * and returns it. Callers should use this to verify the request was sent as intended within the
+ * given time.
+ *
+ * @param timeout how long to wait before giving up, in units of {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the {@code timeout} parameter
+ * @return the head of the request queue
+ */
+ RecordedRequest takeRequest(long timeout, TimeUnit unit) throws InterruptedException;
+
+ /**
+ * Returns the last (most recent) HTTP request processed by the {@link okhttp3.mockwebserver.MockWebServer}.
+ *
+ * n.b. This method clears the request queue.
+ *
+ * @return the most recent RecordedRequest or null if none was processed.
+ */
+ RecordedRequest getLastRequest() throws InterruptedException;
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServerException.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServerException.java
new file mode 100644
index 00000000000..3631068cf47
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/MockServerException.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+public class MockServerException extends RuntimeException {
+
+ private static final long serialVersionUID = 2158577731194403856L;
+
+ public MockServerException(String message) {
+ super(message);
+ }
+
+ public MockServerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Wraps the provided {@link Throwable} in a MockServerException in case it's checked exception.
+ *
+ *
+ * For RuntimeException instances, the original exception is returned.
+ *
+ * @param cause Throwable to wrap.
+ * @return the original exception in case it's unchecked, or a MockServerException wrapping it.
+ */
+ public static RuntimeException launderThrowable(Throwable cause) {
+ return launderThrowable("An error has occurred.", cause);
+ }
+
+ /**
+ * Wraps the provided {@link Throwable} in a MockServerException in case it's checked exception.
+ *
+ *
+ * For RuntimeException instances, the original exception is returned.
+ *
+ * @param message Message to use for the exception.
+ * @param cause Throwable to wrap.
+ * @return the original exception in case it's unchecked, or a MockServerException wrapping it.
+ */
+ public static RuntimeException launderThrowable(String message, Throwable cause) {
+ if (cause instanceof RuntimeException) {
+ return (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else if (cause instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new MockServerException(message, cause);
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerRequest.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerRequest.java
new file mode 100644
index 00000000000..ceb7b962c74
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerRequest.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+public interface ServerRequest {
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerResponse.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerResponse.java
new file mode 100644
index 00000000000..2b5e9cb34d4
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/ServerResponse.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.RecordedRequest;
+
+public interface ServerResponse {
+
+ boolean isRepeatable();
+
+ MockResponse toMockResponse(RecordedRequest recordedRequest);
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Attribute.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Attribute.java
new file mode 100644
index 00000000000..abc5c63a800
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Attribute.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static io.fabric8.mockwebserver.crud.AttributeType.WITH;
+
+public class Attribute {
+
+ private final Key key;
+ private final List values;
+ private final AttributeType type;
+
+ public Attribute(Key key, List values, AttributeType type) {
+ this.key = key;
+ this.values = values;
+ this.type = type;
+ }
+
+ public Attribute(Key key, Value value, AttributeType type) {
+ this(key, Collections.singletonList(value), type);
+ }
+
+ public Attribute(String key, String value, AttributeType type) {
+ this(new Key(key), new Value(value), type);
+ }
+
+ public Attribute(String key, List values, AttributeType type) {
+ this(new Key(key), values.stream().map(Value::new).collect(Collectors.toList()), type);
+ }
+
+ public Attribute(Key key, Value value) {
+ this(key, value, WITH);
+ }
+
+ public Attribute(String key, String value) {
+ this(new Key(key), new Value(value));
+ }
+
+ public Key getKey() {
+ return key;
+ }
+
+ public List getValues() {
+ return values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Attribute attribute = (Attribute) o;
+ return Objects.equals(key, attribute.key) && Objects.equals(values, attribute.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, values);
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ "key:" + key +
+ ", values:" + values +
+ '}';
+ }
+
+ public AttributeType getType() {
+ return type;
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeExtractor.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeExtractor.java
new file mode 100644
index 00000000000..d3e3650ac53
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeExtractor.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+public interface AttributeExtractor {
+
+ AttributeSet fromPath(String path);
+
+ AttributeSet fromResource(String resource);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeSet.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeSet.java
new file mode 100644
index 00000000000..df68305f914
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeSet.java
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class AttributeSet {
+
+ // Package-private for testing
+ final Map attributes;
+
+ public static AttributeSet merge(AttributeSet... attributeSets) {
+ Map all = new HashMap<>();
+ if (attributeSets != null) {
+ for (AttributeSet f : attributeSets) {
+ if (f != null && f.attributes != null) {
+ all.putAll(f.attributes);
+ }
+ }
+ }
+ return new AttributeSet(all);
+ }
+
+ public static AttributeSet map(Attribute... attributes) {
+ Map all = new HashMap<>();
+ if (attributes != null) {
+ for (Attribute a : attributes) {
+ all.put(a.getKey(), a);
+ }
+ }
+ return new AttributeSet(all);
+ }
+
+ public AttributeSet(Attribute... attributes) {
+ this(Arrays.asList(attributes));
+ }
+
+ public AttributeSet(Collection attributes) {
+ this(AttributeSet.map(attributes.toArray(new Attribute[0])).attributes);
+ }
+
+ public AttributeSet(Map attributes) {
+ this.attributes = attributes;
+ }
+
+ public AttributeSet add(Attribute... attr) {
+ Map all = new HashMap<>(attributes);
+ for (Attribute a : attr) {
+ all.put(a.getKey(), a);
+ }
+ return new AttributeSet(all);
+ }
+
+ public boolean containsKey(String key) {
+ return containsKey(new Key(key));
+ }
+
+ public boolean containsKey(Key key) {
+ return attributes.containsKey(key);
+ }
+
+ /**
+ * matches if attributes in db has (or doesn't if WITHOUT command) a set of candidate attributes
+ * Also supports EXISTS and NOT_EXISTS operations
+ *
+ * @param candidate - set of candidate attributes
+ * @return match
+ */
+ public boolean matches(AttributeSet candidate) {
+ return candidate.attributes.values()
+ .stream()
+ .allMatch(this::satisfiesAttribute);
+ }
+
+ private boolean satisfiesAttribute(Attribute c) {
+ switch (c.getType()) {
+ case EXISTS:
+ return attributes.containsKey(c.getKey());
+ case NOT_EXISTS:
+ return !attributes.containsKey(c.getKey());
+ case IN: {
+ if (attributes.containsKey(c.getKey())) {
+ if (attributes.get(c.getKey()).getValues().size() > 1) {
+ throw new IllegalArgumentException("Attribute " + c.getKey() + " has multiple values, can't use IN operation");
+ }
+ return c.getValues().contains(attributes.get(c.getKey()).getValues().iterator().next());
+ }
+ return false;
+ }
+ case NOT_IN: {
+ if (attributes.containsKey(c.getKey())) {
+ if (attributes.get(c.getKey()).getValues().size() > 1) {
+ throw new IllegalArgumentException("Attribute " + c.getKey() + " has multiple values, can't use NOT_IN operation");
+ }
+ return !c.getValues().contains(attributes.get(c.getKey()).getValues().iterator().next());
+ }
+ return true;
+ }
+ case WITHOUT:
+ return !attributes.containsValue(c);
+ case WITH:
+ default:
+ return attributes.containsValue(c);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ AttributeSet that = (AttributeSet) o;
+ return Objects.equals(attributes, that.attributes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(attributes);
+ }
+
+ public Attribute getAttribute(String key) {
+ return attributes.get(new Key(key));
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ "attributes: " + attributes +
+ '}';
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeType.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeType.java
new file mode 100644
index 00000000000..209a49c749b
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/AttributeType.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+public enum AttributeType {
+ WITH,
+ WITHOUT,
+ EXISTS,
+ NOT_EXISTS,
+ IN,
+ NOT_IN
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/CrudDispatcher.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/CrudDispatcher.java
new file mode 100644
index 00000000000..f1d059b6115
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/CrudDispatcher.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import io.fabric8.mockwebserver.Context;
+import io.fabric8.mockwebserver.MockServerException;
+import io.fabric8.zjsonpatch.JsonPatch;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.RecordedRequest;
+
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CrudDispatcher extends Dispatcher {
+
+ private static final String POST = "POST";
+ private static final String PUT = "PUT";
+ private static final String PATCH = "PATCH";
+ private static final String GET = "GET";
+ private static final String DELETE = "DELETE";
+
+ protected final Map map = Collections.synchronizedMap(new LinkedHashMap<>());
+
+ protected final Context context;
+ protected final AttributeExtractor attributeExtractor;
+ protected final ResponseComposer responseComposer;
+
+ public CrudDispatcher(Context context, AttributeExtractor attributeExtractor, ResponseComposer responseComposer) {
+ this.context = context;
+ this.attributeExtractor = attributeExtractor;
+ this.responseComposer = responseComposer;
+ }
+
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ String path = request.getPath();
+ switch (request.getMethod().toUpperCase()) {
+ case POST:
+ return handleCreate(request);
+ case PUT:
+ return handleUpdate(request);
+ case PATCH:
+ return handlePatch(request);
+ case GET:
+ return handleGet(path);
+ case DELETE:
+ return handleDelete(path);
+ default:
+ return null;
+ }
+ }
+
+ public MockResponse handleCreate(RecordedRequest request) {
+ return handleCreate(request.getPath(), request.getBody().readUtf8());
+ }
+
+ /**
+ * Adds the specified object to the in-memory db.
+ *
+ * @param path for the request.
+ * @param body Request body as String (UTF-8).
+ * @return a MockResponse to be dispatched.
+ */
+ public MockResponse handleCreate(String path, String body) {
+ MockResponse response = new MockResponse();
+ AttributeSet features = AttributeSet.merge(attributeExtractor.fromPath(path), attributeExtractor.fromResource(body));
+ synchronized (map) {
+ map.put(features, body);
+ }
+ response.setBody(body);
+ response.setResponseCode(202);
+ return response;
+ }
+
+ public MockResponse handlePatch(RecordedRequest request) {
+ return handlePatch(request.getPath(), request.getBody().readUtf8());
+ }
+
+ /**
+ * Patches the specified object to the in-memory db.
+ *
+ * @param path for the request.
+ * @param body Request body as String (UTF-8).
+ * @return a MockResponse to be dispatched.
+ */
+ public MockResponse handlePatch(String path, String body) {
+ MockResponse response = new MockResponse();
+ String existingObjectBody = doGet(path);
+ if (existingObjectBody == null) {
+ response.setResponseCode(404);
+ } else {
+ try {
+ JsonNode patch = context.getMapper().readTree(body);
+ JsonNode source = context.getMapper().readTree(existingObjectBody);
+ JsonNode updated = JsonPatch.apply(patch, source);
+ String updatedAsString = context.getMapper().writeValueAsString(updated);
+ AttributeSet features = AttributeSet.merge(attributeExtractor.fromPath(path),
+ attributeExtractor.fromResource(updatedAsString));
+ synchronized (map) {
+ map.put(features, updatedAsString);
+ }
+ response.setResponseCode(202);
+ response.setBody(updatedAsString);
+ } catch (Exception e) {
+ throw new MockServerException("Exception when handling CRUD patch", e);
+ }
+
+ }
+ return response;
+ }
+
+ public MockResponse handleUpdate(RecordedRequest request) {
+ return handleUpdate(request.getPath(), request.getBody().readUtf8());
+ }
+
+ /**
+ * Updates the specified object to the in-memory db.
+ *
+ * @param path for the request.
+ * @param body Request body as String (UTF-8).
+ * @return a MockResponse to be dispatched.
+ */
+ public MockResponse handleUpdate(String path, String body) {
+ final String currentItem = doGet(path);
+ final MockResponse response = handleCreate(path, body);
+ if (currentItem == null) {
+ response.setResponseCode(HttpURLConnection.HTTP_CREATED);
+ }
+ return response;
+ }
+
+ /**
+ * Performs a get for the corresponding object from the in-memory db.
+ *
+ * @param path for the request.
+ * @return a MockResponse to be dispatched.
+ */
+ public MockResponse handleGet(String path) {
+ MockResponse response = new MockResponse();
+
+ String body = doGet(path);
+ if (body == null) {
+ response.setResponseCode(404);
+ } else {
+ response.setResponseCode(200);
+ response.setBody(body);
+ }
+ return response;
+ }
+
+ /**
+ * Performs a delete for the corresponding object from the in-memory db.
+ *
+ * @param path for the request.
+ * @return a MockResponse to be dispatched.
+ */
+ public MockResponse handleDelete(String path) {
+ MockResponse response = new MockResponse();
+ List items = new ArrayList<>();
+ AttributeSet query = attributeExtractor.fromPath(path);
+
+ synchronized (map) {
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getKey().matches(query)) {
+ items.add(entry.getKey());
+ }
+ }
+ if (!items.isEmpty()) {
+ for (AttributeSet item : items) {
+ map.remove(item);
+ }
+ response.setResponseCode(200);
+ } else {
+ response.setResponseCode(404);
+ }
+ }
+ return response;
+ }
+
+ public Map getMap() {
+ return map;
+ }
+
+ public AttributeExtractor getAttributeExtractor() {
+ return attributeExtractor;
+ }
+
+ public ResponseComposer getResponseComposer() {
+ return responseComposer;
+ }
+
+ private String doGet(String path) {
+ List items = new ArrayList<>();
+ AttributeSet query = attributeExtractor.fromPath(path);
+ synchronized (map) {
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getKey().matches(query)) {
+ items.add(entry.getValue());
+ }
+ }
+ }
+
+ if (items.isEmpty()) {
+ return null;
+ } else if (items.size() == 1) {
+ return items.get(0);
+ } else {
+ return responseComposer.compose(items);
+ }
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Key.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Key.java
new file mode 100644
index 00000000000..ca41a2803ea
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Key.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+import java.util.Objects;
+
+public class Key {
+
+ private final String name;
+
+ public Key(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Key key = (Key) o;
+ return Objects.equals(name, key.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/ResponseComposer.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/ResponseComposer.java
new file mode 100644
index 00000000000..14d1b8c5700
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/ResponseComposer.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+import java.util.Collection;
+
+public interface ResponseComposer {
+
+ String compose(Collection items);
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Value.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Value.java
new file mode 100644
index 00000000000..047ac4b9d31
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/crud/Value.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.crud;
+
+public class Value {
+
+ private static final String ANY = "*";
+
+ private final String val;
+
+ public Value(String value) {
+ this.val = value;
+ }
+
+ @Override
+ // TODO: There's a BUG here, equals({val: "*"} is true but might have different hashCode
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ if (ANY.equals(val)) {
+ return true;
+ }
+
+ Value key = (Value) o;
+
+ if (ANY.equals(key.val)) {
+ return true;
+ }
+ return val != null ? val.equals(key.val) : key.val == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return val != null ? val.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return val;
+ }
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayPathable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayPathable.java
new file mode 100644
index 00000000000..bf135262b1f
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayPathable.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface DelayPathable extends Delayable>,
+ Pathable {
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayTimesOrOnceable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayTimesOrOnceable.java
new file mode 100644
index 00000000000..afe11633109
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/DelayTimesOrOnceable.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface DelayTimesOrOnceable extends Delayable, TimesOrOnceable {
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Delayable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Delayable.java
new file mode 100644
index 00000000000..6627982076d
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Delayable.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+import java.util.concurrent.TimeUnit;
+
+public interface Delayable {
+
+ T delay(long delay, TimeUnit delayUnit);
+
+ T delay(long delayInMilliseconds);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Doneable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Doneable.java
new file mode 100644
index 00000000000..88f9dcacc34
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Doneable.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Doneable {
+ T done();
+}
\ No newline at end of file
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Emitable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Emitable.java
new file mode 100644
index 00000000000..3ab2823018c
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Emitable.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Emitable {
+
+ /**
+ * Emit an event. This will be received by the client's onMessage.
+ *
+ * @param event
+ * @return
+ */
+ T andEmit(Object event);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/EventDoneable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/EventDoneable.java
new file mode 100644
index 00000000000..2d744379542
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/EventDoneable.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface EventDoneable extends Eventable>, Doneable {
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Eventable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Eventable.java
new file mode 100644
index 00000000000..ff6a1a2c647
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Eventable.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Eventable {
+
+ Emitable> expect(Object in);
+
+ Emitable> expectHttpRequest(final String path);
+
+ Emitable> expectSentWebSocketMessage(final Object in);
+
+ Emitable waitFor(long millis);
+
+ Emitable immediately();
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Failable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Failable.java
new file mode 100644
index 00000000000..dcd3178e3df
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Failable.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Failable {
+
+ T failure(Object response, Exception e);
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Function.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Function.java
new file mode 100644
index 00000000000..990910a61de
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Function.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Function {
+ O apply(I input);
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpHeaderable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpHeaderable.java
new file mode 100644
index 00000000000..f4c7c80636f
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpHeaderable.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface HttpHeaderable {
+
+ T withHeader(String header);
+
+ T withHeader(String name, String value);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethod.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethod.java
new file mode 100644
index 00000000000..817af73b793
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethod.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public enum HttpMethod {
+
+ GET,
+ POST,
+ PUT,
+ PATCH,
+ DELETE,
+ OPTIONS,
+ CONNECT,
+ ANY
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethodable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethodable.java
new file mode 100644
index 00000000000..7d48218bc24
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethodable.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface HttpMethodable {
+
+ T any();
+
+ T post();
+
+ T get();
+
+ T put();
+
+ T delete();
+
+ T patch();
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpStatusable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpStatusable.java
new file mode 100644
index 00000000000..c39de4d17bf
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpStatusable.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface HttpStatusable {
+
+ T withStatus(int statusCode);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/MockServerExpectation.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/MockServerExpectation.java
new file mode 100644
index 00000000000..c21c65be934
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/MockServerExpectation.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface MockServerExpectation
+ extends HttpMethodable>>>,
+ DelayPathable>>,
+ ReturnOrWebsocketable>,
+ TimesOnceableOrHttpHeaderable {
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Onceable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Onceable.java
new file mode 100644
index 00000000000..b586b042923
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Onceable.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Onceable {
+
+ T once();
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Openable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Openable.java
new file mode 100644
index 00000000000..bb88bd9cf20
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Openable.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Openable {
+
+ T open(Object... response);
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Pathable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Pathable.java
new file mode 100644
index 00000000000..c26cb2ba974
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Pathable.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+public interface Pathable {
+
+ T withPath(String path);
+
+}
diff --git a/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Replyable.java b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Replyable.java
new file mode 100644
index 00000000000..4f7669f6350
--- /dev/null
+++ b/junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Replyable.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 io.fabric8.mockwebserver.dsl;
+
+import io.fabric8.mockwebserver.utils.BodyProvider;
+import io.fabric8.mockwebserver.utils.ResponseProvider;
+
+import java.util.List;
+
+public interface Replyable {
+
+ T andReply(int statusCode, BodyProvider