Camel 2.0-M2 Release

git-svn-id: https://svn.apache.org/repos/asf/camel/tags/camel-2.0-M2@783997 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/camel-core/src/main/java/org/apache/camel/Exchange.java b/camel-core/src/main/java/org/apache/camel/Exchange.java
index 38524d2..2e22c9e 100644
--- a/camel-core/src/main/java/org/apache/camel/Exchange.java
+++ b/camel-core/src/main/java/org/apache/camel/Exchange.java
@@ -59,6 +59,7 @@
     String FILE_NAME_PRODUCED   = "CamelFileNameProduced";
     String FILE_PATH            = "CamelFilePath";
     String FILE_PARENT          = "CamelFileParent";
+    String FILTERED             = "CamelFiltered";
 
     String HTTP_CHARACTER_ENCODING = "CamelHttpCharacterEncoding";
     String HTTP_METHOD             = "CamelHttpMethod";
diff --git a/camel-core/src/main/java/org/apache/camel/processor/Enricher.java b/camel-core/src/main/java/org/apache/camel/processor/Enricher.java
index fe73c45..ade86ee 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/Enricher.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/Enricher.java
@@ -23,6 +23,8 @@
 import org.apache.camel.impl.DefaultExchange;
 import org.apache.camel.impl.ServiceSupport;
 import org.apache.camel.processor.aggregate.AggregationStrategy;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 import static org.apache.camel.util.ExchangeHelper.copyResultsPreservePattern;
 
@@ -35,8 +37,8 @@
  */
 public class Enricher extends ServiceSupport implements Processor {
 
+    private static final transient Log LOG = LogFactory.getLog(Enricher.class);
     private AggregationStrategy aggregationStrategy;
-
     private Producer producer;
 
     /**
@@ -99,10 +101,19 @@
             copyResultsPreservePattern(exchange, resourceExchange);
         } else {
             prepareResult(exchange);
+
             // aggregate original exchange and resource exchange
-            Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange);
-            // copy aggregation result onto original exchange (preserving pattern)
-            copyResultsPreservePattern(exchange, aggregatedExchange);
+            // but do not aggregate if the resource exchange was filtered
+            Boolean filtered = resourceExchange.getProperty(Exchange.FILTERED, Boolean.class);
+            if (filtered == null || !filtered) {
+                Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange);
+                // copy aggregation result onto original exchange (preserving pattern)
+                copyResultsPreservePattern(exchange, aggregatedExchange);
+            } else {
+                if (LOG.isTraceEnabled()) {
+                    LOG.trace("Cannot aggregate exchange as its filtered: " + resourceExchange);
+                }
+            }
         }
     }
 
diff --git a/camel-core/src/main/java/org/apache/camel/processor/FilterProcessor.java b/camel-core/src/main/java/org/apache/camel/processor/FilterProcessor.java
index 3739cb3..1a07e60 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/FilterProcessor.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/FilterProcessor.java
@@ -19,6 +19,8 @@
 import org.apache.camel.Exchange;
 import org.apache.camel.Predicate;
 import org.apache.camel.Processor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * The processor which implements the
@@ -27,6 +29,7 @@
  * @version $Revision$
  */
 public class FilterProcessor extends DelegateProcessor {
+    private static final Log LOG = LogFactory.getLog(FilterProcessor.class);
     private final Predicate predicate;
 
     public FilterProcessor(Predicate predicate, Processor processor) {
@@ -37,6 +40,12 @@
     public void process(Exchange exchange) throws Exception {
         if (predicate.matches(exchange)) {
             super.process(exchange);
+        } else {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Marking exchange as filtered: " + exchange);
+            }
+            // mark this exchange as filtered
+            exchange.setProperty(Exchange.FILTERED, Boolean.TRUE);
         }
     }
 
diff --git a/camel-core/src/main/java/org/apache/camel/processor/MulticastProcessor.java b/camel-core/src/main/java/org/apache/camel/processor/MulticastProcessor.java
index 4d430a5..151e70a 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/MulticastProcessor.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/MulticastProcessor.java
@@ -92,6 +92,7 @@
     
     public MulticastProcessor(Collection<Processor> processors, AggregationStrategy aggregationStrategy, boolean parallelProcessing, ExecutorService executorService, boolean streaming) {
         notNull(processors, "processors");
+        // TODO: end() does not work correctly with Splitter
         this.processors = processors;
         this.aggregationStrategy = aggregationStrategy;
         this.isParallelProcessing = parallelProcessing;
@@ -204,8 +205,14 @@
      * @param exchange the exchange to be added to the result
      */
     protected synchronized void doAggregate(AtomicExchange result, Exchange exchange) {
-        if (aggregationStrategy != null) {
+        // only aggregate if the exchange is not filtered (eg by the FilterProcessor)
+        Boolean filtered = exchange.getProperty(Exchange.FILTERED, Boolean.class);
+        if (aggregationStrategy != null && (filtered == null || !filtered)) {
             result.set(aggregationStrategy.aggregate(result.get(), exchange));
+        } else {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Cannot aggregate exchange as its filtered: " + exchange);
+            }
         }
     }
 
diff --git a/camel-core/src/main/java/org/apache/camel/processor/aggregate/DefaultAggregationCollection.java b/camel-core/src/main/java/org/apache/camel/processor/aggregate/DefaultAggregationCollection.java
index e27776a..6874a9a 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/aggregate/DefaultAggregationCollection.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/aggregate/DefaultAggregationCollection.java
@@ -57,11 +57,22 @@
 
     @Override
     public boolean add(Exchange exchange) {
+        // do not add exchange if it was filtered
+        Boolean filtered = exchange.getProperty(Exchange.FILTERED, Boolean.class);
+        if (filtered != null && filtered) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Cannot aggregate exchange as its filtered: " + exchange);
+            }
+            return false;
+        }
+
         Object correlationKey = correlationExpression.evaluate(exchange, Object.class);
         if (LOG.isTraceEnabled()) {
-            LOG.trace("Evaluated expression: " + correlationExpression + " as CorrelationKey: " + correlationKey);
+            LOG.trace("Evaluated expression: " + correlationExpression + " as correlation key: " + correlationKey);
         }
 
+        // TODO: correlationKey evalutated to null should be skipped by default
+
         Exchange oldExchange = aggregated.get(correlationKey);
         Exchange newExchange = exchange;
 
@@ -80,7 +91,7 @@
         // the strategy may just update the old exchange and return it
         if (!newExchange.equals(oldExchange)) {
             if (LOG.isTraceEnabled()) {
-                LOG.trace("Put exchange:" + newExchange + " with coorelation key:"  + correlationKey);
+                LOG.trace("Put exchange:" + newExchange + " with correlation key:"  + correlationKey);
             }
             aggregated.put(correlationKey, newExchange);
         }
diff --git a/camel-core/src/test/java/org/apache/camel/processor/AggregateShouldSkipFilteredExchanges.java b/camel-core/src/test/java/org/apache/camel/processor/AggregateShouldSkipFilteredExchanges.java
new file mode 100644
index 0000000..ce0596e
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/processor/AggregateShouldSkipFilteredExchanges.java
@@ -0,0 +1,81 @@
+/**
+ * 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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.processor.aggregate.AggregationStrategy;
+
+/**
+ * Unit test to verify that Aggregate aggregator does not included filtered exchanges.
+ *
+ * @version $Revision$
+ */
+public class AggregateShouldSkipFilteredExchanges extends ContextTestSupport {
+
+    public void testAggregateWithFilter() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hello World,Bye World");
+
+        MockEndpoint filtered = getMockEndpoint("mock:filtered");
+        filtered.expectedBodiesReceived("Hello World", "Bye World");
+
+        template.sendBodyAndHeader("direct:start", "Hello World", "id", 1);
+        template.sendBodyAndHeader("direct:start", "Hi there", "id", 1);
+        template.sendBodyAndHeader("direct:start", "Bye World", "id", 1);
+        template.sendBodyAndHeader("direct:start", "How do you do?", "id", 1);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                Predicate goodWord = body().contains("World");
+
+                from("direct:start")
+                    .filter(goodWord)
+                    .to("mock:filtered")
+                    .aggregate(header("id"), new MyAggregationStrategy())
+                        .to("mock:result")
+                    .end();
+            }
+        };
+    }
+
+    private class MyAggregationStrategy implements AggregationStrategy {
+
+        public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+            String newBody = newExchange.getIn().getBody(String.class);
+
+            if (oldExchange == null) {
+                return newExchange;
+            }
+
+            String body = oldExchange.getIn().getBody(String.class);
+            body = body + "," + newBody;
+            oldExchange.getIn().setBody(body);
+            return oldExchange;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/camel-core/src/test/java/org/apache/camel/processor/EnrichShouldSkipFilteredExchanges.java b/camel-core/src/test/java/org/apache/camel/processor/EnrichShouldSkipFilteredExchanges.java
new file mode 100644
index 0000000..41ded81
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/processor/EnrichShouldSkipFilteredExchanges.java
@@ -0,0 +1,85 @@
+/**
+ * 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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.processor.aggregate.AggregationStrategy;
+
+/**
+ * Unit test to verify that Enrich aggregator does not included filtered exchanges.
+ *
+ * @version $Revision$
+ */
+public class EnrichShouldSkipFilteredExchanges extends ContextTestSupport {
+
+    public void testEnrichWithFilterNotFiltered() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hello World,Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testEnrichWithFilterFiltered() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hi there");
+
+        template.sendBody("direct:start", "Hi there");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .enrich("direct:enrich", new MyAggregationStrategy())
+                    .to("mock:result");
+
+                Predicate goodWord = body().contains("World");
+                from("direct:enrich")
+                    .filter(goodWord)
+                    .to("mock:filtered");
+            }
+        };
+    }
+
+    private class MyAggregationStrategy implements AggregationStrategy {
+
+        public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+            String newBody = newExchange.getIn().getBody(String.class);
+            assertTrue("Should have been filtered: " + newBody, newBody.contains("World"));
+
+            if (oldExchange == null) {
+                return newExchange;
+            }
+
+            String body = oldExchange.getIn().getBody(String.class);
+            body = body + "," + newBody;
+            oldExchange.getIn().setBody(body);
+            return oldExchange;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/camel-core/src/test/java/org/apache/camel/processor/MulticastShouldSkipFilteredExchanges.java b/camel-core/src/test/java/org/apache/camel/processor/MulticastShouldSkipFilteredExchanges.java
new file mode 100644
index 0000000..faad756
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/processor/MulticastShouldSkipFilteredExchanges.java
@@ -0,0 +1,90 @@
+/**
+ * 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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.processor.aggregate.AggregationStrategy;
+
+/**
+ * Unit test to verify that Multicast aggregator does not included filtered exchanges.
+ *
+ * @version $Revision$
+ */
+public class MulticastShouldSkipFilteredExchanges extends ContextTestSupport {
+
+    public void testMulticastWithFilterNotFiltered() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hello World,Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testMulticastWithFilterFiltered() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hi there");
+
+        template.sendBody("direct:start", "Hi there");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .to("direct:multicast")
+                    .to("mock:result");
+
+                from("direct:multicast")
+                    .multicast(new MyAggregationStrategy())
+                    .to("direct:a")
+                    .to("direct:b");
+
+                Predicate goodWord = body().contains("World");
+                from("direct:a", "direct:b")
+                    .filter(goodWord)
+                    .to("mock:filtered");
+            }
+        };
+    }
+
+    private class MyAggregationStrategy implements AggregationStrategy {
+
+        public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+            String newBody = newExchange.getIn().getBody(String.class);
+            assertTrue("Should have been filtered: " + newBody, newBody.contains("World"));
+
+            if (oldExchange == null) {
+                return newExchange;
+            }
+
+            String body = oldExchange.getIn().getBody(String.class);
+            body = body + "," + newBody;
+            oldExchange.getIn().setBody(body);
+            return oldExchange;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/camel-core/src/test/java/org/apache/camel/processor/SplitShouldSkipFilteredExchanges.java b/camel-core/src/test/java/org/apache/camel/processor/SplitShouldSkipFilteredExchanges.java
new file mode 100644
index 0000000..468446a
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/processor/SplitShouldSkipFilteredExchanges.java
@@ -0,0 +1,89 @@
+/**
+ * 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.processor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.processor.aggregate.AggregationStrategy;
+
+/**
+ * Unit test to verify that Splitter aggregator does not included filtered exchanges.
+ *
+ * @version $Revision$
+ */
+public class SplitShouldSkipFilteredExchanges extends ContextTestSupport {
+
+    public void testSplitWithFilter() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("Hello World,Bye World");
+
+        MockEndpoint filtered = getMockEndpoint("mock:filtered");
+        filtered.expectedBodiesReceived("Hello World", "Bye World");
+
+        List<String> body = new ArrayList<String>();
+        body.add("Hello World");
+        body.add("Hi there");
+        body.add("Bye World");
+        body.add("How do you do?");
+
+        template.sendBody("direct:start", body);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .to("direct:split")
+                    .to("mock:result");
+
+                Predicate goodWord = body().contains("World");
+                from("direct:split")
+                    .split(body(List.class), new MyAggregationStrategy())
+                        .filter(goodWord)
+                        .to("mock:filtered");
+            }
+        };
+    }
+
+    private class MyAggregationStrategy implements AggregationStrategy {
+
+        public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+            String newBody = newExchange.getIn().getBody(String.class);
+            assertTrue("Should have been filtered: " + newBody, newBody.contains("World"));
+
+            if (oldExchange == null) {
+                return newExchange;
+            }
+
+            String body = oldExchange.getIn().getBody(String.class);
+            body = body + "," + newBody;
+            oldExchange.getIn().setBody(body);
+            return oldExchange;
+        }
+
+    }
+}
diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/CxfSpringEndpoint.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/CxfSpringEndpoint.java
index a605ac8..f638067 100644
--- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/CxfSpringEndpoint.java
+++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/CxfSpringEndpoint.java
@@ -17,6 +17,7 @@
 package org.apache.camel.component.cxf;
 
 import java.lang.reflect.Proxy;
+
 import javax.xml.namespace.QName;
 
 import org.apache.camel.CamelContext;
@@ -32,7 +33,7 @@
 import org.apache.cxf.frontend.ClientProxyFactoryBean;
 import org.apache.cxf.frontend.ServerFactoryBean;
 import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
-import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
 
 /**
  * Defines the <a href="http://camel.apache.org/cxf.html">CXF Endpoint</a>
@@ -174,8 +175,8 @@
 
     void configure(Object beanInstance) {
         // check the ApplicationContext states first , and call the refresh if necessary
-        if (((SpringCamelContext)getCamelContext()).getApplicationContext() instanceof AbstractApplicationContext) {
-            AbstractApplicationContext context = (AbstractApplicationContext)((SpringCamelContext)getCamelContext()).getApplicationContext();
+        if (((SpringCamelContext)getCamelContext()).getApplicationContext() instanceof ConfigurableApplicationContext) {
+            ConfigurableApplicationContext context = (ConfigurableApplicationContext)((SpringCamelContext)getCamelContext()).getApplicationContext();
             if (!context.isActive()) {
                 context.refresh();
             }
diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBean.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBean.java
index 73fba1e..7f6f2bc 100644
--- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBean.java
+++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBean.java
@@ -18,13 +18,18 @@
 
 import java.util.List;
 
+import org.apache.cxf.BusFactory;
 import org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory;
 import org.apache.cxf.service.factory.ReflectionServiceFactoryBean;
 
+import org.springframework.beans.factory.DisposableBean;
+
 /**
  *
  */
-public class CxfEndpointBean extends AbstractWSDLBasedEndpointFactory {
+public class CxfEndpointBean extends AbstractWSDLBasedEndpointFactory
+    implements DisposableBean {
+    
     private List handlers;
 
     public CxfEndpointBean() {
@@ -42,4 +47,11 @@
     public void setHandlers(List handlers) {
         this.handlers = handlers;
     }
+
+    public void destroy() throws Exception {
+        // Clean up the BusFactory's defaultBus
+        BusFactory.setDefaultBus(null);
+        BusFactory.setThreadDefaultBus(null);
+        
+    }
 }
diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBeanDefinitionParser.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBeanDefinitionParser.java
index 73ee4cf..85e5015 100644
--- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBeanDefinitionParser.java
+++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/spring/CxfEndpointBeanDefinitionParser.java
@@ -26,7 +26,9 @@
 import org.apache.cxf.BusFactory;
 import org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor;
 import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.Configurer;
 import org.apache.cxf.configuration.spring.AbstractBeanDefinitionParser;
+import org.apache.cxf.configuration.spring.ConfigurerImpl;
 import org.apache.cxf.service.factory.ReflectionServiceFactoryBean;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.PropertyValue;
@@ -36,6 +38,7 @@
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
 
 public class CxfEndpointBeanDefinitionParser extends AbstractCxfBeanDefinitionParser {
 
diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/util/CxfUtils.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/util/CxfUtils.java
index 8823c5a..5b748b0 100644
--- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/util/CxfUtils.java
+++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/util/CxfUtils.java
@@ -34,9 +34,7 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import com.sun.org.apache.xml.internal.serialize.OutputFormat;
-import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
-
+import org.apache.camel.converter.jaxp.XmlConverter;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.io.CachedOutputStream;
@@ -63,7 +61,8 @@
         visitNodesForNameSpace(element, namespaces);
         W3CDOMStreamWriter writer = new W3CDOMStreamWriter();
         writeElement(element, writer, namespaces);
-        return getStringFromDoc(writer.getDocument());
+        XmlConverter converter = new XmlConverter();
+        return converter.toString(converter.toSource(writer.getDocument()));
         
     }
     
@@ -164,20 +163,6 @@
         
     }
 
-    private static String getStringFromDoc(Document document) throws IOException {
-        //Serialize DOM
-        OutputFormat format    = new OutputFormat(document);
-        format.setOmitXMLDeclaration(true);
-        // as a String
-        StringWriter stringOut = new StringWriter();    
-        XMLSerializer serial   = new XMLSerializer(stringOut, 
-                                                    format);
-        serial.serialize(document);
-       
-        return stringOut.toString(); 
-       
-    }
-
     private static void visitNodesForNameSpace(Node node, Map<String, String> namespaces) {
         if (node instanceof Element) {
             Element element = (Element)node;
diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/spring/AbstractSpringBeanTestSupport.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/spring/AbstractSpringBeanTestSupport.java
index 9f6d031..9a40bcf 100644
--- a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/spring/AbstractSpringBeanTestSupport.java
+++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/spring/AbstractSpringBeanTestSupport.java
@@ -32,8 +32,7 @@
     }
 
     protected void tearDown() throws Exception {
-        ctx.close();
-        BusFactory.setDefaultBus(null);
+        ctx.close();        
     }
 
 }
diff --git a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailBinding.java b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailBinding.java
index 3052acc..1860483 100644
--- a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailBinding.java
+++ b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailBinding.java
@@ -94,40 +94,72 @@
 
         // if there is an alternativebody provided, set up a mime multipart alternative message
         if (hasAlternativeBody(endpoint.getConfiguration(), exchange.getIn())) {
-            createMultipartAlternativeMessage(mimeMessage, exchange.getIn(), endpoint.getConfiguration()); 
+            createMultipartAlternativeMessage(mimeMessage, endpoint.getConfiguration(), exchange);
         } else {
             if (exchange.getIn().hasAttachments()) {
-                appendAttachmentsFromCamel(mimeMessage, exchange.getIn(), endpoint.getConfiguration());
+                appendAttachmentsFromCamel(mimeMessage, endpoint.getConfiguration(), exchange);
             } else {
-                String contentType = populateContentType(endpoint, mimeMessage, exchange);
-                if (contentType == null) {
-                    mimeMessage.setText(exchange.getIn().getBody(String.class));
-                } else if (contentType.startsWith("text/plain")) {
-                    String charset = ObjectHelper.after(contentType, "charset=");
-                    if (charset != null) {
-                        mimeMessage.setText(exchange.getIn().getBody(String.class), charset);
-                    } else {
-                        mimeMessage.setText(exchange.getIn().getBody(String.class));
-                    }
-                } else {
-                    // store content in a byte array data store
-                    DataSource ds = new ByteArrayDataSource(exchange.getIn().getBody(String.class), contentType);
-                    mimeMessage.setDataHandler(new DataHandler(ds));
-                }
+                populateContentOnMimeMessage(mimeMessage, endpoint.getConfiguration(), exchange);
             }
         }
     }
 
