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