camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From davscl...@apache.org
Subject [4/4] camel git commit: CAMEL-10599: Add ReloadStrategy to allow watching for file changes and reload routes on the fly.
Date Tue, 20 Dec 2016 12:05:03 GMT
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 <davsclaus@apache.org>
Authored: Tue Dec 20 12:19:51 2016 +0100
Committer: Claus Ibsen <davsclaus@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>


Mime
View raw message