All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mockserver.mock.RequestMatchers Maven / Gradle / Ivy

There is a newer version: 5.15.0
Show newest version
package org.mockserver.mock;

import org.mockserver.closurecallback.websocketregistry.WebSocketClientRegistry;
import org.mockserver.collections.CircularPriorityQueue;
import org.mockserver.configuration.ConfigurationProperties;
import org.mockserver.log.model.LogEntry;
import org.mockserver.logging.MockServerLogger;
import org.mockserver.matchers.HttpRequestMatcher;
import org.mockserver.matchers.MatchDifference;
import org.mockserver.matchers.MatcherBuilder;
import org.mockserver.metrics.Metrics;
import org.mockserver.model.Action;
import org.mockserver.model.HttpObjectCallback;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.RequestDefinition;
import org.mockserver.scheduler.Scheduler;
import org.mockserver.ui.MockServerMatcherNotifier;
import org.slf4j.event.Level;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static org.mockserver.configuration.ConfigurationProperties.maxExpectations;
import static org.mockserver.log.model.LogEntry.LogMessageType.*;
import static org.mockserver.matchers.HttpRequestMatcher.EXPECTATION_PRIORITY_COMPARATOR;
import static org.mockserver.metrics.Metrics.Name.*;
import static org.slf4j.event.Level.DEBUG;

/**
 * @author jamesdbloom
 */
public class RequestMatchers extends MockServerMatcherNotifier {

    final CircularPriorityQueue httpRequestMatchers = new CircularPriorityQueue<>(
        maxExpectations(),
        HttpRequestMatcher.class,
        EXPECTATION_PRIORITY_COMPARATOR,
        httpRequestMatcher -> httpRequestMatcher.getExpectation().getId()
    );
    private final AtomicReference> httpRequestMatchersCopy = new AtomicReference<>();
    private final MockServerLogger mockServerLogger;
    private final Scheduler scheduler;
    private WebSocketClientRegistry webSocketClientRegistry;
    private MatcherBuilder matcherBuilder;

    public RequestMatchers(MockServerLogger mockServerLogger, Scheduler scheduler, WebSocketClientRegistry webSocketClientRegistry) {
        super(scheduler);
        this.scheduler = scheduler;
        this.matcherBuilder = new MatcherBuilder(mockServerLogger);
        this.mockServerLogger = mockServerLogger;
        this.webSocketClientRegistry = webSocketClientRegistry;
    }

    public Expectation add(Expectation expectation, Cause cause) {
        Expectation upsertedExpectation = null;
        if (expectation != null) {
            upsertedExpectation = httpRequestMatchers
                .getByKey(expectation.getId())
                .map(httpRequestMatcher -> {
                    if (httpRequestMatcher.getExpectation() != null && httpRequestMatcher.getExpectation().getAction() != null) {
                        Metrics.decrement(httpRequestMatcher.getExpectation().getAction().getType());
                    }
                    if (httpRequestMatcher.update(expectation)) {
                        mockServerLogger.logEvent(
                            new LogEntry()
                                .setType(UPDATED_EXPECTATION)
                                .setLogLevel(Level.DEBUG)
                                .setHttpRequest(expectation.getHttpRequest())
                                .setMessageFormat("updated expectation:{}")
                                .setArguments(expectation.clone())
                        );
                        if (expectation.getAction() != null) {
                            Metrics.increment(expectation.getAction().getType());
                        }
                    }
                    return httpRequestMatcher;
                })
                .orElseGet(() -> addPrioritisedExpectation(expectation))
                .getExpectation();
            notifyListeners(this, cause);
        }
        return upsertedExpectation;
    }

