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