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