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 2023/04/20 07:34:37 UTC

[camel] branch main updated: [Camel-19280] camel coap observe (#9898)

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


The following commit(s) were added to refs/heads/main by this push:
     new fa3167901e7 [Camel-19280] camel coap observe (#9898)
fa3167901e7 is described below

commit fa3167901e792c980e6bd4b37841a7b5ab081dda
Author: akutri <73...@users.noreply.github.com>
AuthorDate: Thu Apr 20 09:34:28 2023 +0200

    [Camel-19280] camel coap observe (#9898)
    
    * CAMEL-19280: camel-coap: cleanup: remove unused CoAPConsumer.resources
    
    * CAMEL-19280: camel-coap: refactor: move splitting of URI-path into segments to CoAPHelper.java
    
    * CAMEL-19280: camel-coap: add notifications to resources, to implement server-side observable resources (RFC-7641)
    
    * CAMEL-19280: camel-coap: add headers CamelCoapMaxAge and CamelCoapETag
    
    * CAMEL-19280: camel-coap: refactor: move re-usable part of CoAPProducer.getClient() to CoAPEndpoint.createClient()
    
    * CAMEL-19280: camel-coap: refactor: move re-usable part of CoAPProducer.process() to CoAPHelper.convertCoapResponseToMessage()
    
    * CAMEL-19280: camel-coap: add observe=true to source endpoint, to implement client-side observations (RFC-7641)
    
    * CAMEL-19280: camel-coap: refactor: move Observer functionality to CoAPObserver.java
    
    * CAMEL-19280: camel-coap: refactor: move Notifier functionality to CoAPNotifier.java
    
    * CAMEL-19280: camel-coap: match placeholders in URI-path when notifying observers.
    
    * CAMEL-19280: camel-coap: observations must be restarted automatically after an error occurs
    
    * CAMEL-19280: camel-coap: code style
---
 .../apache/camel/catalog/components/coap+tcp.json  |   5 +
 .../org/apache/camel/catalog/components/coap.json  |   5 +
 .../apache/camel/catalog/components/coaps+tcp.json |   5 +
 .../org/apache/camel/catalog/components/coaps.json |   5 +
 .../apache/camel/catalog/schemas/camel-spring.xsd  |   2 +-
 .../apache/camel/coap/CoAPEndpointConfigurer.java  |   9 ++
 .../apache/camel/coap/CoAPEndpointUriFactory.java  |   5 +-
 .../resources/org/apache/camel/coap/coap+tcp.json  |   5 +
 .../resources/org/apache/camel/coap/coap.json      |   5 +
 .../resources/org/apache/camel/coap/coaps+tcp.json |   5 +
 .../resources/org/apache/camel/coap/coaps.json     |   5 +
 .../org/apache/camel/coap/CamelCoapResource.java   |  10 ++
 .../java/org/apache/camel/coap/CoAPConstants.java  |   4 +
 .../java/org/apache/camel/coap/CoAPConsumer.java   |  37 ++-----
 .../java/org/apache/camel/coap/CoAPEndpoint.java   | 112 ++++++++++++++++++-
 .../java/org/apache/camel/coap/CoAPHelper.java     |  30 +++++
 .../java/org/apache/camel/coap/CoAPNotifier.java   |  72 ++++++++++++
 .../java/org/apache/camel/coap/CoAPObserver.java   |  74 +++++++++++++
 .../java/org/apache/camel/coap/CoAPProducer.java   |  44 +-------
 .../org/apache/camel/coap/CoAPObserveTest.java     | 103 +++++++++++++++++
 .../endpoint/dsl/CoAPEndpointBuilderFactory.java   | 123 +++++++++++++++++++++
 21 files changed, 590 insertions(+), 75 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap+tcp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap+tcp.json
index dde2b41ad21..1b8bf9c1295 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap+tcp.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap+tcp.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap.json
index 6d7319da305..2ba2538edd4 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coap.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps+tcp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps+tcp.json
index de68a888cad..d390208ab34 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps+tcp.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps+tcp.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps.json
index b32d00ed1ae..5c82b93cedd 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/coaps.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 19eccf3d812..681337b4264 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -384,7 +384,7 @@ Enriches a message with data from a secondary resource
   <xs:element name="errorHandler" nillable="true" type="xs:anyType">
     <xs:annotation>
       <xs:documentation xml:lang="en"><![CDATA[
-Camel error handling.
+Error handler settings
       ]]></xs:documentation>
     </xs:annotation>
   </xs:element>
diff --git a/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointConfigurer.java b/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointConfigurer.java
index 52283eccef8..a1c5618d9e0 100644
--- a/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointConfigurer.java
+++ b/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointConfigurer.java
@@ -36,6 +36,9 @@ public class CoAPEndpointConfigurer extends PropertyConfigurerSupport implements
         case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "notify": target.setNotify(property(camelContext, boolean.class, value)); return true;
+        case "observable": target.setObservable(property(camelContext, boolean.class, value)); return true;
+        case "observe": target.setObserve(property(camelContext, boolean.class, value)); return true;
         case "privatekey":
         case "privateKey": target.setPrivateKey(property(camelContext, java.security.PrivateKey.class, value)); return true;
         case "pskstore":
@@ -70,6 +73,9 @@ public class CoAPEndpointConfigurer extends PropertyConfigurerSupport implements
         case "exchangePattern": return org.apache.camel.ExchangePattern.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
+        case "notify": return boolean.class;
+        case "observable": return boolean.class;
+        case "observe": return boolean.class;
         case "privatekey":
         case "privateKey": return java.security.PrivateKey.class;
         case "pskstore":
@@ -105,6 +111,9 @@ public class CoAPEndpointConfigurer extends PropertyConfigurerSupport implements
         case "exchangePattern": return target.getExchangePattern();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
+        case "notify": return target.isNotify();
+        case "observable": return target.isObservable();
+        case "observe": return target.isObserve();
         case "privatekey":
         case "privateKey": return target.getPrivateKey();
         case "pskstore":
diff --git a/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointUriFactory.java b/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointUriFactory.java
index ed7d93f69eb..155dfe54481 100644
--- a/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointUriFactory.java
+++ b/components/camel-coap/src/generated/java/org/apache/camel/coap/CoAPEndpointUriFactory.java
@@ -22,7 +22,7 @@ public class CoAPEndpointUriFactory extends org.apache.camel.support.component.E
     private static final Set<String> SECRET_PROPERTY_NAMES;
     private static final Set<String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(15);
+        Set<String> props = new HashSet<>(18);
         props.add("alias");
         props.add("bridgeErrorHandler");
         props.add("cipherSuites");
@@ -31,6 +31,9 @@ public class CoAPEndpointUriFactory extends org.apache.camel.support.component.E
         props.add("exceptionHandler");
         props.add("exchangePattern");
         props.add("lazyStartProducer");
+        props.add("notify");
+        props.add("observable");
+        props.add("observe");
         props.add("privateKey");
         props.add("pskStore");
         props.add("publicKey");
diff --git a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap+tcp.json b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap+tcp.json
index dde2b41ad21..1b8bf9c1295 100644
--- a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap+tcp.json
+++ b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap+tcp.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap.json b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap.json
index 6d7319da305..2ba2538edd4 100644
--- a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap.json
+++ b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coap.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps+tcp.json b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps+tcp.json
index de68a888cad..d390208ab34 100644
--- a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps+tcp.json
+++ b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps+tcp.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps.json b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps.json
index b32d00ed1ae..5c82b93cedd 100644
--- a/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps.json
+++ b/components/camel-coap/src/generated/resources/org/apache/camel/coap/coaps.json
@@ -28,6 +28,8 @@
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
   },
   "headers": {
+    "CamelCoapETag": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP ETag for the response.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_ETAG" },
+    "CamelCoapMaxAge": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP Max-Age for the response body.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_MAX_AGE" },
     "CamelCoapMethod": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The request method that the CoAP producer should use when calling the target CoAP server URI. Valid options are DELETE, GET, PING, POST & PUT.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_METHOD" },
     "CamelCoapResponseCode": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The CoAP response code sent by the external server. See RFC 7252 for details of what each code means.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_RESPONSE_CODE" },
     "CamelCoapUri": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The URI of a CoAP server to call. Will override any existing URI configured directly on the endpoint.", "constantName": "org.apache.camel.coap.CoAPConstants#COAP_URI" },
@@ -36,9 +38,12 @@
   "properties": {
     "uri": { "kind": "path", "displayName": "Uri", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "description": "The URI for the CoAP endpoint" },
     "coapMethodRestrict": { "kind": "parameter", "displayName": "Coap Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "DELETE", "GET", "POST", "PUT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list of methods that the CoAP consumer will bind to. The default is to bind to all methods (DELETE, GET, POST, PUT)." },
+    "observable": { "kind": "parameter", "displayName": "Observable", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Make CoAP resource observable for source endpoint, based on RFC 7641." },
+    "observe": { "kind": "parameter", "displayName": "Observe", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Send an observe request from a source endpoint, based on RFC 7641." },
     "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
     "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the con [...]
     "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "notify": { "kind": "parameter", "displayName": "Notify", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination endpoint, with an URI that matches an existing source endpoint URI." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
     "alias": { "kind": "parameter", "displayName": "Alias", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "NONE", "WANT", "REQUIRE" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the alias used to query the KeyStore for the private key and certificate. This parameter is used when we are enabling TLS with certificates on the service side, and similarly on the client side when TLS  [...]
     "cipherSuites": { "kind": "parameter", "displayName": "Cipher Suites", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the cipherSuites String. This is a comma separated String of ciphersuites to configure. If it is not specified, then it falls back to getting the ciphersuites from the sslContextParameters object." },
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java
index a9383960971..d9ce57885b5 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java
@@ -140,6 +140,16 @@ final class CamelCoapResource extends CoapResource {
             consumer.getProcessor().process(camelExchange);
             Message target = camelExchange.getMessage();
 
+            Long maxAge = target.getHeader(CoAPConstants.COAP_MAX_AGE, Long.class);
+            if (maxAge != null) {
+                cexchange.setMaxAge(maxAge);
+            }
+
+            byte[] eTag = target.getHeader(CoAPConstants.COAP_ETAG, byte[].class);
+            if (eTag != null) {
+                cexchange.setETag(eTag);
+            }
+
             int format = MediaTypeRegistry.parse(target.getHeader(CoAPConstants.CONTENT_TYPE, String.class));
             cexchange.respond(ResponseCode.CONTENT, target.getBody(byte[].class), format);
 
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConstants.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConstants.java
index 211bdb7a202..ce72a421f75 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConstants.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConstants.java
@@ -41,6 +41,10 @@ public interface CoAPConstants {
     /**
      * CoAP exchange header names
      */
+    @Metadata(description = "The CoAP ETag for the response.", javaType = "byte[]")
+    String COAP_ETAG = "CamelCoapETag";
+    @Metadata(description = "The CoAP Max-Age for the response body.", javaType = "java.lang.Long")
+    String COAP_MAX_AGE = "CamelCoapMaxAge";
     @Metadata(description = "The request method that the CoAP producer should use when calling the target CoAP\n" +
                             "server URI. Valid options are DELETE, GET, PING, POST & PUT.",
               javaType = "String")
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java
index f8c3097e54d..4a16694131e 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java
@@ -16,12 +16,10 @@
  */
 package org.apache.camel.coap;
 
-import java.util.LinkedList;
-import java.util.List;
+import java.util.Iterator;
 
 import org.apache.camel.Processor;
 import org.apache.camel.support.DefaultConsumer;
-import org.eclipse.californium.core.CoapResource;
 import org.eclipse.californium.core.server.resources.Resource;
 
 /**
@@ -29,7 +27,6 @@ import org.eclipse.californium.core.server.resources.Resource;
  */
 public class CoAPConsumer extends DefaultConsumer {
     private final CoAPEndpoint endpoint;
-    private List<CoapResource> resources = new LinkedList<>();
 
     public CoAPConsumer(final CoAPEndpoint endpoint, final Processor processor) {
         super(endpoint, processor);
@@ -44,39 +41,21 @@ public class CoAPConsumer extends DefaultConsumer {
     protected void doStart() throws Exception {
         super.doStart();
 
-        String path = endpoint.getUri().getPath();
-        if (path.startsWith("/")) {
-            path = path.substring(1);
-        }
+        Iterator<String> pathSegmentIterator = endpoint.getPathSegmentsFromURI().iterator();
         Resource cr = endpoint.getCoapServer().getRoot();
-        while (!path.isEmpty()) {
-            int idx = path.indexOf('/');
-            String part1 = path;
-            if (idx != -1) {
-                part1 = path.substring(0, idx);
-                path = path.substring(idx + 1);
-            } else {
-                path = "";
-            }
-            Resource child = cr.getChild(part1);
+        while (pathSegmentIterator.hasNext()) {
+            String pathSegment = pathSegmentIterator.next();
+            Resource child = cr.getChild(pathSegment);
             if (child == null) {
-                child = new CamelCoapResource(part1, this);
+                child = new CamelCoapResource(pathSegment, this);
+                ((CamelCoapResource) child).setObservable(endpoint.isObservable());
                 cr.add(child);
                 cr = child;
-            } else if (path.isEmpty()) {
+            } else if (!pathSegmentIterator.hasNext()) {
                 ((CamelCoapResource) child).addConsumer(this);
             } else {
                 cr = child;
             }
         }
     }
-
-    @Override
-    protected void doStop() throws Exception {
-        for (CoapResource r : resources) {
-            r.getParent().delete(r);
-        }
-        resources.clear();
-        super.doStop();
-    }
 }
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java
index 3814cb91773..97f3c697920 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java
@@ -27,8 +27,11 @@ import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.List;
 
+import javax.net.ssl.SSLContext;
+
 import org.apache.camel.Category;
 import org.apache.camel.Consumer;
 import org.apache.camel.Processor;
@@ -40,7 +43,13 @@ import org.apache.camel.support.DefaultEndpoint;
 import org.apache.camel.support.jsse.ClientAuthentication;
 import org.apache.camel.support.jsse.KeyManagersParameters;
 import org.apache.camel.support.jsse.SSLContextParameters;
+import org.eclipse.californium.core.CoapClient;
 import org.eclipse.californium.core.CoapServer;
+import org.eclipse.californium.core.network.CoapEndpoint;
+import org.eclipse.californium.core.network.config.NetworkConfig;
+import org.eclipse.californium.core.server.resources.Resource;
+import org.eclipse.californium.elements.tcp.netty.TcpClientConnector;
+import org.eclipse.californium.elements.tcp.netty.TlsClientConnector;
 import org.eclipse.californium.scandium.DTLSConnector;
 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
 import org.eclipse.californium.scandium.dtls.CertificateType;
@@ -76,6 +85,12 @@ public class CoAPEndpoint extends DefaultEndpoint {
     private SSLContextParameters sslContextParameters;
     @UriParam(label = "security", defaultValue = "true")
     private boolean recommendedCipherSuitesOnly = true;
+    @UriParam(label = "consumer", defaultValue = "false")
+    private boolean observe;
+    @UriParam(label = "consumer", defaultValue = "false")
+    private boolean observable;
+    @UriParam(label = "producer", defaultValue = "false")
+    private boolean notify;
 
     private CoAPComponent component;
 
@@ -103,12 +118,21 @@ public class CoAPEndpoint extends DefaultEndpoint {
 
     @Override
     public Producer createProducer() throws Exception {
-        return new CoAPProducer(this);
+        if (isNotify()) {
+            return new CoAPNotifier(this);
+        } else {
+            return new CoAPProducer(this);
+        }
     }
 
     @Override
     public Consumer createConsumer(Processor processor) throws Exception {
-        CoAPConsumer consumer = new CoAPConsumer(this, processor);
+        final Consumer consumer;
+        if (isObserve()) {
+            consumer = new CoAPObserver(this, processor);
+        } else {
+            consumer = new CoAPConsumer(this, processor);
+        }
         configureConsumer(consumer);
         return consumer;
     }
@@ -124,6 +148,23 @@ public class CoAPEndpoint extends DefaultEndpoint {
         return uri;
     }
 
+    public CamelCoapResource getCamelCoapResource(String path) throws IOException, GeneralSecurityException {
+        Iterator<String> pathSegments = CoAPHelper.getPathSegmentsFromPath(path).iterator();
+        if (!pathSegments.hasNext()) {
+            return null;
+        }
+
+        Resource current = getCoapServer().getRoot();
+        while (pathSegments.hasNext() && current != null) {
+            current = current.getChild(pathSegments.next());
+        }
+        return (CamelCoapResource) current;
+    }
+
+    public List<String> getPathSegmentsFromURI() {
+        return CoAPHelper.getPathSegmentsFromPath(getUri().getPath());
+    }
+
     public CoapServer getCoapServer() throws IOException, GeneralSecurityException {
         return component.getServer(getUri().getPort(), this);
     }
@@ -150,6 +191,40 @@ public class CoAPEndpoint extends DefaultEndpoint {
         this.alias = alias;
     }
 
+    public boolean isObserve() {
+        return observe;
+    }
+
+    /**
+     * Send an observe request from a source endpoint, based on RFC 7641.
+     */
+    public void setObserve(boolean observe) {
+        this.observe = observe;
+    }
+
+    public boolean isObservable() {
+        return observable;
+    }
+
+    /**
+     * Make CoAP resource observable for source endpoint, based on RFC 7641.
+     */
+    public void setObservable(boolean observable) {
+        this.observable = observable;
+    }
+
+    public boolean isNotify() {
+        return notify;
+    }
+
+    /**
+     * Notify observers that the resource of this URI has changed, based on RFC 7641. Use this flag on a destination
+     * endpoint, with an URI that matches an existing source endpoint URI.
+     */
+    public void setNotify(boolean notify) {
+        this.notify = notify;
+    }
+
     /**
      * Get the SSLContextParameters object for setting up TLS. This is required for coaps+tcp, and for coaps when we are
      * using certificates for TLS (as opposed to RPK or PKS).
@@ -413,4 +488,37 @@ public class CoAPEndpoint extends DefaultEndpoint {
 
         return new DTLSConnector(builder.build());
     }
+
+    public CoapClient createCoapClient(URI uri) throws IOException, GeneralSecurityException {
+        CoapClient client = new CoapClient(uri);
+
+        // Configure TLS and / or TCP
+        if (CoAPEndpoint.enableDTLS(uri)) {
+            DTLSConnector connector = createDTLSConnector(null, true);
+            CoapEndpoint.Builder coapBuilder = new CoapEndpoint.Builder();
+            coapBuilder.setConnector(connector);
+
+            client.setEndpoint(coapBuilder.build());
+        } else if (CoAPEndpoint.enableTCP(getUri())) {
+            NetworkConfig config = NetworkConfig.createStandardWithoutFile();
+            int tcpThreads = config.getInt(NetworkConfig.Keys.TCP_WORKER_THREADS);
+            int tcpConnectTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECT_TIMEOUT);
+            int tcpIdleTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECTION_IDLE_TIMEOUT);
+            TcpClientConnector tcpConnector = null;
+
+            // TLS + TCP
+            if (getUri().getScheme().startsWith("coaps")) {
+                SSLContext sslContext = getSslContextParameters().createSSLContext(getCamelContext());
+                tcpConnector = new TlsClientConnector(sslContext, tcpThreads, tcpConnectTimeout, tcpIdleTimeout);
+            } else {
+                tcpConnector = new TcpClientConnector(tcpThreads, tcpConnectTimeout, tcpIdleTimeout);
+            }
+
+            CoapEndpoint.Builder tcpBuilder = new CoapEndpoint.Builder();
+            tcpBuilder.setConnector(tcpConnector);
+
+            client.setEndpoint(tcpBuilder.build());
+        }
+        return client;
+    }
 }
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPHelper.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPHelper.java
index 2701fc9113b..d2c12a101fa 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPHelper.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPHelper.java
@@ -16,9 +16,15 @@
  */
 package org.apache.camel.coap;
 
+import java.util.LinkedList;
+import java.util.List;
+
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
 import org.apache.camel.util.ObjectHelper;
 import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapResponse;
+import org.eclipse.californium.core.coap.MediaTypeRegistry;
 
 /**
  * Various helper methods for CoAP
@@ -58,4 +64,28 @@ public final class CoAPHelper {
         }
         return CoAPConstants.METHOD_RESTRICT_ALL;
     }
+
+    public static List<String> getPathSegmentsFromPath(String path) {
+        List<String> segments = new LinkedList<>();
+        if (path.startsWith("/")) {
+            path = path.substring(1);
+        }
+        while (!path.isEmpty()) {
+            int idx = path.indexOf('/');
+            if (idx == -1) {
+                segments.add(path);
+                break;
+            }
+            segments.add(path.substring(0, idx));
+            path = path.substring(idx + 1);
+        }
+        return segments;
+    }
+
+    public static void convertCoapResponseToMessage(CoapResponse coapResponse, Message message) {
+        String mt = MediaTypeRegistry.toString(coapResponse.getOptions().getContentFormat());
+        message.setHeader(CoAPConstants.CONTENT_TYPE, mt);
+        message.setHeader(CoAPConstants.COAP_RESPONSE_CODE, coapResponse.getCode().toString());
+        message.setBody(coapResponse.getPayload());
+    }
 }
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPNotifier.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPNotifier.java
new file mode 100644
index 00000000000..e54b0d841b2
--- /dev/null
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPNotifier.java
@@ -0,0 +1,72 @@
+/*
+ * 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.coap;
+
+import java.net.URI;
+import java.util.Optional;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.support.DefaultProducer;
+import org.eclipse.californium.core.CoapClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The CoAP notifier.
+ */
+public class CoAPNotifier extends DefaultProducer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CoAPNotifier.class);
+
+    private final CoAPEndpoint endpoint;
+    private CoapClient client;
+
+    public CoAPNotifier(CoAPEndpoint endpoint) {
+        super(endpoint);
+        this.endpoint = endpoint;
+    }
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        URI uri = Optional.ofNullable(exchange.getIn().getHeader(CoAPConstants.COAP_URI, URI.class))
+                .orElse(endpoint.getUri());
+        CamelCoapResource resource = endpoint.getCamelCoapResource(uri.getPath());
+        if (resource == null) {
+            throw new IllegalStateException("Resource not found: " + endpoint.getUri());
+        }
+        if (!resource.isObservable()) {
+            LOG.warn("Ignoring notification attempt for resource that is not observable: " + endpoint.getUri());
+            return;
+        }
+
+        resource.changed(observeRelation -> {
+            // this implementation only supports notifying URIs with all {placeholders}
+            // replaced or with all {placeholders} intact.
+            if (uri.getPath().equals(resource.getPath() + resource.getName())) {
+                // resource path == notified path, including any {placeholder} path segments.
+                return true;
+            } else {
+                // resource path != notified path. This is true when the resource path contains
+                // {placeholders} while the notified path does not.
+                // Only notify a client if the requested path == notified uri.
+                String observedPath = observeRelation.getExchange().getRequest().getOptions().getUriPathString();
+                return uri.getPath().equals(observedPath);
+            }
+        });
+    }
+
+}
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPObserver.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPObserver.java
new file mode 100644
index 00000000000..90e0694c0b1
--- /dev/null
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPObserver.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.coap;
+
+import java.io.IOException;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.support.DefaultConsumer;
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapHandler;
+import org.eclipse.californium.core.CoapResponse;
+
+/**
+ * The CoAP observer.
+ */
+public class CoAPObserver extends DefaultConsumer implements CoapHandler {
+    private final CoAPEndpoint endpoint;
+    private CoapClient client;
+
+    public CoAPObserver(final CoAPEndpoint endpoint, final Processor processor) {
+        super(endpoint, processor);
+        this.endpoint = endpoint;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+        if (this.client == null) {
+            client = endpoint.createCoapClient(endpoint.getUri());
+        }
+        startObserve();
+    }
+
+    @Override
+    public void onLoad(CoapResponse response) {
+        Exchange camelExchange = createExchange(false);
+        try {
+            CoAPHelper.convertCoapResponseToMessage(response, camelExchange.getMessage());
+            getProcessor().process(camelExchange);
+        } catch (Exception ignored) {
+        } finally {
+            Exception exception = camelExchange.getException();
+            if (exception != null) {
+                getExceptionHandler().handleException("Error processing observed update", camelExchange, exception);
+            }
+            releaseExchange(camelExchange, false);
+        }
+    }
+
+    @Override
+    public void onError() {
+        getExceptionHandler().handleException(new IOException("CoAP request timed out or has been rejected by the server"));
+        startObserve();
+    }
+
+    private void startObserve() {
+        this.client.observe(this);
+    }
+}
diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
index fc60f19bc95..1f3ef470eeb 100644
--- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
+++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
@@ -20,19 +20,12 @@ import java.io.IOException;
 import java.net.URI;
 import java.security.GeneralSecurityException;
 
-import javax.net.ssl.SSLContext;
-
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.support.DefaultProducer;
 import org.eclipse.californium.core.CoapClient;
 import org.eclipse.californium.core.CoapResponse;
 import org.eclipse.californium.core.coap.MediaTypeRegistry;
-import org.eclipse.californium.core.network.CoapEndpoint;
-import org.eclipse.californium.core.network.config.NetworkConfig;
-import org.eclipse.californium.elements.tcp.netty.TcpClientConnector;
-import org.eclipse.californium.elements.tcp.netty.TlsClientConnector;
-import org.eclipse.californium.scandium.DTLSConnector;
 
 /**
  * The CoAP producer.
@@ -81,11 +74,7 @@ public class CoAPProducer extends DefaultProducer {
         }
 
         if (response != null) {
-            Message resp = exchange.getOut();
-            String mt = MediaTypeRegistry.toString(response.getOptions().getContentFormat());
-            resp.setHeader(CoAPConstants.CONTENT_TYPE, mt);
-            resp.setHeader(CoAPConstants.COAP_RESPONSE_CODE, response.getCode().toString());
-            resp.setBody(response.getPayload());
+            CoAPHelper.convertCoapResponseToMessage(response, exchange.getOut());
         }
 
         if (method.equalsIgnoreCase(CoAPConstants.METHOD_PING)) {
@@ -100,36 +89,7 @@ public class CoAPProducer extends DefaultProducer {
             if (uri == null) {
                 uri = endpoint.getUri();
             }
-            client = new CoapClient(uri);
-
-            // Configure TLS and / or TCP
-            if (CoAPEndpoint.enableDTLS(uri)) {
-                DTLSConnector connector = endpoint.createDTLSConnector(null, true);
-                CoapEndpoint.Builder coapBuilder = new CoapEndpoint.Builder();
-                coapBuilder.setConnector(connector);
-
-                client.setEndpoint(coapBuilder.build());
-            } else if (CoAPEndpoint.enableTCP(endpoint.getUri())) {
-                NetworkConfig config = NetworkConfig.createStandardWithoutFile();
-                int tcpThreads = config.getInt(NetworkConfig.Keys.TCP_WORKER_THREADS);
-                int tcpConnectTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECT_TIMEOUT);
-                int tcpIdleTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECTION_IDLE_TIMEOUT);
-                TcpClientConnector tcpConnector = null;
-
-                // TLS + TCP
-                if (endpoint.getUri().getScheme().startsWith("coaps")) {
-                    SSLContext sslContext = endpoint.getSslContextParameters().createSSLContext(endpoint.getCamelContext());
-                    tcpConnector = new TlsClientConnector(sslContext, tcpThreads, tcpConnectTimeout, tcpIdleTimeout);
-                } else {
-                    tcpConnector = new TcpClientConnector(tcpThreads, tcpConnectTimeout, tcpIdleTimeout);
-                }
-
-                CoapEndpoint.Builder tcpBuilder = new CoapEndpoint.Builder();
-                tcpBuilder.setConnector(tcpConnector);
-
-                client.setEndpoint(tcpBuilder.build());
-            }
-
+            client = endpoint.createCoapClient(uri);
         }
         return client;
     }
diff --git a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPObserveTest.java b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPObserveTest.java
new file mode 100644
index 00000000000..6248b62b085
--- /dev/null
+++ b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPObserveTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.coap;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapHandler;
+import org.eclipse.californium.core.CoapResponse;
+import org.junit.jupiter.api.Test;
+
+public class CoAPObserveTest extends CoAPTestSupport {
+
+    @Produce("direct:notify")
+    protected ProducerTemplate notify;
+
+    @Produce("mock:coapHandlerResults")
+    protected ProducerTemplate coapHandlerResults;
+
+    @Test
+    void testServerObservable() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:coapHandlerResults");
+        mock.expectedBodiesReceived("Hello 0");
+
+        CoapClient client = createClient("/TestResource");
+        client.observe(new CoapHandler() {
+            @Override
+            public void onLoad(CoapResponse response) {
+                coapHandlerResults.sendBody(response.getResponseText());
+            }
+
+            @Override
+            public void onError() {
+                coapHandlerResults.sendBody(null);
+            }
+        });
+
+        MockEndpoint.assertIsSatisfied(context());
+        mock.reset();
+
+        mock.expectedBodiesReceivedInAnyOrder("Hello 1", "Hello 2");
+
+        notify.sendBody(null);
+        notify.sendBody(null);
+
+        MockEndpoint.assertIsSatisfied(context());
+    }
+
+    @Test
+    void testClientAndServerObservable() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:sourceResults");
+        mock.expectedBodiesReceived("Hello 0");
+
+        MockEndpoint.assertIsSatisfied(context());
+        mock.reset();
+
+        mock.expectedBodiesReceivedInAnyOrder("Hello 1", "Hello 2");
+
+        notify.sendBody(null);
+        notify.sendBody(null);
+
+        MockEndpoint.assertIsSatisfied(context());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                AtomicInteger i = new AtomicInteger(0);
+
+                fromF("coap://localhost:%d/TestResource?observable=true", PORT)
+                        .process(exchange -> exchange.getMessage().setBody("Hello " + i.get()));
+
+                from("direct:notify")
+                        .process(exchange -> i.incrementAndGet())
+                        .toF("coap://localhost:%d/TestResource?notify=true", PORT);
+
+                fromF("coap://localhost:%d/TestResource?observe=true", PORT)
+                        .to("mock:sourceResults");
+            }
+        };
+    }
+
+}
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/CoAPEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/CoAPEndpointBuilderFactory.java
index f917d824a66..d8eb30b9b68 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/CoAPEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/CoAPEndpointBuilderFactory.java
@@ -59,6 +59,68 @@ public interface CoAPEndpointBuilderFactory {
             doSetProperty("coapMethodRestrict", coapMethodRestrict);
             return this;
         }
+        /**
+         * Make CoAP resource observable for source endpoint, based on RFC 7641.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: consumer
+         * 
+         * @param observable the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointConsumerBuilder observable(boolean observable) {
+            doSetProperty("observable", observable);
+            return this;
+        }
+        /**
+         * Make CoAP resource observable for source endpoint, based on RFC 7641.
+         * 
+         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
+         * type.
+         * 
+         * Default: false
+         * Group: consumer
+         * 
+         * @param observable the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointConsumerBuilder observable(String observable) {
+            doSetProperty("observable", observable);
+            return this;
+        }
+        /**
+         * Send an observe request from a source endpoint, based on RFC 7641.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: consumer
+         * 
+         * @param observe the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointConsumerBuilder observe(boolean observe) {
+            doSetProperty("observe", observe);
+            return this;
+        }
+        /**
+         * Send an observe request from a source endpoint, based on RFC 7641.
+         * 
+         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
+         * type.
+         * 
+         * Default: false
+         * Group: consumer
+         * 
+         * @param observe the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointConsumerBuilder observe(String observe) {
+            doSetProperty("observe", observe);
+            return this;
+        }
         /**
          * Sets the alias used to query the KeyStore for the private key and
          * certificate. This parameter is used when we are enabling TLS with
@@ -450,6 +512,41 @@ public interface CoAPEndpointBuilderFactory {
         default AdvancedCoAPEndpointProducerBuilder advanced() {
             return (AdvancedCoAPEndpointProducerBuilder) this;
         }
+        /**
+         * Notify observers that the resource of this URI has changed, based on
+         * RFC 7641. Use this flag on a destination endpoint, with an URI that
+         * matches an existing source endpoint URI.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param notify the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointProducerBuilder notify(boolean notify) {
+            doSetProperty("notify", notify);
+            return this;
+        }
+        /**
+         * Notify observers that the resource of this URI has changed, based on
+         * RFC 7641. Use this flag on a destination endpoint, with an URI that
+         * matches an existing source endpoint URI.
+         * 
+         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
+         * type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param notify the value to set
+         * @return the dsl builder
+         */
+        default CoAPEndpointProducerBuilder notify(String notify) {
+            doSetProperty("notify", notify);
+            return this;
+        }
         /**
          * Sets the alias used to query the KeyStore for the private key and
          * certificate. This parameter is used when we are enabling TLS with
@@ -1165,6 +1262,32 @@ public interface CoAPEndpointBuilderFactory {
          */
         private static final CoAPHeaderNameBuilder INSTANCE = new CoAPHeaderNameBuilder();
 
+        /**
+         * The CoAP ETag for the response.
+         * 
+         * The option is a: {@code byte[]} type.
+         * 
+         * Group: common
+         * 
+         * @return the name of the header {@code CoapETag}.
+         */
+        public String coapETag() {
+            return "CoapETag";
+        }
+
+        /**
+         * The CoAP Max-Age for the response body.
+         * 
+         * The option is a: {@code java.lang.Long} type.
+         * 
+         * Group: common
+         * 
+         * @return the name of the header {@code CoapMaxAge}.
+         */
+        public String coapMaxAge() {
+            return "CoapMaxAge";
+        }
+
         /**
          * The request method that the CoAP producer should use when calling the
          * target CoAP server URI. Valid options are DELETE, GET, PING, POST