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