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}