You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2016/12/20 12:05:00 UTC

[1/4] camel git commit: Those options are secret

Repository: camel
Updated Branches:
  refs/heads/master c0642aef5 -> b3f3eabbf


Those options are secret


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b3f3eabb
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b3f3eabb
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b3f3eabb

Branch: refs/heads/master
Commit: b3f3eabbf869b46c3f6c92ee7378faeda9ba832d
Parents: 945e0b4
Author: Claus Ibsen <da...@apache.org>
Authored: Tue Dec 20 13:03:58 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Tue Dec 20 13:04:51 2016 +0100

----------------------------------------------------------------------
 .../camel-twitter/src/main/docs/twitter-component.adoc      | 8 ++++----
 .../apache/camel/component/twitter/TwitterComponent.java    | 9 +++++++++
 .../camel/component/twitter/TwitterConfiguration.java       | 8 ++++----
 .../camel/component/twitter/UriConfigurationTest.java       | 4 ----
 4 files changed, 17 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/b3f3eabb/components/camel-twitter/src/main/docs/twitter-component.adoc
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/docs/twitter-component.adoc b/components/camel-twitter/src/main/docs/twitter-component.adoc
index d514a41..e63fcf8 100644
--- a/components/camel-twitter/src/main/docs/twitter-component.adoc
+++ b/components/camel-twitter/src/main/docs/twitter-component.adoc
@@ -159,10 +159,6 @@ The Twitter component supports 43 endpoint options which are listed below:
 |=======================================================================
 | Name | Group | Default | Java Type | Description
 | kind | common |  | String | *Required* What polling mode to use direct polling or event based. The event mode is only supported when the endpoint kind is event based.
-| accessToken | common |  | String | The access token. Can also be configured on the TwitterComponent level instead.
-| accessTokenSecret | common |  | String | The access secret. Can also be configured on the TwitterComponent level instead.
-| consumerKey | common |  | String | The consumer key. Can also be configured on the TwitterComponent level instead.
-| consumerSecret | common |  | String | The consumer secret. Can also be configured on the TwitterComponent level instead.
 | user | common |  | String | Username used for user timeline consumption direct message production etc.
 | bridgeErrorHandler | consumer | false | boolean | Allows for bridging the consumer to the Camel routing Error Handler which mean any exceptions occurred while the consumer is trying to pickup incoming messages or the likes will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions that will be logged at WARN/ERROR level and ignored.
 | sendEmptyMessageWhenIdle | consumer | false | boolean | If the polling consumer did not poll any files you can enable this option to send an empty message (no body) instead.
@@ -201,6 +197,10 @@ The Twitter component supports 43 endpoint options which are listed below:
 | httpProxyPassword | proxy |  | String | The http proxy password which can be used for the camel-twitter. Can also be configured on the TwitterComponent level instead.
 | httpProxyPort | proxy |  | Integer | The http proxy port which can be used for the camel-twitter. Can also be configured on the TwitterComponent level instead.
 | httpProxyUser | proxy |  | String | The http proxy user which can be used for the camel-twitter. Can also be configured on the TwitterComponent level instead.
+| accessToken | security |  | String | The access token. Can also be configured on the TwitterComponent level instead.
+| accessTokenSecret | security |  | String | The access secret. Can also be configured on the TwitterComponent level instead.
+| consumerKey | security |  | String | The consumer key. Can also be configured on the TwitterComponent level instead.
+| consumerSecret | security |  | String | The consumer secret. Can also be configured on the TwitterComponent level instead.
 |=======================================================================
 {% endraw %}
 // endpoint options: END

http://git-wip-us.apache.org/repos/asf/camel/blob/b3f3eabb/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
index 8551f7b..adc4f28 100644
--- a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
@@ -20,19 +20,28 @@ import java.util.Map;
 
 import org.apache.camel.Endpoint;
 import org.apache.camel.impl.UriEndpointComponent;