    public void update(Expectation[] expectations, Cause cause) {
        AtomicInteger numberOfChanges = new AtomicInteger(0);
        if (expectations != null) {
            Map httpRequestMatchersByKey = httpRequestMatchers.keyMap();
            Set existingKeys = new HashSet<>(httpRequestMatchersByKey.keySet());
            Arrays
                .stream(expectations)
                .forEach(expectation -> {
                    existingKeys.remove(expectation.getId());
                    if (httpRequestMatchersByKey.containsKey(expectation.getId())) {
                        HttpRequestMatcher httpRequestMatcher = httpRequestMatchersByKey.get(expectation.getId());
                        if (httpRequestMatcher.getExpectation() != null && httpRequestMatcher.getExpectation().getAction() != null) {
                            Metrics.decrement(httpRequestMatcher.getExpectation().getAction().getType());
                        }
                        if (httpRequestMatcher.update(expectation)) {
                            numberOfChanges.getAndIncrement();
                            mockServerLogger.logEvent(
                                new LogEntry()
                                    .setType(UPDATED_EXPECTATION)
                                    .setLogLevel(Level.INFO)
                                    .setHttpRequest(expectation.getHttpRequest())
                                    .setMessageFormat("updated expectation:{}")
                                    .setArguments(expectation.clone())
                            );
                            if (expectation.getAction() != null) {
                                Metrics.increment(expectation.getAction().getType());
                            }
                        }
                    } else {
                        addPrioritisedExpectation(expectation);
                        numberOfChanges.getAndIncrement();
                    }
                });
            existingKeys
                .forEach(key -> {
                    numberOfChanges.getAndIncrement();
                    HttpRequestMatcher httpRequestMatcher = httpRequestMatchersByKey.get(key);
                    removeHttpRequestMatcher(httpRequestMatcher, cause, false);
                    if (httpRequestMatcher.getExpectation() != null && httpRequestMatcher.getExpectation().getAction() != null) {
                        Metrics.decrement(httpRequestMatcher.getExpectation().getAction().getType());
                    }
                });
            if (numberOfChanges.get() > 0) {
                notifyListeners(this, cause);
            }
        }
    }

    private HttpRequestMatcher addPrioritisedExpectation(Expectation expectation) {
        HttpRequestMatcher httpRequestMatcher = matcherBuilder.transformsToMatcher(expectation);
        httpRequestMatchers.add(httpRequestMatcher);
        if (expectation.getAction() != null) {
            Metrics.increment(expectation.getAction().getType());
        }
        mockServerLogger.logEvent(
            new LogEntry()
                .setType(CREATED_EXPECTATION)
                .setLogLevel(Level.INFO)
                .setHttpRequest(expectation.getHttpRequest())
                .setMessageFormat("creating expectation:{}")
                .setArguments(expectation.clone())
        );
        return httpRequestMatcher;
    }

    public int size() {
        return httpRequestMatchers.size();
    }

    public void setMaxSize(int maxSize) {
        httpRequestMatchers.setMaxSize(maxSize);
    }

    public void reset(Cause cause) {
        new ArrayList<>(httpRequestMatchers).forEach(httpRequestMatcher -> removeHttpRequestMatcher(httpRequestMatcher, cause, false));
        Metrics.clearActionMetrics();
        notifyListeners(this, cause);
    }

    public void reset() {
        reset(Cause.API);
    }

    public Expectation firstMatchingExpectation(HttpRequest httpRequest) {
        Expectation matchingExpectation = null;
        for (HttpRequestMatcher httpRequestMatcher : getHttpRequestMatchersCopy()) {
            boolean remainingMatchesDecremented = false;
            if (httpRequestMatcher.matches(MockServerLogger.isEnabled(DEBUG) ? new MatchDifference(httpRequest) : null, httpRequest)) {
                matchingExpectation = httpRequestMatcher.getExpectation();
                httpRequestMatcher.setResponseInProgress(true);
                if (matchingExpectation.decrementRemainingMatches()) {
                    remainingMatchesDecremented = true;
                }
            } else if (!httpRequestMatcher.isResponseInProgress() && !httpRequestMatcher.isActive()) {
                scheduler.submit(() -> removeHttpRequestMatcher(httpRequestMatcher));
            }
            if (remainingMatchesDecremented) {
                notifyListeners(this, Cause.API);
            }
            if (matchingExpectation != null) {
                break;
            }
        }
        if (ConfigurationProperties.metricsEnabled()) {
            if (matchingExpectation == null || matchingExpectation.getAction() == null) {
                Metrics.increment(EXPECTATION_NOT_MATCHED_COUNT);
            } else if (matchingExpectation.getAction().getType().direction == Action.Direction.FORWARD) {
                Metrics.increment(FORWARD_EXPECTATION_MATCHED_COUNT);
            } else {
                Metrics.increment(RESPONSE_EXPECTATION_MATCHED_COUNT);
            }
        }
        return matchingExpectation;
    }

