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 <[email protected]>
Authored: Mon Mar 9 17:53:38 2015 -0400
Committer: Hiram Chirino <[email protected]>
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>