You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ch...@apache.org on 2015/03/11 17:03:35 UTC

camel git commit: Fix for CAMEL-8452: Camel route model - Preserve {{ }} placeholders in model

Repository: camel
Updated Branches:
  refs/heads/master 3754f4c46 -> e08c8700b


Fix for CAMEL-8452: Camel route model - Preserve {{ }} placeholders in model

We preserve just changed properties of ProcessorDefinitions before we create the associated Processor and restore the original values after the the processor is created.


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

Branch: refs/heads/master
Commit: e08c8700b1001b7ccff52be5ae0d92ee3fa231e8
Parents: 3754f4c
Author: Hiram Chirino <hi...@hiramchirino.com>
Authored: Mon Mar 9 17:53:38 2015 -0400
Committer: Hiram Chirino <hi...@hiramchirino.com>
Committed: Wed Mar 11 11:33:39 2015 -0400

----------------------------------------------------------------------
 .../camel/model/DataFormatDefinition.java       |  20 +-
 .../InterceptSendToEndpointDefinition.java      |   5 +-
 .../apache/camel/model/ProcessorDefinition.java |  22 ++
 .../camel/model/ProcessorDefinitionHelper.java  | 225 +++++++++++++------
 .../camel/model/RouteDefinitionHelper.java      |  67 +++++-
 .../properties/PropertiesRouteFromTest.java     |   8 +-
 .../properties/PropertiesRouteIdTest.java       |   2 +-
 .../util/DumpModelAsXmlPlaceholdersTest.java    |  62 +++++
 .../spring/DumpModelAsXmlPlaceholdersTest.java  |  44 ++++
 .../component/properties/cheese.properties      |   2 +
 .../spring/DumpModelAsXmlPlaceholdersTest.xml   |  37 +++
 11 files changed, 406 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/main/java/org/apache/camel/model/DataFormatDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/DataFormatDefinition.java b/camel-core/src/main/java/org/apache/camel/model/DataFormatDefinition.java
