001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.camel.component.mock; 019 020 import java.util.ArrayList; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.concurrent.CountDownLatch; 025 import java.util.concurrent.TimeUnit; 026 027 import org.apache.camel.Component; 028 import org.apache.camel.Consumer; 029 import org.apache.camel.Exchange; 030 import org.apache.camel.Processor; 031 import org.apache.camel.Producer; 032 import org.apache.camel.Message; 033 import org.apache.camel.impl.DefaultEndpoint; 034 import org.apache.camel.impl.DefaultExchange; 035 import org.apache.camel.impl.DefaultProducer; 036 import org.apache.camel.util.ObjectHelper; 037 import org.apache.commons.logging.Log; 038 import org.apache.commons.logging.LogFactory; 039 040 /** 041 * A Mock endpoint which provides a literate, fluent API for testing routes using 042 * a <a href="http://jmock.org/">JMock style</a> API. 043 * 044 * @version $Revision: 1.1 $ 045 */ 046 public class MockEndpoint extends DefaultEndpoint<Exchange> { 047 private static final transient Log log = LogFactory.getLog(MockEndpoint.class); 048 private int expectedCount = -1; 049 private Map<Integer, Processor> processors = new HashMap<Integer, Processor>(); 050 private List<Exchange> receivedExchanges = new ArrayList<Exchange>(); 051 private List<Throwable> failures = new ArrayList<Throwable>(); 052 private List<Runnable> tests = new ArrayList<Runnable>(); 053 private CountDownLatch latch; 054 private long sleepForEmptyTest = 0L; 055 private int expectedMinimumCount=-1; 056 057 public static void assertWait(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException { 058 long start = System.currentTimeMillis(); 059 long left = unit.toMillis(timeout); 060 long end = start + left; 061 for (MockEndpoint endpoint : endpoints) { 062 if( !endpoint.await(left, TimeUnit.MILLISECONDS) ) 063 throw new AssertionError("Timeout waiting for endpoints to receive enough messages. "+endpoint.getEndpointUri()+" timed out."); 064 left = end - System.currentTimeMillis(); 065 if( left <= 0 ) 066 left = 0; 067 } 068 } 069 070 public static void assertIsSatisfied(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException { 071 assertWait(timeout, unit, endpoints); 072 for (MockEndpoint endpoint : endpoints) { 073 endpoint.assertIsSatisfied(); 074 } 075 } 076 077 public static void assertIsSatisfied(MockEndpoint... endpoints) throws InterruptedException { 078 for (MockEndpoint endpoint : endpoints) { 079 endpoint.assertIsSatisfied(); 080 } 081 } 082 083 public static void expectsMessageCount(int count, MockEndpoint... endpoints) throws InterruptedException { 084 for (MockEndpoint endpoint : endpoints) { 085 endpoint.expectsMessageCount(count); 086 } 087 } 088 089 public MockEndpoint(String endpointUri, Component component) { 090 super(endpointUri, component); 091 } 092 093 public Exchange createExchange() { 094 return new DefaultExchange(getContext()); 095 } 096 097 public Consumer<Exchange> createConsumer(Processor processor) throws Exception { 098 throw new UnsupportedOperationException("You cannot consume from this endpoint"); 099 } 100 101 public Producer<Exchange> createProducer() throws Exception { 102 return new DefaultProducer<Exchange>(this) { 103 public void process(Exchange exchange) { 104 onExchange(exchange); 105 } 106 }; 107 } 108 109 // Testing API 110 //------------------------------------------------------------------------- 111 112 /** 113 * Validates that all the available expectations on this endpoint are satisfied; or throw an exception 114 */ 115 public void assertIsSatisfied() throws InterruptedException { 116 assertIsSatisfied(sleepForEmptyTest); 117 } 118 119 /** 120 * Validates that all the available expectations on this endpoint are satisfied; or throw an exception 121 */ 122 public void assertIsSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException { 123 if (latch != null) { 124 // now lets wait for the results 125 latch.await(10, TimeUnit.SECONDS); 126 } 127 else if (expectedCount == 0) { 128 // lets wait a little bit just in case 129 if (timeoutForEmptyEndpoints > 0) { 130 Thread.sleep(timeoutForEmptyEndpoints); 131 } 132 } 133 134 if (expectedCount >= 0) { 135 int receivedCounter = getReceivedCounter(); 136 assertEquals("Received message count" , expectedCount, receivedCounter); 137 } 138 139 if( expectedMinimumCount >= 0 ) { 140 int receivedCounter = getReceivedCounter(); 141 assertTrue("Received message count "+receivedCounter+", expected at least "+expectedCount, expectedCount <= receivedCounter); 142 143 } 144 145 for (Runnable test : tests) { 146 test.run(); 147 } 148 149 for (Throwable failure : failures) { 150 if (failure != null) { 151 log.error("Caught on " + getEndpointUri() + " Exception: " + failure, failure); 152 fail("Failed due to caught exception: " + failure); 153 } 154 } 155 } 156 157 /** 158 * Specifies the expected number of message exchanges that should be received by this endpoint 159 * 160 * @param expectedCount the number of message exchanges that should be expected by this endpoint 161 */ 162 public void expectedMessageCount(int expectedCount) { 163 this.expectedCount = expectedCount; 164 if (expectedCount <= 0) { 165 latch = null; 166 } 167 else { 168 latch = new CountDownLatch(expectedCount); 169 } 170 } 171 172 /** 173 * Specifies the minimum number of expected message exchanges that should be received by this endpoint 174 * 175 * @param expectedCount the number of message exchanges that should be expected by this endpoint 176 */ 177 public void expectedMinimumMessageCount(int expectedCount) { 178 this.expectedMinimumCount = expectedCount; 179 if (expectedCount <= 0) { 180 latch = null; 181 } 182 else { 183 latch = new CountDownLatch(expectedMinimumCount); 184 } 185 } 186 187 /** 188 * Adds an expectation that the given body values are received by this endpoint 189 */ 190 public void expectedBodiesReceived(final List bodies) { 191 expectedMessageCount(bodies.size()); 192 193 expects(new Runnable() { 194 public void run() { 195 int counter = 0; 196 for (Object expectedBody : bodies) { 197 Exchange exchange = getReceivedExchanges().get(counter++); 198 assertTrue("No exchange received for counter: " + counter, exchange != null); 199 200 Message in = exchange.getIn(); 201 202 Object actualBody = (expectedBody != null) 203 ? in.getBody(expectedBody.getClass()) : in.getBody(); 204 205 assertEquals("Body of message: " + counter, expectedBody, actualBody); 206 207 log.debug(getEndpointUri() + " >>>> message: " + counter + " with body: " + actualBody); 208 } 209 } 210 }); 211 } 212 213 /** 214 * Adds an expectation that the given body values are received by this endpoint 215 */ 216 public void expectedBodiesReceived(Object... bodies) { 217 List bodyList = new ArrayList(); 218 for (Object body : bodies) { 219 bodyList.add(body); 220 } 221 expectedBodiesReceived(bodyList); 222 } 223 224 225 /** 226 * Adds the expection which will be invoked when enough messages are received 227 */ 228 public void expects(Runnable runnable) { 229 tests.add(runnable); 230 } 231 232 /** 233 * Adds an assertion to the given message index 234 * 235 * @param messageIndex the number of the message 236 * @return the assertion clause 237 */ 238 public AssertionClause message(final int messageIndex) { 239 AssertionClause clause = new AssertionClause() { 240 public void run() { 241 applyAssertionOn(MockEndpoint.this, messageIndex, assertExchangeReceived(messageIndex)); 242 } 243 }; 244 expects(clause); 245 return clause; 246 } 247 248 /** 249 * Adds an assertion to all the received messages 250 * 251 * @return the assertion clause 252 */ 253 public AssertionClause allMessages() { 254 AssertionClause clause = new AssertionClause() { 255 public void run() { 256 List<Exchange> list = getReceivedExchanges(); 257 int index = 0; 258 for (Exchange exchange : list) { 259 applyAssertionOn(MockEndpoint.this, index++, exchange); 260 } 261 } 262 }; 263 expects(clause); 264 return clause; 265 } 266 267 /** 268 * Asserts that the given index of message is received (starting at zero) 269 */ 270 public Exchange assertExchangeReceived(int index) { 271 int count = getReceivedCounter(); 272 assertTrue("Not enough messages received. Was: " + count, count > index); 273 return getReceivedExchanges().get(index); 274 } 275 276 // Properties 277 //------------------------------------------------------------------------- 278 public List<Throwable> getFailures() { 279 return failures; 280 } 281 282 public int getReceivedCounter() { 283 return getReceivedExchanges().size(); 284 } 285 286 public List<Exchange> getReceivedExchanges() { 287 return receivedExchanges; 288 } 289 290 public int getExpectedCount() { 291 return expectedCount; 292 } 293 294 public long getSleepForEmptyTest() { 295 return sleepForEmptyTest; 296 } 297 298 /** 299 * Allows a sleep to be specified to wait to check that this endpoint really is empty when 300 * {@link #expectedMessageCount(int)} is called with zero 301 * 302 * @param sleepForEmptyTest the milliseconds to sleep for to determine that this endpoint really is empty 303 */ 304 public void setSleepForEmptyTest(long sleepForEmptyTest) { 305 this.sleepForEmptyTest = sleepForEmptyTest; 306 } 307 308 // Implementation methods 309 //------------------------------------------------------------------------- 310 protected synchronized void onExchange(Exchange exchange) { 311 try { 312 log.debug(getEndpointUri() + " >>>> " + exchange); 313 314 receivedExchanges.add(exchange); 315 316 Processor processor = processors.get(getReceivedCounter()); 317 if (processor != null) { 318 processor.process(exchange); 319 } 320 321 if (latch != null) { 322 latch.countDown(); 323 } 324 } 325 catch (Exception e) { 326 failures.add(e); 327 } 328 } 329 330 protected void assertEquals(String message, Object expectedValue, Object actualValue) { 331 if (!ObjectHelper.equals(expectedValue, actualValue)) { 332 fail(message + ". Expected: <" + expectedValue + "> but was: <" + actualValue + ">"); 333 } 334 } 335 336 protected void assertTrue(String message, boolean predicate) { 337 if (!predicate) { 338 fail(message); 339 } 340 } 341 342 protected void fail(Object message) { 343 throw new AssertionError(getEndpointUri() + " " + message); 344 } 345 346 public int getExpectedMinimumCount() { 347 return expectedMinimumCount; 348 } 349 350 public void await() throws InterruptedException { 351 if( latch!=null ) { 352 latch.await(); 353 } 354 } 355 356 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { 357 if( latch!=null ) { 358 return latch.await(timeout, unit); 359 } 360 return true; 361 } 362 363 public boolean isSingleton() { 364 return true; 365 } 366 367 }