You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2012/08/24 09:50:02 UTC

svn commit: r1376847 - in /camel/trunk/components: camel-spring-javaconfig/ camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/ camel-test-spring/src/main/java/org/apache/camel/test/spring/

Author: ningjiang
Date: Fri Aug 24 07:50:02 2012
New Revision: 1376847

URL: http://svn.apache.org/viewvc?rev=1376847&view=rev
Log:
CAMEL-5494 support for Camel's testing annotations with spring javaconfig out of box

Added:
    camel/trunk/components/camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/CamelSpringDelegatingTestContextLoaderTest.java
    camel/trunk/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java
Modified:
    camel/trunk/components/camel-spring-javaconfig/pom.xml

Modified: camel/trunk/components/camel-spring-javaconfig/pom.xml
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-spring-javaconfig/pom.xml?rev=1376847&r1=1376846&r2=1376847&view=diff
==============================================================================
--- camel/trunk/components/camel-spring-javaconfig/pom.xml (original)
+++ camel/trunk/components/camel-spring-javaconfig/pom.xml Fri Aug 24 07:50:02 2012
@@ -39,7 +39,8 @@
         org.springframework.*;version="[3,4)",
         *
     </camel.osgi.import>
-    <junit-version>4.8.1</junit-version>
+    <!-- The test module has the dependency of Spring 3.1.1, but the module can run with Spring 3.0.x out of box -->
+    <spring-version>3.1.1.RELEASE</spring-version>
   </properties>
   
   <dependencies>
@@ -58,6 +59,11 @@
       </exclusions>      
     </dependency>
     <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <version>${spring-version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.servicemix.bundles</groupId>
       <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
       <version>2.1_3_6</version>
@@ -67,13 +73,12 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>${junit-version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-test</artifactId>
-      <version>${spring-version}</version>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test-spring</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>javax.annotation</groupId>

Added: camel/trunk/components/camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/CamelSpringDelegatingTestContextLoaderTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/CamelSpringDelegatingTestContextLoaderTest.java?rev=1376847&view=auto
==============================================================================
--- camel/trunk/components/camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/CamelSpringDelegatingTestContextLoaderTest.java (added)
+++ camel/trunk/components/camel-spring-javaconfig/src/test/java/org/apache/camel/spring/javaconfig/test/CamelSpringDelegatingTestContextLoaderTest.java Fri Aug 24 07:50:02 2012
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.javaconfig.test;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
+import org.apache.camel.test.junit4.CamelSpringJUnit4ClassRunner;
+import org.apache.camel.test.spring.CamelSpringDelegatingTestContextLoader;
+import org.apache.camel.test.spring.MockEndpoints;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+/**
+ * Test for CamelSpringDelegatingTestContextLoader.
+ * 
+ */
+@RunWith(CamelSpringJUnit4ClassRunner.class)
+@ContextConfiguration(
+        classes = {CamelSpringDelegatingTestContextLoaderTest.TestConfig.class},
+        loader = CamelSpringDelegatingTestContextLoader.class
+)
+@MockEndpoints
+public class CamelSpringDelegatingTestContextLoaderTest {
+    @EndpointInject(uri = "mock:direct:end")
+    protected MockEndpoint endEndpoint;
+
+    @EndpointInject(uri = "mock:direct:error")
+    protected MockEndpoint errorEndpoint;
+
+    @Produce(uri = "direct:test")
+    protected ProducerTemplate testProducer;
+
+    @Configuration
+    public static class TestConfig extends SingleRouteCamelConfiguration {
+        @Bean
+        @Override
+        public RouteBuilder route() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() throws Exception {
+                    from("direct:test").errorHandler(deadLetterChannel("direct:error")).to("direct:end");
+
+                    from("direct:error").log("Received message on direct:error endpoint.");
+
+                    from("direct:end").log("Received message on direct:end endpoint.");
+                }
+            };
+        }
+    }
+
+    @Test
+    public void testRoute() throws InterruptedException {
+        endEndpoint.expectedMessageCount(1);
+        errorEndpoint.expectedMessageCount(0);
+
+        testProducer.sendBody("<name>test</name>");
+
+        endEndpoint.assertIsSatisfied();
+        errorEndpoint.assertIsSatisfied();
+    }
+}
\ No newline at end of file

