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     */
017    package org.apache.camel.component.mock;
018    
019    import java.util.ArrayList;
020    import java.util.Date;
021    import java.util.List;
022    
023    import org.apache.camel.Exchange;
024    import org.apache.camel.Expression;
025    import org.apache.camel.Predicate;
026    import org.apache.camel.builder.ExpressionClause;
027    import org.apache.camel.builder.ExpressionClauseSupport;
028    import org.apache.camel.builder.ValueBuilder;
029    import org.apache.camel.model.language.ExpressionDefinition;
030    import org.apache.camel.util.PredicateAssertHelper;
031    
032    /**
033     * A builder of assertions on message exchanges
034     *
035     * @version 
036     */
037    public 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            protected Predicate onNewPredicate(Predicate predicate) {
146                predicate = super.onNewPredicate(predicate);
147                addPredicate(predicate);
148                return predicate;
149            }
150        }
151    }