001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.component.mock;
018
019import java.util.Date;
020import java.util.LinkedHashSet;
021import java.util.Set;
022
023import org.apache.camel.Exchange;
024import org.apache.camel.Expression;
025import org.apache.camel.Predicate;
026import org.apache.camel.StreamCache;
027import org.apache.camel.builder.ExpressionClause;
028import org.apache.camel.builder.ExpressionClauseSupport;
029import org.apache.camel.builder.ValueBuilder;
030import org.apache.camel.model.language.ExpressionDefinition;
031import org.apache.camel.util.PredicateAssertHelper;
032
033/**
034 * A builder of assertions on message exchanges
035 *
036 * @version 
037 */
038public abstract class AssertionClause extends ExpressionClauseSupport<ValueBuilder> implements Runnable {
039
040    protected final MockEndpoint mock;
041    protected volatile int currentIndex;
042    private final Set<Predicate> predicates = new LinkedHashSet<>();
043    private final Expression previous = new PreviousTimestamp();
044    private final Expression next = new NextTimestamp();
045
046    public AssertionClause(MockEndpoint mock) {
047        super(null);
048        this.mock = mock;
049    }
050
051    // Builder methods
052    // -------------------------------------------------------------------------
053
054    public ValueBuilder expression(Expression expression) {
055        // must override this method as we provide null in the constructor
056        super.expression(expression);
057        return new PredicateValueBuilder(getExpressionValue());
058    }
059
060    public ValueBuilder expression(ExpressionDefinition expression) {
061        // must override this method as we provide null in the constructor
062        super.expression(expression);
063        return new PredicateValueBuilder(expression.createExpression(mock.getCamelContext()));
064    }
065
066    /**
067     * Adds the given predicate to this assertion clause
068     */
069    public AssertionClause predicate(Predicate predicate) {
070        addPredicate(predicate);
071        return this;
072    }
073
074    /**
075     * Adds the given predicate to this assertion clause
076     */
077    public ExpressionClause<AssertionClause> predicate() {
078        ExpressionClause<AssertionClause> clause = new ExpressionClause<>(this);
079        addPredicate(clause);
080        return clause;
081    }
082
083    /**
084     * Adds a {@link TimeClause} predicate for message arriving.
085     */
086    public TimeClause arrives() {
087        final TimeClause clause = new TimeClause(previous, next);
088        addPredicate(new Predicate() {
089            public boolean matches(Exchange exchange) {
090                return clause.matches(exchange);
091            }
092
093            @Override
094            public String toString() {
095                return "arrives " + clause.toString() + " exchange";
096            }
097        });
098        return clause;
099    }
100
101    /**
102     * Performs any assertions on the given exchange
103     */
104    protected void applyAssertionOn(MockEndpoint endpoint, int index, Exchange exchange) {
105        for (Predicate predicate : predicates) {
106            currentIndex = index;
107
108            Object value = exchange.hasOut() ? exchange.getOut().getBody() : exchange.getIn().getBody();
109            // if the value is StreamCache then ensure its readable before evaluating any predicates
110            // by resetting it (this is also what StreamCachingAdvice does)
111            if (value instanceof StreamCache) {
112                ((StreamCache) value).reset();
113            }
114
115            PredicateAssertHelper.assertMatches(predicate, "Assertion error at index " + index + " on mock " + endpoint.getEndpointUri() + " with predicate: ", exchange);
116        }
117    }
118
119    protected void addPredicate(Predicate predicate) {
120        predicates.add(predicate);
121    }
122
123    @SuppressWarnings("unchecked")
124    private final class PreviousTimestamp implements Expression {
125        public <T> T evaluate(Exchange exchange, Class<T> type) {
126            Date answer = null;
127            if (currentIndex > 0 && mock.getReceivedCounter() > 0) {
128                answer = mock.getReceivedExchanges().get(currentIndex - 1).getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
129            }
130            return (T) answer;
131        }
132    }
133
134    @SuppressWarnings("unchecked")
135    private final class NextTimestamp implements Expression {
136        public <T> T evaluate(Exchange exchange, Class<T> type) {
137            Date answer = null;
138            if (currentIndex < mock.getReceivedCounter() - 1) {
139                answer = mock.getReceivedExchanges().get(currentIndex + 1).getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
140            }
141            return (T) answer;
142        }
143    }
144
145    /**
146     * Public class needed for fluent builders
147     */
148    public final class PredicateValueBuilder extends ValueBuilder {
149
150        public PredicateValueBuilder(Expression expression) {
151            super(expression);
152        }
153
154        @Override
155        protected Predicate onNewPredicate(Predicate predicate) {
156            predicate = super.onNewPredicate(predicate);
157            addPredicate(predicate);
158            return predicate;
159        }
160
161        @Override
162        protected ValueBuilder onNewValueBuilder(Expression exp) {
163            return new PredicateValueBuilder(exp);
164        }
165    }
166}