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