-    protected String populateContentType(MailEndpoint endpoint, MimeMessage mimeMessage, Exchange exchange) throws MessagingException {
+    protected String determineContentType(MailConfiguration configuration, Exchange exchange) {
         // see if we got any content type set
-        String contentType = endpoint.getConfiguration().getContentType();
+        String contentType = configuration.getContentType();
         if (exchange.getIn().getHeader("contentType") != null) {
             contentType = exchange.getIn().getHeader("contentType", String.class);
         } else if (exchange.getIn().getHeader(Exchange.CONTENT_TYPE) != null) {
             contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
         }
-        if (contentType != null) {
-            mimeMessage.setHeader("Content-Type", contentType);
+
+        // fix content type to include a space after semi colon if missing
+        if (contentType != null && contentType.contains(";")) {
+            String before = ObjectHelper.before(contentType, ";");
+            String after = ObjectHelper.after(contentType, ";");
+            if (before != null && after != null) {
+                contentType = before.trim() + "; " + after.trim();
+            }
+        }
+
+        return contentType;
+    }
+
+    protected String populateContentOnMimeMessage(MimeMessage part, MailConfiguration configuration, Exchange exchange) throws MessagingException, IOException {
+        String contentType = determineContentType(configuration, exchange);
+        if (contentType == null) {
+            part.setText(exchange.getIn().getBody(String.class));
+        } else {
+            if (contentType.startsWith("text/plain")) {
+                String charset = ObjectHelper.after(contentType, "charset=");
+                if (charset != null) {
+                    part.setText(exchange.getIn().getBody(String.class), charset.trim());
+                } else {
+                    part.setText(exchange.getIn().getBody(String.class));
+                }
+            } else {
+                // store content in a byte array data store
+                DataSource ds = new ByteArrayDataSource(exchange.getIn().getBody(String.class), contentType);
+                part.setDataHandler(new DataHandler(ds));
+            }
+            part.setHeader("Content-Type", contentType);
+        }
+        return contentType;
+    }
+
+    protected String populateContentOnBodyPart(BodyPart part, MailConfiguration configuration, Exchange exchange) throws MessagingException, IOException {
+        String contentType = determineContentType(configuration, exchange);
+        if (contentType == null) {
+            part.setText(exchange.getIn().getBody(String.class));
+        } else {
+            if (contentType.startsWith("text/plain")) {
+                part.setText(exchange.getIn().getBody(String.class));
+            } else {
+                // store content in a byte array data store
+                DataSource ds = new ByteArrayDataSource(exchange.getIn().getBody(String.class), contentType);
+                part.setDataHandler(new DataHandler(ds));
+            }
+            part.setHeader("Content-Type", contentType);
         }
         return contentType;
     }
@@ -221,30 +253,31 @@
     /**
      * Appends the Mail attachments from the Camel {@link MailMessage}
      */
-    protected void appendAttachmentsFromCamel(MimeMessage mimeMessage, org.apache.camel.Message camelMessage,
-                                              MailConfiguration configuration)
-        throws MessagingException {
-        
+    protected void appendAttachmentsFromCamel(MimeMessage mimeMessage, MailConfiguration configuration,
+        Exchange exchange) throws MessagingException, IOException {
+
         // Put parts in message
-        mimeMessage.setContent(createMixedMultipartAttachments(camelMessage, configuration));
+        mimeMessage.setContent(createMixedMultipartAttachments(configuration, exchange));
     }
 
-    private MimeMultipart createMixedMultipartAttachments(org.apache.camel.Message camelMessage, MailConfiguration configuration) throws MessagingException {
+    private MimeMultipart createMixedMultipartAttachments(MailConfiguration configuration, Exchange exchange)
+        throws MessagingException, IOException {
+
         // fill the body with text
         MimeMultipart multipart = new MimeMultipart();
         multipart.setSubType("mixed");
-        addBodyToMultipart(camelMessage, configuration, multipart);
+        addBodyToMultipart(configuration, multipart, exchange);
         String partDisposition = configuration.isUseInlineAttachments() ?  Part.INLINE : Part.ATTACHMENT;
-        if (camelMessage.hasAttachments()) {
-            addAttachmentsToMultipart(camelMessage, multipart, partDisposition);
+        if (exchange.getIn().hasAttachments()) {
+            addAttachmentsToMultipart(multipart, partDisposition, exchange);
         }
         return multipart;
     }
 
-    protected void addAttachmentsToMultipart(org.apache.camel.Message camelMessage, MimeMultipart multipart, String partDisposition) throws MessagingException {
+    protected void addAttachmentsToMultipart(MimeMultipart multipart, String partDisposition, Exchange exchange) throws MessagingException {
         LOG.trace("Adding attachments +++ start +++");
         int i = 0;
-        for (Map.Entry<String, DataHandler> entry : camelMessage.getAttachments().entrySet()) {
+        for (Map.Entry<String, DataHandler> entry : exchange.getIn().getAttachments().entrySet()) {
             String attachmentFilename = entry.getKey();
             DataHandler handler = entry.getValue();
 
@@ -254,7 +287,7 @@
                 LOG.trace("Attachment #" + i + ": FileName: " + attachmentFilename);
             }
             if (handler != null) {
-                if (shouldAddAttachment(camelMessage, attachmentFilename, handler)) {
+                if (shouldAddAttachment(exchange, attachmentFilename, handler)) {
                     // Create another body part
                     BodyPart messageBodyPart = new MimeBodyPart();
                     // Set the data handler to the attachment
@@ -294,53 +327,54 @@
         LOG.trace("Adding attachments +++ done +++");
     }
 
-    protected void createMultipartAlternativeMessage(MimeMessage mimeMessage, org.apache.camel.Message camelMessage, MailConfiguration configuration)
-        throws MessagingException { 
+    protected void createMultipartAlternativeMessage(MimeMessage mimeMessage, MailConfiguration configuration,
+                                                     Exchange exchange) throws MessagingException, IOException {
 
         MimeMultipart multipartAlternative = new MimeMultipart("alternative");
         mimeMessage.setContent(multipartAlternative);
 
         BodyPart plainText = new MimeBodyPart();
-        plainText.setText(getAlternativeBody(configuration, camelMessage));
+        plainText.setText(getAlternativeBody(configuration, exchange.getIn()));
         multipartAlternative.addBodyPart(plainText);
 
         // if there are no attachments, add the body to the same mulitpart message
-        if (!camelMessage.hasAttachments()) {
-            addBodyToMultipart(camelMessage, configuration, multipartAlternative);
+        if (!exchange.getIn().hasAttachments()) {
+            addBodyToMultipart(configuration, multipartAlternative, exchange);
         } else {
             // if there are attachments, but they aren't set to be inline, add them to
-            // treat them as normal. It will append a multipart-mixed with the attachments and the
-            // body text
+            // treat them as normal. It will append a multipart-mixed with the attachments and the body text
             if (!configuration.isUseInlineAttachments()) {
                 BodyPart mixedAttachments = new MimeBodyPart();
-                mixedAttachments.setContent(createMixedMultipartAttachments(camelMessage, configuration));
+                mixedAttachments.setContent(createMixedMultipartAttachments(configuration, exchange));
                 multipartAlternative.addBodyPart(mixedAttachments);
-                //appendAttachmentsFromCamel(mimeMessage, camelMessage, configuration);
-            } else { // if the attachments are set to be inline, attach them as inline attachments
+            } else {
+                // if the attachments are set to be inline, attach them as inline attachments
                 MimeMultipart multipartRelated = new MimeMultipart("related");
                 BodyPart related = new MimeBodyPart();
 
                 related.setContent(multipartRelated);
                 multipartAlternative.addBodyPart(related);
 
-                addBodyToMultipart(camelMessage, configuration, multipartRelated);
+                addBodyToMultipart(configuration, multipartRelated, exchange);
 
-                addAttachmentsToMultipart(camelMessage, multipartRelated, Part.INLINE);
+                addAttachmentsToMultipart(multipartRelated, Part.INLINE, exchange);
             }
         }
 
     }
 
-    protected void addBodyToMultipart(org.apache.camel.Message camelMessage, MailConfiguration configuration, MimeMultipart activeMultipart) throws MessagingException {
+    protected void addBodyToMultipart(MailConfiguration configuration, MimeMultipart activeMultipart, Exchange exchange)
+        throws MessagingException, IOException {
+
         BodyPart bodyMessage = new MimeBodyPart();
-        bodyMessage.setContent(camelMessage.getBody(String.class), configuration.getContentType());
+        populateContentOnBodyPart(bodyMessage, configuration, exchange);
         activeMultipart.addBodyPart(bodyMessage);
     }
 
     /**
      * Strategy to allow filtering of attachments which are added on the Mail message
      */
-    protected boolean shouldAddAttachment(org.apache.camel.Message camelMessage, String attachmentFilename, DataHandler handler) {
+    protected boolean shouldAddAttachment(Exchange exchange, String attachmentFilename, DataHandler handler) {
         return true;
     }
 
diff --git a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailCustomContentTypeTest.java b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailCustomContentTypeTest.java
index 5162ace..e808f42 100644
--- a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailCustomContentTypeTest.java
+++ b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailCustomContentTypeTest.java
@@ -37,7 +37,7 @@
         Message msg = box.get(0);
 
         assertTrue(msg.getContentType().startsWith("text/html"));
-        assertEquals("text/html;charset=UTF-8", msg.getContentType());
+        assertEquals("text/html; charset=UTF-8", msg.getContentType());
         assertEquals("<html><body><h1>Hello</h1>World</body></html>", msg.getContent());
     }
 
@@ -50,14 +50,14 @@
         Message msg = box.get(0);
 
         assertTrue(msg.getContentType().startsWith("text/html"));
-        assertEquals("text/html;charset=iso-8859-1", msg.getContentType());
+        assertEquals("text/html; charset=iso-8859-1", msg.getContentType());
         assertEquals("<html><body><h1>Hello</h1>World</body></html>", msg.getContent());
     }
 
     public void testSendPlainMailContentTypeInHeader() throws Exception {
         Mailbox.clearAll();
 
-        template.sendBodyAndHeader("direct:b", "Hello World", "contentType", "text/plain;charset=iso-8859-1");
+        template.sendBodyAndHeader("direct:b", "Hello World", "contentType", "text/plain; charset=iso-8859-1");
 
         Mailbox box = Mailbox.get("claus@localhost");
         Message msg = box.get(0);
@@ -68,7 +68,7 @@
     public void testSendPlainMailContentTypeInHeader2() throws Exception {
         Mailbox.clearAll();
 
-        template.sendBodyAndHeader("direct:b", "Hello World", Exchange.CONTENT_TYPE, "text/plain;charset=iso-8859-1");
+        template.sendBodyAndHeader("direct:b", "Hello World", Exchange.CONTENT_TYPE, "text/plain; charset=iso-8859-1");
 
         Mailbox box = Mailbox.get("claus@localhost");
         Message msg = box.get(0);
@@ -76,6 +76,19 @@
         assertEquals("Hello World", msg.getContent());
     }
 
+    public void testSendPlainMailContentTypeTinyTypeInHeader() throws Exception {
+        Mailbox.clearAll();
+
+        // Camel will fixup the Content-Type if you do not have a space after the semi colon
+        template.sendBodyAndHeader("direct:b", "Hello World", "contentType", "text/plain;charset=iso-8859-1");
+
+        Mailbox box = Mailbox.get("claus@localhost");
+        Message msg = box.get(0);
+        // the content type should have a space after the semi colon
+        assertEquals("text/plain; charset=iso-8859-1", msg.getContentType());
+        assertEquals("Hello World", msg.getContent());
+    }
+
     protected RouteBuilder createRouteBuilder() throws Exception {
         return new RouteBuilder() {
             public void configure() throws Exception {
diff --git a/components/camel-web/pom.xml b/components/camel-web/pom.xml
index 6e1571f..107e123 100644
--- a/components/camel-web/pom.xml
+++ b/components/camel-web/pom.xml
@@ -73,6 +73,14 @@
       <artifactId>camel-spring</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-groovy</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.camel</groupId>
+    	<artifactId>camel-ruby</artifactId>
+    </dependency>
+    <dependency>
       <groupId>com.sun.jersey.contribs</groupId>
       <artifactId>jersey-spring</artifactId>
       <version>${jersey-version}</version>
diff --git a/components/camel-web/src/main/java/org/apache/camel/web/resources/RouteResource.java b/components/camel-web/src/main/java/org/apache/camel/web/resources/RouteResource.java
index aa3e018..259ba4e 100644
--- a/components/camel-web/src/main/java/org/apache/camel/web/resources/RouteResource.java
+++ b/components/camel-web/src/main/java/org/apache/camel/web/resources/RouteResource.java
@@ -16,12 +16,15 @@
  */
 package org.apache.camel.web.resources;
 
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collections;
+import java.util.List;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -39,12 +42,17 @@
 
 import com.sun.jersey.api.representation.Form;
 import com.sun.jersey.api.view.Viewable;
+import groovy.lang.GroovyClassLoader;
 
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.ruby.RubyCamel;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.view.RouteDotGenerator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jruby.Main;
 
 /**
  * A single Camel Route which is used to implement one or more
@@ -54,11 +62,23 @@
  */
 public class RouteResource extends CamelChildResourceSupport {
     private static final transient Log LOG = LogFactory.getLog(RouteResource.class);
+    private static final String LANGUAGE_XML = "Xml";
+    private static final String LANGUAGE_GROOVY = "Groovy";
+    private static final String LANGUAGE_RUBY = "Ruby";
+    private static final String LANGUAGE_SCALA = "Scala";
 
     private RouteDefinition route;
     private String error = "";
     private String id;
 
+    // what language is used to define this route
+    private String language = LANGUAGE_XML;
+  
+    // the route configuration: when language is Xml, the routeDefinition is
+    // null; when language is Groovy/Scala/Ruby, the routeDefinition contains
+    // the route definition class. It must be initialized because RouteResource
+    // is stateless.
+    private String routeDefinition = "";
 
     public RouteResource(RoutesResource routesResource, RouteDefinition route) {
         super(routesResource.getContextResource());
@@ -83,15 +103,40 @@
         Marshaller marshaller = context.createMarshaller();
         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         // TODO fix to use "" namespace prefix
-        // using this https://jaxb.dev.java.net/nonav/2.1.10/docs/vendorProperties.html#prefixmapper
+        // using this
+        // https://jaxb.dev.java.net/nonav/2.1.10/docs/vendorProperties.html#prefixmapper
         StringWriter buffer = new StringWriter();
         marshaller.marshal(route, buffer);
         return buffer.toString();
     }
 
     /**
-     * Returns the Graphviz DOT <a href="http://camel.apache.org/visualisation.html">Visualisation</a>
-     * of this route
+     * Returns the language
+     */
+    public String getLanguage() {
+        return language;
+    }
+
+    /**
+     * Returns the content of the route definition class
+     */
+    public String getRouteDefinition() {
+        if (language.equals(LANGUAGE_XML)) {
+            try {
+                return getRouteXml();
+            } catch (JAXBException e) {
+                // e.printStackTrace();
+                return "Error on marshal the route definition!";
+            }
+        } else {
+            return routeDefinition;
+        }
+    }
+
+    /**
+     * Returns the Graphviz DOT <a
+     * href="http://camel.apache.org/visualisation.html">Visualisation</a> of
+     * this route
      */
     @GET
     @Produces(Constants.DOT_MIMETYPE)
@@ -100,7 +145,6 @@
         return generator.getRoutesText(getCamelContext());
     }
 
-
     /**
      * Allows a route definition to be updated
      */
@@ -115,46 +159,188 @@
         getCamelContext().addRouteDefinitions(Collections.singletonList(routeDefinition));
     }
 
+    /**
+     * Allows a routes builder to be updated
+     */
+    public void postRoutes(RouteBuilder builder) throws Exception {
+        // remove current route
+        DefaultCamelContext camelContext = (DefaultCamelContext)getCamelContext();
+        camelContext.removeRouteDefinition(id);
+
+        // lets install the updated routes
+        camelContext.addRoutes(builder);
+    }
 
     /**
      * Updates a route definition using form encoded data from a web form
-     *
-     * @param formData is the form data POSTed typically from a HTML form with the <code>route</code> field used to encode
-     *                 the XML text of the new route definition
+     * 
+     * @param formData is the form data POSTed typically from a HTML form with
+     *            the <code>route</code> field used to encode the XML text of
+     *            the new route definition
      */
     @POST
     @Consumes("application/x-www-form-urlencoded")
     public Response postRouteForm(@Context UriInfo uriInfo, Form formData) throws URISyntaxException {
         // TODO replace the Form class with an injected bean?
-        String xml = formData.getFirst("route", String.class);
+        String language = formData.getFirst("language", String.class);
+        String body = formData.getFirst("route", String.class);
         if (LOG.isDebugEnabled()) {
-            LOG.debug("new XML is: " + xml);
+            LOG.debug("new Route is: " + body);
         }
-        if (xml == null) {
-            error = "No XML submitted!";
-        } else {
-            try {
-                JAXBContext context = JAXBContext.newInstance(Constants.JAXB_PACKAGES);
-                Unmarshaller unmarshaller = context.createUnmarshaller();
-                Object value = unmarshaller.unmarshal(new StringReader(xml));
-                if (value instanceof RouteDefinition) {
-                    RouteDefinition routeDefinition = (RouteDefinition) value;
-                    postRoute(routeDefinition);
-                    return Response.seeOther(new URI("/routes")).build();
-                } else {
-                    error = "Posted XML is not a route but is of type " + ObjectHelper.className(value);
-                }
-            } catch (JAXBException e) {
-                error = "Failed to parse XML: " + e.getMessage();
-            } catch (Exception e) {
-                error = "Failed to install route: " + e.getMessage();
+        LOG.info(body);
+        if (body == null) {
+            error = "No Route submitted!";
+        } else if (language.equals(LANGUAGE_XML)) {
+            return parseXml(body);
+        } else if (language.equals(LANGUAGE_GROOVY)) {
+            return parseGroovy(body);
+        } else if (language.equals(LANGUAGE_RUBY)) {
+            return parseRuby(body);
+        } else if (language.equals(LANGUAGE_SCALA)) {
+            return parseScala(body);
+        }
+        error = "Not supproted language!";
+        return Response.ok(new Viewable("edit", this)).build();
+
+    }
+
+    /**
+     * process the route configuration defined in Xml
+     */
+    private Response parseXml(String xml) {
+        try {
+            JAXBContext context = JAXBContext.newInstance(Constants.JAXB_PACKAGES);
+            Unmarshaller unmarshaller = context.createUnmarshaller();
+            Object value = unmarshaller.unmarshal(new StringReader(xml));
+            if (value instanceof RouteDefinition) {
+                RouteDefinition routeDefinition = (RouteDefinition)value;
+                postRoute(routeDefinition);
+                return Response.seeOther(new URI("/routes")).build();
+            } else {
+                error = "Posted XML is not a route but is of type " + ObjectHelper.className(value);
             }
+        } catch (JAXBException e) {
+            error = "Failed to parse XML: " + e.getMessage();
+        } catch (Exception e) {
+            error = "Failed to install route: " + e.getMessage();
         }
         // lets re-render the form
         return Response.ok(new Viewable("edit", this)).build();
     }
 
     /**
+     * process the route configuration defined in Groovy class
+     */
+    private Response parseGroovy(String route) {
+        try {
+            // store the route definition
+            File file = storeRoute(route, LANGUAGE_GROOVY);
+
+            // load the definition class into a RouteBuilder instance
+            GroovyClassLoader classLoader = new GroovyClassLoader();
+            Class clazz = classLoader.parseClass(file);
+            RouteBuilder builder = (RouteBuilder)clazz.newInstance();
+            LOG.info("Loaded builder: " + builder);
+
+            postRoutes(builder);
+
+            return Response.seeOther(new URI("/routes")).build();
+
+        } catch (IOException e) {
+            // e.printStackTrace();
+            error = "Failed to store the route: " + e.getMessage();
+        } catch (InstantiationException e) {
+            // e.printStackTrace();
+            error = "Failed to instantiate the route: " + e.getMessage();
+
+        } catch (IllegalAccessException e) {
+            // e.printStackTrace();
+            error = "Failed to instantiate the route: " + e.getMessage();
+        } catch (Exception e) {
+            // e.printStackTrace();
+            error = "Failed to edit the route: " + e.getMessage();
+        }
+        // lets re-render the form
+        return Response.ok(new Viewable("edit", this)).build();
+    }
+
+    /**
+     * process the route configuration defined in Ruby class
+     */
+    private Response parseRuby(String route) {
+        try {
+            // add the script of addRouteBuilder into ruby script
+            route += "\n RubyCamel.addRouteBuilder(RubyRoute.new)";
+
+            // store the route definition
+            File file = storeRoute(route, LANGUAGE_RUBY);
+
+            // execute the ruby script, which will store the RouteBuilder
+            // instances into RubyCamel
+            String[] args = {file.getAbsolutePath()};
+            Main.main(args);
+
+            // get the route builders from the RubyCamel and add them into this
+            // route
+            List<RouteBuilder> list = RubyCamel.getRoutes();
+            for (RouteBuilder builder : list) {
+                postRoutes(builder);
+            }
+
+            return Response.seeOther(new URI("/routes")).build();
+
+        } catch (IOException e) {
+            // e.printStackTrace();
+            error = "Failed to store the route: " + e.getMessage();
+        } catch (Exception e) {
+            // e.printStackTrace();
+            error = "Failed to edit the route: " + e.getMessage();
+
+        }
+        // lets re-render the form
+        return Response.ok(new Viewable("edit", this)).build();
+    }
+
+    /**
+     * process the route configuration defined in Scala class
+     */
+    private Response parseScala(String route) {
+        try {
+
+            // store the route definition
+            File file = storeRoute(route, LANGUAGE_SCALA);
+
+            // load the definition class
+
+            return Response.seeOther(new URI("/routes")).build();
+
+        } catch (IOException e) {
+            // e.printStackTrace();
+            error = "Failed to store the route: " + e.getMessage();
+        } catch (Exception e) {
+            // e.printStackTrace();
+            error = "Failed to edit the route: " + e.getMessage();
+        }
+        // lets re-render the form
+        return Response.ok(new Viewable("edit", this)).build();
+    }
+
+    /**
+     * Stores the route definition class into a file
+     */
+    private File storeRoute(String route, String language) throws IOException {
+        // create a temporary file to store the route definition class
+        File file = File.createTempFile("Route-", "." + language);
+        FileWriter fw = new FileWriter(file);
+
+        // write the route into the file
+        fw.write(route);
+        fw.flush();
+        fw.close();
+        return file;
+    }
+
+    /**
      * Looks up an individual route
      */
     @Path("status")
diff --git a/components/camel-web/src/main/webapp/org/apache/camel/web/resources/RouteResource/edit.jsp b/components/camel-web/src/main/webapp/org/apache/camel/web/resources/RouteResource/edit.jsp
index 7220141..c7b2aae 100644
--- a/components/camel-web/src/main/webapp/org/apache/camel/web/resources/RouteResource/edit.jsp
+++ b/components/camel-web/src/main/webapp/org/apache/camel/web/resources/RouteResource/edit.jsp
@@ -1,28 +1,33 @@
 <html>
 <head>
-  <script type='text/javascript' src="<c:url value='/js/dojo/dojo.js'/>"></script>
-  <script type='text/javascript' src="<c:url value='/js/bespin/editor/embed.js'/>"></script>
-  <script type='text/javascript' src="<c:url value='/js/route.js'/>"></script>
+<script type='text/javascript' src="<c:url value='/js/dojo/dojo.js'/>"></script>
+<script type='text/javascript'
+	src="<c:url value='/js/bespin/editor/embed.js'/>"></script>
+<script type='text/javascript' src="<c:url value='/js/route.js'/>"></script>
 
-  <title>Edit ${it.route.id}</title>
+<title>Edit ${it.route.id}</title>
 </head>
 <body>
 
-<form id="routeForm" action="<c:url value="/routes/${it.route.id}"/>" method="post">
+<form id="routeForm" action="<c:url value="/routes/${it.route.id}"/>"
+	method="post">
 <table>
-  <tr>
-    <td>
-      <h2>Edit ${it.route.id}</h2>
-    </td>
-    <td>
-      <input type="submit" value="Save">
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2">
-      <textarea id="route" name="route" style="width: 800px; height: 300px; border: 10px solid #ddd; -moz-border-radius: 10px; -webkit-border-radius: 10px;">${it.routeXml}</textarea>
-    </td>
-  </tr> 
+	<tr>
+		<td>
+		<h2>Edit ${it.route.id}</h2>
+		</td>
+		<td><input type="submit" value="Save">&nbsp; as &nbsp;<select id="language" name="language">
+			<option value="Xml" selected>XML</option>
+			<option value="Groovy">Groovy</option>
+			<option value="Ruby">Ruby</option>
+			<option value="Scala">Scala</option>
+		</select></td>
+	</tr>
+	<tr>
+		<td colspan="2"><textarea id="route" name="route"
+			style="width: 800px; height: 300px; border: 10px solid #ddd; -moz-border-radius: 10px; -webkit-border-radius: 10px;">${it.routeDefinition}</textarea>
+		</td>
+	</tr>
 </table>
 
 <div class="error">${it.error}</div>
diff --git a/components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java b/components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java
index d3ef7d4..9f5c0db 100644
--- a/components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java
+++ b/components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java
@@ -60,6 +60,12 @@
                 LOG.info("Open private chat to: " + privateChat.getParticipant());
             }
         } else {
+            // add the presence packet listener to the connection so we only get packets that concers us
+            // we must add the listener before creating the muc
+            final ToContainsFilter toFilter = new ToContainsFilter(endpoint.getParticipant());
+            final AndFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), toFilter);
+            connection.addPacketListener(this, packetFilter);
+
             muc = new MultiUserChat(connection, endpoint.resolveRoom(connection));
             muc.addMessageListener(this);
             DiscussionHistory history = new DiscussionHistory();
@@ -69,11 +75,6 @@
             if (LOG.isInfoEnabled()) {
                 LOG.info("Joined room: " + muc.getRoom() + " as: " + endpoint.getNickname());
             }
-
-            // add the presence packet listener to the connection so we only get packets that concers us
-            final ToContainsFilter toFilter = new ToContainsFilter(endpoint.getParticipant());
-            final AndFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), toFilter);
-            connection.addPacketListener(this, packetFilter);
         }
 
         super.doStart();