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