You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/12/04 10:54:41 UTC

[camel] 02/04: CAMEL-17272: camel-spring-xml - Classic Spring XML add support for external route configuration XML files

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 5c8713bb6cebeb53bbac8c6d1a80edcf712eab53
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Dec 4 11:29:02 2021 +0100

    CAMEL-17272: camel-spring-xml - Classic Spring XML add support for external route configuration XML files
---
 .../org/apache/camel/spring/xml/camelContext.json  |  1 +
 .../camel/spring/xml/CamelContextFactoryBean.java  | 15 ++++++
 .../spring/xml/handler/CamelNamespaceHandler.java  |  1 +
 .../SpringRoutesConfigurationExternalTest.java     | 48 +++++++++++++++++
 .../SpringRoutesConfigurationExternalTest.xml      | 57 ++++++++++++++++++++
 .../services/org/apache/camel/model.properties     |  1 +
 .../resources/org/apache/camel/model/jaxb.index    |  1 +
 .../camel/model/routeConfigurationContextRef.json  | 16 ++++++
 .../RouteConfigurationContextRefDefinition.java    | 62 ++++++++++++++++++++++
 ...uteConfigurationContextRefDefinitionHelper.java | 53 ++++++++++++++++++
 .../core/xml/AbstractCamelContextFactoryBean.java  | 19 +++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  9 ++++
 12 files changed, 283 insertions(+)

