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.test.junit4;
018
019 import java.io.InputStream;
020 import java.util.Hashtable;
021 import java.util.Map;
022 import java.util.Properties;
023 import java.util.concurrent.TimeUnit;
024 import javax.naming.Context;
025 import javax.naming.InitialContext;
026
027 import org.apache.camel.CamelContext;
028 import org.apache.camel.ConsumerTemplate;
029 import org.apache.camel.Endpoint;
030 import org.apache.camel.Exchange;
031 import org.apache.camel.Expression;
032 import org.apache.camel.Message;
033 import org.apache.camel.Predicate;
034 import org.apache.camel.Processor;
035 import org.apache.camel.ProducerTemplate;
036 import org.apache.camel.Service;
037 import org.apache.camel.builder.RouteBuilder;
038 import org.apache.camel.component.mock.MockEndpoint;
039 import org.apache.camel.impl.BreakpointSupport;
040 import org.apache.camel.impl.DefaultCamelContext;
041 import org.apache.camel.impl.DefaultDebugger;
042 import org.apache.camel.impl.InterceptSendToMockEndpointStrategy;
043 import org.apache.camel.impl.JndiRegistry;
044 import org.apache.camel.management.JmxSystemPropertyKeys;
045 import org.apache.camel.model.ProcessorDefinition;
046 import org.apache.camel.spi.Language;
047 import org.apache.camel.spring.CamelBeanPostProcessor;
048 import org.junit.After;
049 import org.junit.Before;
050
051 /**
052 * A useful base class which creates a {@link org.apache.camel.CamelContext} with some routes
053 * along with a {@link org.apache.camel.ProducerTemplate} for use in the test case
054 *
055 * @version
056 */
057 public abstract class CamelTestSupport extends TestSupport {
058
059 protected volatile CamelContext context;
060 protected volatile ProducerTemplate template;
061 protected volatile ConsumerTemplate consumer;
062 private boolean useRouteBuilder = true;
063 private Service camelContextService;
064 private final DebugBreakpoint breakpoint = new DebugBreakpoint();
065
066 /**
067 * Use the RouteBuilder or not
068 * @return
069 * If the return value is true, the camel context will be started in the setup method.
070 * If the return value is false, the camel context will not be started in the setup method.
071 */
072 public boolean isUseRouteBuilder() {
073 return useRouteBuilder;
074 }
075
076 /**
077 * Override to enable auto mocking endpoints based on the pattern.
078 * <p/>
079 * Return <tt>*</tt> to mock all endpoints.
080 *
081 * @see org.apache.camel.util.EndpointHelper#matchEndpoint(String, String)
082 */
083 public String isMockEndpoints() {
084 return null;
085 }
086
087 public void setUseRouteBuilder(boolean useRouteBuilder) {
088 this.useRouteBuilder = useRouteBuilder;
089 }
090
091 public Service getCamelContextService() {
092 return camelContextService;
093 }
094
095 /**
096 * Allows a service to be registered a separate lifecycle service to start
097 * and stop the context; such as for Spring when the ApplicationContext is
098 * started and stopped, rather than directly stopping the CamelContext
099 */
100 public void setCamelContextService(Service camelContextService) {
101 this.camelContextService = camelContextService;
102 }
103
104 @Before
105 public void setUp() throws Exception {
106 log.info("********************************************************************************");
107 log.info("Testing: " + getTestMethodName() + "(" + getClass().getName() + ")");
108 log.info("********************************************************************************");
109
110 log.debug("setUp test");
111 if (!useJmx()) {
112 disableJMX();
113 } else {
114 enableJMX();
115 }
116
117 context = createCamelContext();
118 assertValidContext(context);
119
120 // reduce default shutdown timeout to avoid waiting for 300 seconds
121 context.getShutdownStrategy().setTimeout(getShutdownTimeout());
122
123 // set debugger
124 context.setDebugger(new DefaultDebugger());
125 context.getDebugger().addBreakpoint(breakpoint);
126 // note: when stopping CamelContext it will automatic remove the breakpoint
127
128 template = context.createProducerTemplate();
129 template.start();
130 consumer = context.createConsumerTemplate();
131 consumer.start();
132
133 // enable auto mocking if enabled
134 String pattern = isMockEndpoints();
135 if (pattern != null) {
136 context.addRegisterEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern));
137 }
138
139 postProcessTest();
140
141 if (isUseRouteBuilder()) {
142 RouteBuilder[] builders = createRouteBuilders();
143 for (RouteBuilder builder : builders) {
144 log.debug("Using created route builder: " + builder);
145 context.addRoutes(builder);
146 }
147 startCamelContext();
148 log.debug("Routing Rules are: " + context.getRoutes());
149 } else {
150 log.debug("Using route builder from the created context: " + context);
151 }
152 log.debug("Routing Rules are: " + context.getRoutes());
153 }
154
155 @After
156 public void tearDown() throws Exception {
157 log.info("Testing done: " + this);
158
159 log.debug("tearDown test");
160 if (consumer != null) {
161 consumer.stop();
162 }
163 if (template != null) {
164 template.stop();
165 }
166 stopCamelContext();
167 }
168
169 /**
170 * Returns the timeout to use when shutting down (unit in seconds).
171 * <p/>
172 * Will default use 10 seconds.
173 *
174 * @return the timeout to use
175 */
176 protected int getShutdownTimeout() {
177 return 10;
178 }
179
180 /**
181 * Whether or not JMX should be used during testing.
182 *
183 * @return <tt>false</tt> by default.
184 */
185 protected boolean useJmx() {
186 return false;
187 }
188
189 /**
190 * Lets post process this test instance to process any Camel annotations.
191 * Note that using Spring Test or Guice is a more powerful approach.
192 */
193 protected void postProcessTest() throws Exception {
194 CamelBeanPostProcessor processor = new CamelBeanPostProcessor();
195 processor.setCamelContext(context);
196 processor.postProcessBeforeInitialization(this, "this");
197 }
198
199 protected void stopCamelContext() throws Exception {
200 if (camelContextService != null) {
201 camelContextService.stop();
202 } else {
203 if (context != null) {
204 context.stop();
205 }
206 }
207 }
208
209 protected void startCamelContext() throws Exception {
210 if (camelContextService != null) {
211 camelContextService.start();
212 } else {
213 if (context instanceof DefaultCamelContext) {
214 DefaultCamelContext defaultCamelContext = (DefaultCamelContext)context;
215 if (!defaultCamelContext.isStarted()) {
216 defaultCamelContext.start();
217 }
218 } else {
219 context.start();
220 }
221 }
222 }
223
224 protected CamelContext createCamelContext() throws Exception {
225 return new DefaultCamelContext(createRegistry());
226 }
227
228 protected JndiRegistry createRegistry() throws Exception {
229 return new JndiRegistry(createJndiContext());
230 }
231
232 @SuppressWarnings("unchecked")
233 protected Context createJndiContext() throws Exception {
234 Properties properties = new Properties();
235
236 // jndi.properties is optional
237 InputStream in = getClass().getClassLoader().getResourceAsStream("jndi.properties");
238 if (in != null) {
239 log.debug("Using jndi.properties from classpath root");
240 properties.load(in);
241 } else {
242 properties.put("java.naming.factory.initial", "org.apache.camel.util.jndi.CamelInitialContextFactory");
243 }
244 return new InitialContext(new Hashtable(properties));
245 }
246
247 /**
248 * Factory method which derived classes can use to create a {@link RouteBuilder}
249 * to define the routes for testing
250 */
251 protected RouteBuilder createRouteBuilder() throws Exception {
252 return new RouteBuilder() {
253 public void configure() {
254 // no routes added by default
255 }
256 };
257 }
258
259 /**
260 * Factory method which derived classes can use to create an array of
261 * {@link org.apache.camel.builder.RouteBuilder}s to define the routes for testing
262 *
263 * @see #createRouteBuilder()
264 */
265 protected RouteBuilder[] createRouteBuilders() throws Exception {
266 return new RouteBuilder[] {createRouteBuilder()};
267 }
268
269 /**
270 * Resolves a mandatory endpoint for the given URI or an exception is thrown
271 *
272 * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
273 * @return the endpoint
274 */
275 protected Endpoint resolveMandatoryEndpoint(String uri) {
276 return resolveMandatoryEndpoint(context, uri);
277 }
278
279 /**
280 * Resolves a mandatory endpoint for the given URI and expected type or an exception is thrown
281 *
282 * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
283 * @return the endpoint
284 */
285 protected <T extends Endpoint> T resolveMandatoryEndpoint(String uri, Class<T> endpointType) {
286 return resolveMandatoryEndpoint(context, uri, endpointType);
287 }
288
289 /**
290 * Resolves the mandatory Mock endpoint using a URI of the form <code>mock:someName</code>
291 *
292 * @param uri the URI which typically starts with "mock:" and has some name
293 * @return the mandatory mock endpoint or an exception is thrown if it could not be resolved
294 */
295 protected MockEndpoint getMockEndpoint(String uri) {
296 return resolveMandatoryEndpoint(uri, MockEndpoint.class);
297 }
298
299 /**
300 * Sends a message to the given endpoint URI with the body value
301 *
302 * @param endpointUri the URI of the endpoint to send to
303 * @param body the body for the message
304 */
305 protected void sendBody(String endpointUri, final Object body) {
306 template.send(endpointUri, new Processor() {
307 public void process(Exchange exchange) {
308 Message in = exchange.getIn();
309 in.setBody(body);
310 }
311 });
312 }
313
314 /**
315 * Sends a message to the given endpoint URI with the body value and specified headers
316 *
317 * @param endpointUri the URI of the endpoint to send to
318 * @param body the body for the message
319 * @param headers any headers to set on the message
320 */
321 protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
322 template.send(endpointUri, new Processor() {
323 public void process(Exchange exchange) {
324 Message in = exchange.getIn();
325 in.setBody(body);
326 for (Map.Entry<String, Object> entry : headers.entrySet()) {
327 in.setHeader(entry.getKey(), entry.getValue());
328 }
329 }
330 });
331 }
332
333 /**
334 * Sends messages to the given endpoint for each of the specified bodies
335 *
336 * @param endpointUri the endpoint URI to send to
337 * @param bodies the bodies to send, one per message
338 */
339 protected void sendBodies(String endpointUri, Object... bodies) {
340 for (Object body : bodies) {
341 sendBody(endpointUri, body);
342 }
343 }
344
345 /**
346 * Creates an exchange with the given body
347 */
348 protected Exchange createExchangeWithBody(Object body) {
349 return createExchangeWithBody(context, body);
350 }
351
352 /**
353 * Asserts that the given language name and expression evaluates to the
354 * given value on a specific exchange
355 */
356 protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
357 Language language = assertResolveLanguage(languageName);
358
359 Expression expression = language.createExpression(expressionText);
360 assertNotNull("No Expression could be created for text: " + expressionText + " language: " + language, expression);
361
362 assertExpression(expression, exchange, expectedValue);
363 }
364
365 /**
366 * Asserts that the given language name and predicate expression evaluates
367 * to the expected value on the message exchange
368 */
369 protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
370 Language language = assertResolveLanguage(languageName);
371
372 Predicate predicate = language.createPredicate(expressionText);
373 assertNotNull("No Predicate could be created for text: " + expressionText + " language: " + language, predicate);
374
375 assertPredicate(predicate, exchange, expected);
376 }
377
378 /**
379 * Asserts that the language name can be resolved
380 */
381 protected Language assertResolveLanguage(String languageName) {
382 Language language = context.resolveLanguage(languageName);
383 assertNotNull("No language found for name: " + languageName, language);
384 return language;
385 }
386
387 /**
388 * Asserts that all the expectations of the Mock endpoints are valid
389 */
390 protected void assertMockEndpointsSatisfied() throws InterruptedException {
391 MockEndpoint.assertIsSatisfied(context);
392 }
393
394 /**
395 * Asserts that all the expectations of the Mock endpoints are valid
396 */
397 protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
398 MockEndpoint.assertIsSatisfied(context, timeout, unit);
399 }
400
401 /**
402 * Reset all Mock endpoints.
403 */
404 protected void resetMocks() {
405 MockEndpoint.resetMocks(context);
406 }
407
408 protected void assertValidContext(CamelContext context) {
409 assertNotNull("No context found!", context);
410 }
411
412 protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
413 T endpoint = context.getEndpoint(uri, type);
414 assertNotNull("No endpoint found for uri: " + uri, endpoint);
415 return endpoint;
416 }
417
418 protected Endpoint getMandatoryEndpoint(String uri) {
419 Endpoint endpoint = context.getEndpoint(uri);
420 assertNotNull("No endpoint found for uri: " + uri, endpoint);
421 return endpoint;
422 }
423
424 /**
425 * Disables the JMX agent. Must be called before the {@link #setUp()} method.
426 */
427 protected void disableJMX() {
428 System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
429 }
430
431 /**
432 * Enables the JMX agent. Must be called before the {@link #setUp()} method.
433 */
434 protected void enableJMX() {
435 System.setProperty(JmxSystemPropertyKeys.DISABLED, "false");
436 }
437
438 /**
439 * Single step debugs and Camel invokes this method before entering the given processor
440 */
441 protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition definition,
442 String id, String label) {
443 }
444
445 /**
446 * Single step debugs and Camel invokes this method after processing the given processor
447 */
448 protected void debugAfter(Exchange exchange, Processor processor, ProcessorDefinition definition,
449 String id, String label, long timeTaken) {
450 }
451
452 /**
453 * To easily debug by overriding the <tt>debugBefore</tt> and <tt>debugAfter</tt> methods.
454 */
455 private class DebugBreakpoint extends BreakpointSupport {
456
457 @Override
458 public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) {
459 CamelTestSupport.this.debugBefore(exchange, processor, definition, definition.getId(), definition.getLabel());
460 }
461
462 @Override
463 public void afterProcess(Exchange exchange, Processor processor, ProcessorDefinition definition, long timeTaken) {
464 CamelTestSupport.this.debugAfter(exchange, processor, definition, definition.getId(), definition.getLabel(), timeTaken);
465 }
466 }
467
468 }