    public void clear(RequestDefinition requestDefinition) {
        if (requestDefinition != null) {
            HttpRequestMatcher clearHttpRequestMatcher = matcherBuilder.transformsToMatcher(requestDefinition);
            for (HttpRequestMatcher httpRequestMatcher : getHttpRequestMatchersCopy()) {
                RequestDefinition request = httpRequestMatcher
                    .getExpectation()
                    .getHttpRequest()
                    .clone()
                    .withLogCorrelationId(requestDefinition.getLogCorrelationId());
                if (clearHttpRequestMatcher.matches(request)) {
                    removeHttpRequestMatcher(httpRequestMatcher);
                }
            }
            mockServerLogger.logEvent(
                new LogEntry()
                    .setType(CLEARED)
                    .setLogLevel(Level.INFO)
                    .setCorrelationId(requestDefinition.getLogCorrelationId())
                    .setHttpRequest(requestDefinition)
                    .setMessageFormat("cleared expectations that match:{}")
                    .setArguments(requestDefinition)
            );
        } else {
            reset();
        }
    }

    Expectation postProcess(Expectation expectation) {
        if (expectation != null) {
            for (HttpRequestMatcher httpRequestMatcher : getHttpRequestMatchersCopy()) {
                if (httpRequestMatcher.getExpectation() == expectation) {
                    if (!expectation.isActive()) {
                        removeHttpRequestMatcher(httpRequestMatcher);
                        break;
                    }
                    httpRequestMatcher.setResponseInProgress(false);
                }
            }
        }
        return expectation;
    }

    private void removeHttpRequestMatcher(HttpRequestMatcher httpRequestMatcher) {
        removeHttpRequestMatcher(httpRequestMatcher, Cause.API, true);
    }

    @SuppressWarnings("rawtypes")
    private void removeHttpRequestMatcher(HttpRequestMatcher httpRequestMatcher, Cause cause, boolean notifyAndUpdateMetrics) {
        if (httpRequestMatchers.remove(httpRequestMatcher)) {
            if (httpRequestMatcher.getExpectation() != null) {
                mockServerLogger.logEvent(
                    new LogEntry()
                        .setType(REMOVED_EXPECTATION)
                        .setLogLevel(Level.INFO)
                        .setHttpRequest(httpRequestMatcher.getExpectation().getHttpRequest())
                        .setMessageFormat("removed expectation:{}")
                        .setArguments(httpRequestMatcher.getExpectation().clone())
                );
            }
            if (httpRequestMatcher.getExpectation() != null) {
                final Action action = httpRequestMatcher.getExpectation().getAction();
                if (action instanceof HttpObjectCallback) {
                    webSocketClientRegistry.unregisterClient(((HttpObjectCallback) action).getClientId());
                }
                if (notifyAndUpdateMetrics && action != null) {
                    Metrics.decrement(action.getType());
                }
            }
            if (notifyAndUpdateMetrics) {
                notifyListeners(this, cause);
            }
        }
    }

    public List retrieveActiveExpectations(RequestDefinition requestDefinition) {
        if (requestDefinition == null) {
            return httpRequestMatchers.stream().map(HttpRequestMatcher::getExpectation).collect(Collectors.toList());
        } else {
            List expectations = new ArrayList<>();
            HttpRequestMatcher requestMatcher = matcherBuilder.transformsToMatcher(requestDefinition);
            for (HttpRequestMatcher httpRequestMatcher : getHttpRequestMatchersCopy()) {
                if (requestMatcher.matches(httpRequestMatcher.getExpectation().getHttpRequest())) {
                    expectations.add(httpRequestMatcher.getExpectation());
                }
            }
            return expectations;
        }
    }

    public List retrieveRequestMatchers(RequestDefinition requestDefinition) {
        if (requestDefinition == null) {
            return new ArrayList<>(httpRequestMatchers);
        } else {
            List httpRequestMatchers = new ArrayList<>();
            HttpRequestMatcher requestMatcher = matcherBuilder.transformsToMatcher(requestDefinition);
            for (HttpRequestMatcher httpRequestMatcher : getHttpRequestMatchersCopy()) {
                if (requestMatcher.matches(httpRequestMatcher.getExpectation().getHttpRequest())) {
                    httpRequestMatchers.add(httpRequestMatcher);
                }
            }
            return httpRequestMatchers;
        }
    }

    public boolean isEmpty() {
        return httpRequestMatchers.isEmpty();
    }

    protected void notifyListeners(final RequestMatchers notifier, Cause cause) {
        httpRequestMatchersCopy.set(null);
        super.notifyListeners(notifier, cause);
    }


    private List getHttpRequestMatchersCopy() {
        httpRequestMatchersCopy.compareAndSet(null, httpRequestMatchers.toSortedList());
        return httpRequestMatchersCopy.get();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy