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    }