Added: camel/trunk/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java?rev=1376847&view=auto
==============================================================================
--- camel/trunk/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java (added)
+++ camel/trunk/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java Fri Aug 24 07:50:02 2012
@@ -0,0 +1,377 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.impl.DefaultDebugger;
+import org.apache.camel.impl.InterceptSendToMockEndpointStrategy;
+import org.apache.camel.management.JmxSystemPropertyKeys;
+import org.apache.camel.spi.Breakpoint;
+import org.apache.camel.spi.Debugger;
+import org.apache.camel.spring.SpringCamelContext;
+import org.apache.camel.test.spring.CamelSpringTestHelper.DoToSpringCamelContextsStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.context.support.DelegatingSmartContextLoader;
+
+/**
+ * Custom TestContextLoader which fixes issues in Camel's JavaConfigContextLoader. (adds support for Camel's test annotations)
+ * <br>
+ * <em>This loader can handle either classes or locations for configuring the context.</em>
+ * <br>
+ */
+public class CamelSpringDelegatingTestContextLoader extends DelegatingSmartContextLoader
+{
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+    
+    
+    /*
+     * (non-Javadoc)
+     * @see org.springframework.test.context.support.DelegatingSmartContextLoader#loadContext(org.springframework.test.context.MergedContextConfiguration)
+     */
+    @Override
+    public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
+        
+        Class<?> testClass = getTestClass();
+        
+        if (logger.isDebugEnabled()) {
+            logger.debug(String.format("Loading ApplicationContext for merged context configuration [%s].",
+                mergedConfig));
+        }
+        
+        // Pre CamelContext(s) instantiation setup
+        handleDisableJmx(null, testClass);
+        
+        try {            
+            SpringCamelContext.setNoStart(true);
+            ConfigurableApplicationContext context = (ConfigurableApplicationContext) super.loadContext(mergedConfig);
+            SpringCamelContext.setNoStart(false);
+            
+            return loadContext(context, testClass);
+        } finally {
+            cleanup(testClass);
+        }
+    }
+
+    /**
+     * Performs the bulk of the Spring application context loading/customization.
+     *
+     * @param context the partially configured context.  The context should have the bean definitions loaded, but nothing else.
+     * @param testClass the test class being executed
+     *
+     * @return the initialized (refreshed) Spring application context
+     *
+     * @throws Exception if there is an error during initialization/customization
+     */
+    public ApplicationContext loadContext(ConfigurableApplicationContext context, Class<?> testClass)
+        throws Exception {
+            
+        AnnotationConfigUtils.registerAnnotationConfigProcessors((BeanDefinitionRegistry) context);
+        
+        // Post CamelContext(s) instantiation but pre CamelContext(s) start setup
+        handleProvidesBreakpoint(context, testClass);
+        handleShutdownTimeout(context, testClass);
+        handleMockEndpoints(context, testClass);
+        handleMockEndpointsAndSkip(context, testClass);
+        
+        // CamelContext(s) startup
+        handleCamelContextStartup(context, testClass);
+        
+        return context;
+    }
+    
+
+    /**
+     * Cleanup/restore global state to defaults / pre-test values after the test setup
+     * is complete. 
+     * 
+     * @param testClass the test class being executed
+     */
+    protected void cleanup(Class<?> testClass) {
+        SpringCamelContext.setNoStart(false);
+        
+        if (testClass.isAnnotationPresent(DisableJmx.class)) {
+            if (CamelSpringTestHelper.getOriginalJmxDisabled() == null) {
+                System.clearProperty(JmxSystemPropertyKeys.DISABLED);
+            } else {
+                System.setProperty(JmxSystemPropertyKeys.DISABLED,
+                    CamelSpringTestHelper.getOriginalJmxDisabled());
+            }
+        }
+    }
+    
+    /**
+     * Returns all methods defined in {@code clazz} and its superclasses/interfaces.
+     */
+    protected Collection<Method> getAllMethods(Class<?> clazz)  {
+        
+        Set<Method> methods = new HashSet<Method>();
+        Class<?> currentClass = clazz;
+        
+        while (currentClass != null) {
+            methods.addAll(Arrays.asList(clazz.getMethods()));
+            currentClass = currentClass.getSuperclass(); 
+        }
+                
+        return methods;
+    }
+
+    /**
+     * Handles disabling of JMX on Camel contexts based on {@link DisableJmx}.
+     *
+     * @param context the initialized Spring context
+     * @param testClass the test class being executed
+     */
+    protected void handleDisableJmx(ConfigurableApplicationContext context, Class<?> testClass) {
+        CamelSpringTestHelper.setOriginalJmxDisabledValue(
+                System.getProperty(JmxSystemPropertyKeys.DISABLED));
+        
+        if (testClass.isAnnotationPresent(DisableJmx.class)) {
+            if (testClass.getAnnotation(DisableJmx.class).value()) {
+                logger.info("Disabling Camel JMX globally as DisableJmx annotation was found "
+                        + "and disableJmx is set to true.");
+                
+                System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
+                
+            } else {
+                logger.info("Enabling Camel JMX as DisableJmx annotation was found "
+                        + "and disableJmx is set to false.");
+                
+                System.clearProperty(JmxSystemPropertyKeys.DISABLED);
+            }
+        } else {
+            logger.info("Disabling Camel JMX globally for tests by default.  Use the DisableJMX annotation to "
+                    + "override the default setting.");
+            
+            System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
+        }
+    }
+    
+    /**
+     * Handles the processing of the {@link ProvidesBreakpoint} annotation on a test class.  Exists here
+     * as it is needed in 
+     *
+     * @param context the initialized Spring context containing the Camel context(s) to insert breakpoints into 
+     * @param testClass the test class being processed
+     * @param log the logger to use
+     * @param statics if static methods or instance methods should be processed
+     *
+     * @throws Exception if there is an error processing the class
+     */
+    protected void handleProvidesBreakpoint(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+            
+        Collection<Method> methods = getAllMethods(testClass);
+        final List<Breakpoint> breakpoints = new LinkedList<Breakpoint>();
+        
+        for (Method method : methods) {
+            
+            if (AnnotationUtils.findAnnotation(method, ProvidesBreakpoint.class) != null) {
+                Class<?>[] argTypes = method.getParameterTypes();
+                if (argTypes.length != 0) {
+                    throw new IllegalArgumentException("Method [" + method.getName()
+                           + "] is annotated with ProvidesBreakpoint but is not a no-argument method.");
+                } else if (!Breakpoint.class.isAssignableFrom(method.getReturnType())) {
+                    throw new IllegalArgumentException("Method [" + method.getName()
+                           + "] is annotated with ProvidesBreakpoint but does not return a Breakpoint.");
+                } else if (!Modifier.isStatic(method.getModifiers())) {
+                    throw new IllegalArgumentException("Method [" + method.getName()
+                           + "] is annotated with ProvidesBreakpoint but is not static.");
+                } else if (!Modifier.isPublic(method.getModifiers())) {
+                    throw new IllegalArgumentException("Method [" + method.getName()
+                           + "] is annotated with ProvidesBreakpoint but is not public.");
+                }
+                
+                try {
+                    breakpoints.add((Breakpoint) method.invoke(null, new Object[] {}));
+                } catch (Exception e) {
+                    throw new RuntimeException("Method [" + method.getName()
+                           + "] threw exception during evaluation.", e);
+                }
+            }
+        }
+        
+        if (breakpoints.size() != 0) {
+        
+            CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() {
+                
+                public void execute(String contextName, SpringCamelContext camelContext)
+                    throws Exception {
+                    
+                    Debugger debugger = camelContext.getDebugger();
+                    if (debugger == null) {
+                        debugger = new DefaultDebugger();
+                        camelContext.setDebugger(debugger);
+                    }
+                    
+                    for (Breakpoint breakpoint : breakpoints) {
+                        logger.info(
+                                 "Adding Breakpoint [{}] to CamelContext with name [{}].",
+                                 breakpoint, contextName);
+                        
+                        debugger.addBreakpoint(breakpoint);
+                    }
+                }
+            });
+        }
+    }
+    
+    
+    /**
+     * Handles updating shutdown timeouts on Camel contexts based on {@link ShutdownTimeout}.
+     *
+     * @param context the initialized Spring context
+     * @param testClass the test class being executed
+     */
+    protected void handleShutdownTimeout(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+        
+        final int shutdownTimeout;
+        final TimeUnit shutdownTimeUnit;
+        if (testClass.isAnnotationPresent(ShutdownTimeout.class)) {
+            shutdownTimeout = testClass.getAnnotation(ShutdownTimeout.class).value();
+            shutdownTimeUnit = testClass.getAnnotation(ShutdownTimeout.class).timeUnit();
+        } else {
+            shutdownTimeout = 10;
+            shutdownTimeUnit = TimeUnit.SECONDS;
+        }
+        
+        CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() {
+            
+            public void execute(String contextName, SpringCamelContext camelContext)
+                throws Exception {
+                
+                logger.info(
+                        "Setting shutdown timeout to [{} {}] on CamelContext with name [{}].",
+                        new Object[] {shutdownTimeout, shutdownTimeUnit, contextName});
+                camelContext.getShutdownStrategy().setTimeout(shutdownTimeout);
+                camelContext.getShutdownStrategy().setTimeUnit(shutdownTimeUnit);
+            }
+        });
+    }
+    
+    /**
+     * Handles auto-intercepting of endpoints with mocks based on {@link MockEndpoints}.
+     *
+     * @param context the initialized Spring context
+     * @param testClass the test class being executed
+     */
+    protected void handleMockEndpoints(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+        if (testClass.isAnnotationPresent(MockEndpoints.class)) {
+            
+            final String mockEndpoints = testClass.getAnnotation(
+                    MockEndpoints.class).value();
+            CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() {
+                
+                public void execute(String contextName, SpringCamelContext camelContext)
+                    throws Exception {
+                    
+                    logger.info("Enabling auto mocking of endpoints matching pattern [{}] on "
+                            + "CamelContext with name [{}].", mockEndpoints, contextName);
+                    camelContext.addRegisterEndpointCallback(
+                            new InterceptSendToMockEndpointStrategy(mockEndpoints));
+                }
+            });
+        }
+    }
+    
+    /**
+     * Handles auto-intercepting of endpoints with mocks based on {@link MockEndpoints} and skipping the
+     * original endpoint.
+     *
+     * @param context the initialized Spring context
+     * @param testClass the test class being executed
+     */
+    protected void handleMockEndpointsAndSkip(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+        if (testClass.isAnnotationPresent(MockEndpoints.class)) {
+            
+            final String mockEndpoints = testClass.getAnnotation(
+                    MockEndpoints.class).value();
+            CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() {
+                
+                public void execute(String contextName, SpringCamelContext camelContext)
+                    throws Exception {
+                    
+                    logger.info("Enabling auto mocking and skipping of endpoints matching pattern [{}] on "
+                            + "CamelContext with name [{}].", mockEndpoints, contextName);
+                    camelContext.addRegisterEndpointCallback(
+                            new InterceptSendToMockEndpointStrategy(mockEndpoints, true));
+                }
+            });
+        }
+    }
+    
+    /**
+     * Handles starting of Camel contexts based on {@link UseAdviceWith} and other state in the JVM.
+     *
+     * @param context the initialized Spring context
+     * @param testClass the test class being executed
+     */
+    protected void handleCamelContextStartup(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+        boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
+        if (skip) {
+            logger.info("Skipping starting CamelContext(s) as system property " 
+                    + "skipStartingCamelContext is set to be true.");
+        } else if (testClass.isAnnotationPresent(UseAdviceWith.class)) {
+            if (testClass.getAnnotation(UseAdviceWith.class).value()) {
+                logger.info("Skipping starting CamelContext(s) as UseAdviceWith annotation was found "
+                        + "and isUseAdviceWith is set to true.");
+                skip = true;
+            } else {
+                logger.info("Starting CamelContext(s) as UseAdviceWith annotation was found, but "
+                        + "isUseAdviceWith is set to false.");
+                skip = false;
+            }
+        }
+        
+        if (!skip) {
+            CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() {
+                public void execute(String contextName,
+                        SpringCamelContext camelContext) throws Exception {
+                    logger.info("Starting CamelContext with name [{}].", contextName);
+                    camelContext.start();
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns the class under test in order to enable inspection of annotations while the
+     * Spring context is being created.
+     * 
+     * @return the test class that is being executed
+     * @see CamelSpringTestHelper
+     */
+    protected Class<?> getTestClass() {
+        return CamelSpringTestHelper.getTestClass();
+    }
+
+}
\ No newline at end of file