CAMEL-6291: Fixed <routeContextRef> to not share the routes between the <camelContext>s as that can cause side-effects.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/f85ca1d0 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f85ca1d0 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f85ca1d0 Branch: refs/heads/camel-2.11.x Commit: f85ca1d001ef05422e262c7b8e6a7a69ec397e67 Parents: 9938a4d Author: Claus Ibsen <davscl...@apache.org> Authored: Mon Aug 26 11:09:06 2013 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Mon Aug 26 11:49:55 2013 +0200 ---------------------------------------------------------------------- .../camel/model/RouteContextRefDefinition.java | 11 +- .../model/RouteContextRefDefinitionHelper.java | 135 +++++++++++++++++++ ...PlaceholderMultipleCamelContextRefsTest.java | 61 +++++++++ .../src/test/resources/mycamel-1.properties | 18 +++ .../src/test/resources/mycamel-2.properties | 18 +++ ...yPlaceholderMultipleCamelContextRefsTest.xml | 42 ++++++ 6 files changed, 275 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java index 12e59cf..87667a3 100644 --- a/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java @@ -23,8 +23,6 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import org.apache.camel.CamelContext; -import org.apache.camel.util.CamelContextHelper; -import org.apache.camel.util.ObjectHelper; /** * Represents an XML <routeContextRef/> element @@ -55,14 +53,7 @@ public class RouteContextRefDefinition { @SuppressWarnings({"unchecked", "rawtypes"}) public List<RouteDefinition> lookupRoutes(CamelContext camelContext) { - ObjectHelper.notNull(camelContext, "camelContext", this); - ObjectHelper.notNull(ref, "ref", this); - - List answer = CamelContextHelper.lookup(camelContext, ref, List.class); - if (answer == null) { - throw new IllegalArgumentException("Cannot find RouteContext with id " + ref); - } - return answer; + return RouteContextRefDefinitionHelper.lookupRoutes(camelContext, ref); } } http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinitionHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinitionHelper.java b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinitionHelper.java new file mode 100644 index 0000000..0f69104 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinitionHelper.java @@ -0,0 +1,135 @@ +/** + * 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.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import org.apache.camel.CamelContext; +import org.apache.camel.model.language.NamespaceAwareExpression; +import org.apache.camel.util.CamelContextHelper; +import org.apache.camel.util.ObjectHelper; + +/** + * Helper for {@link RouteContextRefDefinition}. + */ +public final class RouteContextRefDefinitionHelper { + + private static JAXBContext jaxbContext; + + + /** + * Lookup the routes from the {@link RouteContextRefDefinition}. + * <p/> + * This implmementation must be used to lookup the routes as it performs a deep clone of the routes + * as a {@link RouteContextRefDefinition} can be re-used with multiple {@link CamelContext} and each + * context should have their own instances of the routes. This is to ensure no side-effefts and sharing + * of instances between the contexts. For example such as property placeholders may be context specific + * so the routes should not use placeholders from another {@link CamelContext}. + * + * @param camelContext the CamelContext + * @param ref the id of the {@link RouteContextRefDefinition} to lookup and get the rotues. + * @return the routes. + */ + public static synchronized List<RouteDefinition> lookupRoutes(CamelContext camelContext, String ref) { + ObjectHelper.notNull(camelContext, "camelContext"); + ObjectHelper.notNull(ref, "ref"); + + List<RouteDefinition> answer = CamelContextHelper.lookup(camelContext, ref, List.class); + if (answer == null) { + throw new IllegalArgumentException("Cannot find RouteContext with id " + ref); + } + + // must clone the route definitions as they can be reused with multiple CamelContexts + // and they would need their own instances of the definitions to not have side effects among + // the CamelContext - for example property placeholder resolutions etc. + List<RouteDefinition> clones = new ArrayList<RouteDefinition>(answer.size()); + try { + JAXBContext jaxb = getOrCreateJAXBContext(); + for (RouteDefinition def : answer) { + RouteDefinition clone = cloneRouteDefinition(jaxb, def); + if (clone != null) { + clones.add(clone); + } + } + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + + return clones; + } + + private static synchronized JAXBContext getOrCreateJAXBContext() throws JAXBException { + if (jaxbContext == null) { + // must use classloader from CamelContext to have JAXB working + jaxbContext = JAXBContext.newInstance(Constants.JAXB_CONTEXT_PACKAGES, CamelContext.class.getClassLoader()); + } + return jaxbContext; + } + + private static RouteDefinition cloneRouteDefinition(JAXBContext jaxbContext, RouteDefinition def) throws JAXBException { + Marshaller marshal = jaxbContext.createMarshaller(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + marshal.marshal(def, bos); + + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + Object clone = unmarshaller.unmarshal(bis); + + if (clone != null && clone instanceof RouteDefinition) { + RouteDefinition def2 = (RouteDefinition) clone; + + // need to clone the namespaces also as they are not JAXB marshalled (as they are transient) + Iterator<ExpressionNode> it = ProcessorDefinitionHelper.filterTypeInOutputs(def.getOutputs(), ExpressionNode.class); + Iterator<ExpressionNode> it2 = ProcessorDefinitionHelper.filterTypeInOutputs(def2.getOutputs(), ExpressionNode.class); + while (it.hasNext() && it2.hasNext()) { + ExpressionNode node = it.next(); + ExpressionNode node2 = it2.next(); + + NamespaceAwareExpression name = null; + NamespaceAwareExpression name2 = null; + if (node.getExpression() instanceof NamespaceAwareExpression) { + name = (NamespaceAwareExpression) node.getExpression(); + } + if (node2.getExpression() instanceof NamespaceAwareExpression) { + name2 = (NamespaceAwareExpression) node2.getExpression(); + } + + if (name != null && name2 != null && name.getNamespaces() != null && !name.getNamespaces().isEmpty()) { + Map<String, String> map = new HashMap<String, String>(); + map.putAll(name.getNamespaces()); + name2.setNamespaces(map); + } + } + + return def2; + } + + return null; + } + + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/components/camel-spring/src/test/java/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.java ---------------------------------------------------------------------- diff --git a/components/camel-spring/src/test/java/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.java b/components/camel-spring/src/test/java/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.java new file mode 100644 index 0000000..4d953cb --- /dev/null +++ b/components/camel-spring/src/test/java/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.java @@ -0,0 +1,61 @@ +/** + * 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.config; + +import junit.framework.TestCase; +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.component.mock.MockEndpoint; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class RouteRefPropertyPlaceholderMultipleCamelContextRefsTest extends TestCase { + + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.xml"); + } + + public void testSpringTwoCamelContextDirectEndpoint() throws Exception { + AbstractXmlApplicationContext ac = createApplicationContext(); + ac.start(); + + CamelContext camel1 = ac.getBean("myCamel-1", CamelContext.class); + CamelContext camel2 = ac.getBean("myCamel-2", CamelContext.class); + + Endpoint start1 = camel1.getEndpoint("direct:start"); + Endpoint start2 = camel2.getEndpoint("direct:start"); + assertNotSame(start1, start2); + + MockEndpoint mock1 = camel1.getEndpoint("mock:end-1", MockEndpoint.class); + mock1.expectedBodiesReceived("Hello World"); + + MockEndpoint mock2 = camel2.getEndpoint("mock:end-2", MockEndpoint.class); + mock2.expectedBodiesReceived("Bye World"); + + camel1.createProducerTemplate().sendBody("direct:start", "Hello World"); + camel2.createProducerTemplate().sendBody("direct:start", "Bye World"); + + mock1.assertIsSatisfied(); + mock2.assertIsSatisfied(); + + ac.stop(); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/components/camel-spring/src/test/resources/mycamel-1.properties ---------------------------------------------------------------------- diff --git a/components/camel-spring/src/test/resources/mycamel-1.properties b/components/camel-spring/src/test/resources/mycamel-1.properties new file mode 100644 index 0000000..508e0dc --- /dev/null +++ b/components/camel-spring/src/test/resources/mycamel-1.properties @@ -0,0 +1,18 @@ +## ------------------------------------------------------------------------ +## 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. +## ------------------------------------------------------------------------ + +end=mock:end-1 \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/components/camel-spring/src/test/resources/mycamel-2.properties ---------------------------------------------------------------------- diff --git a/components/camel-spring/src/test/resources/mycamel-2.properties b/components/camel-spring/src/test/resources/mycamel-2.properties new file mode 100644 index 0000000..34fecb9 --- /dev/null +++ b/components/camel-spring/src/test/resources/mycamel-2.properties @@ -0,0 +1,18 @@ +## ------------------------------------------------------------------------ +## 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. +## ------------------------------------------------------------------------ + +end=mock:end-2 \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/f85ca1d0/components/camel-spring/src/test/resources/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.xml ---------------------------------------------------------------------- diff --git a/components/camel-spring/src/test/resources/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.xml b/components/camel-spring/src/test/resources/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.xml new file mode 100644 index 0000000..b690aa6 --- /dev/null +++ b/components/camel-spring/src/test/resources/org/apache/camel/spring/config/RouteRefPropertyPlaceholderMultipleCamelContextRefsTest.xml @@ -0,0 +1,42 @@ +<?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 + "> + + <routeContext id="myCoolRoute" xmlns="http://camel.apache.org/schema/spring"> + <route id="cool"> + <from uri="direct:start"/> + <to uri="{{end}}"/> + </route> + </routeContext> + + <camelContext id="myCamel-1" xmlns="http://camel.apache.org/schema/spring"> + <propertyPlaceholder id="properties1" location="mycamel-1.properties"/> + <routeContextRef ref="myCoolRoute"/> + </camelContext> + + <camelContext id="myCamel-2" xmlns="http://camel.apache.org/schema/spring"> + <propertyPlaceholder id="properties2" location="mycamel-2.properties"/> + <routeContextRef ref="myCoolRoute"/> + </camelContext> + +</beans>