/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.matchers;

import java.util.List;

import net.jadler.Request;
import org.apache.commons.lang.Validate;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;

/**
 * A {@link RequestMatcher} used for matching a request header.
 */
public class HeaderRequestMatcher extends RequestMatcher<List<String>> {

    private final String headerName;
    private final String desc;

    /**
     * Protected constructor useful only when subtyping. For creating instances of this class use
     * {@link #requestHeader(java.lang.String, org.hamcrest.Matcher)} instead.
     * @param pred a predicate to be applied on the given request header
     * @param headerName name of a request header (case insensitive)
     */
    protected HeaderRequestMatcher(final Matcher<? super List<String>> pred, final String headerName) {
        super(pred);

        Validate.notEmpty(headerName, "headerName cannot be empty");
        this.headerName = headerName;

        this.desc = "header \"" + headerName + "\" is";
    }

    /**
     * Retrieves a header (defined in {@link #HeaderRequestMatcher(org.hamcrest.Matcher, java.lang.String)})
     * of the given request.
     * @param req request to retrieve the header from
     * @return the request header as a list of values or {@code null} if there is no such a header in the request
     */
    @Override
    protected List<String> retrieveValue(final Request req) {
        return req.getHeaders().getValues(this.headerName);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String provideDescription() {
        return this.desc;
    }

    /**
     * Factory method to create new instance of this matcher.
     * @param headerName name of a request header
     * @param pred a predicate to be applied on the request header
     * @return new instance of this matcher
     */
    @Factory
    public static HeaderRequestMatcher requestHeader(final String headerName,
            final Matcher<? super List<String>> pred) {
        return new HeaderRequestMatcher(pred, headerName);
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler;

import org.apache.commons.lang.Validate;
import org.hamcrest.Matcher;

import java.util.ArrayList;
import java.util.List;

import static net.jadler.matchers.BodyRequestMatcher.requestBody;
import static net.jadler.matchers.HeaderRequestMatcher.requestHeader;
import static net.jadler.matchers.MethodRequestMatcher.requestMethod;
import static net.jadler.matchers.ParameterRequestMatcher.requestParameter;
import static net.jadler.matchers.PathRequestMatcher.requestPath;
import static net.jadler.matchers.QueryStringRequestMatcher.requestQueryString;
import static net.jadler.matchers.RawBodyRequestMatcher.requestRawBody;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.notNullValue;

/**
 * A base implementation of the {@link RequestMatching} interface. Collects all request predicates to a protected
 * collection available in extending classes.
 *
 * @param <T> type (either class or interface) of the class extending this abstract class. This type will be returned
 * by all methods introduced in {@link RequestMatching} implemented by this class so fluid request matching
 * is possible.
 */
public abstract class AbstractRequestMatching<T extends RequestMatching<T>> implements RequestMatching<T> {

    protected final List<Matcher<? super Request>> predicates;

    protected AbstractRequestMatching() {
        this.predicates = new ArrayList<Matcher<? super Request>>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public T that(final Matcher<? super Request> predicate) {
        Validate.notNull(predicate, "predicate cannot be null");

        this.predicates.add(predicate);
        return (T)this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingMethodEqualTo(final String method) {
        Validate.notEmpty(method, "method cannot be empty");

        return havingMethod(equalToIgnoringCase(method));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingMethod(final Matcher<? super String> predicate) {
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestMethod(predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingBodyEqualTo(final String requestBody) {
        Validate.notNull(requestBody, "requestBody cannot be null, use an empty string instead");

        return havingBody(equalTo(requestBody));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingBody(final Matcher<? super String> predicate) {
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestBody(predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingRawBodyEqualTo(final byte[] requestBody) {
        Validate.notNull(requestBody, "requestBody cannot be null, use an empty array instead");

        return that(requestRawBody(equalTo(requestBody)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingPathEqualTo(final String path) {
        Validate.notEmpty(path, "path cannot be empty");

        return havingPath(equalTo(path));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingPath(final Matcher<? super String> predicate) {
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestPath(predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingQueryStringEqualTo(final String queryString) {
        return havingQueryString(equalTo(queryString));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingQueryString(final Matcher<? super String> predicate) {
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestQueryString(predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingParameterEqualTo(final String name, final String value) {
        Validate.notNull(value, "value cannot be null");

        return havingParameter(name, hasItem(value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingParameter(final String name, final Matcher<? super List<String>> predicate) {
        Validate.notEmpty(name, "name cannot be empty");
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestParameter(name, predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingParameter(final String name) {
        return havingParameter(name, notNullValue());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public T havingParameters(final String... names) {

        for (final String name: names) {
            havingParameter(name);
        }

        return (T)this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingHeaderEqualTo(final String name, final String value) {
        Validate.notNull(value, "value cannot be null");

        return havingHeader(name, hasItem(value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingHeader(final String name, final Matcher<? super List<String>> predicate) {
        Validate.notEmpty(name, "name cannot be empty");
        Validate.notNull(predicate, "predicate cannot be null");

        return that(requestHeader(name, predicate));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T havingHeader(final String name) {
        return havingHeader(name, notNullValue());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public T havingHeaders(final String... names) {
        Validate.notNull(names, "names cannot be null");

        for (final String name: names) {
            havingHeader(name);
        }

        return (T)this;
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.lang.Validate;

/**
 *
This class represents a key-value data structure with following features:

 *
 *

 *
multiple values for one key

 *
key case insensitivity

 *
immutability

 *

 *
 *
This structure is used in Jadler for modeling request/response headers and request parameters.

 *
 *
Please note this class is immutable and therefore thread safe. All addition operations
 * ({@link #add(java.lang.String, java.lang.String)}, {@link #addAll(net.jadler.KeyValues)} create new instances
 * rather than modifying the instance.

 *
 * @see Request
 * @see net.jadler.stubbing.StubResponse
 */
public class KeyValues {

    private final MultiMap values;

    /**
     * An empty instance.
     */
    public static final KeyValues EMPTY = new KeyValues();

    /**
     * Creates new empty instance.
     */
    public KeyValues() {
        this.values = new MultiValueMap();
    }

    /**
     * Adds new key-value pair. Supports multi-values for one key (if there has already been added
     * some value with this key, additional value is added instead of rewriting). Please note this method
     * creates new instance containing all existing values plus the new one rather than modifying this instance.
     * @param key key (cannot be empty)
     * @param value value (cannot be {@code null}, however can be empty for valueless headers)
     * @return an exact copy of this instance containing all existing values plus the new one
     */
    @SuppressWarnings("unchecked")
    public KeyValues add(final String key, final String value) {
        Validate.notEmpty(key, "key cannot be empty");
        Validate.notNull(value, "value cannot be null, use an empty string instead");

        final KeyValues res = new KeyValues();
        res.values.putAll(this.values);
        res.values.put(key.toLowerCase(), value);

        return res;
    }

    /**
     * Adds all values from the given instance. Supports multi-values for one key (if there has already been added
     * some value with this key, additional value is added instead of rewriting). Please note this method
     * creates new instance containing all existing values plus the new ones rather than modifying this instance.
     * @param keyValues values to be added no(cannot be {@code null})
     * @return an exact copy of this instance containing all existing values plus the new ones
     */
    @SuppressWarnings("unchecked")
    public KeyValues addAll(final KeyValues keyValues) {
        Validate.notNull(keyValues, "keyValues cannot be null");

        final KeyValues res = new KeyValues();
        res.values.putAll(this.values);
        res.values.putAll(keyValues.values);
        return res;
    }

    /**
     * Returns the first value for the given key
     * @param key key (case insensitive)
     * @return single (first) value for the given key or {@code null}, if there is no such a key in this instance
     */
    public String getValue(final String key) {
        Validate.notEmpty(key, "key cannot be empty");

        final List<String> allValues = this.getValues(key);
        return allValues != null ? allValues.get(0) : null;
    }

    /**
     * Returns all values for the given key
     * @param key key (case insensitive)
     * @return all values of the given header or {@code null}, if there is no such a key in this instance
     */
    public List<String> getValues(final String key) {
        Validate.notEmpty(key, "name cannot be empty");

        @SuppressWarnings("unchecked")
        final List<String> result = (List<String>) values.get(key.toLowerCase());
        return result == null || result.isEmpty() ? null : new ArrayList<String>(result);
    }

    /**
     * @return all keys (lower-cased) from this instance (never returns {@code null})
     */
    public Set<String> getKeys() {
        @SuppressWarnings("unchecked")
        final Set<String> result = new HashSet<String>(this.values.keySet());
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        for (@SuppressWarnings("unchecked")final Iterator<Map.Entry<String, Collection<String>>> it
                = this.values.entrySet().iterator(); it.hasNext();) {
            final Map.Entry<String, Collection<String>> e = it.next();

            for (final Iterator<String> it2 = e.getValue().iterator(); it2.hasNext();) {
                sb.append(e.getKey()).append(": ").append(it2.next());
                if (it2.hasNext()) {
                    sb.append(", ");
                }
            }

            if (it.hasNext()) {
                sb.append(", ");
            }
        }

        return sb.toString();
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 43 * hash + (this.values != null ? this.values.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final KeyValues other = (KeyValues) obj;
        if (this.values != other.values && (this.values == null || !this.values.equals(other.values))) {
            return false;
        }
        return true;
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.stubbing;

import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import net.jadler.KeyValues;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;

/**
 * An internal package private holder for stub response data. This class is mutable so the data can be set on the fly.
 */
class MutableStubResponse {
    private Charset encoding;
    private final MultiMap headers;
    private String stringBody;
    private byte[] rawBody;
    private int status;
    private long delay;

    /**
     * Creates new empty stub http response definition.
     */
    MutableStubResponse() {
        this.headers = new MultiValueMap();
    }

    /**
     * @return encoding of the stub response body
     */
    Charset getEncoding() {
        return this.encoding;
    }

    /**
     * @param encoding encoding of the stub response body
     */
    void setEncoding(final Charset encoding) {
        this.encoding = encoding;
    }

    /**
     * @return http status of the stub response
     */
    int getStatus() {
        return status;
    }

    /**
     * @param status http status of the stub response
     */
    void setStatus(final int status) {
        this.status = status;
    }

    /**
     * @return body as set by {@link #setBody(java.lang.String)} or {@code null} if the body was not set as a string
     */
    String getStringBody() {
        return this.stringBody;
    }

    /**
     * @return body as set by {@link #setBody(byte[])} or {@code null} if the body was not set as an array of bytes
     */
    byte[] getRawBody() {
        return this.rawBody;
    }

    /**
     * Sets the stub response body as a string.
     * Calling this method also resets any previous calls of {@link #setBody(byte[]) }
     * @param body stub response body
     */
    void setBody(final String body) {
        this.stringBody = body;
        this.rawBody = null;
    }

    /**
     * Sets the stub response body as an array of bytes.
     * Calling this method also resets any previous calls of {@link #setBody(java.lang.String) }
     * @param body stub response body
     */
    void setBody(byte[] body) {
        this.rawBody = body;
        this.stringBody = null;
    }

    /**
     * @return stub response headers
     */
    MultiMap getHeaders() {
        return this.headers;
    }

    /**
     * Adds a new header to this stub response. If there already exists a header with this name
     * in this stub response, multiple values will be sent.
     * @param name header name
     * @param value header value
     */
    void addHeader(final String name, final String value) {
        this.headers.put(name, value);
    }

    /**
     * Adds headers to this stub response. If there already exists a header with a same name
     * in this stub response, multiple values will be sent.
     * @param headers response headers (both keys and values must be of type String)
     */
    @SuppressWarnings("unchecked")
    void addHeaders(final MultiMap headers) {
        this.headers.putAll(headers);
    }

    /**
     * Removes all occurrences of the given header in this stub response (using a case insensitive search)
     * and sets its single value.
     * @param name header name
     * @param value header value
     */
    @SuppressWarnings("unchecked")
    void setHeaderCaseInsensitive(final String name, final String value) {
        final MultiMap result = new MultiValueMap();

        for (final Object o: this.headers.keySet()) {
            final String key = (String) o; //fucking non-generics MultiMap
            if (!name.equalsIgnoreCase(key)) {
                  //copy all other headers to the result multimap
                for(final String s: (Collection<String>)this.headers.get(o)) {
                    result.put(o, s);
                }
            }
        }

        this.headers.clear();
        this.headers.putAll(result);
        this.addHeader(name, value);
    }

    /**
     * @return a delay (in millis) this stub response will be returned after
     */
    long getDelay() {
        return delay;
    }

    /**
     * @param delay a delay (in millis) this stub response will be returned after
     */
    void setDelay(final long delay) {
        this.delay = delay;
    }

    /**
     * @return a {@link StubResponse} instance created from data stored in this object
     */
    StubResponse toStubResponse() {
        final StubResponse.Builder builder = StubResponse.builder()
                .status(status)
                .delay(delay, TimeUnit.MILLISECONDS)
                .headers(this.createHeaders());

        if (this.stringBody != null) {
            if (this.encoding == null) {
                throw new IllegalStateException("The response body encoding has not been set yet, "
                        + "cannot generate a StubResponse instance.");
            }
            else {
                builder.body(this.stringBody, this.encoding);
            }
        }
        else if (this.rawBody != null) {
            builder.body(rawBody);
        }
        else {
            throw new IllegalStateException("The response body has not been set yet, "
                    + "cannot generate a StubResponse instance.");
        }

        return builder.build();
    }

    private KeyValues createHeaders() {
        KeyValues res = new KeyValues();

        for (@SuppressWarnings("unchecked") final Iterator<String> itKey =
                (Iterator<String>) this.headers.keySet().iterator(); itKey.hasNext();) {
            final String key = itKey.next();

            for (@SuppressWarnings("unchecked") final Iterator<String> itValue =
                    ((Collection<String>)this.headers.get(key)).iterator(); itValue.hasNext();) {
                res = res.add(key, itValue.next());
            }
        }

        return res;
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.stubbing.server.jdk;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import net.jadler.Request;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class RequestUtils {
    private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i).*\\bcharset=\\s*\"?([^\\s;\"]*)");

    static Request convert(final HttpExchange httpExchange) throws IOException {
        final Request.Builder builder = Request.builder()
                .method(httpExchange.getRequestMethod())
                .requestURI(httpExchange.getRequestURI())
                .body(IOUtils.toByteArray(httpExchange.getRequestBody()));

        addEncoding(builder, httpExchange);
        addHeaders(builder, httpExchange);

        return builder.build();
    }

    //package protected for testing purposes
    static void addEncoding(final Request.Builder builder, final HttpExchange httpExchange) {
        final String contentType = httpExchange.getRequestHeaders().getFirst("Content-Type");
        if (contentType != null) {
            final Matcher matcher = CHARSET_PATTERN.matcher(contentType);
            if (matcher.matches()) {
                try {
                    builder.encoding(Charset.forName(matcher.group(1)));
                }
                catch (UnsupportedCharsetException e) {
                    //just ignore, fallback encoding will be used instead
                }
            }
        }
    }

    private static void addHeaders(final Request.Builder builder, final HttpExchange httpExchange) {
        final Headers requestHeaders = httpExchange.getRequestHeaders();
        for (final String key : requestHeaders.keySet()) {
            for (final String value : requestHeaders.get(key)) {
                builder.header(key, value);
            }
        }
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler;

import java.nio.charset.Charset;
import net.jadler.exception.JadlerException;
import net.jadler.mocking.VerificationException;
import net.jadler.mocking.Verifying;
import net.jadler.stubbing.RequestStubbing;
import net.jadler.stubbing.server.StubHttpServerManager;
import net.jadler.stubbing.server.StubHttpServer;
import net.jadler.stubbing.ResponseStubbing;

/**
 *
This class is a gateway to the whole Jadler library. Jadler is a powerful yet simple to use
 * http mocking library for writing integration tests in the http environment. It provides a convenient way
 * to create a stub http server which serves all http requests sent during a test execution
 * by returning stub responses according to defined rules.

 *
 *
Jadler Usage Basics

 *
 *
Let's have a simple component with one operation:

 *
 *

 * public interface AccountManager {
 *   Account getAccount(String id);
 * }
 *

 *
 *
An implementation of the {@code getAccount} operation is supposed to send a GET http request to
 * {@code /accounts/{id}} where {@code {id}} stands for the method {@code id} parameter, deserialize the http response
 * to an {@code Account} instance and return it. If there is no such account (the GET request returned 404),
 * {@code null} must be returned. If some problem occurs (50x http response), a runtime exception must be thrown.

 *
 *
For the integration testing of this component it would be great to have a way to start a stub http server
 * which would return predefined stub responses. This is where Jadler comes to help.

 *
 *
Let's write such an integration test using jUnit:

 *
 *

 * ...
 * import static net.jadler.Jadler.*;
 * ...
 *
 * public class AccountManagerImplTest {
 *
 *     private static final String ID = "123";
 *     private static final String ACCOUNT_JSON = "{\"account\":{\"id\": \"123\"}}";
 *
 *
 *     {@literal @}Before
 *     public void setUp() {
 *         initJadler();
 *     }
 *
 *     {@literal @}After
 *     public void tearDown() {
 *         closeJadler();
 *     }
 *
 *     {@literal @}Test
 *     public void getAccount() {
 *         onRequest()
 *             .havingMethodEqualTo("GET")
 *             .havingPathEqualTo("/accounts/" + ID)
 *         .respond()
 *             .withBody(ACCOUNT_JSON)
 *             .withStatus(200);
 *
 *         final AccountManager am = new AccountManagerImpl("http", "localhost", port());
 *
 *         final Account account = ag.getAccount(ID);
 *
 *         assertThat(account, is(notNullValue()));
 *         assertThat(account.getId(), is(ID));
 *     }
 * }
 *

 *
 *
There are three main parts of this test. The setUp phase just initializes Jadler (which includes
 * starting a stub http server), while the tearDown phase just closes all resources. Nothing
 * interesting so far.

 *
 *
All the magic happens in the test method. New http stub is defined, in the THEN part
 * the http stub server is instructed to return a specific http response
 * (200 http status with a body defined in the {@code ACCOUNT_JSON} constant) if the incoming http request
 * fits the given conditions defined in the WHEN part (must be a GET request to {@code /accounts/123}).

 *
 *
In order to communicate with the http stub server instead of the real web service, the tested instance
 * must be configured to access {@code localhost} using the http protocol (https will be supported
 * in a latter version of Jadler) connecting to a port which can be retrieved using the {@link Jadler#port()} method.

 *
 *
The rest of the test method is business as usual. The {@code getAccount(String)} is executed and some
 * assertions are evaluated.

 *
 *
Now lets write two more test methods to test the 404 and 500 scenarios:

 *
 *

 * {@literal @}Test
 * public void getAccountNotFound() {
 *     onRequest()
 *         .havingMethodEqualTo("GET")
 *         .havingPathEqualTo("/accounts/" + ID)
 *     .respond()
 *         .withStatus(404);
 *
 *     final AccountManager am = new AccountManagerImpl("http", "localhost", port());
 *
 *     Account account = am.getAccount(ID);
 *
 *     assertThat(account, is(nullValue()));
 * }
 *
 *
 * {@literal @}Test(expected=RuntimeException.class)
 * public void getAccountError() {
 *     onRequest()
 *         .havingMethodEqualTo("GET")
 *         .havingPathEqualTo("/accounts/" + ID)
 *     .respond()
 *         .withStatus(500);
 *
 *     final AccountManager am = new AccountManagerImpl("http", "localhost", port());
 *
 *     am.getAccount(ID);
 * }
 *

 *
 *
The first test method checks the {@code getAccount(String)} method returns {@code null} if 404 is returned
 * from the server. The second one tests a runtime exception is thrown upon 500 http response.

 *
 *
 *
Multiple responses definition

 *
Sometimes you need to define more subsequent messages in your testing scenario. Let's test here
 * your code can recover from an unexpected 500 response and retry the POST receiving 201 this time:

 *
 *

 * onRequest()
 *     .havingPathEqualTo("/accounts")
 *     .havingMethodEqualTo("POST")
 * .respond()
 *     .withStatus(500)
 * .thenRespond()
 *     .withStatus(201);
 *

 *
 *
The stub server will return a stub http response with 500 response status for the first request
 * which suits the stub rule. A stub response with 201 response status will be returned for the second request
 * (and all subsequent requests as well).

 *
 *
More suitable stub rules

 *
 *
It's not uncommon that more stub rules can be applied (the incoming request fits more than one WHEN
 * part). Let's have the following example:

 *
 *

 * onRequest()
 *     .havingPathEqualTo("/accounts")
 * .respond()
 *     .withStatus(201);
 *
 * onRequest()
 *     .havingMethodEqualTo("POST")
 * .respond()
 *     .withStatus(202);
 *

 *
 *
If a POST http request was sent to {@code /accounts} both rules would be applicable. However, the latter stub
 * gets priority over the former one. In this example, an http response with {@code 202} status code would be
 * returned.

 *
 *
Advanced http stubbing

 *
The WHEN part

 *
So far two {@code having*} methods have been introduced,
 * {@link RequestStubbing#havingMethodEqualTo(java.lang.String)} to check the http method equality and
 * {@link RequestStubbing#havingPathEqualTo(java.lang.String)} to check the path equality. But there's more!

 *
 *
You can use {@link RequestStubbing#havingBodyEqualTo(java.lang.String)} and
 * {@link RequestStubbing#havingRawBodyEqualTo(byte[])}} to check the request body equality
 * (either as a string or as an array of bytes).

 *
 *
Feel free to to use {@link RequestStubbing#havingQueryStringEqualTo(java.lang.String)}
 * to test the query string value.

 *
 *
And finally don't hesitate to use {@link RequestStubbing#havingParameterEqualTo(java.lang.String, java.lang.String)}
 * or {@link RequestStubbing#havingHeaderEqualTo(java.lang.String, java.lang.String)}
 * for a check whether there is an http parameter / header in the incoming request with a given value.
 * If an existence check is sufficient you can use {@link RequestStubbing#havingParameter(java.lang.String)},
 * {@link RequestStubbing#havingParameters(java.lang.String[])} or
 * {@link RequestStubbing#havingHeader(java.lang.String)}, {@link RequestStubbing#havingHeaders(java.lang.String[])}
 * instead.

 *
 *
So let's write some advanced http stub here:

 *
 *

 * onRequest()
 *     .havingMethodEqualTo("POST")
 *     .havingPathEqualTo("/accounts")
 *     .havingBodyEqualTo("{\"account\":{}}")
 *     .havingHeaderEqualTo("Content-Type", "application/json")
 *     .havingParameterEqualTo("force", "1")
 * .respond()
 *     .withStatus(201);
 *

 *
 *
The 201 stub response will be returned if the incoming request was a {@code POST} request to {@code /accounts}
 * with the specified body, {@code application/json} content type header and a {@code force} http parameter set to
 * {@code 1}.

 *
 *
The THEN part

 *
There are much more options than just setting the http response status using the
 * {@link net.jadler.stubbing.ResponseStubbing#withStatus(int)} in the THEN part of an http stub.

 *
 *
You will probably have to define the stub response body as a string very often. That's what the
 * {@link net.jadler.stubbing.ResponseStubbing#withBody(java.lang.String)} and
 * {@link net.jadler.stubbing.ResponseStubbing#withBody(java.io.Reader)} methods are for. These
 * are very often used in conjunction of
 * {@link net.jadler.stubbing.ResponseStubbing#withEncoding(java.nio.charset.Charset)} to define the
 * encoding of the response body

 *
 *
If you'd like to define the stub response body binary, feel free to use either
 * {@link net.jadler.stubbing.ResponseStubbing#withBody(byte[])} or
 * {@link net.jadler.stubbing.ResponseStubbing#withBody(java.io.InputStream)}.

 *
 *
Setting a stub response header is another common http stubbing use case. Just call
 * {@link net.jadler.stubbing.ResponseStubbing#withHeader(java.lang.String, java.lang.String)} to
 * set such header. For setting the {@code Content-Type} header you can use specially tailored
 * {@link net.jadler.stubbing.ResponseStubbing#withContentType(java.lang.String)} method.

 *
 *
And finally sometimes you would like to simulate a network latency. To do so just call the
 * {@link net.jadler.stubbing.ResponseStubbing#withDelay(long, java.util.concurrent.TimeUnit)} method.
 * The stub response will be returned after the specified amount of time or later.

 *
 *
Let's define the THEN part precisely:

 *
 *

 * onRequest()
 *     .havingMethodEqualTo("POST")
 *     .havingPathEqualTo("/accounts")
 *     .havingBodyEqualTo("{\"account\":{}}")
 *     .havingHeaderEqualTo("Content-Type", "application/json")
 *     .havingParameterEqualTo("force", "1")
 * .respond()
 *     .withDelay(2, SECONDS)
 *     .withStatus(201)
 *     .withBody("{\"account\":{\"id\" : 1}}")
 *     .withEncoding(Charset.forName("UTF-8"))
 *     .withContentType("application/json; charset=UTF-8")
 *     .withHeader("Location", "/accounts/1");
 *

 *
 *
If the incoming http request fulfills the WHEN part, a stub response will be returned after at least
 * 2 seconds. The response will have 201 status code, defined json body encoded using UTF-8 and both
 * {@code Content-Type} and {@code Location} headers set to proper values.

 *
 *
 *
 Even more advanced http stubbing

 *
 *
Fine-tuning the WHEN part using predicates

 *
So far we have been using the equality check to define the WHEN part. However it's quite useful
 * to be able to use other predicates (non empty string, contains string, ...) then just
 * the request value equality.

 *
 *
Jadler uses Hamcrest as a predicates library. Not only
 * it provides many already implemented predicates (called matchers) but also a simple way to implement
 * your own ones if necessary. More on Hamcrest usage to be found in this
 * tutorial.

 *
 *
So let's write the following stub: if an incoming request has a non-empty body and the request method
 * is not PUT and the path value starts with /accounts then return an empty response
 * with the 200 http status:

 *
 *

 * onRequest()
 *     .havingBody(not(isEmptyOrNullString()))
 *     .havingPath(startsWith("/accounts"))
 *     .havingMethod(not(equalToIgnoringCase("PUT")))
 * .respond()
 *     .withStatus(200);
 *

 *
 *
You can use following having* methods for defining the WHEN part
 * using a Hamcrest string matcher:

 *
 *

 *
{@link RequestStubbing#havingBody(org.hamcrest.Matcher)}

 *
{@link RequestStubbing#havingMethod(org.hamcrest.Matcher)}

 *
{@link RequestStubbing#havingQueryString(org.hamcrest.Matcher)}

 *
{@link RequestStubbing#havingPath(org.hamcrest.Matcher)}

 *

 *
 *
For adding predicates about request parameters and headers use
 * {@link RequestStubbing#havingHeader(java.lang.String, org.hamcrest.Matcher)} and
 * {@link RequestStubbing#havingParameter(java.lang.String, org.hamcrest.Matcher)} methods. Since a request header or
 * parameter can have more than one value, these methods accept a list of strings predicates.

 *
 *
All introduced methods allow user to add a predicate about a part of an http request (body, method, ...).
 * If you need to add a predicate about the whole request object (of type {@link Request}),
 * you can use the {@link RequestStubbing#that(org.hamcrest.Matcher)} method:

 *
 *

 *   //meetsCriteria() is some factory method returning a Matcher<Request> instance
 *
 * onRequest()
 *     .that(meetsCriteria())
 * .respond()
 *     .withStatus(204);
 *

 *
 *
 *
Fine-tuning the THEN part using defaults

 *
 *
It's pretty common many THEN parts share similar settings. Let's have two or more stubs returning
 * an http response with 200 http status. Instead of calling {@link ResponseStubbing#withStatus(int)} during
 * every stubbing Jadler can be instructed to use 200 as a default http status:

 *
 *

 * {@literal @}Before
 * public void setUp() {
 *     initJadler()
 *         .withDefaultResponseStatus(200);
 * }
 *

 *
 *
This particular test setup configures Jadler to return http stub responses with 200 http
 * status by default. This default can always be overwritten by calling the {@link ResponseStubbing#withStatus(int)}
 * method in the particular stubbing.

 *
 *
The following example demonstrates all response defaults options:

 *
 *

 *   {@literal @}Before
 *   public void setUp() {
 *       initJadler()
 *           .withDefaultResponseStatus(202)
 *           .withDefaultResponseContentType("text/plain")
 *           .withDefaultResponseEncoding(Charset.forName("ISO-8859-1"))
 *           .withDefaultResponseHeader("X-DEFAULT-HEADER", "default_value");
 *   }
 *

 *
 *
If not redefined in the particular stubbing, every stub response will have 202 http status, {@code Content-Type}
 * header set to {@code text/plain}, response body encoded using {@code ISO-8859-1} and a header named
 * {@code X-DEFAULT-HEADER} set to {@code default_value}.

 *
 *
And finally if no default nor stubbing-specific status code is defined 200 will be used. And if no default
 * nor stubbing-specific response body encoding is defined, {@code UTF-8} will be used by default.

 *
 *
 *
Generating a stub response dynamically

 *
 *
In some integration testing scenarios it's necessary to generate a stub http response dynamically. This
 * is a case where the {@code with*} methods aren't sufficient. However Jadler comes to help here with with
 * the {@link net.jadler.stubbing.Responder} interface which allows to define the stub response dynamically
 * according to the received request:

 *
 *

 * onRequest()
 *     .havingMethodEqualTo("POST")
 *     .havingPathEqualTo("/accounts")
 *     .respondUsing(new Responder() {
 *
 *         private final AtomicInteger cnt = new AtomicInteger(1);
 *
 *         {@literal @}Override
 *         public StubResponse nextResponse(final Request request) {
 *              final int current = cnt.getAndIncrement();
 *              final String headerValue = request.getHeaders().getValue("x-custom-request-header");
 *              return StubResponse.builder()
 *                      .status(current % 2 == 0 ? 200 : 500)
 *                      .header("x-custom-response-header", headerValue)
 *                      .build();
 *         }
 *     });
 *

 *
 *
The intention to define the stub response dynamically is expressed by using
 * {@link net.jadler.stubbing.RequestStubbing#respondUsing(net.jadler.stubbing.Responder)}. This method takes
 * a {@link net.jadler.stubbing.Responder} implementation as a parameter, Jadler subsequently uses the
 * {@link net.jadler.stubbing.Responder#nextResponse(net.jadler.Request)} method to generate stub responses for
 * all requests fitting the given WHEN part.

 *
 *
In the previous example the http status of a stub response is {@code 200} for even requests and {@code 500}
 * for odd requests. And the value of the {@code x-custom-request-header} request header is used as
 * a response header.

 *
 *
As you can see in the example this {@link net.jadler.stubbing.Responder} implementation is thread-safe
 * (by using {@link java.util.concurrent.atomic.AtomicInteger} here). This is important for tests of parallel nature
 * (more than one client can send requests fitting the WHEN part in parallel). Of course if requests are sent
 * in a serial way (which is the most common case) there is no need for the thread-safety of the implementation.

 *
 *
Please note this dynamic way of defining stub responses should be used as rarely as possible as
 * it very often signalizes a problem either with test granularity or somewhere in the tested code. However there
 * could be very specific testing scenarios where this functionality might be handy.

 *
 *
 *
Request Receipt Verification

 *
 *
While the Jadler library is invaluable in supporting your test scenarios by providing a stub http server,
 * it has even more to offer.

 *
 *
Very often it's necessary not only to provide a stub http response but also to verify that a specific
 * http request was received during a test execution. Let's add a removal operation to the already introduced
 * {@code AccountManager} interface:

 *
 *

 * public interface AccountManager {
 *   Account getAccount(String id);
 *
 *   void deleteAccount(String id);
 * }
 *

 *
 *
The {@code deleteAccount} operation is supposed to delete an account by sending a {@code DELETE} http request
 * to {@code /accounts/{id}} where {@code {id}} stands for the operation {@code id} parameter. If the response status is
 * 204 the removal is considered successful and the execution is finished successfully. Let's write an integration
 * test for this scenario:

 *
 *

 * ...
 * import static net.jadler.Jadler.*;
 * ...
 *
 * public class AccountManagerImplTest {
 *
 *     private static final String ID = "123";
 *
 *     {@literal @}Before
 *     public void setUp() {
 *         initJadler();
 *     }
 *
 *     {@literal @}After
 *         public void tearDown() {
 *         closeJadler();
 *     }
 *
 *     {@literal @}Test
 *     public void deleteAccount() {
 *         onRequest()
 *             .havingMethodEqualTo("DELETE")
 *             .havingPathEqualTo("/accounts/" + ID)
 *         .respond()
 *             .withStatus(204);
 *
 *         final AccountManager am = new AccountManagerImpl("http", "localhost", port());
 *
 *         final Account account = am.deleteAccount(ID);
 *
 *         verifyThatRequest()
 *             .havingMethodEqualTo("DELETE")
 *             .havingPathEqualTo("/accounts/" + ID)
           .receivedOnce();
 *     }
 * }
 *

 *
 *
The first part of this test is business as usual. An http stub is created and the tested method
 * {@code deleteAccount} is invoked. However in this test case we would like to test whether the {@code DELETE} http
 * request was really sent during the execution of the method.

 *
 *
This is where Jadler comes again to help. Calling {@link #verifyThatRequest()} signalizes an intention to
 * verify a number of requests received so far meeting the given criteria. The criteria is defined using exactly
 * the same {@code having*} methods which has been already described in the stubbing section
 * (the methods are defined in the {@link RequestMatching} interface).

 *
 *
The request definition must be followed by calling one of the {@code received*} methods. The already
 * introduced {@link Verifying#receivedOnce()} method verifies there has been received exactly one request meeting
 * the given criteria so far. If the verification fails a {@link VerificationException} instance is thrown and
 * the exact reason is logged on the {@code INFO} level.

 *
 *
There are three more verification methods. {@link Verifying#receivedNever()} verifies there has not been
 * received any request meeting the given criteria so far. {@link Verifying#receivedTimes(int)} allows to define
 * the exact number of requests meeting the given criteria. And finally
 * {@link Verifying#receivedTimes(org.hamcrest.Matcher)} allows to apply a Hamcrest matcher on the number of
 * requests meeting the given criteria. The following example shows how to verify there have been at most
 * 3 DELETE requests sent so far:

 *
 *

 * verifyThatRequest()
 *     .havingMethodEqualTo("DELETE")
 * .receivedTimes(lessThan(4));
 *

 *
 *
This verification feature is implemented by recording all incoming http requests (including their bodies). In
 * some very specific corner cases this implementation can cause troubles. For example imagine a long running
 * performance test using Jadler for stubbing some remote http service. Since such a test can issue thousands
 * or even millions of requests the memory consumption probably would affect the test results (either
 * by a performance slowdown or even crashes). In this specific scenarios you should consider disabling
 * the incoming requests recording:

 *
 *

 * {@literal @}Before
 * public void setUp() {
 *     initJadler()
 *             .withRequestsRecordingDisabled();
 * }
 *

 *
 *
Once the request recording has been disabled, calling {@link net.jadler.mocking.Mocker#verifyThatRequest()}
 * will result in {@link IllegalStateException}.

 *
 *
Please note you should ignore this option almost every time you use Jadler unless you are really
 * convinced about it. Because premature optimization is the root of all evil, you know.

 *
 *
Jadler Lifecycle

 *
 *
As already demonstrated, the standard Jadler lifecycle consists of the following steps:

 *
 *

 *
starting Jadler including the underlying http server (by calling one of the {@code initJadler*} methods of the
 *   {@link Jadler} facade) in the setUp phase of a test

 *
 *
stubbing using the {@link Jadler#onRequest()} method at the beginning of the test method

 *
 *
calling the code to be tested

 *
 *
doing some verification using {@link Jadler#verifyThatRequest()} if necessary

 *
 *
closing Jadler including the underlying http server (by calling the {@link Jadler#closeJadler()}) method
 *   in the tearDown phase of a test

 *

 *
 *
These steps are then repeated for every test in a test suite. This lifecycle is fully covered by the static
 * {@link Jadler} facade which encapsulates and manages an instance of the core {@link JadlerMocker} component.

 *
 *
Creating mocker instances manually

 *
 *
There are few specific scenarios when creating {@link JadlerMocker} instances manually (instead of using the
 * {@link Jadler} facade) can be handy. Some specific integration tests may require starting more than just one mocker
 * on different ports (simulating requesting multiple different http servers). If this is the case,
 * all the mocker instances have to be created manually (since the facade encapsulates just one mocker instance).

 *
 *
To achieve this each mocker must be created and disposed before and after every test:

 *
 *

 * public class ManualTest {
 *
 *     private JadlerMocker mocker;
 *     private int port;
 *
 *     {@literal @}Before
 *     public void setUp() {
 *         mocker = new JadlerMocker(new JettyStubHttpServer());
 *         mocker.start();
 *         port = getStubHttpServerPort();
 *     }
 *
 *     {@literal @}After
 *     public void tearDown() {
 *         mocker.close();
 *     }
 *
 *     {@literal @}Test
 *     public void testSomething() {
 *         mocker.onRequest().respond().withStatus(404);
 *
 *           //call the code to be tested here
 *
 *         mocker.verifyThatRequest().receivedOnce();
 *     }
 * }
 *

 *
 *
 *
Simplified Jadler Lifecycle Management

 *
 *
In all previous examples the jUnit {@literal @}Before and {@literal @}After sections were used to manage
 * the Jadler lifecycle. If jUnit 4.11 (or newer) is on the classpath a simple Jadler
 * rule {@link net.jadler.junit.rule.JadlerRule}
 * can be used instead:

 *
 *

 * public class AccountManagerImplTest {
 *
 *     {@literal @}Rule
 *     public JadlerRule jadlerRule = new JadlerRule();
 *
 *     ...
 * }
 *

 *
 *
This piece of code starts Jadler on a random port at the beginning of each test and closes it at the end.
 * A specific port can be defined as well: {@code new JadlerRule(12345);}. Please note this is exactly the same as
 * calling {@link Jadler#initJadler()} and {@link Jadler#closeJadler()} in the {@code setUp} and {@code tearDown}
 * methods.

 *
 *
To use this rule the {@code jadler-junit} artifact must be on the classpath.

 */
public class Jadler {

      //since jUnit might execute tests in a thread other than the thread executing the setup and teardown methods
      //when specific conditions are met (a timeout value is specified for the given
      //test, see http://junit.org/apidocs/org/junit/Test.html for details), the thread local container
      //is inheritable so the content is copied automatically to the child thread
    private static final ThreadLocal<JadlerMocker> jadlerMockerContainer = new InheritableThreadLocal<JadlerMocker>();
    private static final String JETTY_SERVER_CLASS = "net.jadler.stubbing.server.jetty.JettyStubHttpServer";

    private Jadler() {
        //gtfo
    }

    /**
     *
Initializes Jadler and starts a default stub server {@link net.jadler.stubbing.server.jetty.JettyStubHttpServer}
     * serving the http protocol listening on any free port. The port number can be retrieved using {@link #port()}.

     *
     *
This should be preferably called in the {@code setUp} method of the test suite.

     *
     * @return {@link OngoingConfiguration} instance for additional configuration and tweaking
     * (use its {@code with*} methods)
     */
    public static OngoingConfiguration initJadler() {
        return initInternal(new JadlerMocker(getJettyServer()));
    }

    /**
     *
Initializes Jadler and starts a default stub server {@link net.jadler.stubbing.server.jetty.JettyStubHttpServer}
     * serving the http protocol listening on the given port.

     *
     *
This should be preferably called in the {@code setUp} method of the test suite.

     *
     * @param port port the stub server will be listening on
     * @return {@link OngoingConfiguration} instance for additional configuration and tweaking
     * (use its {@code with*} methods)
     */
    public static OngoingConfiguration initJadlerListeningOn(final int port) {
        return initInternal(new JadlerMocker(getJettyServer(port)));
    }

    /**
     *
Initializes Jadler and starts the given {@link StubHttpServer}.

     *
     *
This should be preferably called in the {@code setUp} method of the test suite

     *
     * @param server stub http server instance
     * @return {@link OngoingConfiguration} instance for additional configuration and tweaking
     * (use its {@code with*} methods)
     */
    public static OngoingConfiguration initJadlerUsing(final StubHttpServer server) {
        return initInternal(new JadlerMocker(server));
    }

    /**
     *
Stops the underlying {@link StubHttpServer} and closes Jadler.

     *
     *
This should be preferably called in the {@code tearDown} method of a test suite.

     */
    public static void closeJadler() {
        final StubHttpServerManager serverManager = jadlerMockerContainer.get();
        if (serverManager != null && serverManager.isStarted()) {
            serverManager.close();
        }

        jadlerMockerContainer.set(null);
    }

    /**
     *
Resets Jadler by clearing all previously created stubs as well as stored received requests.

     *
     *
While the standard Jadler lifecycle consists of initializing Jadler and starting the
     * underlying stub server (using {@link #initJadler()}) in the setUp section of a test and stopping
     * the server (using {@link #closeJadler()}) in the tearDown section, in some specific scenarios
     * it could be useful to reuse initialized Jadler in all tests instead.

     *
     *
Here's an example code using jUnit which demonstrates usage of this method in a test lifecycle:

     *
     *

     * public class JadlerResetIntegrationTest {
     *
     *     {@literal @}BeforeClass
     *     public static void beforeTests() {
     *         initJadler();
     *     }
     *
     *     {@literal @}AfterClass
     *     public static void afterTests() {
     *         closeJadler();
     *     }
     *
     *     {@literal @}After
     *     public void reset() {
     *         resetJadler();
     *     }
     *
     *     {@literal @}Test
     *     public void test1() {
     *         mocker.onRequest().respond().withStatus(201);
     *
     *         //do an http request here, 201 should be returned from the stub server
     *
     *         verifyThatRequest().receivedOnce();
     *     }
     *
     *     {@literal @}Test
     *     public void test2() {
     *         mocker.onRequest().respond().withStatus(400);
     *
     *         //do an http request here, 400 should be returned from the stub server
     *
     *         verifyThatRequest().receivedOnce();
     *     }
     * }
     *

     *
     *
Please note the standard lifecycle should be always preferred since it ensures a full independence
     * of all tests in a suite. However performance issues may appear theoretically while starting and stopping
     * the server as a part of each test. If this is your case the alternative lifecycle might be handy.

     *
     *
Also note that calling this method in a test body always signalizes a poorly written test
     * with a problem with the granularity. In this case consider writing more fine grained tests instead of using this
     * method.

     *
     * @see JadlerMocker#reset()
     */
    public static void resetJadler() {
        final JadlerMocker mocker = jadlerMockerContainer.get();
        if (mocker != null) {
            mocker.reset();
        }
    }

    /**
     * Use this method to retrieve the port the underlying http stub server is listening on
     * @return the port the underlying http stub server is listening on
     * @throws IllegalStateException if Jadler has not been initialized yet
     */
    public static int port() {
        checkInitialized();
        return jadlerMockerContainer.get().getStubHttpServerPort();
    }

    /**
     * Starts new http stubbing (defining new WHEN-THEN rule).
     * @return stubbing object for ongoing stubbing
     */
    public static RequestStubbing onRequest() {
        checkInitialized();
        return jadlerMockerContainer.get().onRequest();
    }

    /**
     * Starts new verification (checking that an http request with given properties was or was not received)
     * @return verifying object for ongoing verifying
     */
    public static Verifying verifyThatRequest() {
        checkInitialized();
        return jadlerMockerContainer.get().verifyThatRequest();
    }

    private static void checkInitialized() {
        if (jadlerMockerContainer.get() == null) {
            throw new IllegalStateException("Jadler has not been initialized yet.");
        }
    }

    private static OngoingConfiguration initInternal(final JadlerMocker jadlerMocker) {
        if (jadlerMockerContainer.get() != null) {
            throw new IllegalStateException("Jadler seems to have been initialized already.");
        }

        jadlerMockerContainer.set(jadlerMocker);
        jadlerMocker.start();
        return OngoingConfiguration.INSTANCE;
    }

    private static StubHttpServer getJettyServer() {
        final Class<?> clazz = getJettyStubHttpServerClass();
        try {
            return (StubHttpServer) clazz.newInstance();
        }
        catch (final Exception e) {
            throw new JadlerException("Cannot instantiate default Jetty stub server", e);
        }
    }

    private static StubHttpServer getJettyServer(final int port) {
        final Class<?> clazz = getJettyStubHttpServerClass();
        try {
            return (StubHttpServer) clazz.getConstructor(int.class).newInstance(port);
        }
        catch (final Exception e) {
            throw new JadlerException("Cannot instantiate default Jetty stub server with the given port", e);
        }
    }

    private static Class<?> getJettyStubHttpServerClass() {
        try {
            return Class.forName(JETTY_SERVER_CLASS);
        }
        catch (final ClassNotFoundException e) {
            throw new JadlerException("Class " + JETTY_SERVER_CLASS + " cannot be found. "
                    + "Either add jadler-jetty to your classpath or use the initJadlerUsing method to specify the "
                    + "stub server explicitly.", e);
        }
    }

    /**
     * This class serves as a DSL support for additional Jadler configuration.
     */
    public static class OngoingConfiguration implements JadlerConfiguration {
        private static final OngoingConfiguration INSTANCE = new OngoingConfiguration();

        private OngoingConfiguration() {
            //private constructor, instances of this class should never be created directly
        }

        /**
         * @return this ongoing configuration
         * @deprecated added just for backward compatibility reasons, does nothing but returning this ongoing
         * configuration instance. Use the configuration methods of this instance directly instead.
         */
        @Deprecated
        public OngoingConfiguration that() {
            return this;
        }

        /**
         * @deprecated use {@link #withDefaultResponseStatus(int)} instead
         *
         * Sets the default http response status. This value will be used for all stub responses with no
         * specific http status defined. (see {@link ResponseStubbing#withStatus(int)})
         * @param defaultStatus default http response status
         * @return this ongoing configuration
         */
        @Deprecated
        public OngoingConfiguration respondsWithDefaultStatus(final int defaultStatus) {
            return this.withDefaultResponseStatus(defaultStatus);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public OngoingConfiguration withDefaultResponseStatus(final int defaultStatus) {
            jadlerMockerContainer.get().setDefaultStatus(defaultStatus);
            return this;
        }

        /**
         * @deprecated use {@link #withDefaultResponseHeader(java.lang.String, java.lang.String)} instead
         *
         * Defines a response header that will be sent in every http stub response.
         * Can be called repeatedly to define more headers.
         * @param name name of the header
         * @param value header value
         * @return this ongoing configuration
         */
        @Deprecated
        public OngoingConfiguration respondsWithDefaultHeader(final String name, final String value) {
            return this.withDefaultResponseHeader(name, value);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public OngoingConfiguration withDefaultResponseHeader(final String name, final String value) {
            jadlerMockerContainer.get().addDefaultHeader(name, value);
            return this;
        }

        /**
         * @deprecated use {@link #withDefaultResponseEncoding(java.nio.charset.Charset)} instead
         *
         * Defines a default encoding of every stub http response. This value will be used for all stub responses
         * with no specific encoding defined. (see {@link ResponseStubbing#withEncoding(java.nio.charset.Charset)})
         * @param defaultEncoding default stub response encoding
         * @return this ongoing configuration
         */
        @Deprecated
        public OngoingConfiguration respondsWithDefaultEncoding(final Charset defaultEncoding) {
            return this.withDefaultResponseEncoding(defaultEncoding);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public OngoingConfiguration withDefaultResponseEncoding(final Charset defaultEncoding) {
            jadlerMockerContainer.get().setDefaultEncoding(defaultEncoding);
            return this;
        }

        /**
         * @deprecated use {@link #withRequestsRecordingDisabled()} instead
         *
         *
Disables incoming http requests recording.

         *
         *
Jadler mocking (verification) capabilities are implemented by storing all incoming requests (including their
         * bodies). This could cause troubles in some very specific testing scenarios, for further explanation jump
         * straight to {@link JadlerMocker#setRecordRequests(boolean)}.

         *
         *
Please note this method should be used very rarely and definitely should not be treated as a default.

         *
         * @see JadlerMocker#setRecordRequests(boolean)
         * @return this ongoing configuration
         */
        @Deprecated
        public OngoingConfiguration skipsRequestsRecording() {
            return this.withRequestsRecordingDisabled();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public OngoingConfiguration withRequestsRecordingDisabled() {
            jadlerMockerContainer.get().setRecordRequests(false);
            return this;
        }

        /**
         * @deprecated use {@link #withDefaultResponseContentType(java.lang.String)} instead
         *
         * Defines a default content type of every stub http response. This value will be used for all stub responses
         * with no specific content type defined. (see {@link ResponseStubbing#withContentType(java.lang.String)})
         * @param defaultContentType default {@code Content-Type} header of every http stub response
         * @return this ongoing configuration
         */
        @Deprecated
        public OngoingConfiguration respondsWithDefaultContentType(final String defaultContentType) {
            return this.withDefaultResponseContentType(defaultContentType);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public OngoingConfiguration withDefaultResponseContentType(final String defaultContentType) {
            return this.withDefaultResponseHeader("Content-Type", defaultContentType);
        }
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.matchers;

import net.jadler.exception.JadlerException;
import net.jadler.Request;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;

import static org.apache.commons.lang.Validate.notNull;

/**
 * Convenient base class for all Jadler request matchers.
 * @param <T> type of the value retrieved from the given request to be matched
 */
public abstract class RequestMatcher<T> extends BaseMatcher<Request> {

    protected final Matcher<? super T> pred;

    /**
     * @param pred predicate to be applied on the value retrieved from the given request (cannot be {@code null})
     */
    protected RequestMatcher(final Matcher<? super T> pred) {
        notNull(pred, "pred cannot be null");
        this.pred = pred;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void describeMismatch(final Object item, final Description description) {
        final T value;
        try {
            value = this.retrieveValue((Request) item);
        } catch (Exception ex) {
            throw new JadlerException("An error occurred while retrieving a value from the http request "
                    + "for mismatch description", ex);
        }

        description.appendText("REQUIRED: ");
        description.appendDescriptionOf(this);
        description.appendText(" BUT ");
        this.pred.describeMismatch(value, description);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void describeTo(final Description description) {
        description.appendText(this.provideDescription());
        description.appendText(" ");
        description.appendDescriptionOf(this.pred);
    }

    /**
     * Checks whether the given {@link Request} object matches this matcher.
     * @param o {@link Request} object to be matched by this matcher. If this param is not of type {@link Request} this
     * method will always return {@code false}.
     * @return {@code true} if the value retrieved using {@link #retrieveValue(net.jadler.Request)} from the given
     * {@link Request} object matches the predicate registered by the {@link #RequestMatcher(org.hamcrest.Matcher)},
     * otherwise {@code false}.
     */
    @Override
    public boolean matches(final Object o) {
        if (!(o instanceof Request)) {
            return false;
        }

        T value;
        try {
            value = this.retrieveValue((Request) o);
        }
        catch (final Exception e) {
            throw new JadlerException("An error occurred while retrieving a value from the http request", e);
        }

        return this.pred.matches(value);
    }

    /**
     * Reads a value of the given request object (the value can be anything retrievable from the request
     * object: method, header, body,...).
     * @param req request object to read a value from
     * @return a value retrieved from the given request object.
     * @throws Exception when something goes wrong. This exception will be handler correctly by Jadler.
     */
    protected abstract T retrieveValue(final Request req) throws Exception;

    /**
     *
Provides a description of this matcher in form of a string consisting of "noun verb", where
     * noun describes the value retrieved using {@link #retrieveValue(net.jadler.Request)} and verb is usually a correct
     * form of to be.

     *
     *
If the {@link #retrieveValue(net.jadler.Request)} provided a request method, this method would return
     * method is string for example.

     * @return matcher description
     */
    protected abstract String provideDescription();
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.stubbing;

import net.jadler.exception.JadlerException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import net.jadler.AbstractRequestMatching;
import net.jadler.Jadler;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.lang.Validate;

/**
 * Internal class for defining http stubs in a fluid fashion. You shouldn't create instances
 * of this class on your own, please see {@link Jadler#onRequest()}
 * for more information on creating instances of this class.
 */
public class Stubbing extends AbstractRequestMatching<RequestStubbing> implements RequestStubbing, ResponseStubbing {

    private static final String CONTENT_TYPE_HEADER = "Content-Type";

    Responder responder;
    final List<MutableStubResponse> stubResponses;
    private final MultiMap defaultHeaders;
    private final int defaultStatus;
    private final Charset defaultEncoding;

    /**
     * @param defaultHeaders default headers to be present in every http stub response
     * @param defaultStatus default http status of every http stub response
     * (can be overridden in the particular stub)
     * @param defaultEncoding default encoding of every stub response body (can be overridden in the particular stub)
     */
    @SuppressWarnings("unchecked")
    Stubbing(final Charset defaultEncoding, final int defaultStatus, final MultiMap defaultHeaders) {

        this.stubResponses = new ArrayList<MutableStubResponse>();
        this.defaultHeaders = new MultiValueMap();
        this.defaultHeaders.putAll(defaultHeaders);
        this.defaultStatus = defaultStatus;
        this.defaultEncoding = defaultEncoding;
        this.responder = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing respond() {
        return this.thenRespond();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing thenRespond() {
        final MutableStubResponse response = new MutableStubResponse();

        response.addHeaders(defaultHeaders);
        response.setStatus(defaultStatus);
        response.setEncoding(defaultEncoding);
        response.setBody("");

        stubResponses.add(response);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void respondUsing(final Responder responder) {
        Validate.notNull(responder, "responder cannot be null");

        this.responder = responder;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withContentType(final String contentType) {
        currentResponse().setHeaderCaseInsensitive(CONTENT_TYPE_HEADER, contentType);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withEncoding(final Charset encoding) {
        currentResponse().setEncoding(encoding);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withBody(final String responseBody) {
        currentResponse().setBody(responseBody);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withBody(final Reader reader) {
        try {
            final String responseBody;

            try {
                responseBody = IOUtils.toString(reader);
            } catch (final IOException ex) {
                throw new JadlerException("An error ocurred while reading the response body from "
                        + "the given Reader instance.", ex);
            }

            return this.withBody(responseBody);
        }
        finally {
            IOUtils.closeQuietly(reader);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withBody(final InputStream is) {
        try {
            final byte[] responseBody;

            try {
                responseBody = IOUtils.toByteArray(is);
            }
            catch (final IOException e) {
                throw new JadlerException("A problem occurred while reading the given input stream", e);
            }

            return this.withBody(responseBody);
        }
        finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withBody(final byte[] responseBody) {
        currentResponse().setBody(responseBody);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withHeader(final String name, final String value) {
        currentResponse().addHeader(name, value);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withStatus(final int status) {
        currentResponse().setStatus(status);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ResponseStubbing withDelay(long delayValue, TimeUnit delayUnit) {
        currentResponse().setDelay(java.util.concurrent.TimeUnit.MILLISECONDS.convert(delayValue, delayUnit));
        return this;
    }

    /**
     * Creates a {@link HttpStub} instance from this Stubbing instance.
     * Must be called once this stubbing has been finished.
     * @return {@link HttpStub} instance configured using values from this stubbing
     */
    public HttpStub createRule() {
        if (this.responder != null) {
            return new HttpStub(predicates, this.responder);
        }

        final List<StubResponse> res = new ArrayList<StubResponse>(this.stubResponses.size());
        for(final MutableStubResponse msr: this.stubResponses) {
            res.add(msr.toStubResponse());
        }

        return new HttpStub(predicates, new StaticResponder(res));
    }

    private MutableStubResponse currentResponse() {
        return stubResponses.get(stubResponses.size() - 1);
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.stubbing;

import java.nio.charset.Charset;
import org.apache.commons.collections.MultiMap;

/**
 * Factory class for creating {@link Stubbing} instances.
 */
public class StubbingFactory {

    /**
     * Creates new stubbing instance.
     * @param defaultEncoding default encoding of every stub response body
     * @param defaultStatus default http status of every stub response
     * @param defaultHeaders default response headers of every stub response
     * @return new {@link Stubbing} instance
     */
    public Stubbing createStubbing(final Charset defaultEncoding, final int defaultStatus,
            final MultiMap defaultHeaders) {

        return new Stubbing(defaultEncoding, defaultStatus, defaultHeaders);
    }
}


/*
 * Copyright (c) 2012 - 2016 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler.stubbing.server.jetty;

import net.jadler.RequestManager;
import net.jadler.stubbing.server.StubHttpServer;
import org.eclipse.jetty.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang.Validate;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.nio.SelectChannelConnector;

/**
 * Default stub http server implementation using Jetty as an http server.
 */
public class JettyStubHttpServer implements StubHttpServer {

    private static final Logger logger = LoggerFactory.getLogger(JettyStubHttpServer.class);
    private final Server server;
    private final Connector httpConnector;

    public JettyStubHttpServer() {
        this(0);
    }

    public JettyStubHttpServer(final int port) {
        this.server = new Server();
        this.server.setSendServerVersion(false);
        this.server.setSendDateHeader(true);

        this.httpConnector = new SelectChannelConnector();
        this.httpConnector.setPort(port);
        server.addConnector(this.httpConnector);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerRequestManager(final RequestManager ruleProvider) {
        Validate.notNull(ruleProvider, "ruleProvider cannot be null");

        server.setHandler(new JadlerHandler(ruleProvider));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void start() throws Exception {
        logger.debug("starting jetty");
        server.start();
        logger.debug("jetty started");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void stop() throws Exception {
        logger.debug("stopping jetty");
        server.stop();
        logger.debug("jetty stopped");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getPort() {
        return httpConnector.getLocalPort();
    }
}


package com.firebase.security.token;

import org.apache.commons.codec.binary.Base64;
import org.json.JSONException;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;

/**
 * JWT encoder.
 *
 * @author vikrum
 */
public class JWTEncoder {

    private static final String TOKEN_SEP = ".";
    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
    private static final String HMAC_256 = "HmacSHA256";

    /**
     * Encode and sign a set of claims.
     *
     * @param claims
     * @param secret
     * @return
     */
    public static String encode(JSONObject claims, String secret) {
        String encodedHeader = getCommonHeader();
        String encodedClaims = encodeJson(claims);

        String secureBits = new StringBuilder(encodedHeader).append(TOKEN_SEP).append(encodedClaims).toString();

        String sig = sign(secret, secureBits);

        return new StringBuilder(secureBits).append(TOKEN_SEP).append(sig).toString();
    }

    private static String sign(String secret, String secureBits) {
        try {
            Mac sha256_HMAC = Mac.getInstance(HMAC_256);
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(UTF8_CHARSET), HMAC_256);
            sha256_HMAC.init(secret_key);
            byte sig[] = sha256_HMAC.doFinal(secureBits.getBytes(UTF8_CHARSET));
            return Base64.encodeBase64URLSafeString(sig);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String getCommonHeader() {
        try {
            JSONObject headerJson = new JSONObject();
            headerJson.put("typ", "JWT");
            headerJson.put("alg", "HS256");
            return encodeJson(headerJson);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    private static String encodeJson(JSONObject jsonData) {
        return Base64.encodeBase64URLSafeString(jsonData.toString().getBytes(UTF8_CHARSET));
    }
}


package com.firebase.security.token;

import java.util.Date;

/**
 * Token options.
 *
 * @author vikrum
 */
public class TokenOptions {

    private Date expires;
    private Date notBefore;
    private boolean admin;
    private boolean debug;

    /**
     * Copies the date, since Date objects are mutable.
     */
    private Date copyDate(Date date) {
        return (date != null) ? new Date(date.getTime()) : null;
    }

    /**
     * Default constructor.
     */
    public TokenOptions() {
        expires = null;
        notBefore = null;
        admin = false;
        debug = false;
    }

    /**
     * Parametrized constructor.
     *
     * @param expires   The date/time at which the token should no longer be considered valid. (default is never).
     * @param notBefore The date/time before which the token should not be considered valid. (default is now).
     * @param admin     Set to true to bypass all security rules (you can use this for trusted server code).
     * @param debug     Set to true to enable debug mode (so you can see the results of Rules API operations).
     */
    public TokenOptions(Date expires, Date notBefore, boolean admin, boolean debug) {
        super();
        this.expires = copyDate(expires);
        this.notBefore = copyDate(notBefore);
        this.admin = admin;
        this.debug = debug;
    }

    /**
     * @return the expires
     */
    public Date getExpires() {
        return expires;
    }

    /**
     * @param expires the expires to set
     */
    public void setExpires(Date expires) {
        this.expires = copyDate(expires);
    }

    /**
     * @return the notBefore
     */
    public Date getNotBefore() {
        return notBefore;
    }

    /**
     * @param notBefore the notBefore to set
     */
    public void setNotBefore(Date notBefore) {
        this.notBefore = copyDate(notBefore);
    }

    /**
     * @return the admin
     */
    public boolean isAdmin() {
        return admin;
    }

    /**
     * @param admin the admin to set
     */
    public void setAdmin(boolean admin) {
        this.admin = admin;
    }

    /**
     * @return the debug
     */
    public boolean isDebug() {
        return debug;
    }

    /**
     * @param debug the debug to set
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

}


package com.firebase.security.token;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.Date;
import java.util.Map;

/**
 * Firebase JWT token generator.
 *
 * @author vikrum
 */
public class TokenGenerator {

    private static final int TOKEN_VERSION = 0;

    private final String firebaseSecret;

    /**
     * Default constructor given a Firebase secret.
     *
     * @param firebaseSecret
     */
    public TokenGenerator(String firebaseSecret) {
        super();
        this.firebaseSecret = firebaseSecret;
    }

    /**
     * Create a token for the given object.
     *
     * @param data
     * @return
     */
    public String createToken(Map<String, Object> data) {
        return createToken(data, new TokenOptions());
    }

    /**
     * Create a token for the given object and options.
     *
     * @param data
     * @param options
     * @return
     */
    public String createToken(Map<String, Object> data, TokenOptions options) {
        if ((data == null || data.size() == 0) && (options == null || (!options.isAdmin() && !options.isDebug()))) {
            throw new IllegalArgumentException("TokenGenerator.createToken: data is empty and no options are set.  This token will have no effect on Firebase.");
        }

        JSONObject claims = new JSONObject();

        try {
            claims.put("v", TOKEN_VERSION);
            claims.put("iat", new Date().getTime() / 1000);

            boolean isAdminToken = (options != null && options.isAdmin());
            validateToken("TokenGenerator.createToken", data, isAdminToken);

            if (data != null && data.size() > 0) {
                claims.put("d", new JSONObject(data));
            }

            // Handle options
            if (options != null) {
                if (options.getExpires() != null) {
                    claims.put("exp", options.getExpires().getTime() / 1000);
                }

                if (options.getNotBefore() != null) {
                    claims.put("nbf", options.getNotBefore().getTime() / 1000);
                }

                // Only add these claims if they're true to avoid sending them over the wire when false.
                if (options.isAdmin()) {
                    claims.put("admin", options.isAdmin());
                }

                if (options.isDebug()) {
                    claims.put("debug", options.isDebug());
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        String token = computeToken(claims);
        if (token.length() > 1024) {
            throw new IllegalArgumentException("TokenGenerator.createToken: Generated token is too long. The token cannot be longer than 1024 bytes.");
        }
        return token;
    }

    private String computeToken(JSONObject claims) {
        return JWTEncoder.encode(claims, firebaseSecret);
    }

    private void validateToken(String functionName, Map<String, Object> data, boolean isAdminToken) {
        boolean containsUid = (data != null && data.containsKey("uid"));
        if ((!containsUid && !isAdminToken) || (containsUid && !(data.get("uid") instanceof String))) {
            throw new IllegalArgumentException(functionName + ": Data payload must contain a \"uid\" key that must be a string.");
        } else if (containsUid && data.get("uid").toString().length() > 256) {
            throw new IllegalArgumentException(functionName + ": Data payload must contain a \"uid\" key that must not be longer than 256 characters.");
        }
    }
}


package reactor.spring.selector;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import reactor.bus.selector.ObjectSelector;
import reactor.bus.selector.Selector;

/**
 * An implementation of {@link reactor.bus.selector.Selector} that uses a SpEL expression to evaluate the match.
 *
 * @author Jon Brisbin
 */
public class ExpressionSelector extends ObjectSelector<Object, Expression> {

	private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser();

	private final EvaluationContext evalCtx;

	public ExpressionSelector(Expression expr, EvaluationContext evalCtx) {
		super(expr);
		this.evalCtx = evalCtx;
	}

	@Override
	public boolean matches(Object key) {
		return getObject().getValue(evalCtx, key, Boolean.class);
	}

	/**
	 * Shorthand helper method for creating an {@code ExpressionSelector}.
	 *
	 * @param expr
	 * 		The expression to parse.
	 *
	 * @return A new {@link reactor.bus.selector.Selector}
	 */
	public static Selector E(String expr) {
		return expressionSelector(expr, (BeanFactory)null);
	}

	/**
	 * Helper method for creating an {@code ExpressionSelector}.
	 *
	 * @param expr
	 * 		The expression to parse.
	 *
	 * @return A new {@link reactor.bus.selector.Selector}
	 */
	public static Selector expressionSelector(String expr) {
		return expressionSelector(expr, (BeanFactory)null);
	}

	/**
	 * Helper method for creating an {@code ExpressionSelector}.
	 *
	 * @param expr
	 * 		The expression to parse.
	 * @param beanFactory
	 * 		The {@link org.springframework.beans.factory.BeanFactory} to use to resolve references in the expression.
	 *
	 * @return A new {@link reactor.bus.selector.Selector}
	 */
	public static Selector expressionSelector(String expr, BeanFactory beanFactory) {
		StandardEvaluationContext evalCtx = new StandardEvaluationContext();
		if(null != beanFactory) {
			evalCtx.setBeanResolver(new BeanFactoryResolver(beanFactory));
		}
		return expressionSelector(expr, evalCtx);
	}

    /**
     * Helper method for creating an {@code ExpressionSelector}.
     *
     * @param expr
     * 		The expression to parse.
     * @param evalCtx
     * 		The {@link org.springframework.expression.EvaluationContext} to use.
     *
     * @return A new {@link reactor.bus.selector.Selector}
     */
    public static Selector expressionSelector(String expr, EvaluationContext evalCtx) {
        return new ExpressionSelector(SPEL_PARSER.parseExpression(expr), evalCtx);
    }

    /**
     * Helper method for creating an {@code ExpressionSelector}.
     *
     * @param expr
     * 		The expression to parse.
     * @param evalCtx
     * 		The {@link org.springframework.expression.EvaluationContext} to use.
     * @param mode
     * 		The {@link org.springframework.expression.spel.SpelCompilerMode} to use.
     *
     * @return A new {@link reactor.bus.selector.Selector}
     */
    public static Selector expressionSelector(String expr, EvaluationContext evalCtx, SpelCompilerMode mode) {
        SpelParserConfiguration configuration = new SpelParserConfiguration(mode, null);
        SpelExpressionParser parser = new SpelExpressionParser(configuration);
        return new ExpressionSelector(parser.parseExpression(expr), evalCtx);
    }

}


package com.auth0.android.google;

import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;

/**
 * Helper class to wrap all the GoogleApiClient and GoogleApiAvailability calls.
 */
class GoogleAPI implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    private static final String TAG = GoogleAPI.class.getSimpleName();
    private Activity activity;
    private final GoogleCallback googleCallback;

    private GoogleApiClient client;
    private boolean resolvingError;
    private int signInRequestCode;
    private int errorResolutionRequestCode;
    private boolean rememberLastLogin;

    /**
     * @param activity       a valid activity context to use
     * @param serverClientId the OAuth 2.0 server client id obtained when creating a new credential on the Google API's console.
     * @param scopes         the list of scopes to request to the Google Auth API.
     * @param googleCallback to notify token reception and occurred errors.
     */
    GoogleAPI(@NonNull Activity activity, @NonNull String serverClientId, @NonNull Scope[] scopes, @NonNull GoogleCallback googleCallback) {
        this.activity = activity;
        this.googleCallback = googleCallback;
        this.client = createGoogleAPIClient(serverClientId, scopes);
    }

    /**
     * Whether it should clear the session and logout any existing user before trying to authenticate or not.
     *
     * @param rememberLastLogin the new force flag value.
     */
    public void rememberLastLogin(boolean rememberLastLogin) {
        this.rememberLastLogin = rememberLastLogin;
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        requestGoogleAccount(signInRequestCode);
    }

    @Override
    public void onConnectionSuspended(int code) {
        Log.v(TAG, "Connection suspended with code: " + code);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        if (resolvingError) {
            // Already attempting to resolve an error.
            return;
        }
        if (connectionResult.hasResolution()) {
            Log.v(TAG, "Connection failed. Trying to start the resolution.");
            try {
                resolvingError = true;
                connectionResult.startResolutionForResult(activity, errorResolutionRequestCode);
            } catch (IntentSender.SendIntentException e) {
                client.connect();
            }
        } else {
            Log.v(TAG, "Connection failed. No resolution was possible.");
            googleCallback.onError(getErrorDialog(connectionResult.getErrorCode(), errorResolutionRequestCode));
            resolvingError = true;
        }
    }

    /**
     * Checks if the Google Play Services required package is available in the device.
     *
     * @return a code int explaining whether the package is available or not.
     */
    int isGooglePlayServicesAvailable() {
        return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(activity);
    }

    /**
     * Helper to create a new dialog from an error code.
     *
     * @param errorCode   the code int to explain
     * @param requestCode the request code to use when you expect the result.
     * @return a new dialog instance ready to show
     */
    Dialog getErrorDialog(int errorCode, int requestCode) {
        final Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(activity, errorCode, requestCode);
        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialogInterface) {
                resolvingError = false;
            }
        });
        return dialog;
    }

    /**
     * Checks if the Google API Client is connected and tries to request the user Account.
     *
     * @param signInRequestCode          the request code to use when you expect the sign in result.
     * @param errorResolutionRequestCode the request code to use when you expect the erorr resolution result.
     */
    void connectAndRequestGoogleAccount(int signInRequestCode, int errorResolutionRequestCode) {
        if (client.isConnected()) {
            requestGoogleAccount(signInRequestCode);
        } else if (!client.isConnecting()) {
            this.signInRequestCode = signInRequestCode;
            this.errorResolutionRequestCode = errorResolutionRequestCode;
            client.connect();
        }
    }

    /**
     * Given the data received in the activity.onActivityResult() call, tries to parse the
     * content to figure out if it's an error resolution result or a sign up result.
     *
     * @param requestCode request code received
     * @param resultCode  result code received
     * @param intent      intent received
     * @return whether the data was handled by this provider or not.
     */
    boolean parseSignInResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == errorResolutionRequestCode) {
            resolvingError = false;
            if (resultCode == Activity.RESULT_OK) {
                connectAndRequestGoogleAccount(signInRequestCode, errorResolutionRequestCode);
            }
            return true;
        } else if (requestCode == signInRequestCode) {
            if (resultCode == Activity.RESULT_OK) {
                final GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(intent);
                if (result.isSuccess()) {
                    googleCallback.onSuccess(result.getSignInAccount());
                } else if (result.getStatus().isCanceled()) {
                    googleCallback.onCancel();
                }
            }
            return true;
        }

        return false;
    }

    /**
     * Clears this helper state. After this method is called the provider should not be used.
     */
    public void logoutAndClearState() {
        if (client != null && client.isConnected()) {
            logout();
            client.disconnect();
        }
        activity = null;
        client = null;
    }

    /**
     * Logs out the current signed in account.
     */
    void logout() {
        try {
            Auth.GoogleSignInApi.signOut(client).setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(@NonNull Status status) {
                    if (!status.isSuccess()) {
                        Log.w(TAG, "Couldn't clear account and credentials");
                    }
                }
            });
        } catch (IllegalStateException e) {
            Log.e(TAG, "Failed to clear the Google Plus Session", e);
        }
    }

    /**
     * Disconnects the current GoogleAPIClient instance. After this method is called the provider should not be used.
     */
    void disconnect() {
        if (client != null && client.isConnected()) {
            client.disconnect();
        }
        client = null;
    }

    private GoogleApiClient createGoogleAPIClient(String serverClientId, Scope[] scopes) {
        final GoogleSignInOptions.Builder gsoBuilder = new GoogleSignInOptions.Builder()
                .requestIdToken(serverClientId);
        if (scopes.length == 1) {
            gsoBuilder.requestScopes(scopes[0]);
        } else if (scopes.length > 1) {
            gsoBuilder.requestScopes(scopes[0], scopes);
        }

        final GoogleApiClient.Builder builder = new GoogleApiClient.Builder(activity, this, this)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gsoBuilder.build());

        return builder.build();
    }

    private void requestGoogleAccount(int signInRequestCode) {
        if (!rememberLastLogin) {
            logout();
        }
        final Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(client);
        activity.startActivityForResult(signInIntent, signInRequestCode);
    }

}


package com.hubspot.baragon.service.edgecache.cloudflare;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.hubspot.baragon.models.BaragonRequest;
import com.hubspot.baragon.service.config.EdgeCacheConfiguration;
import com.hubspot.baragon.service.edgecache.EdgeCache;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareClient;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareClientException;
import com.hubspot.baragon.service.edgecache.cloudflare.client.models.CloudflareDnsRecord;
import com.hubspot.baragon.service.edgecache.cloudflare.client.models.CloudflareZone;

/**
 * An implementation of a proxying edge cache backed by Cloudflare.
 *
 * Config example:
 * ...
 * edgeCache:
 *   enabled: true
 *   edgeCacheClass: CLOUDFLARE
 *   integrationSettings:
 *     apiBase: https://api.cloudflare.com/client/v4/
 *     apiEmail: email@host.net
 *     apiKey: some-key
 *     cacheTagFormat: cache-tag-for-%-service
 * ...
 */
public class CloudflareEdgeCache implements EdgeCache {
  private static final Logger LOG = LoggerFactory.getLogger(CloudflareEdgeCache.class);

  private final CloudflareClient cf;
  private final EdgeCacheConfiguration edgeCacheConfiguration;

  @Inject
  public CloudflareEdgeCache(CloudflareClient cf, EdgeCacheConfiguration edgeCacheConfiguration) {
    this.cf = cf;
    this.edgeCacheConfiguration = edgeCacheConfiguration;
  }

  /**
   * Invalidation will eventually occur when the TTL expires, so it's not a showstopper if this fails.
   */
  @Override
  public boolean invalidateIfNecessary(BaragonRequest request) {
    if (request.getLoadBalancerService().getEdgeCacheDomains().isEmpty()) {
      return false;
    }

    try {
      boolean allSucceeded = true;
      for (String edgeCacheDNS : request.getLoadBalancerService().getEdgeCacheDomains()) {
        List<CloudflareZone> matchingZones = getCloudflareZone(edgeCacheDNS);

        if (matchingZones.isEmpty()) {
          LOG.warn("`edgeCacheDNS` was defined on the request, but no matching Cloudflare Zone was found!");
          return false;
        }

        for (CloudflareZone matchingZone : matchingZones) {
          String zoneId = matchingZone.getId();
          Optional<CloudflareDnsRecord> matchingDnsRecord = getCloudflareDnsRecord(edgeCacheDNS, zoneId);

          if (!matchingDnsRecord.isPresent()) {
            LOG.warn("`edgeCacheDNS` was defined on the request, but no matching Cloudflare DNS Record was found!");
            return false;
          }

          if (!matchingDnsRecord.get().isProxied()) {
            LOG.warn("`edgeCacheDNS` was defined on the request, but {} is not a proxied DNS record!", edgeCacheDNS);
            return false;
          }

          String cacheTag = String.format(
              edgeCacheConfiguration.getIntegrationSettings().get("cacheTagFormat"),
              request.getLoadBalancerService().getServiceId()
          );

          LOG.debug("Sending cache purge request against {} for {} to Cloudflare...", matchingDnsRecord.get().getName(), cacheTag);

          allSucceeded = cf.purgeEdgeCache(zoneId, Collections.singletonList(cacheTag)) && allSucceeded;
        }
      }
      return allSucceeded;

    } catch (CloudflareClientException e) {
      LOG.error("Unable to invalidate Cloudflare cache for request {}", request, e);
      return false;
    }
  }

  private Optional<CloudflareDnsRecord> getCloudflareDnsRecord(String edgeCacheDNS, String zoneId) throws CloudflareClientException {
    return Optional.ofNullable(cf.getDnsRecord(zoneId, edgeCacheDNS));
  }

  private List<CloudflareZone> getCloudflareZone(String edgeCacheDNS) throws CloudflareClientException {
    String baseDomain = getBaseDomain(edgeCacheDNS);
    return cf.getZone(baseDomain);
  }

  private String getBaseDomain(String dns) {
    String[] domainTokens = dns.split("\\.");
    return String.format("%s.%s", domainTokens[domainTokens.length - 2], domainTokens[domainTokens.length - 1]);
  }

}