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