This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new d272656 CAMEL-12463: camel-core: allow to add metadata/properties to a route d272656 is described below commit d272656c837964d925fd6904bb69bb4bcb00a689 Author: lburgazzoli <lburgazz...@gmail.com> AuthorDate: Mon May 14 16:45:06 2018 +0200 CAMEL-12463: camel-core: allow to add metadata/properties to a route --- .gitignore | 1 + .../api/management/mbean/CamelOpenMBeanTypes.java | 15 ++++- .../api/management/mbean/ManagedRouteMBean.java | 5 ++ .../org/apache/camel/impl/DefaultRouteContext.java | 29 +++++++++ .../camel/management/mbean/ManagedRoute.java | 32 +++++++++ .../org/apache/camel/model/RouteDefinition.java | 34 +++++++++- .../management/ManagedRouteGetPropertiesTest.java | 76 ++++++++++++++++++++++ .../apache/camel/model/RoutePropertiesTest.java | 74 +++++++++++++++++++++ .../apache/camel/model/XmlRoutePropertiesTest.java | 38 +++++++++++ .../org/apache/camel/model/routeProperties.xml | 21 ++++-- .../boot/actuate/endpoint/CamelRoutesEndpoint.java | 15 +++++ .../actuate/endpoint/CamelRoutesEndpointTest.java | 2 + .../src/test/resources/camel/camelContext.xml | 3 + 13 files changed, 335 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 956cf21..79c9cc9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ components/camel-solr/data *.epoch .factorypath .pmd +.sts4-cache diff --git a/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java b/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java index 5e9f048..43e59a7 100644 --- a/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java +++ b/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java @@ -241,9 +241,6 @@ public final class CamelOpenMBeanTypes { new OpenType[]{SimpleType.STRING, SimpleType.BOOLEAN, SimpleType.BOOLEAN, SimpleType.STRING}); } - - - public static CompositeType camelHealthDetailsCompositeType() throws OpenDataException { return new CompositeType("healthDetails", "Health Details", new String[]{"id", "group", "state", "enabled", "interval", "failureThreshold"}, @@ -255,4 +252,16 @@ public final class CamelOpenMBeanTypes { CompositeType ct = camelHealthDetailsCompositeType(); return new TabularType("healthDetails", "Health Details", ct, new String[]{"id"}); } + + public static CompositeType camelRoutePropertiesCompositeType() throws OpenDataException { + return new CompositeType("routeProperties", "Route Properties", + new String[]{"key", "value"}, + new String[]{"Key", "Value"}, + new OpenType[]{SimpleType.STRING, SimpleType.STRING}); + } + + public static TabularType camelRoutePropertiesTabularType() throws OpenDataException { + CompositeType ct = camelRoutePropertiesCompositeType(); + return new TabularType("routeProperties", "Route Properties", ct, new String[]{"key"}); + } } diff --git a/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java b/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java index d8d197e..544ebab 100644 --- a/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java +++ b/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java @@ -16,6 +16,8 @@ */ package org.apache.camel.api.management.mbean; +import javax.management.openmbean.TabularData; + import org.apache.camel.Experimental; import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedOperation; @@ -29,6 +31,9 @@ public interface ManagedRouteMBean extends ManagedPerformanceCounterMBean { @ManagedAttribute(description = "Route Group") String getRouteGroup(); + @ManagedAttribute(description = "Route Properties") + TabularData getRouteProperties(); + @ManagedAttribute(description = "Route Description") String getDescription(); diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java index faff06e..1eadc24 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java @@ -33,6 +33,7 @@ import org.apache.camel.ShutdownRoute; import org.apache.camel.ShutdownRunningTask; import org.apache.camel.model.FromDefinition; import org.apache.camel.model.ProcessorDefinition; +import org.apache.camel.model.PropertyDefinition; import org.apache.camel.model.RouteDefinition; import org.apache.camel.processor.CamelInternalProcessor; import org.apache.camel.processor.ContractAdvice; @@ -237,6 +238,34 @@ public class DefaultRouteContext implements RouteContext { } edcr.getProperties().put(Route.REST_PROPERTY, rest); + List<PropertyDefinition> properties = route.getRouteProperties(); + if (properties != null) { + final String[] reservedProperties = new String[] { + Route.ID_PROPERTY, + Route.PARENT_PROPERTY, + Route.GROUP_PROPERTY, + Route.REST_PROPERTY, + Route.DESCRIPTION_PROPERTY + }; + + for (PropertyDefinition prop : properties) { + try { + final String key = CamelContextHelper.parseText(camelContext, prop.getKey()); + final String val = CamelContextHelper.parseText(camelContext, prop.getValue()); + + for (String property : reservedProperties) { + if (property.equalsIgnoreCase(key)) { + throw new IllegalArgumentException("Cannot set route property " + property + " as it is a reserved property"); + } + } + + edcr.getProperties().put(key, val); + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + } + // after the route is created then set the route on the policy processor so we get hold of it CamelInternalProcessor.RoutePolicyAdvice task = internal.getAdvice(CamelInternalProcessor.RoutePolicyAdvice.class); if (task != null) { diff --git a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java index 0d83c63..f0e51be 100644 --- a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java +++ b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java @@ -33,6 +33,11 @@ import javax.management.ObjectName; import javax.management.Query; import javax.management.QueryExp; import javax.management.StringValueExp; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; import org.w3c.dom.Document; @@ -42,6 +47,7 @@ import org.apache.camel.Route; import org.apache.camel.ServiceStatus; import org.apache.camel.TimerListener; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes; import org.apache.camel.api.management.mbean.ManagedProcessorMBean; import org.apache.camel.api.management.mbean.ManagedRouteMBean; import org.apache.camel.model.ModelCamelContext; @@ -103,6 +109,32 @@ public class ManagedRoute extends ManagedPerformanceCounter implements TimerList return route.getGroup(); } + @Override + public TabularData getRouteProperties() { + try { + final Map<String, Object> properties = route.getProperties(); + final TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.camelRoutePropertiesTabularType()); + final CompositeType ct = CamelOpenMBeanTypes.camelRoutePropertiesCompositeType(); + + // gather route properties + for (Map.Entry<String, Object> entry : properties.entrySet()) { + final String key = entry.getKey(); + final String val = context.getTypeConverter().convertTo(String.class, entry.getValue()); + + CompositeData data = new CompositeDataSupport( + ct, + new String[]{"key", "value"}, + new Object[]{key, val} + ); + + answer.put(data); + } + return answer; + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + public String getDescription() { return description; } diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java index 3f7a32c..65cc05b 100644 --- a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; @@ -65,7 +66,7 @@ import org.apache.camel.util.ObjectHelper; */ @Metadata(label = "configuration") @XmlRootElement(name = "route") -@XmlType(propOrder = {"inputs", "inputType", "outputType", "outputs"}) +@XmlType(propOrder = {"inputs", "inputType", "outputType", "outputs", "routeProperties"}) @XmlAccessorType(XmlAccessType.PROPERTY) // must use XmlAccessType.PROPERTY as there is some custom logic needed to be executed in the setter methods public class RouteDefinition extends ProcessorDefinition<RouteDefinition> { @@ -95,6 +96,7 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> { private RestBindingDefinition restBindingDefinition; private InputTypeDefinition inputType; private OutputTypeDefinition outputType; + private List<PropertyDefinition> routeProperties; public RouteDefinition() { } @@ -819,6 +821,23 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> { return this; } + /** + * Adds a custom property on the route. + */ + public RouteDefinition routeProperty(String key, String value) { + if (routeProperties == null) { + routeProperties = new ArrayList<>(); + } + + PropertyDefinition prop = new PropertyDefinition(); + prop.setKey(key); + prop.setValue(value); + + routeProperties.add(prop); + + return this; + } + // Properties // ----------------------------------------------------------------------- @@ -1167,6 +1186,19 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> { return this.outputType; } + public List<PropertyDefinition> getRouteProperties() { + return routeProperties; + } + + /** + * To set metadata as properties on the route. + */ + @XmlElement(name = "routeProperty") + @Metadata(label = "advanced") + public void setRouteProperties(List<PropertyDefinition> routeProperties) { + this.routeProperties = routeProperties; + } + // Implementation methods // ------------------------------------------------------------------------- protected RouteContext addRoutes(CamelContext camelContext, Collection<Route> routes, FromDefinition fromType) throws Exception { diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedRouteGetPropertiesTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedRouteGetPropertiesTest.java new file mode 100644 index 0000000..4da2099 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/management/ManagedRouteGetPropertiesTest.java @@ -0,0 +1,76 @@ +/** + * 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.management; + +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.openmbean.TabularData; + +import org.apache.camel.builder.RouteBuilder; + +/** + * @version + */ +public class ManagedRouteGetPropertiesTest extends ManagementTestSupport { + + public void testGetProperties() throws Exception { + // JMX tests don't work well on AIX CI servers (hangs them) + if (isPlatform("aix")) { + return; + } + + MBeanServer mbeanServer = getMBeanServer(); + ObjectName on = getRouteObjectName(mbeanServer); + + // should be started + String routeId = (String) mbeanServer.getAttribute(on, "RouteId"); + assertEquals("myRoute", routeId); + + TabularData data = (TabularData) mbeanServer.invoke(on, "getRouteProperties", null, null); + + assertNotNull(data); + assertTrue(data.containsKey(new Object[] { "key1" })); + assertEquals("key1", data.get(new Object[] { "key1" }).get("key")); + assertEquals("val1", data.get(new Object[] { "key1" }).get("value")); + assertTrue(data.containsKey(new Object[] { "key2" })); + assertEquals("key2", data.get(new Object[] { "key2" }).get("key")); + assertEquals("val2", data.get(new Object[] { "key2" }).get("value")); + } + + + static ObjectName getRouteObjectName(MBeanServer mbeanServer) throws Exception { + Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=routes,*"), null); + assertEquals(1, set.size()); + + return set.iterator().next(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .routeId("myRoute") + .routeProperty("key1", "val1") + .routeProperty("key2", "val2") + .to("mock:result"); + } + }; + } +} diff --git a/camel-core/src/test/java/org/apache/camel/model/RoutePropertiesTest.java b/camel-core/src/test/java/org/apache/camel/model/RoutePropertiesTest.java new file mode 100644 index 0000000..b7956cd --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/model/RoutePropertiesTest.java @@ -0,0 +1,74 @@ +/** + * 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 org.apache.camel.ContextTestSupport; +import org.apache.camel.FailedToCreateRouteException; +import org.apache.camel.Route; +import org.apache.camel.builder.RouteBuilder; + +public class RoutePropertiesTest extends ContextTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + public void testRouteProperties() throws Exception { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .routeId("route-id") + .routeProperty("key1", "val1") + .routeProperty("key2", "val2") + .to("mock:output"); + } + }); + + context.start(); + + RouteDefinition definition = context.getRouteDefinition("route-id"); + Route route = context.getRoute("route-id"); + + assertNotNull(definition.getRouteProperties()); + assertEquals(2, definition.getRouteProperties().size()); + + assertNotNull(route.getProperties()); + assertEquals("val1", route.getProperties().get("key1")); + assertEquals("val2", route.getProperties().get("key2")); + } + + public void testRoutePropertiesFailuer() throws Exception { + try { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .routeId("route-id") + .routeProperty(Route.ID_PROPERTY, "the id") + .to("mock:output"); + } + }); + + context.start(); + + fail(""); + } catch (FailedToCreateRouteException e) { + } + } +} diff --git a/camel-core/src/test/java/org/apache/camel/model/XmlRoutePropertiesTest.java b/camel-core/src/test/java/org/apache/camel/model/XmlRoutePropertiesTest.java new file mode 100644 index 0000000..a8e34a4 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/model/XmlRoutePropertiesTest.java @@ -0,0 +1,38 @@ +/** + * 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.util.Objects; +import javax.xml.bind.JAXBException; + +public class XmlRoutePropertiesTest extends XmlTestSupport { + + public void testXmlRouteGroup() throws JAXBException { + RouteContainer context = assertParseAsJaxb("routeProperties.xml"); + RouteDefinition route = assertOneElement(context.getRoutes()); + + assertEquals("route-id", route.getId()); + assertNotNull(route.getRouteProperties()); + + assertTrue(route.getRouteProperties().stream().anyMatch( + p -> Objects.equals("key1", p.getKey()) && Objects.equals("val1", p.getValue()) + )); + assertTrue(route.getRouteProperties().stream().anyMatch( + p -> Objects.equals("key2", p.getKey()) && Objects.equals("val2", p.getValue()) + )); + } +} diff --git a/components/camel-spring-boot/src/test/resources/camel/camelContext.xml b/camel-core/src/test/resources/org/apache/camel/model/routeProperties.xml similarity index 66% copy from components/camel-spring-boot/src/test/resources/camel/camelContext.xml copy to camel-core/src/test/resources/org/apache/camel/model/routeProperties.xml index 72ebbe8..b425f6c 100644 --- a/components/camel-spring-boot/src/test/resources/camel/camelContext.xml +++ b/camel-core/src/test/resources/org/apache/camel/model/routeProperties.xml @@ -17,9 +17,18 @@ limitations under the License. --> -<routes xmlns="http://camel.apache.org/schema/spring"> - <route id="xmlAutoLoading"> - <from uri="direct:xmlAutoLoading"/> - <to uri="mock:xmlAutoLoading"/> - </route> -</routes> \ No newline at end of file +<routes xmlns="http://camel.apache.org/schema/spring" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + <!-- + xsi:schemaLocation="http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd" + --> + + <route id="route-id"> + <routeProperty key="key1" value="val1"/> + <routeProperty key="key2" value="val2"/> + + <from uri="seda:a"/> + <to uri="seda:b"/> + </route> +</routes> diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java index 734a8c2..3fc376a 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java @@ -16,8 +16,11 @@ */ package org.apache.camel.spring.boot.actuate.endpoint; +import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -208,6 +211,8 @@ public class CamelRoutesEndpoint { private final String group; + private final Map<String, Object> properties; + private final String description; private final String uptime; @@ -223,6 +228,12 @@ public class CamelRoutesEndpoint { this.uptime = route.getUptime(); this.uptimeMillis = route.getUptimeMillis(); + if (route.getProperties() != null) { + this.properties = new HashMap<>(route.getProperties()); + } else { + this.properties = Collections.emptyMap(); + } + if (route instanceof StatefulService) { this.status = ((StatefulService) route).getStatus().name(); } else { @@ -238,6 +249,10 @@ public class CamelRoutesEndpoint { return group; } + public Map<String, Object> getProperties() { + return properties; + } + public String getDescription() { return description; } diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java index 095b68d..a7b6f40 100644 --- a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java +++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java @@ -59,6 +59,8 @@ public class CamelRoutesEndpointTest extends Assert { assertEquals(routes.size(), camelContext.getRoutes().size()); assertTrue(routes.stream().anyMatch(r -> "foo-route".equals(r.getId()))); assertTrue(routes.stream().anyMatch(r -> "foo-route-group".equals(r.getGroup()))); + assertTrue(routes.stream().anyMatch(r -> r.getProperties().containsKey("key1") && "val1".equals(r.getProperties().get("key1")))); + assertTrue(routes.stream().anyMatch(r -> r.getProperties().containsKey("key2") && "val2".equals(r.getProperties().get("key2")))); } @Test(expected = IllegalArgumentException.class) diff --git a/components/camel-spring-boot/src/test/resources/camel/camelContext.xml b/components/camel-spring-boot/src/test/resources/camel/camelContext.xml index 72ebbe8..9922a0a 100644 --- a/components/camel-spring-boot/src/test/resources/camel/camelContext.xml +++ b/components/camel-spring-boot/src/test/resources/camel/camelContext.xml @@ -19,6 +19,9 @@ --> <routes xmlns="http://camel.apache.org/schema/spring"> <route id="xmlAutoLoading"> + <routeProperty key="key1" value="val1"/> + <routeProperty key="key2" value="val2"/> + <from uri="direct:xmlAutoLoading"/> <to uri="mock:xmlAutoLoading"/> </route> -- To stop receiving notification emails like this one, please contact lburgazz...@apache.org.