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    }