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.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    import org.junit.After;
047    import org.junit.Before;
048    
049    /**
050     * A useful base class which creates a {@link org.apache.camel.CamelContext} with some routes
051     * along with a {@link org.apache.camel.ProducerTemplate} for use in the test case
052     *
053     * @version $Revision: 786457 $
054     */
055    public abstract class CamelTestSupport extends TestSupport {    
056        
057        protected CamelContext context;
058        protected ProducerTemplate template;
059        protected ConsumerTemplate consumer;
060        private boolean useRouteBuilder = true;
061        private Service camelContextService;
062    
063        public boolean isUseRouteBuilder() {
064            return useRouteBuilder;
065        }
066    
067        public void setUseRouteBuilder(boolean useRouteBuilder) {
068            this.useRouteBuilder = useRouteBuilder;
069        }
070    
071        public Service getCamelContextService() {
072            return camelContextService;
073        }
074    
075        /**
076         * Allows a service to be registered a separate lifecycle service to start
077         * and stop the context; such as for Spring when the ApplicationContext is
078         * started and stopped, rather than directly stopping the CamelContext
079         */
080        public void setCamelContextService(Service camelContextService) {
081            this.camelContextService = camelContextService;
082        }
083    
084        @Before
085        public void setUp() throws Exception {
086            context = createCamelContext();
087            assertValidContext(context);
088    
089            template = context.createProducerTemplate();
090            consumer = context.createConsumerTemplate();
091    
092            postProcessTest();
093            
094            if (isUseRouteBuilder()) {
095                RouteBuilder[] builders = createRouteBuilders();
096                for (RouteBuilder builder : builders) {
097                    log.debug("Using created route builder: " + builder);
098                    context.addRoutes(builder);
099                }
100                startCamelContext();
101                log.debug("Routing Rules are: " + context.getRoutes());
102            } else {
103                log.debug("Using route builder from the created context: " + context);
104            }
105            log.debug("Routing Rules are: " + context.getRoutes());
106        }
107    
108        @After
109        public void tearDown() throws Exception {
110            log.debug("tearDown test ");
111            if (consumer != null) {
112                consumer.stop();
113            }
114            if (template != null) {
115                template.stop();
116            }
117            stopCamelContext();
118        }
119    
120        /**
121         * Lets post process this test instance to process any Camel annotations.
122         * Note that using Spring Test or Guice is a more powerful approach.
123         */
124        protected void postProcessTest() throws Exception {
125            CamelBeanPostProcessor processor = new CamelBeanPostProcessor();
126            processor.setCamelContext(context);
127            processor.postProcessBeforeInitialization(this, "this");
128        }
129        
130        protected void stopCamelContext() throws Exception {
131            if (camelContextService != null) {
132                camelContextService.stop();
133            } else {
134                if (context != null) {
135                    context.stop();
136                }    
137            }
138        }
139    
140        protected void startCamelContext() throws Exception {
141            if (camelContextService != null) {
142                camelContextService.start();
143            } else {
144                if (context instanceof DefaultCamelContext) {
145                    DefaultCamelContext defaultCamelContext = (DefaultCamelContext)context;
146                    if (!defaultCamelContext.isStarted()) {
147                        defaultCamelContext.start();
148                    }
149                } else {
150                    context.start();
151                }
152            }
153        }
154    
155        protected CamelContext createCamelContext() throws Exception {
156            return new DefaultCamelContext(createRegistry());
157        }
158    
159        protected JndiRegistry createRegistry() throws Exception {
160            return new JndiRegistry(createJndiContext());
161        }
162    
163        protected Context createJndiContext() throws Exception {
164            Properties properties = new Properties();
165    
166            // jndi.properties is optional
167            InputStream in = getClass().getClassLoader().getResourceAsStream("jndi.properties");
168            if (in != null) {
169                log.debug("Using jndi.properties from classpath root");
170                properties.load(in);
171            } else {            
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                }
241            });
242        }
243    
244        /**
245         * Sends a message to the given endpoint URI with the body value and specified headers
246         *
247         * @param endpointUri the URI of the endpoint to send to
248         * @param body        the body for the message
249         * @param headers     any headers to set on the message
250         */
251        protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
252            template.send(endpointUri, new Processor() {
253                public void process(Exchange exchange) {
254                    Message in = exchange.getIn();
255                    in.setBody(body);                
256                    for (Map.Entry<String, Object> entry : headers.entrySet()) {
257                        in.setHeader(entry.getKey(), entry.getValue());
258                    }
259                }
260            });
261        }
262    
263        /**
264         * Sends messages to the given endpoint for each of the specified bodies
265         *
266         * @param endpointUri the endpoint URI to send to
267         * @param bodies      the bodies to send, one per message
268         */
269        protected void sendBodies(String endpointUri, Object... bodies) {
270            for (Object body : bodies) {
271                sendBody(endpointUri, body);
272            }
273        }
274    
275        /**
276         * Creates an exchange with the given body
277         */
278        protected Exchange createExchangeWithBody(Object body) {
279            return createExchangeWithBody(context, body);
280        }
281    
282        /**
283         * Asserts that the given language name and expression evaluates to the
284         * given value on a specific exchange
285         */
286        protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
287            Language language = assertResolveLanguage(languageName);
288    
289            Expression expression = language.createExpression(expressionText);
290            assertNotNull("No Expression could be created for text: " + expressionText + " language: " + language, expression);
291    
292            assertExpression(expression, exchange, expectedValue);
293        }
294    
295        /**
296         * Asserts that the given language name and predicate expression evaluates
297         * to the expected value on the message exchange
298         */
299        protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
300            Language language = assertResolveLanguage(languageName);
301    
302            Predicate predicate = language.createPredicate(expressionText);
303            assertNotNull("No Predicate could be created for text: " + expressionText + " language: " + language, predicate);
304    
305            assertPredicate(predicate, exchange, expected);
306        }
307    
308        /**
309         * Asserts that the language name can be resolved
310         */
311        protected Language assertResolveLanguage(String languageName) {
312            Language language = context.resolveLanguage(languageName);
313            assertNotNull("No language found for name: " + languageName, language);
314            return language;
315        }
316    
317        /**
318         * Asserts that all the expectations of the Mock endpoints are valid
319         */
320        protected void assertMockEndpointsSatisfied() throws InterruptedException {
321            MockEndpoint.assertIsSatisfied(context);
322        }
323    
324        protected void assertValidContext(CamelContext context) {
325            assertNotNull("No context found!", context);
326        }
327    
328        protected <T> List<T> getSingletonEndpoints(Class<T> type) {
329            return CamelContextHelper.getSingletonEndpoints(context, type);
330        }
331    
332        protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
333            T endpoint = context.getEndpoint(uri, type);
334            assertNotNull("No endpoint found for uri: " + uri, endpoint);
335            return endpoint;
336        }
337    
338        protected Endpoint getMandatoryEndpoint(String uri) {
339            Endpoint endpoint = context.getEndpoint(uri);
340            assertNotNull("No endpoint found for uri: " + uri, endpoint);
341            return endpoint;
342        }
343    
344        /**
345         * Disables the JMX agent. Must be called before the {@link #setUp()} method.
346         */
347        protected void disableJMX() {
348            System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
349        }
350    
351        /**
352         * Enables the JMX agent. Must be called before the {@link #setUp()} method.
353         */
354        protected void enableJMX() {
355            System.setProperty(JmxSystemPropertyKeys.DISABLED, "false");
356        }
357    }