index d2d9669..75fe121 100644
--- a/camel-core/src/main/java/org/apache/camel/model/DataFormatDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/DataFormatDefinition.java
@@ -85,6 +85,7 @@ public class DataFormatDefinition extends IdentifiedType {
 
     public DataFormat getDataFormat(RouteContext routeContext) {
         if (dataFormat == null) {
+            Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
 
             // resolve properties before we create the data format
             try {
@@ -92,14 +93,17 @@ public class DataFormatDefinition extends IdentifiedType {
             } catch (Exception e) {
                 throw new IllegalArgumentException("Error resolving property placeholders on data format: " + this, e);
             }
-
-            dataFormat = createDataFormat(routeContext);
-            if (dataFormat != null) {
-                configureDataFormat(dataFormat, routeContext.getCamelContext());
-            } else {
-                throw new IllegalArgumentException(
-                        "Data format '" + (dataFormatName != null ? dataFormatName : "<null>") + "' could not be created. "
-                                + "Ensure that the data format is valid and the associated Camel component is present on the classpath");
+            try {
+                dataFormat = createDataFormat(routeContext);
+                if (dataFormat != null) {
+                    configureDataFormat(dataFormat, routeContext.getCamelContext());
+                } else {
+                    throw new IllegalArgumentException(
+                            "Data format '" + (dataFormatName != null ? dataFormatName : "<null>") + "' could not be created. "
+                                    + "Ensure that the data format is valid and the associated Camel component is present on the classpath");
+                }
+            } finally {
+                propertyPlaceholdersChangeReverter.run();
             }
         }
         return dataFormat;

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/main/java/org/apache/camel/model/InterceptSendToEndpointDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/InterceptSendToEndpointDefinition.java b/camel-core/src/main/java/org/apache/camel/model/InterceptSendToEndpointDefinition.java
index c50298a..a79401d 100644
--- a/camel-core/src/main/java/org/apache/camel/model/InterceptSendToEndpointDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/InterceptSendToEndpointDefinition.java
@@ -87,6 +87,7 @@ public class InterceptSendToEndpointDefinition extends OutputDefinition<Intercep
     public Processor createProcessor(final RouteContext routeContext) throws Exception {
         // create the detour
         final Processor detour = this.createChildProcessor(routeContext, true);
+        final String matchURI = getUri();
 
         // register endpoint callback so we can proxy the endpoint
         routeContext.getCamelContext().addRegisterEndpointCallback(new EndpointStrategy() {
@@ -94,7 +95,7 @@ public class InterceptSendToEndpointDefinition extends OutputDefinition<Intercep
                 if (endpoint instanceof InterceptSendToEndpoint) {
                     // endpoint already decorated
                     return endpoint;
-                } else if (getUri() == null || matchPattern(routeContext.getCamelContext(), uri, getUri())) {
+                } else if (matchURI == null || matchPattern(routeContext.getCamelContext(), uri, matchURI)) {
                     // only proxy if the uri is matched decorate endpoint with our proxy
                     // should be false by default
                     boolean skip = getSkipSendToOriginalEndpoint() != null && getSkipSendToOriginalEndpoint();
@@ -116,7 +117,7 @@ public class InterceptSendToEndpointDefinition extends OutputDefinition<Intercep
         List<ProcessorDefinition<?>> outputs = route.getOutputs();
         outputs.remove(this);
 
-        return new InterceptEndpointProcessor(uri, detour);
+        return new InterceptEndpointProcessor(matchURI, detour);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 997b604..d335008 100644
--- a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -67,6 +67,7 @@ import org.apache.camel.spi.InterceptStrategy;
 import org.apache.camel.spi.LifecycleStrategy;
 import org.apache.camel.spi.Policy;
 import org.apache.camel.spi.RouteContext;
+import org.apache.camel.util.IntrospectionSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -405,6 +406,16 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
     }
 
     protected Processor createOutputsProcessor(RouteContext routeContext, Collection<ProcessorDefinition<?>> outputs) throws Exception {
+        // We will save list of actions to restore the outputs back to the original state.
+        Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
+        try {
+            return createOutputsProcessorImpl(routeContext, outputs);
+        } finally {
+            propertyPlaceholdersChangeReverter.run();
+        }
+    }
+
+    protected Processor createOutputsProcessorImpl(RouteContext routeContext, Collection<ProcessorDefinition<?>> outputs) throws Exception {
         List<Processor> list = new ArrayList<Processor>();
         for (ProcessorDefinition<?> output : outputs) {
 
@@ -471,6 +482,17 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
      * Creates the processor and wraps it in any necessary interceptors and error handlers
      */
     protected Processor makeProcessor(RouteContext routeContext) throws Exception {
+        // We will save list of actions to restore the definition back to the original state.
+        Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
+        try {
+            return makeProcessorImpl(routeContext);
+        } finally {
+            // Lets restore
+            propertyPlaceholdersChangeReverter.run();
+        }
+    }
+
+    private Processor makeProcessorImpl(RouteContext routeContext) throws Exception {
         Processor processor = null;
 
         // allow any custom logic before we create the processor

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
index 47cae82..e11c384 100644
--- a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
@@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory;
 public final class ProcessorDefinitionHelper {
 
     private static final Logger LOG = LoggerFactory.getLogger(ProcessorDefinitionHelper.class);
+    private static final ThreadLocal<RestoreAction> CURRENT_RESTORE_ACTION = new ThreadLocal<RestoreAction>();
 
     private ProcessorDefinitionHelper() {
     }
@@ -50,9 +51,9 @@ public final class ProcessorDefinitionHelper {
     /**
      * Looks for the given type in the list of outputs and recurring all the children as well.
      *
-     * @param outputs  list of outputs, can be null or empty.
-     * @param type     the type to look for
-     * @return         the found definitions, or <tt>null</tt> if not found
+     * @param outputs list of outputs, can be null or empty.
+     * @param type    the type to look for
+     * @return the found definitions, or <tt>null</tt> if not found
      */
     public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) {
         return filterTypeInOutputs(outputs, type, -1);
@@ -61,10 +62,10 @@ public final class ProcessorDefinitionHelper {
     /**
      * Looks for the given type in the list of outputs and recurring all the children as well.
      *
-     * @param outputs  list of outputs, can be null or empty.
-     * @param type     the type to look for
-     * @param maxDeep  maximum levels deep to traverse
-     * @return         the found definitions, or <tt>null</tt> if not found
+     * @param outputs list of outputs, can be null or empty.
+     * @param type    the type to look for
+     * @param maxDeep maximum levels deep to traverse
+     * @return the found definitions, or <tt>null</tt> if not found
      */
     public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type, int maxDeep) {
         List<T> found = new ArrayList<T>();
@@ -76,9 +77,9 @@ public final class ProcessorDefinitionHelper {
      * Looks for the given type in the list of outputs and recurring all the children as well.
      * Will stop at first found and return it.
      *
-     * @param outputs  list of outputs, can be null or empty.
-     * @param type     the type to look for
-     * @return         the first found type, or <tt>null</tt> if not found
+     * @param outputs list of outputs, can be null or empty.
+     * @param type    the type to look for
+     * @return the first found type, or <tt>null</tt> if not found
      */
     public static <T> T findFirstTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) {
         List<T> found = new ArrayList<T>();
@@ -93,7 +94,7 @@ public final class ProcessorDefinitionHelper {
      * Is the given child the first in the outputs from the parent?
      *
      * @param parentType the type the parent must be
-     * @param node the node
+     * @param node       the node
      * @return <tt>true</tt> if first child, <tt>false</tt> otherwise
      */
     public static boolean isFirstChildOfType(Class<?> parentType, ProcessorDefinition<?> node) {
@@ -114,9 +115,10 @@ public final class ProcessorDefinitionHelper {
 
     /**
      * Is the given node parent(s) of the given type
-     * @param parentType   the parent type
-     * @param node         the current node
-     * @param recursive    whether or not to check grand parent(s) as well
+     *
+     * @param parentType the parent type
+     * @param node       the current node
+     * @param recursive  whether or not to check grand parent(s) as well
      * @return <tt>true</tt> if parent(s) is of given type, <tt>false</tt> otherwise
      */
     public static boolean isParentOfType(Class<?> parentType, ProcessorDefinition<?> node, boolean recursive) {
@@ -174,9 +176,9 @@ public final class ProcessorDefinitionHelper {
     /**
      * Traverses the node, including its children (recursive), and gathers all the node ids.
      *
-     * @param node  the target node
-     * @param set   set to store ids, if <tt>null</tt> a new set will be created
-     * @param onlyCustomId  whether to only store custom assigned ids (ie. {@link org.apache.camel.model.OptionalIdentifiedDefinition#hasCustomIdAssigned()}
+     * @param node            the target node
+     * @param set             set to store ids, if <tt>null</tt> a new set will be created
+     * @param onlyCustomId    whether to only store custom assigned ids (ie. {@link org.apache.camel.model.OptionalIdentifiedDefinition#hasCustomIdAssigned()}
      * @param includeAbstract whether to include abstract nodes (ie. {@link org.apache.camel.model.ProcessorDefinition#isAbstract()}
      * @return the set with the found ids.
      */
@@ -257,12 +259,12 @@ public final class ProcessorDefinitionHelper {
 
                 // ensure to add ourself if we match also
                 if (type.isInstance(choice)) {
-                    found.add((T)choice);
+                    found.add((T) choice);
                 }
 
                 for (WhenDefinition when : choice.getWhenClauses()) {
                     if (type.isInstance(when)) {
-                        found.add((T)when);   
+                        found.add((T) when);
                     }
                     List<ProcessorDefinition<?>> children = when.getOutputs();
                     doFindType(children, type, found, ++current, maxDeep);
@@ -284,7 +286,7 @@ public final class ProcessorDefinitionHelper {
 
                 // ensure to add ourself if we match also
                 if (type.isInstance(doTry)) {
-                    found.add((T)doTry);
+                    found.add((T) doTry);
                 }
 
                 List<ProcessorDefinition<?>> doTryOut = doTry.getOutputsWithoutCatches();
@@ -309,7 +311,7 @@ public final class ProcessorDefinitionHelper {
 
                 // ensure to add ourself if we match also
                 if (type.isInstance(outDef)) {
-                    found.add((T)outDef);
+                    found.add((T) outDef);
                 }
 
                 List<ProcessorDefinition<?>> outDefOut = outDef.getOutputs();
@@ -320,7 +322,7 @@ public final class ProcessorDefinitionHelper {
             }
 
             if (type.isInstance(out)) {
-                found.add((T)out);
+                found.add((T) out);
             }
 
             // try children as well
@@ -334,8 +336,8 @@ public final class ProcessorDefinitionHelper {
      * <p/>
      * Is used for check if the route output has any real outputs (non abstracts)
      *
-     * @param outputs           the outputs
-     * @param excludeAbstract   whether or not to exclude abstract outputs (e.g. skip onException etc.)
+     * @param outputs         the outputs
+     * @param excludeAbstract whether or not to exclude abstract outputs (e.g. skip onException etc.)
      * @return <tt>true</tt> if has outputs, otherwise <tt>false</tt> is returned
      */
     @SuppressWarnings({"unchecked", "rawtypes"})
@@ -364,16 +366,16 @@ public final class ProcessorDefinitionHelper {
      * This is used to know if a new thread pool will be created, and therefore is not shared by others, and therefore
      * exclusive to the definition.
      *
-     * @param routeContext   the route context
-     * @param definition     the node definition which may leverage executor service.
-     * @param useDefault     whether to fallback and use a default thread pool, if no explicit configured
+     * @param routeContext the route context
+     * @param definition   the node definition which may leverage executor service.
+     * @param useDefault   whether to fallback and use a default thread pool, if no explicit configured
      * @return <tt>true</tt> if a new thread pool will be created, <tt>false</tt> if not
      * @see #getConfiguredExecutorService(org.apache.camel.spi.RouteContext, String, ExecutorServiceAwareDefinition, boolean)
      */
     public static boolean willCreateNewThreadPool(RouteContext routeContext, ExecutorServiceAwareDefinition<?> definition, boolean useDefault) {
         ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager();
         ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext());
-        
+
         if (definition.getExecutorService() != null) {
             // no there is a custom thread pool configured
             return false;
@@ -394,14 +396,15 @@ public final class ProcessorDefinitionHelper {
      * <p/>
      * This method will lookup for configured thread pool in the following order
      * <ul>
-     *   <li>from the {@link org.apache.camel.spi.Registry} if found</li>
-     *   <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
-     *   <li>if none found, then <tt>null</tt> is returned.</li>
+     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
+     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
+     * <li>if none found, then <tt>null</tt> is returned.</li>
      * </ul>
-     * @param routeContext   the route context
-     * @param name           name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
-     *                       is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
-     * @param source         the source to use the thread pool
+     *
+     * @param routeContext       the route context
+     * @param name               name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
+     *                           is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
+     * @param source             the source to use the thread pool
      * @param executorServiceRef reference name of the thread pool
      * @return the executor service, or <tt>null</tt> if none was found.
      */
@@ -426,19 +429,19 @@ public final class ProcessorDefinitionHelper {
      * <p/>
      * This method will lookup for configured thread pool in the following order
      * <ul>
-     *   <li>from the definition if any explicit configured executor service.</li>
-     *   <li>from the {@link org.apache.camel.spi.Registry} if found</li>
-     *   <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
-     *   <li>if none found, then <tt>null</tt> is returned.</li>
+     * <li>from the definition if any explicit configured executor service.</li>
+     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
+     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
+     * <li>if none found, then <tt>null</tt> is returned.</li>
      * </ul>
      * The various {@link ExecutorServiceAwareDefinition} should use this helper method to ensure they support
      * configured executor services in the same coherent way.
      *
-     * @param routeContext   the route context
-     * @param name           name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
-     *                       is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
-     * @param definition     the node definition which may leverage executor service.
-     * @param useDefault     whether to fallback and use a default thread pool, if no explicit configured
+     * @param routeContext the route context
+     * @param name         name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
+     *                     is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
+     * @param definition   the node definition which may leverage executor service.
+     * @param useDefault   whether to fallback and use a default thread pool, if no explicit configured
      * @return the configured executor service, or <tt>null</tt> if none was configured.
      * @throws IllegalArgumentException is thrown if lookup of executor service in {@link org.apache.camel.spi.Registry} was not found
      */
@@ -471,14 +474,15 @@ public final class ProcessorDefinitionHelper {
      * <p/>
      * This method will lookup for configured thread pool in the following order
      * <ul>
-     *   <li>from the {@link org.apache.camel.spi.Registry} if found</li>
-     *   <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
-     *   <li>if none found, then <tt>null</tt> is returned.</li>
+     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
+     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
+     * <li>if none found, then <tt>null</tt> is returned.</li>
      * </ul>
-     * @param routeContext   the route context
-     * @param name           name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
-     *                       is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
-     * @param source         the source to use the thread pool
+     *
+     * @param routeContext       the route context
+     * @param name               name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
+     *                           is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
+     * @param source             the source to use the thread pool
      * @param executorServiceRef reference name of the thread pool
      * @return the executor service, or <tt>null</tt> if none was found.
      */
@@ -503,26 +507,26 @@ public final class ProcessorDefinitionHelper {
      * <p/>
      * This method will lookup for configured thread pool in the following order
      * <ul>
-     *   <li>from the definition if any explicit configured executor service.</li>
-     *   <li>from the {@link org.apache.camel.spi.Registry} if found</li>
-     *   <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
-     *   <li>if none found, then <tt>null</tt> is returned.</li>
+     * <li>from the definition if any explicit configured executor service.</li>
+     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
+     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li>
+     * <li>if none found, then <tt>null</tt> is returned.</li>
      * </ul>
      * The various {@link ExecutorServiceAwareDefinition} should use this helper method to ensure they support
      * configured executor services in the same coherent way.
      *
-     * @param routeContext   the rout context
-     * @param name           name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
-     *                       is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
-     * @param definition     the node definition which may leverage executor service.
-     * @param useDefault     whether to fallback and use a default thread pool, if no explicit configured
+     * @param routeContext the rout context
+     * @param name         name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService}
+     *                     is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}.
+     * @param definition   the node definition which may leverage executor service.
+     * @param useDefault   whether to fallback and use a default thread pool, if no explicit configured
      * @return the configured executor service, or <tt>null</tt> if none was configured.
      * @throws IllegalArgumentException is thrown if the found instance is not a ScheduledExecutorService type,
-     * or lookup of executor service in {@link org.apache.camel.spi.Registry} was not found
+     *                                  or lookup of executor service in {@link org.apache.camel.spi.Registry} was not found
      */
     public static ScheduledExecutorService getConfiguredScheduledExecutorService(RouteContext routeContext, String name,
-                                                               ExecutorServiceAwareDefinition<?> definition,
-                                                               boolean useDefault) throws IllegalArgumentException {
+                                                                                 ExecutorServiceAwareDefinition<?> definition,
+                                                                                 boolean useDefault) throws IllegalArgumentException {
         ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager();
         ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext());
 
@@ -547,6 +551,93 @@ public final class ProcessorDefinitionHelper {
     }
 
     /**
+     * The RestoreAction is used to track all the undo/restore actions
+     * that need to be performed to undo any resolution to property placeholders
+     * that have been applied to the camel route defs.  This class is private
+     * so it does not get used directly.  It's mainly used by the {@see createPropertyPlaceholdersChangeReverter()}
+     * method.
+     */
+    private static final class RestoreAction implements Runnable {
+
+        private final RestoreAction prevChange;
+        private final ArrayList<Runnable> actions = new ArrayList<Runnable>();
+
+        private RestoreAction(RestoreAction prevChange) {
+            this.prevChange = prevChange;
+        }
+
+        @Override
+        public void run() {
+            for (Runnable action : actions) {
+                action.run();
+            }
+            actions.clear();
+            if (prevChange == null) {
+                CURRENT_RESTORE_ACTION.remove();
+            } else {
+                CURRENT_RESTORE_ACTION.set(prevChange);
+            }
+        }
+    }
+
+    /**
+     * Creates a Runnable which when run will revert property placeholder
+     * updates to the camel route definitions that were done after this method
+     * is called.  The Runnable MUST be executed and MUST be executed in the
+     * same thread this method is called from.  Therefore it's recommend you
+     * use it in try/finally block like in the following example:
+     * <p/>
+     * <pre>
+     *   Runnable undo = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
+     *   try {
+     *       // All property resolutions in this block will be reverted.
+     *   } finally {
+     *       undo.run();
+     *   }
+     * </pre>
+     *
+     * @return a Runnable that when run, will revert any property place holder
+     * changes that occurred on the current thread .
+     */
+    public static Runnable createPropertyPlaceholdersChangeReverter() {
+        RestoreAction prevChanges = CURRENT_RESTORE_ACTION.get();
+        RestoreAction rc = new RestoreAction(prevChanges);
+        CURRENT_RESTORE_ACTION.set(rc);
+        return rc;
+    }
+
+    private static void addRestoreAction(final Object target, final Map<String, Object> properties) {
+        if (properties.isEmpty()) {
+            return;
+        }
+
+        RestoreAction restoreAction = CURRENT_RESTORE_ACTION.get();
+        if (restoreAction == null) {
+            return;
+        }
+
+        restoreAction.actions.add(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    IntrospectionSupport.setProperties(null, target, properties);
+                } catch (Exception e) {
+                    LOG.warn("Could not restore definition properties", e);
+                }
+            }
+        });
+    }
+
+    public static void addPropertyPlaceholdersChangeRevertAction(Runnable action) {
+        RestoreAction restoreAction = CURRENT_RESTORE_ACTION.get();
+        if (restoreAction == null) {
+            return;
+        }
+
+        restoreAction.actions.add(action);
+    }
+
+    /**
      * Inspects the given definition and resolves any property placeholders from its properties.
      * <p/>
      * This implementation will check all the getter/setter pairs on this instance and for all the values
@@ -619,6 +710,7 @@ public final class ProcessorDefinitionHelper {
             }
         }
 
+        Map<String, Object> changedProperties = new HashMap<String, Object>();
         if (!properties.isEmpty()) {
             LOG.trace("There are {} properties on: {}", properties.size(), definition);
             // lookup and resolve properties for String based properties
@@ -636,6 +728,7 @@ public final class ProcessorDefinitionHelper {
                         if (!changed) {
                             throw new IllegalArgumentException("No setter to set property: " + name + " to: " + text + " on: " + definition);
                         }
+                        changedProperties.put(name, value);
                         if (LOG.isDebugEnabled()) {
                             LOG.debug("Changed property [{}] from: {} to: {}", new Object[]{name, value, text});
                         }
@@ -643,6 +736,7 @@ public final class ProcessorDefinitionHelper {
                 }
             }
         }
+        addRestoreAction(definition, changedProperties);
     }
 
     /**
@@ -651,7 +745,7 @@ public final class ProcessorDefinitionHelper {
      * This implementation will check all the getter/setter pairs on this instance and for all the values
      * (which is a String type) will check if it refers to a known field (such as on Exchange).
      *
-     * @param definition   the definition
+     * @param definition the definition
      */
     public static void resolveKnownConstantFields(Object definition) throws Exception {
         LOG.trace("Resolving known fields for: {}", definition);
@@ -660,6 +754,7 @@ public final class ProcessorDefinitionHelper {
         Map<String, Object> properties = new HashMap<String, Object>();
         IntrospectionSupport.getProperties(definition, properties, null);
 
+        Map<String, Object> changedProperties = new HashMap<String, Object>();
         if (!properties.isEmpty()) {
             LOG.trace("There are {} properties on: {}", properties.size(), definition);
 
@@ -678,6 +773,7 @@ public final class ProcessorDefinitionHelper {
                         if (constant != null) {
                             // invoke setter as the text has changed
                             IntrospectionSupport.setProperty(definition, name, constant);
+                            changedProperties.put(name, value);
                             if (LOG.isDebugEnabled()) {
                                 LOG.debug("Changed property [{}] from: {} to: {}", new Object[]{name, value, constant});
                             }
@@ -688,6 +784,7 @@ public final class ProcessorDefinitionHelper {
                 }
             }
         }
+        addRestoreAction(definition, changedProperties);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java b/camel-core/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
index e869779..a60bc5c 100644
--- a/camel-core/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
@@ -29,6 +29,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.builder.ErrorHandlerBuilder;
 import org.apache.camel.util.CamelContextHelper;
 import org.apache.camel.util.EndpointHelper;
+import org.apache.camel.util.IntrospectionSupport;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.URISupport;
 
@@ -122,21 +123,27 @@ public final class RouteDefinitionHelper {
         // handle custom assigned id's first, and then afterwards assign auto generated ids
         Set<String> customIds = new HashSet<String>();
 
-        for (RouteDefinition route : routes) {
+        for (final RouteDefinition route : routes) {
             // if there was a custom id assigned, then make sure to support property placeholders
             if (route.hasCustomIdAssigned()) {
-                String id = route.getId();
-                id = context.resolvePropertyPlaceholders(id);
+                final String originalId = route.getId();
+                final String id = context.resolvePropertyPlaceholders(originalId);
                 // only set id if its changed, such as we did property placeholder
-                if (!route.getId().equals(id)) {
+                if (!originalId.equals(id)) {
                     route.setId(id);
+                    ProcessorDefinitionHelper.addPropertyPlaceholdersChangeRevertAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            route.setId(originalId);
+                        }
+                    });
                 }
                 customIds.add(id);
             }
         }
 
         // auto assign route ids
-        for (RouteDefinition route : routes) {
+        for (final RouteDefinition route : routes) {
             if (route.getId() == null) {
                 // keep assigning id's until we find a free name
                 boolean done = false;
@@ -146,6 +153,13 @@ public final class RouteDefinitionHelper {
                     done = !customIds.contains(id);
                 }
                 route.setId(id);
+                ProcessorDefinitionHelper.addPropertyPlaceholdersChangeRevertAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        route.setId(null);
+                        route.setCustomId(false);
+                    }
+                });
                 route.setCustomId(false);
                 customIds.add(route.getId());
             }
@@ -252,6 +266,35 @@ public final class RouteDefinitionHelper {
                                     List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions,
                                     List<OnCompletionDefinition> onCompletions) {
 
+        Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
+        try {
+            prepareRouteImp(context, route, onExceptions, intercepts, interceptFromDefinitions, interceptSendToEndpointDefinitions, onCompletions);
+        } finally {
+            // Lets restore
+            propertyPlaceholdersChangeReverter.run();
+        }
+    }
+
+    /**
+     * Prepares the route which supports context scoped features such as onException, interceptors and onCompletions
+     * <p/>
+     * This method does <b>not</b> mark the route as prepared afterwards.
+     *
+     * @param context                            the camel context
+     * @param route                              the route
+     * @param onExceptions                       optional list of onExceptions
+     * @param intercepts                         optional list of interceptors
+     * @param interceptFromDefinitions           optional list of interceptFroms
+     * @param interceptSendToEndpointDefinitions optional list of interceptSendToEndpoints
+     * @param onCompletions                      optional list onCompletions
+     */
+    private static void prepareRouteImp(ModelCamelContext context, RouteDefinition route,
+                                    List<OnExceptionDefinition> onExceptions,
+                                    List<InterceptDefinition> intercepts,
+                                    List<InterceptFromDefinition> interceptFromDefinitions,
+                                    List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions,
+                                    List<OnCompletionDefinition> onCompletions) {
+
         // init the route inputs
         initRouteInputs(context, route.getInputs());
 
@@ -570,18 +613,24 @@ public final class RouteDefinitionHelper {
      * @param context   the camel context
      * @param processor the node
      */
-    public static void forceAssignIds(CamelContext context, ProcessorDefinition processor) {
+    public static void forceAssignIds(CamelContext context, final ProcessorDefinition processor) {
         // force id on the child
         processor.idOrCreate(context.getNodeIdFactory());
 
         // if there was a custom id assigned, then make sure to support property placeholders
         if (processor.hasCustomIdAssigned()) {
-            String id = processor.getId();
             try {
-                id = context.resolvePropertyPlaceholders(id);
+                final String originalId = processor.getId();
+                String id = context.resolvePropertyPlaceholders(originalId);
                 // only set id if its changed, such as we did property placeholder
-                if (!processor.getId().equals(id)) {
+                if (!originalId.equals(id)) {
                     processor.setId(id);
+                    ProcessorDefinitionHelper.addPropertyPlaceholdersChangeRevertAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            processor.setId(originalId);
+                        }
+                    });
                 }
             } catch (Exception e) {
                 throw ObjectHelper.wrapRuntimeCamelException(e);

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
index f3accc9..55daed2 100644
--- a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
+++ b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
@@ -31,15 +31,15 @@ public class PropertiesRouteFromTest extends ContextTestSupport {
 
     public void testPropertiesRouteFrom() throws Exception {
         ProcessorDefinition out = context.getRouteDefinition("foo").getOutputs().get(0);
-        assertEquals("mock:result", ((SendDefinition) out).getUri());
+        assertEquals("{{cool.end}}", ((SendDefinition) out).getUri());
 
         String uri = context.getRouteDefinition("foo").getInputs().get(0).getUri();
-        assertEquals("direct:cool", uri);
+        assertEquals("{{cool.start}}", uri);
 
         // use a routes definition to dump the routes
         String xml = ModelHelper.dumpModelAsXml(context, context.getRouteDefinition("foo"));
-        assertTrue(xml.contains("<from uri=\"direct:cool\"/>"));
-        assertTrue(xml.contains("<to uri=\"mock:result\""));
+        assertTrue(xml.contains("<from uri=\"{{cool.start}}\"/>"));
+        assertTrue(xml.contains("<to uri=\"{{cool.end}}\""));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteIdTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteIdTest.java b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteIdTest.java
index 05baa68..b4bcff7 100644
--- a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteIdTest.java
+++ b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteIdTest.java
@@ -30,7 +30,7 @@ public class PropertiesRouteIdTest extends ContextTestSupport {
         assertNotNull("Route with name Camel should exist", context.getRoute("Camel"));
 
         String id = context.getRouteDefinition("Camel").getOutputs().get(0).getId();
-        assertEquals("Cheese", id);
+        assertEquals("{{cool.other.name}}", id);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
new file mode 100644
index 0000000..dec46f8
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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 org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.properties.PropertiesComponent;
+import org.apache.camel.model.ModelHelper;
+
+/**
+ *
+ */
+public class DumpModelAsXmlPlaceholdersTest extends ContextTestSupport {
+
+    public void testDumpModelAsXml() throws Exception {
+        assertEquals("Gouda", context.getRoutes().get(0).getId());
+        String xml = ModelHelper.dumpModelAsXml(context, context.getRouteDefinition("Gouda"));
+        assertNotNull(xml);
+        log.info(xml);
+        assertTrue(xml.contains("<route customId=\"true\" id=\"Gouda\" xmlns=\"http://camel.apache.org/schema/spring\">"));
+        assertTrue(xml.contains("<from uri=\"direct:start-{{cheese.type}}\"/>"));
+        assertTrue(xml.contains("<to uri=\"direct:end-{{cheese.type}}\" customId=\"true\" id=\"log\"/>"));
+    }
+
+    @Override
+
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start-{{cheese.type}}").routeId("{{cheese.type}}")
+                        .to("direct:end-{{cheese.type}}").id("log");
+            }
+        };
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        PropertiesComponent component = new PropertiesComponent();
+        component.setCamelContext(context);
+        component.setLocation("classpath:org/apache/camel/component/properties/cheese.properties");
+        context.addComponent("properties", component);
+        return context;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java b/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
new file mode 100644
index 0000000..ba60b1a
--- /dev/null
+++ b/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring;
+
+import org.apache.camel.model.ModelHelper;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ *
+ */
+public class DumpModelAsXmlPlaceholdersTest extends SpringTestSupport {
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext("org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.xml");
+    }
+
+    public void testDumpModelAsXml() throws Exception {
+        assertEquals("Gouda", context.getRoutes().get(0).getId());
+        String xml = ModelHelper.dumpModelAsXml(context, context.getRouteDefinition("Gouda"));
+        assertNotNull(xml);
+        System.out.println(xml);
+        log.info(xml);
+        assertTrue(xml.contains("<route customId=\"true\" id=\"Gouda\" xmlns=\"http://camel.apache.org/schema/spring\">"));
+        assertTrue(xml.contains("<from uri=\"direct:start-{{cheese.type}}\"/>"));
+        assertTrue(xml.contains("<to uri=\"direct:end-{{cheese.type}}\" customId=\"true\" id=\"log\"/>"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/components/camel-spring/src/test/resources/org/apache/camel/component/properties/cheese.properties
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/test/resources/org/apache/camel/component/properties/cheese.properties b/components/camel-spring/src/test/resources/org/apache/camel/component/properties/cheese.properties
index 25c1573..5aeff76 100644
--- a/components/camel-spring/src/test/resources/org/apache/camel/component/properties/cheese.properties
+++ b/components/camel-spring/src/test/resources/org/apache/camel/component/properties/cheese.properties
@@ -29,3 +29,5 @@ hi=Bonjour
 hi2=Guten Tag
 
 autoStartup=true
+
+cheese.type=Gouda

http://git-wip-us.apache.org/repos/asf/camel/blob/e08c8700/components/camel-spring/src/test/resources/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.xml
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/test/resources/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.xml b/components/camel-spring/src/test/resources/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.xml
new file mode 100644
index 0000000..3f2725d
--- /dev/null
+++ b/components/camel-spring/src/test/resources/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.xml
@@ -0,0 +1,37 @@
+<?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
+    ">
+
+    <bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
+        <property name="location" value="classpath:org/apache/camel/component/properties/cheese.properties"/>
+    </bean>
+    <bean name="log" class="org.apache.camel.component.log.LogComponent"/>
+
+    <camelContext xmlns="http://camel.apache.org/schema/spring" useMDCLogging="true">
+        <route id="{{cheese.type}}">
+          <from uri="direct:start-{{cheese.type}}"/>
+          <to uri="direct:end-{{cheese.type}}" id="log"/>
+        </route>
+    </camelContext>
+
+</beans>