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