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: 19087 $
051     */
052    public abstract class CamelTestSupport extends TestSupport {    
053        
054        protected CamelContext context;
055        protected ProducerTemplate template;
056        protected 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(10);
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         * Whether or not JMX should be used during testing.
135         *
136         * @return <tt>false</tt> by default.
137         */
138        protected boolean useJmx() {
139            return false;
140        }
141    
142        /**
143         * Lets post process this test instance to process any Camel annotations.
144         * Note that using Spring Test or Guice is a more powerful approach.
145         */
146        protected void postProcessTest() throws Exception {
147            CamelBeanPostProcessor processor = new CamelBeanPostProcessor();
148            processor.setCamelContext(context);
149            processor.postProcessBeforeInitialization(this, "this");
150        }
151    
152        protected void stopCamelContext() throws Exception {
153            if (camelContextService != null) {
154                camelContextService.stop();
155            } else {
156                if (context != null) {
157                    context.stop();
158                }    
159            }
160        }
161    
162        protected void startCamelContext() throws Exception {
163            if (camelContextService != null) {
164                camelContextService.start();
165            } else {
166                if (context instanceof DefaultCamelContext) {
167                    DefaultCamelContext defaultCamelContext = (DefaultCamelContext)context;
168                    if (!defaultCamelContext.isStarted()) {
169                        defaultCamelContext.start();
170                    }
171                } else {
172                    context.start();
173                }
174            }
175        }
176    
177        protected CamelContext createCamelContext() throws Exception {
178            return new DefaultCamelContext(createRegistry());
179        }
180    
181        protected JndiRegistry createRegistry() throws Exception {
182            return new JndiRegistry(createJndiContext());
183        }
184    
185        @SuppressWarnings("unchecked")
186        protected Context createJndiContext() throws Exception {
187            Properties properties = new Properties();
188    
189            // jndi.properties is optional
190            InputStream in = getClass().getClassLoader().getResourceAsStream("jndi.properties");
191            if (in != null) {
192                log.debug("Using jndi.properties from classpath root");
193                properties.load(in);
194            } else {
195                // set the default initial factory
196                properties.put("java.naming.factory.initial", "org.apache.camel.util.jndi.CamelInitialContextFactory");
197            }
198            return new InitialContext(new Hashtable(properties));
199        }
200    
201        /**
202         * Factory method which derived classes can use to create a {@link RouteBuilder}
203         * to define the routes for testing
204         */
205        protected RouteBuilder createRouteBuilder() throws Exception {
206            return new RouteBuilder() {
207                public void configure() {
208                    // no routes added by default
209                }
210            };
211        }
212    
213        /**
214         * Factory method which derived classes can use to create an array of
215         * {@link org.apache.camel.builder.RouteBuilder}s to define the routes for testing
216         *
217         * @see #createRouteBuilder()
218         */
219        protected RouteBuilder[] createRouteBuilders() throws Exception {
220            return new RouteBuilder[] {createRouteBuilder()};
221        }
222    
223        /**
224         * Resolves a mandatory endpoint for the given URI or an exception is thrown
225         *
226         * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
227         * @return the endpoint
228         */
229        protected Endpoint resolveMandatoryEndpoint(String uri) {
230            return resolveMandatoryEndpoint(context, uri);
231        }
232    
233        /**
234         * Resolves a mandatory endpoint for the given URI and expected type or an exception is thrown
235         *
236         * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
237         * @return the endpoint
238         */
239        protected <T extends Endpoint> T resolveMandatoryEndpoint(String uri, Class<T> endpointType) {
240            return resolveMandatoryEndpoint(context, uri, endpointType);
241        }
242    
243        /**
244         * Resolves the mandatory Mock endpoint using a URI of the form <code>mock:someName</code>
245         *
246         * @param uri the URI which typically starts with "mock:" and has some name
247         * @return the mandatory mock endpoint or an exception is thrown if it could not be resolved
248         */
249        protected MockEndpoint getMockEndpoint(String uri) {
250            return resolveMandatoryEndpoint(uri, MockEndpoint.class);
251        }
252    
253        /**
254         * Sends a message to the given endpoint URI with the body value
255         *
256         * @param endpointUri the URI of the endpoint to send to
257         * @param body        the body for the message
258         */
259        protected void sendBody(String endpointUri, final Object body) {
260            template.send(endpointUri, new Processor() {
261                public void process(Exchange exchange) {
262                    Message in = exchange.getIn();
263                    in.setBody(body);
264                    in.setHeader("testCase", getName());
265                }
266            });
267        }
268    
269        /**
270         * Sends a message to the given endpoint URI with the body value and specified headers
271         *
272         * @param endpointUri the URI of the endpoint to send to
273         * @param body        the body for the message
274         * @param headers     any headers to set on the message
275         */
276        protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
277            template.send(endpointUri, new Processor() {
278                public void process(Exchange exchange) {
279                    Message in = exchange.getIn();
280                    in.setBody(body);
281                    in.setHeader("testCase", getName());
282                    for (Map.Entry<String, Object> entry : headers.entrySet()) {
283                        in.setHeader(entry.getKey(), entry.getValue());
284                    }
285                }
286            });
287        }
288    
289        /**
290         * Sends messages to the given endpoint for each of the specified bodies
291         *
292         * @param endpointUri the endpoint URI to send to
293         * @param bodies      the bodies to send, one per message
294         */
295        protected void sendBodies(String endpointUri, Object... bodies) {
296            for (Object body : bodies) {
297                sendBody(endpointUri, body);
298            }
299        }
300    
301        /**
302         * Creates an exchange with the given body
303         */
304        protected Exchange createExchangeWithBody(Object body) {
305            return createExchangeWithBody(context, body);
306        }
307    
308        /**
309         * Asserts that the given language name and expression evaluates to the
310         * given value on a specific exchange
311         */
312        protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
313            Language language = assertResolveLanguage(languageName);
314    
315            Expression expression = language.createExpression(expressionText);
316            assertNotNull("No Expression could be created for text: " + expressionText + " language: " + language, expression);
317    
318            assertExpression(expression, exchange, expectedValue);
319        }
320    
321        /**
322         * Asserts that the given language name and predicate expression evaluates
323         * to the expected value on the message exchange
324         */
325        protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
326            Language language = assertResolveLanguage(languageName);
327    
328            Predicate predicate = language.createPredicate(expressionText);
329            assertNotNull("No Predicate could be created for text: " + expressionText + " language: " + language, predicate);
330    
331            assertPredicate(predicate, exchange, expected);
332        }
333    
334        /**
335         * Asserts that the language name can be resolved
336         */
337        protected Language assertResolveLanguage(String languageName) {
338            Language language = context.resolveLanguage(languageName);
339            assertNotNull("No language found for name: " + languageName, language);
340            return language;
341        }
342    
343        /**
344         * Asserts that all the expectations of the Mock endpoints are valid
345         */
346        protected void assertMockEndpointsSatisfied() throws InterruptedException {
347            MockEndpoint.assertIsSatisfied(context);
348        }
349    
350        /**
351         * Asserts that all the expectations of the Mock endpoints are valid
352         */
353        protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
354            MockEndpoint.assertIsSatisfied(context, timeout, unit);
355        }
356    
357        /**
358         * Reset all Mock endpoints.
359         */
360        protected void resetMocks() {
361            MockEndpoint.resetMocks(context);
362        }
363    
364        protected void assertValidContext(CamelContext context) {
365            assertNotNull("No context found!", context);
366        }
367    
368        protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
369            T endpoint = context.getEndpoint(uri, type);
370            assertNotNull("No endpoint found for uri: " + uri, endpoint);
371            return endpoint;
372        }
373    
374        protected Endpoint getMandatoryEndpoint(String uri) {
375            Endpoint endpoint = context.getEndpoint(uri);
376            assertNotNull("No endpoint found for uri: " + uri, endpoint);
377            return endpoint;
378        }
379    
380        /**
381         * Disables the JMX agent. Must be called before the {@link #setUp()} method.
382         */
383        protected void disableJMX() {
384            System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
385        }
386    
387        /**
388         * Enables the JMX agent. Must be called before the {@link #setUp()} method.
389         */
390        protected void enableJMX() {
391            System.setProperty(JmxSystemPropertyKeys.DISABLED, "false");
392        }
393    
394    }