diff --git a/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json b/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
index 42cb0b3..72f27e7 100644
--- a/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
+++ b/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
@@ -63,6 +63,7 @@
     "resilience4jConfiguration": { "kind": "element", "displayName": "Resilience4j Configuration", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.Resilience4jConfigurationDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Resilience4j Circuit Breaker EIP configurations" },
     "defaultFaultToleranceConfiguration": { "kind": "element", "displayName": "Default Fault Tolerance Configuration", "required": false, "type": "object", "javaType": "org.apache.camel.model.FaultToleranceConfigurationDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "MicroProfile Fault Tolerance EIP default configuration" },
     "faultToleranceConfiguration": { "kind": "element", "displayName": "Fault Tolerance Configuration", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.FaultToleranceConfigurationDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "MicroProfile Circuit Breaker EIP configurations" },
+    "routeConfigurationContextRef": { "kind": "element", "displayName": "Route Configuration Context Ref", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteConfigurationContextRefDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to XML route configurations to include as route configurations in this CamelContext." },
     "routeTemplateContextRef": { "kind": "element", "displayName": "Route Template Context Ref", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteTemplateContextRefDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to XML route templates to include as route templates in this CamelContext." },
     "routeBuilder": { "kind": "element", "displayName": "Route Builder", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteBuilderDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to Java RouteBuilder instances to include as routes in this CamelContext." },
     "routeContextRef": { "kind": "element", "displayName": "Route Context Ref", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteContextRefDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to XML routes to include as routes in this CamelContext." },
diff --git a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
index 6c2bca3..cd47887 100644
--- a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
+++ b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
@@ -57,6 +57,7 @@ import org.apache.camel.model.PackageScanDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RestContextRefDefinition;
 import org.apache.camel.model.RouteBuilderDefinition;
+import org.apache.camel.model.RouteConfigurationContextRefDefinition;
 import org.apache.camel.model.RouteConfigurationDefinition;
 import org.apache.camel.model.RouteContextRefDefinition;
 import org.apache.camel.model.RouteDefinition;
@@ -231,6 +232,8 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     private FaultToleranceConfigurationDefinition defaultFaultToleranceConfiguration;
     @XmlElement(name = "faultToleranceConfiguration", type = Resilience4jConfigurationDefinition.class)
     private List<FaultToleranceConfigurationDefinition> faultToleranceConfigurations;
+    @XmlElement(name = "routeConfigurationContextRef")
+    private List<RouteConfigurationContextRefDefinition> routeConfigurationRefs = new ArrayList<>();
     @XmlElement(name = "routeTemplateContextRef")
     private List<RouteTemplateContextRefDefinition> routeTemplateRefs = new ArrayList<>();
     @XmlElement(name = "routeBuilder")
@@ -1225,6 +1228,18 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     }
 
     @Override
+    public List<RouteConfigurationContextRefDefinition> getRouteConfigurationRefs() {
+        return routeConfigurationRefs;
+    }
+
+    /**
+     * Refers to XML route configurations to include as route configurations in this CamelContext.
+     */
+    public void setRouteConfigurationRefs(List<RouteConfigurationContextRefDefinition> routeConfigurationRefs) {
+        this.routeConfigurationRefs = routeConfigurationRefs;
+    }
+
+    @Override
     public List<RouteTemplateContextRefDefinition> getRouteTemplateRefs() {
         return routeTemplateRefs;
     }
diff --git a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
index d525e8d..13268bd 100644
--- a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
+++ b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
@@ -434,6 +434,7 @@ public class CamelNamespaceHandler extends NamespaceHandlerSupport {
                 builder.addPropertyValue("validators", factoryBean.getValidators());
                 builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions());
                 builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions());
+                builder.addPropertyValue("routeConfigurationRefs", factoryBean.getRouteConfigurationRefs());
                 builder.addPropertyValue("routeTemplateRefs", factoryBean.getRouteTemplateRefs());
                 builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs());
                 builder.addPropertyValue("routeRefs", factoryBean.getRouteRefs());
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.java
new file mode 100644
index 0000000..a372b30
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SpringRoutesConfigurationExternalTest extends SpringTestSupport {
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext(
+                "org/apache/camel/spring/SpringRoutesConfigurationExternalTest.xml");
+    }
+
+    @Test
+    public void testRoutesConfigurationOnException() throws Exception {
+        getMockEndpoint("mock:error").expectedBodiesReceived("Bye World");
+
+        try {
+            template.sendBody("direct:start", "Hello World");
+            fail("Should throw exception");
+        } catch (Exception e) {
+            // expected
+        }
+        template.sendBody("direct:start2", "Bye World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.xml
new file mode 100644
index 0000000..c83f58e
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationExternalTest.xml
@@ -0,0 +1,57 @@
+<?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
+    ">
+
+  <!-- this can be in another XML file that spring imports, but for testing we keep it in the same file -->
+  <routeConfigurationContext id="myConf" xmlns="http://camel.apache.org/schema/spring">
+    <routeConfiguration id="handleError">
+      <onException>
+        <exception>java.lang.Exception</exception>
+        <handled>
+          <constant>true</constant>
+        </handled>
+        <to uri="mock:error"/>
+      </onException>
+    </routeConfiguration>
+  </routeConfigurationContext>
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+
+    <!-- refer to external snippet -->
+    <routeConfigurationContextRef ref="myConf"/>
+
+    <route>
+      <from uri="direct:start"/>
+      <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo"/>
+    </route>
+
+    <route routeConfigurationId="handleError">
+      <from uri="direct:start2"/>
+      <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo2"/>
+    </route>
+
+  </camelContext>
+
+</beans>
diff --git a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
index 9a05154..f08ac58 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
+++ b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
@@ -138,6 +138,7 @@ roundRobin
 route
 routeBuilder
 routeConfiguration
+routeConfigurationContextRef
 routeConfigurations
 routeContextRef
 routeTemplate
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index b/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
index c9a58d7..d1cf5b7 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
@@ -63,6 +63,7 @@ Resilience4jConfigurationDefinition
 RestContextRefDefinition
 RollbackDefinition
 RouteBuilderDefinition
+RouteConfigurationContextRefDefinition
 RouteConfigurationDefinition
 RouteConfigurationsDefinition
 RouteContextRefDefinition
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfigurationContextRef.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfigurationContextRef.json
new file mode 100644
index 0000000..0e7b143
--- /dev/null
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfigurationContextRef.json
@@ -0,0 +1,16 @@
+{
+  "model": {
+    "kind": "model",
+    "name": "routeConfigurationContextRef",
+    "title": "Route Configuration Context Ref",
+    "description": "To refer to an XML file with route configuration defined using the xml-dsl",
+    "deprecated": false,
+    "label": "configuration",
+    "javaType": "org.apache.camel.model.RouteConfigurationContextRefDefinition",
+    "input": false,
+    "output": false
+  },
+  "properties": {
+    "ref": { "kind": "attribute", "displayName": "Ref", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Reference to the route templates in the xml dsl" }
+  }
+}
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinition.java
new file mode 100644
index 0000000..6ebabdd
--- /dev/null
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinition.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.model;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.Metadata;
+
+/**
+ * To refer to an XML file with route configuration defined using the xml-dsl
+ */
+@Metadata(label = "configuration")
+@XmlRootElement(name = "routeConfigurationContextRef")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RouteConfigurationContextRefDefinition {
+    @XmlAttribute(required = true)
+    private String ref;
+
+    public RouteConfigurationContextRefDefinition() {
+    }
+
+    @Override
+    public String toString() {
+        return "RouteConfigurationContextRef[" + getRef() + "]";
+    }
+
+    public String getRef() {
+        return ref;
+    }
+
+    /**
+     * Reference to the route templates in the xml dsl
+     */
+    public void setRef(String ref) {
+        this.ref = ref;
+    }
+
+    public List<RouteConfigurationDefinition> lookupRouteConfigurations(CamelContext camelContext) {
+        return RouteConfigurationContextRefDefinitionHelper.lookupRouteConfigurations(camelContext, ref);
+    }
+
+}
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinitionHelper.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinitionHelper.java
new file mode 100644
index 0000000..b0d878e
--- /dev/null
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContextRefDefinitionHelper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Helper for {@link RouteConfigurationContextRefDefinition}.
+ */
+public final class RouteConfigurationContextRefDefinitionHelper {
+
+    private RouteConfigurationContextRefDefinitionHelper() {
+    }
+
+    /**
+     * Lookup the route configurations from the {@link RouteConfigurationContextRefDefinition}.
+     *
+     * @param  camelContext the CamelContext
+     * @param  ref          the id of the {@link RouteConfigurationContextRefDefinition} to lookup and get the route
+     *                      configurations.
+     * @return              the route configurations.
+     */
+    @SuppressWarnings("unchecked")
+    public static List<RouteConfigurationDefinition> lookupRouteConfigurations(CamelContext camelContext, String ref) {
+        ObjectHelper.notNull(camelContext, "camelContext");
+        ObjectHelper.notNull(ref, "ref");
+
+        List<RouteConfigurationDefinition> answer = CamelContextHelper.lookup(camelContext, ref, List.class);
+        if (answer == null) {
+            throw new IllegalArgumentException("Cannot find RouteConfigurationContext with id " + ref);
+        }
+        return answer;
+    }
+
+}
diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index 5cbb090..79d77b4 100644
--- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -73,6 +73,7 @@ import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RestContextRefDefinition;
 import org.apache.camel.model.RouteBuilderDefinition;
 import org.apache.camel.model.RouteConfigurationContainer;
+import org.apache.camel.model.RouteConfigurationContextRefDefinition;
 import org.apache.camel.model.RouteConfigurationDefinition;
 import org.apache.camel.model.RouteContainer;
 import org.apache.camel.model.RouteContextRefDefinition;
@@ -456,6 +457,9 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
             // add route configurations
             getContext().addRouteConfigurations(getRouteConfigurations());
 
+            // init route configurations
+            initRouteConfigurationRefs();
+
             // init route templates
             initRouteTemplateRefs();
 
@@ -868,6 +872,19 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
         }
     }
 
+    protected void initRouteConfigurationRefs() throws Exception {
+        // add route configuration refs to existing route configurations
+        if (getRouteConfigurationRefs() != null) {
+            for (RouteConfigurationContextRefDefinition ref : getRouteConfigurationRefs()) {
+                List<RouteConfigurationDefinition> defs = ref.lookupRouteConfigurations(getContext());
+                for (RouteConfigurationDefinition def : defs) {
+                    LOG.debug("Adding route configuration from {} -> {}", ref, def);
+                    getRouteConfigurations().add(def);
+                }
+            }
+        }
+    }
+
     protected void initRouteTemplateRefs() throws Exception {
         // add route template refs to existing route templates
         if (getRouteTemplateRefs() != null) {
@@ -1013,6 +1030,8 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
 
     public abstract CamelRouteControllerDefinition getCamelRouteController();
 
+    public abstract List<RouteConfigurationContextRefDefinition> getRouteConfigurationRefs();
+
     public abstract List<RouteBuilderDefinition> getBuilderRefs();
 
     public abstract List<RouteContextRefDefinition> getRouteRefs();
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 8be755e..60eb730 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -936,6 +936,15 @@ public class ModelParser extends BaseParser {
             return identifiedTypeAttributeHandler().accept(def, key, val);
         }, noElementHandler(), noValueHandler());
     }
+    protected RouteConfigurationContextRefDefinition doParseRouteConfigurationContextRefDefinition() throws IOException, XmlPullParserException {
+        return doParse(new RouteConfigurationContextRefDefinition(), (def, key, val) -> {
+            if ("ref".equals(key)) {
+                def.setRef(val);
+                return true;
+            }
+            return false;
+        }, noElementHandler(), noValueHandler());
+    }
     protected RouteConfigurationDefinition doParseRouteConfigurationDefinition() throws IOException, XmlPullParserException {
         return doParse(new RouteConfigurationDefinition(),
             optionalIdentifiedDefinitionAttributeHandler(), (def, key) -> {