+import org.apache.camel.spi.Metadata;
 
 /**
  * Twitter component
  */
 public class TwitterComponent extends UriEndpointComponent {
 
+    @Metadata(label = "security", secret = true)
     private String consumerKey;
+    @Metadata(label = "security", secret = true)
     private String consumerSecret;
+    @Metadata(label = "security", secret = true)
     private String accessToken;
+    @Metadata(label = "security", secret = true)
     private String accessTokenSecret;
+    @Metadata(label = "proxy")
     private String httpProxyHost;
+    @Metadata(label = "proxy")
     private String httpProxyUser;
+    @Metadata(label = "proxy")
     private String httpProxyPassword;
+    @Metadata(label = "proxy")
     private Integer httpProxyPort;
 
     public TwitterComponent() {

http://git-wip-us.apache.org/repos/asf/camel/blob/b3f3eabb/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
index fb6388f..f184739 100644
--- a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
@@ -36,13 +36,13 @@ public class TwitterConfiguration {
     private String kind;
     @UriParam(label = "consumer", defaultValue = "direct", enums = "polling,direct,event")
     private EndpointType type = EndpointType.DIRECT;
-    @UriParam(secret = true)
+    @UriParam(label = "security", secret = true)
     private String accessToken;
-    @UriParam(secret = true)
+    @UriParam(label = "security", secret = true)
     private String accessTokenSecret;
-    @UriParam(secret = true)
+    @UriParam(label = "security", secret = true)
     private String consumerKey;
-    @UriParam(secret = true)
+    @UriParam(label = "security", secret = true)
     private String consumerSecret;
     @UriParam
     private String user;

http://git-wip-us.apache.org/repos/asf/camel/blob/b3f3eabb/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/UriConfigurationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/UriConfigurationTest.java b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/UriConfigurationTest.java
index 62d75ad..c4bcd9b 100644
--- a/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/UriConfigurationTest.java
+++ b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/UriConfigurationTest.java
@@ -73,10 +73,6 @@ public class UriConfigurationTest extends Assert {
         ComponentConfiguration compConf = comp.createComponentConfiguration();
         String json = compConf.createParameterJsonSchema();
         assertNotNull(json);
-
-        // REVIST this comparison test may be sensitive to some changes.
-        assertTrue(json.contains("\"accessToken\": { \"kind\": \"parameter\", \"group\": \"common\", \"type\": \"string\""));
-        assertTrue(json.contains("\"consumerKey\": { \"kind\": \"parameter\", \"group\": \"common\", \"type\": \"string\""));
     }
 
     @Test


[2/4] camel git commit: Those options are secret

Posted by da...@apache.org.
Those options are secret


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b7563bc2
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b7563bc2
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b7563bc2

Branch: refs/heads/master
Commit: b7563bc2c44f800d9a37a50dceff9c3cc170bfc7
Parents: 32e7dc3
Author: Claus Ibsen <da...@apache.org>
Authored: Tue Dec 20 12:55:01 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Tue Dec 20 13:04:51 2016 +0100

----------------------------------------------------------------------
 .../apache/camel/component/twitter/TwitterConfiguration.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/b7563bc2/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
index c665c20..fb6388f 100644
--- a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
@@ -36,13 +36,13 @@ public class TwitterConfiguration {
     private String kind;
     @UriParam(label = "consumer", defaultValue = "direct", enums = "polling,direct,event")
     private EndpointType type = EndpointType.DIRECT;
-    @UriParam
+    @UriParam(secret = true)
     private String accessToken;
-    @UriParam
+    @UriParam(secret = true)
     private String accessTokenSecret;
-    @UriParam
+    @UriParam(secret = true)
     private String consumerKey;
-    @UriParam
+    @UriParam(secret = true)
     private String consumerSecret;
     @UriParam
     private String user;


[3/4] camel git commit: CAMEL-10599: Add ReloadStrategy to allow watching for file changes and reload routes on the fly.

Posted by da...@apache.org.
CAMEL-10599: Add ReloadStrategy to allow watching for file changes and reload routes on the fly.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/945e0b4e
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/945e0b4e
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/945e0b4e

Branch: refs/heads/master
Commit: 945e0b4e9ec7ff5c182b0ad8e9b2dc1120069c64
Parents: b7563bc
Author: Claus Ibsen <da...@apache.org>
Authored: Tue Dec 20 12:58:36 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Tue Dec 20 13:04:51 2016 +0100

----------------------------------------------------------------------
 .../src/main/java/org/apache/camel/util/XmlLineNumberParser.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/945e0b4e/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java b/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
index b26e76e..a95892b 100644
--- a/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
+++ b/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
@@ -159,7 +159,7 @@ public final class XmlLineNumberParser {
                     }
 
                     for (int i = 0; i < attributes.getLength(); i++) {
-                        el.setAttribute(attributes.getQName(i), attributes.getValue(i));
+                        el.setAttribute(transformer.transform(attributes.getQName(i)), transformer.transform(attributes.getValue(i)));
                     }
 
                     el.setUserData(LINE_NUMBER, String.valueOf(this.locator.getLineNumber()), null);


[4/4] camel git commit: CAMEL-10599: Add ReloadStrategy to allow watching for file changes and reload routes on the fly.

Posted by da...@apache.org.
CAMEL-10599: Add ReloadStrategy to allow watching for file changes and reload routes on the fly.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/32e7dc38
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/32e7dc38
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/32e7dc38

Branch: refs/heads/master
Commit: 32e7dc38919fc002842bcac9e4458e10a4ee351a
Parents: c0642ae
Author: Claus Ibsen <da...@apache.org>
Authored: Tue Dec 20 12:19:51 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Tue Dec 20 13:04:51 2016 +0100

----------------------------------------------------------------------
 .../camel/impl/FileWatcherReloadStrategy.java   |   4 +
 .../camel/support/ReloadStrategySupport.java    | 117 ++++++++++++++++++-
 .../org/apache/camel/util/StringHelper.java     |  35 ++++++
 .../apache/camel/util/XmlLineNumberParser.java  |  65 +++++++++--
 .../impl/FileWatcherReloadStrategyTest.java     |  75 +++++++++++-
 .../org/apache/camel/util/StringHelperTest.java |  33 ++++++
 .../camel/util/XmlLineNumberParserTest.java     |  96 +++++++++++++++
 camel-core/src/test/resources/log4j2.properties |   2 +-
 .../org/apache/camel/model/barUpdatedRoute.xml  |  32 +++++
 .../org/apache/camel/util/camel-context.xml     |  48 ++++++++
 .../resources/META-INF/spring/camel-context.xml |  17 ++-
 11 files changed, 500 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/main/java/org/apache/camel/impl/FileWatcherReloadStrategy.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/FileWatcherReloadStrategy.java b/camel-core/src/main/java/org/apache/camel/impl/FileWatcherReloadStrategy.java
index 6c2a321..0393044 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/FileWatcherReloadStrategy.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/FileWatcherReloadStrategy.java
@@ -75,6 +75,8 @@ public class FileWatcherReloadStrategy extends ReloadStrategySupport {
 
     @Override
     protected void doStart() throws Exception {
+        super.doStart();
+
         if (folder == null) {
             // no folder configured
             return;
@@ -109,6 +111,8 @@ public class FileWatcherReloadStrategy extends ReloadStrategySupport {
 
     @Override
     protected void doStop() throws Exception {
+        super.doStop();
+
         if (executorService != null) {
             getCamelContext().getExecutorServiceManager().shutdownGraceful(executorService);
             executorService = null;

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/main/java/org/apache/camel/support/ReloadStrategySupport.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/support/ReloadStrategySupport.java b/camel-core/src/main/java/org/apache/camel/support/ReloadStrategySupport.java
index 6ae317e..219e89f 100644
--- a/camel-core/src/main/java/org/apache/camel/support/ReloadStrategySupport.java
+++ b/camel-core/src/main/java/org/apache/camel/support/ReloadStrategySupport.java
@@ -16,7 +16,10 @@
  */
 package org.apache.camel.support;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -26,10 +29,15 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedOperation;
 import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.spi.ReloadStrategy;
+import org.apache.camel.util.CollectionStringBuffer;
+import org.apache.camel.util.LRUCache;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.ServiceHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.XmlLineNumberParser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,6 +48,9 @@ public abstract class ReloadStrategySupport extends ServiceSupport implements Re
     protected final Logger log = LoggerFactory.getLogger(getClass());
     private CamelContext camelContext;
 
+    // store state
+    private final Map<String, ResourceState> cache = new LRUCache<String, ResourceState>(100);
+
     private int succeeded;
     private int failed;
 
@@ -70,15 +81,29 @@ public abstract class ReloadStrategySupport extends ServiceSupport implements Re
 
     @Override
     public void onReloadXml(CamelContext camelContext, String name, InputStream resource) {
-        log.debug("Reloading CamelContext: {} from XML resource: {}", camelContext.getName(), name);
+        log.debug("Reloading routes from XML resource: {}", name);
 
-        Document dom = camelContext.getTypeConverter().tryConvertTo(Document.class, resource);
-        if (dom == null) {
+        Document dom;
+        String xml;
+        try {
+            xml = camelContext.getTypeConverter().mandatoryConvertTo(String.class, resource);
+            // the JAXB model expects the spring namespace (even for blueprint)
+            dom = XmlLineNumberParser.parseXml(new ByteArrayInputStream(xml.getBytes()), null, "camelContext,routes", "http://camel.apache.org/schema/spring");
+        } catch (Exception e) {
             failed++;
             log.warn("Cannot load the resource " + name + " as XML");
             return;
         }
 
+        ResourceState state = cache.get(name);
+        if (state == null) {
+            state = new ResourceState(name, dom, xml);
+            cache.put(name, state);
+        }
+
+        String oldXml = state.getXml();
+        List<Integer> changed = StringHelper.changedLines(oldXml, xml);
+
         // find the <routes> root
         NodeList list = dom.getElementsByTagName("routes");
         if (list == null || list.getLength() == 0) {
@@ -86,12 +111,39 @@ public abstract class ReloadStrategySupport extends ServiceSupport implements Re
             list = dom.getElementsByTagName("route");
         }
 
+        // collect which routes are updated
+        CollectionStringBuffer csb = new CollectionStringBuffer(",");
+
         if (list != null && list.getLength() > 0) {
             for (int i = 0; i < list.getLength(); i++) {
                 Node node = list.item(i);
+
+                // what line number are this within
+                String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+                String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+                if (lineNumber != null && lineNumberEnd != null && !changed.isEmpty()) {
+                    int start = Integer.valueOf(lineNumber);
+                    int end = Integer.valueOf(lineNumberEnd);
+
+                    boolean within = withinChanged(start, end, changed);
+                    if (within) {
+                        log.debug("Updating route in lines: {}-{}", start, end);
+                    } else {
+                        log.debug("No changes to route in lines: {}-{}", start, end);
+                        continue;
+                    }
+                }
+
                 try {
                     RoutesDefinition routes = ModelHelper.loadRoutesDefinition(camelContext, node);
-                    camelContext.addRouteDefinitions(routes.getRoutes());
+                    if (!routes.getRoutes().isEmpty()) {
+                        // collect route ids and force assign ids if not in use
+                        for (RouteDefinition route : routes.getRoutes()) {
+                            String id = route.idOrCreate(camelContext.getNodeIdFactory());
+                            csb.append(id);
+                        }
+                        camelContext.addRouteDefinitions(routes.getRoutes());
+                    }
                 } catch (Exception e) {
                     failed++;
                     throw ObjectHelper.wrapRuntimeCamelException(e);
@@ -99,10 +151,27 @@ public abstract class ReloadStrategySupport extends ServiceSupport implements Re
             }
         }
 
-        log.info("Reloaded CamelContext: {} from XML resource: {}", camelContext.getName(), name);
+        if (!csb.isEmpty()) {
+            log.info("Reloaded routes: [{}] from XML resource: {}", csb, name);
+        }
+
+        // update cache
+        state = new ResourceState(name, dom, xml);
+        cache.put(name, state);
+
         succeeded++;
     }
 
+    private boolean withinChanged(int start, int end, List<Integer> changed) {
+        for (int change : changed) {
+            log.trace("Changed line: {} within {}-{}", change, start, end);
+            if (change >= start && change <= end) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @ManagedAttribute(description = "Number of reloads succeeded")
     public int getReloadCounter() {
         return succeeded;
@@ -118,4 +187,42 @@ public abstract class ReloadStrategySupport extends ServiceSupport implements Re
         succeeded = 0;
         failed = 0;
     }
+
+    @Override
+    protected void doStart() throws Exception {
+        // noop
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        cache.clear();
+    }
+
+    /**
+     * To keep state of last reloaded resource
+     */
+    private static final class ResourceState {
+        private final String name;
+        private final Document dom;
+        private final String xml;
+
+        ResourceState(String name, Document dom, String xml) {
+            this.name = name;
+            this.dom = dom;
+            this.xml = xml;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Document getDom() {
+            return dom;
+        }
+
+        public String getXml() {
+            return xml;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/StringHelper.java b/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
index 084bea0..7322be0 100644
--- a/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.util;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.function.Function;
@@ -613,4 +616,36 @@ public final class StringHelper {
         }
         return sb.toString();
     }
+
+    /**
+     * Compares old and new text content and report back which lines are changed
+     *
+     * @param oldText  the old text
+     * @param newText  the new text
+     * @return a list of line numbers that are changed in the new text
+     */
+    public static List<Integer> changedLines(String oldText, String newText) {
+        if (oldText == null || oldText.equals(newText)) {
+            return Collections.emptyList();
+        }
+
+        List<Integer> changed = new ArrayList<>();
+
+        String[] oldLines = oldText.split("\n");
+        String[] newLines = newText.split("\n");
+
+        for (int i = 0; i < newLines.length; i++) {
+            String newLine = newLines[i];
+            String oldLine = i < oldLines.length ? oldLines[i] : null;
+            if (oldLine == null) {
+                changed.add(i);
+            } else if (!newLine.equals(oldLine)) {
+                changed.add(i);
+            }
+        }
+
+        return changed;
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java b/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
index 2eb1a28..b26e76e 100644
--- a/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
+++ b/camel-core/src/main/java/org/apache/camel/util/XmlLineNumberParser.java
@@ -72,20 +72,36 @@ public final class XmlLineNumberParser {
      * @throws Exception is thrown if error parsing
      */
     public static Document parseXml(final InputStream is) throws Exception {
-        return parseXml(is, new NoopTransformer());
+        return parseXml(is, null);
     }
 
     /**
      * Parses the XML.
      *
-     * @param is the XML content as an input stream
+     * @param is             the XML content as an input stream
+     * @param xmlTransformer the XML transformer
+     * @return the DOM model
+     * @throws Exception is thrown if error parsing
+     */
+    public static Document parseXml(final InputStream is, final XmlTextTransformer xmlTransformer) throws Exception {
+        return parseXml(is, xmlTransformer, null, null);
+    }
+
+    /**
+     * Parses the XML.
+     *
+     * @param is              the XML content as an input stream
+     * @param xmlTransformer  the XML transformer
+     * @param rootNames       one or more root names that is used as baseline for beginning the parsing, for example camelContext to start parsing
+     *                        when Camel is discovered. Multiple names can be defined separated by comma
+     * @param forceNamespace  an optional namespaces to force assign to each node. This may be needed for JAXB unmarshalling from XML -> POJO.
      * @return the DOM model
      * @throws Exception is thrown if error parsing
      */
-    public static Document parseXml(final InputStream is, final XmlTextTransformer transformer) throws Exception {
+    public static Document parseXml(final InputStream is, XmlTextTransformer xmlTransformer, String rootNames, final String forceNamespace) throws Exception {
         ObjectHelper.notNull(is, "is");
-        ObjectHelper.notNull(transformer, "transformer");
 
+        final XmlTextTransformer transformer = xmlTransformer == null ? new NoopTransformer() : xmlTransformer;
         final Document doc;
         SAXParser parser;
         final SAXParserFactory factory = SAXParserFactory.newInstance();
@@ -112,25 +128,52 @@ public final class XmlLineNumberParser {
             @Override
             public void setDocumentLocator(final Locator locator) {
                 this.locator = locator; // Save the locator, so that it can be used later for line tracking when traversing nodes.
+                this.found = rootNames == null;
+            }
+
+            private boolean isRootName(String qName) {
+                for (String root : rootNames.split(",")) {
+                    if (qName.equals(root)) {
+                        return true;
+                    }
+                }
+                return false;
             }
 
             @Override
             public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
                 addTextIfNeeded();
 
-                Element el = doc.createElement(qName);
-
-                for (int i = 0; i < attributes.getLength(); i++) {
-                    el.setAttribute(transformer.transform(attributes.getQName(i)), transformer.transform(attributes.getValue(i)));
+                if (rootNames != null && !found) {
+                    if (isRootName(qName)) {
+                        found = true;
+                    }
                 }
 
-                el.setUserData(LINE_NUMBER, String.valueOf(this.locator.getLineNumber()), null);
-                el.setUserData(COLUMN_NUMBER, String.valueOf(this.locator.getColumnNumber()), null);
-                elementStack.push(el);
+                if (found) {
+                    Element el;
+                    if (forceNamespace != null) {
+                        el = doc.createElementNS(forceNamespace, qName);
+                    } else {
+                        el = doc.createElement(qName);
+                    }
+
+                    for (int i = 0; i < attributes.getLength(); i++) {
+                        el.setAttribute(attributes.getQName(i), attributes.getValue(i));
+                    }
+
+                    el.setUserData(LINE_NUMBER, String.valueOf(this.locator.getLineNumber()), null);
+                    el.setUserData(COLUMN_NUMBER, String.valueOf(this.locator.getColumnNumber()), null);
+                    elementStack.push(el);
+                }
             }
 
             @Override
             public void endElement(final String uri, final String localName, final String qName) {
+                if (!found) {
+                    return;
+                }
+
                 addTextIfNeeded();
 
                 final Element closedEl = elementStack.isEmpty() ? null : elementStack.pop();

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/java/org/apache/camel/impl/FileWatcherReloadStrategyTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/FileWatcherReloadStrategyTest.java b/camel-core/src/test/java/org/apache/camel/impl/FileWatcherReloadStrategyTest.java
index 6136712..2691c96 100644
--- a/camel-core/src/test/java/org/apache/camel/impl/FileWatcherReloadStrategyTest.java
+++ b/camel-core/src/test/java/org/apache/camel/impl/FileWatcherReloadStrategyTest.java
@@ -73,6 +73,7 @@ public class FileWatcherReloadStrategyTest extends ContextTestSupport {
         assertEquals(1, context.getRoutes().size());
 
         // and the route should work
+        Thread.sleep(1000);
         getMockEndpoint("mock:bar").expectedMessageCount(1);
         template.sendBody("direct:bar", "Hello World");
         assertMockEndpointsSatisfied();
@@ -92,7 +93,6 @@ public class FileWatcherReloadStrategyTest extends ContextTestSupport {
 
             @Override
             public boolean isEnabled(EventObject event) {
-                System.out.println(event);
                 return event instanceof RouteAddedEvent;
             }
         });
@@ -114,6 +114,8 @@ public class FileWatcherReloadStrategyTest extends ContextTestSupport {
         template.sendBody("direct:bar", "Hello World");
         assertMockEndpointsSatisfied();
 
+        resetMocks();
+
         Thread.sleep(1000);
         log.info("Copying file to target/dummy");
 
@@ -122,14 +124,79 @@ public class FileWatcherReloadStrategyTest extends ContextTestSupport {
 
         // wait for that file to be processed and remove/add the route
         // (is slow on osx, so wait up till 20 seconds)
-        latch.await(20, TimeUnit.SECONDS);
-
-        resetMocks();
+        boolean done = latch.await(20, TimeUnit.SECONDS);
+        assertTrue("Should reload file within 20 seconds", done);
 
         // and the route should be changed to route to mock:bar instead of mock:foo
+        Thread.sleep(1000);
         getMockEndpoint("mock:bar").expectedMessageCount(1);
         getMockEndpoint("mock:foo").expectedMessageCount(0);
         template.sendBody("direct:bar", "Bye World");
         assertMockEndpointsSatisfied();
     }
+
+    public void testUpdateXmlRoute() throws Exception {
+        deleteDirectory("target/dummy");
+        createDirectory("target/dummy");
+
+        // the bar route is added two times, at first, and then when updated
+        final CountDownLatch latch = new CountDownLatch(2);
+        context.getManagementStrategy().addEventNotifier(new EventNotifierSupport() {
+            @Override
+            public void notify(EventObject event) throws Exception {
+                latch.countDown();
+            }
+
+            @Override
+            public boolean isEnabled(EventObject event) {
+                return event instanceof RouteAddedEvent;
+            }
+        });
+
+        context.start();
+
+        // there are 0 routes to begin with
+        assertEquals(0, context.getRoutes().size());
+
+        Thread.sleep(1000);
+        log.info("Copying file to target/dummy");
+
+        // create an xml file with some routes
+        FileUtil.copyFile(new File("src/test/resources/org/apache/camel/model/barRoute.xml"), new File("target/dummy/barRoute.xml"));
+
+        // wait for that file to be processed
+        // (is slow on osx, so wait up till 20 seconds)
+        for (int i = 0; i < 20; i++) {
+            if (context.getRoutes().size() > 0) {
+                break;
+            }
+            Thread.sleep(1000);
+        }
+
+        assertEquals(1, context.getRoutes().size());
+
+        // and the route should work
+        getMockEndpoint("mock:bar").expectedMessageCount(1);
+        template.sendBody("direct:bar", "Hello World");
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+
+        // now update the file
+        log.info("Updating file in target/dummy");
+
+        // create an xml file with some routes
+        FileUtil.copyFile(new File("src/test/resources/org/apache/camel/model/barUpdatedRoute.xml"), new File("target/dummy/barRoute.xml"));
+
+        // wait for that file to be processed and remove/add the route
+        // (is slow on osx, so wait up till 20 seconds)
+        boolean done = latch.await(20, TimeUnit.SECONDS);
+        assertTrue("Should reload file within 20 seconds", done);
+
+        // and the route should work with the update
+        Thread.sleep(1000);
+        getMockEndpoint("mock:bar").expectedBodiesReceived("Bye Camel");
+        template.sendBody("direct:bar", "Camel");
+        assertMockEndpointsSatisfied();
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/java/org/apache/camel/util/StringHelperTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/StringHelperTest.java b/camel-core/src/test/java/org/apache/camel/util/StringHelperTest.java
index d9a4631..760971b 100644
--- a/camel-core/src/test/java/org/apache/camel/util/StringHelperTest.java
+++ b/camel-core/src/test/java/org/apache/camel/util/StringHelperTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.util;
 
+import java.util.List;
+
 import junit.framework.TestCase;
 
 /**
@@ -206,4 +208,35 @@ public class StringHelperTest extends TestCase {
         assertEquals("Should get the right class name", "", StringHelper.normalizeClassName("////"));
     }
 
+    public void testChangedLines() {
+        String oldText = "Hello\nWorld\nHow are you";
+        String newText = "Hello\nWorld\nHow are you";
+
+        List<Integer> changed = StringHelper.changedLines(oldText, newText);
+        assertEquals(0, changed.size());
+
+        oldText = "Hello\nWorld\nHow are you";
+        newText = "Hello\nWorld\nHow are you today";
+
+        changed = StringHelper.changedLines(oldText, newText);
+        assertEquals(1, changed.size());
+        assertEquals(2, changed.get(0).intValue());
+
+        oldText = "Hello\nWorld\nHow are you";
+        newText = "Hello\nCamel\nHow are you today";
+
+        changed = StringHelper.changedLines(oldText, newText);
+        assertEquals(2, changed.size());
+        assertEquals(1, changed.get(0).intValue());
+        assertEquals(2, changed.get(1).intValue());
+
+        oldText = "Hello\nWorld\nHow are you";
+        newText = "Hello\nWorld\nHow are you today\nand tomorrow";
+
+        changed = StringHelper.changedLines(oldText, newText);
+        assertEquals(2, changed.size());
+        assertEquals(2, changed.get(0).intValue());
+        assertEquals(3, changed.get(1).intValue());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/java/org/apache/camel/util/XmlLineNumberParserTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/XmlLineNumberParserTest.java b/camel-core/src/test/java/org/apache/camel/util/XmlLineNumberParserTest.java
new file mode 100644
index 0000000..845fd0f
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/XmlLineNumberParserTest.java
@@ -0,0 +1,96 @@
+/**
+ * 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.util;
+
+import java.io.FileInputStream;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import junit.framework.TestCase;
+
+public class XmlLineNumberParserTest extends TestCase {
+
+    public void testParse() throws Exception {
+        FileInputStream fis = new FileInputStream("src/test/resources/org/apache/camel/util/camel-context.xml");
+        Document dom = XmlLineNumberParser.parseXml(fis);
+        assertNotNull(dom);
+
+        NodeList list = dom.getElementsByTagName("beans");
+        assertEquals(1, list.getLength());
+        Node node = list.item(0);
+
+        String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+        String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+
+        assertEquals("23", lineNumber);
+        assertEquals("48", lineNumberEnd);
+    }
+
+    public void testParseCamelContext() throws Exception {
+        FileInputStream fis = new FileInputStream("src/test/resources/org/apache/camel/util/camel-context.xml");
+        Document dom = XmlLineNumberParser.parseXml(fis, null, "camelContext", null);
+        assertNotNull(dom);
+
+        NodeList list = dom.getElementsByTagName("camelContext");
+        assertEquals(1, list.getLength());
+        Node node = list.item(0);
+
+        String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+        String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+
+        assertEquals("28", lineNumber);
+        assertEquals("46", lineNumberEnd);
+    }
+
+    public void testParseCamelContextForceNamespace() throws Exception {
+        FileInputStream fis = new FileInputStream("src/test/resources/org/apache/camel/util/camel-context.xml");
+        Document dom = XmlLineNumberParser.parseXml(fis, null, "camelContext", "http://camel.apache.org/schema/spring");
+        assertNotNull(dom);
+
+        NodeList list = dom.getElementsByTagName("camelContext");
+        assertEquals(1, list.getLength());
+        Node node = list.item(0);
+
+        String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+        String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+
+        String ns = node.getNamespaceURI();
+        assertEquals("http://camel.apache.org/schema/spring", ns);
+
+        assertEquals("28", lineNumber);
+        assertEquals("46", lineNumberEnd);
+
+        // and there are two routes
+        list = dom.getElementsByTagName("route");
+        assertEquals(2, list.getLength());
+        Node node1 = list.item(0);
+        Node node2 = list.item(1);
+
+        String lineNumber1 = (String) node1.getUserData(XmlLineNumberParser.LINE_NUMBER);
+        String lineNumberEnd1 = (String) node1.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+        assertEquals("30", lineNumber1);
+        assertEquals("36", lineNumberEnd1);
+
+        String lineNumber2 = (String) node2.getUserData(XmlLineNumberParser.LINE_NUMBER);
+        String lineNumberEnd2 = (String) node2.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+        assertEquals("38", lineNumber2);
+        assertEquals("44", lineNumberEnd2);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/resources/log4j2.properties
----------------------------------------------------------------------
diff --git a/camel-core/src/test/resources/log4j2.properties b/camel-core/src/test/resources/log4j2.properties
index f066246..6481a17 100644
--- a/camel-core/src/test/resources/log4j2.properties
+++ b/camel-core/src/test/resources/log4j2.properties
@@ -41,7 +41,7 @@ logger.customlogger.appenderRef.file2.ref = file2
 rootLogger.level = INFO
 
 rootLogger.appenderRef.file.ref = file
-# rootLogger.appenderRef.file.ref = console
+#rootLogger.appenderRef.file.ref = console
 
 #logger.camel-core.name = org.apache.camel.impl
 #logger.camel-core.level = DEBUG

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/resources/org/apache/camel/model/barUpdatedRoute.xml
----------------------------------------------------------------------
diff --git a/camel-core/src/test/resources/org/apache/camel/model/barUpdatedRoute.xml b/camel-core/src/test/resources/org/apache/camel/model/barUpdatedRoute.xml
new file mode 100644
index 0000000..048cfa0
--- /dev/null
+++ b/camel-core/src/test/resources/org/apache/camel/model/barUpdatedRoute.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<!-- START SNIPPET: e1 -->
+<routes xmlns="http://camel.apache.org/schema/spring">
+    <!-- here we define the bar route -->
+    <route id="bar">
+        <from uri="direct:bar"/>
+        <transform>
+          <simple>Bye ${body}</simple>
+        </transform>
+        <to uri="mock:bar"/>
+    </route>
+
+    <!-- we could add more routes if we like,
+         but in this example we stick to one route only -->
+</routes>
+<!-- END SNIPPET: e1 -->
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/camel-core/src/test/resources/org/apache/camel/util/camel-context.xml
----------------------------------------------------------------------
diff --git a/camel-core/src/test/resources/org/apache/camel/util/camel-context.xml b/camel-core/src/test/resources/org/apache/camel/util/camel-context.xml
new file mode 100644
index 0000000..0524b4d
--- /dev/null
+++ b/camel-core/src/test/resources/org/apache/camel/util/camel-context.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
+
+  <!-- notice Camel will only update the routes that has been changed, so you can edit either either route or both
+       and save the file, and Camel will update only what is required -->
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+
+    <route id="timer">
+      <from uri="timer:foo"/>
+      <!-- call the 2nd route -->
+      <to uri="direct:foo"/>
+      <!-- try to change me and save this file -->
+      <log message="You said: ${body}"/>
+    </route>
+
+    <route id="foo">
+      <from uri="direct:foo"/>
+      <!-- try to change me and save this file -->
+      <transform>
+        <constant>Hello World</constant>
+      </transform>
+    </route>
+
+  </camelContext>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/camel/blob/32e7dc38/examples/camel-example-reload/src/main/resources/META-INF/spring/camel-context.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-reload/src/main/resources/META-INF/spring/camel-context.xml b/examples/camel-example-reload/src/main/resources/META-INF/spring/camel-context.xml
index f6df299..99b5ac8 100644
--- a/examples/camel-example-reload/src/main/resources/META-INF/spring/camel-context.xml
+++ b/examples/camel-example-reload/src/main/resources/META-INF/spring/camel-context.xml
@@ -22,15 +22,26 @@
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
 
+  <!-- notice Camel will only update the routes that has been changed, so you can edit either either route or both
+       and save the file, and Camel will update only what is required -->
+
   <camelContext xmlns="http://camel.apache.org/schema/spring">
-    <route id="myRoute">
+
+    <route id="timer">
       <from uri="timer:foo"/>
-      <!-- try to change me and save this file -->
+      <to uri="direct:foo"/>
+      <!-- you can try changing me -->
+      <log message="You said: ${body}"/>
+    </route>
+
+    <route id="foo">
+      <from uri="direct:foo"/>
       <transform>
+        <!-- and I can be changed too -->
         <constant>Hello World</constant>
       </transform>
-      <log message="You said: ${body}"/>
     </route>
+
   </camelContext>
 
 </beans>