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>