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.ArrayList;
020import java.util.Date;
021import java.util.List;
022
023import org.apache.camel.Exchange;
024import org.apache.camel.Expression;
025import org.apache.camel.Predicate;
026import org.apache.camel.builder.ExpressionClause;
027import org.apache.camel.builder.ExpressionClauseSupport;
028import org.apache.camel.builder.ValueBuilder;
029import org.apache.camel.model.language.ExpressionDefinition;
030import org.apache.camel.util.PredicateAssertHelper;
031
032/**
033 * A builder of assertions on message exchanges
034 *
035 * @version 
036 */
037public abstract class AssertionClause extends ExpressionClauseSupport<ValueBuilder> implements Runnable {
038
039    protected final MockEndpoint mock;
040    protected volatile int currentIndex;
041    private final List<Predicate> predicates = new ArrayList<Predicate>();
042    private final Expression previous = new PreviousTimestamp();
043    private final Expression next = new NextTimestamp();
044
045    public AssertionClause(MockEndpoint mock) {
046        super(null);
047        this.mock = mock;
048    }
049
050    // Builder methods
051    // -------------------------------------------------------------------------
052
053    public ValueBuilder expression(Expression expression) {
054        // must override this method as we provide null in the constructor
055        super.expression(expression);
056        return new PredicateValueBuilder(getExpressionValue());
057    }
058
059    public ValueBuilder expression(ExpressionDefinition expression) {
060        // must override this method as we provide null in the constructor
061        super.expression(expression);
062        return new PredicateValueBuilder(expression.createExpression(mock.getCamelContext()));
063    }
064
065    /**
066     * Adds the given predicate to this assertion clause
067     */
068    public AssertionClause predicate(Predicate predicate) {
069        addPredicate(predicate);
070        return this;
071    }
072
073    /**
074     * Adds the given predicate to this assertion clause
075     */
076    public ExpressionClause<AssertionClause> predicate() {
077        ExpressionClause<AssertionClause> clause = new ExpressionClause<AssertionClause>(this);
078        addPredicate(clause);
079        return clause;
080    }
081
082    /**
083     * Adds a {@link TimeClause} predicate for message arriving.
084     */
085    public TimeClause arrives() {
086        final TimeClause clause = new TimeClause(previous, next);
087        addPredicate(new Predicate() {
088            public boolean matches(Exchange exchange) {
089                return clause.matches(exchange);
090            }
091
092            @Override
093            public String toString() {
094                return "arrives " + clause.toString() + " exchange";
095            }
096        });
097        return clause;
098    }
099
100    /**
101     * Performs any assertions on the given exchange
102     */
103    protected void applyAssertionOn(MockEndpoint endpoint, int index, Exchange exchange) {
104        for (Predicate predicate : predicates) {
105            currentIndex = index;
106            PredicateAssertHelper.assertMatches(predicate, "Assertion error at index " + index + " on mock " + endpoint.getEndpointUri() + " with predicate: ", exchange);
107        }
108    }
109
110    protected void addPredicate(Predicate predicate) {
111        predicates.add(predicate);
112    }
113
114    @SuppressWarnings("unchecked")
115    private final class PreviousTimestamp implements Expression {
116        public <T> T evaluate(Exchange exchange, Class<T> type) {
117            Date answer = null;
118            if (currentIndex > 0 && mock.getReceivedCounter() > 0) {
119                answer = mock.getReceivedExchanges().get(currentIndex - 1).getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
120            }
121            return (T) answer;
122        }
123    }
124
125    @SuppressWarnings("unchecked")
126    private final class NextTimestamp implements Expression {
127        public <T> T evaluate(Exchange exchange, Class<T> type) {
128            Date answer = null;
129            if (currentIndex < mock.getReceivedCounter() - 1) {
130                answer = mock.getReceivedExchanges().get(currentIndex + 1).getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
131            }
132            return (T) answer;
133        }
134    }
135
136    /**
137     * Public class needed for fluent builders
138     */
139    public final class PredicateValueBuilder extends ValueBuilder {
140
141        public PredicateValueBuilder(Expression expression) {
142            super(expression);
143        }
144
145        @Override
146        protected Predicate onNewPredicate(Predicate predicate) {
147            predicate = super.onNewPredicate(predicate);
148            addPredicate(predicate);
149            return predicate;
150        }
151
152        @Override
153        protected ValueBuilder onNewValueBuilder(Expression exp) {
154            return new PredicateValueBuilder(exp);
155        }
156    }
157}