Repository: eclipse/leshan
Pull request #720 (2019-07-18 15:16:01)

Remove default support of OLD TLV and JSON code
Leshan support old TLV and JSON code. This old code was used by the past because official code was not yet declared to [iana](https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats).

The LWM2M specification is out since a February 2017  and old code should not be used anymore.
So this PR deactivate the support of those codes be default, but this is still possible to activate it like this :
```java
LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setEncoder(new DefaultLwM2mNodeEncoder(true));
```


Patch:
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClient.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClient.java
index 9732430f..8b7c1f58 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClient.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClient.java
@@ -36,8 +36,8 @@ import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.servers.BootstrapHandler;
 import org.eclipse.leshan.client.servers.RegistrationEngine;
 import org.eclipse.leshan.core.californium.EndpointFactory;
-import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
-import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
 import org.eclipse.leshan.util.Validate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,7 +61,8 @@ public class LeshanClient implements LwM2mClient {
 
     public LeshanClient(String endpoint, InetSocketAddress localAddress,
             List<? extends LwM2mObjectEnabler> objectEnablers, NetworkConfig coapConfig, Builder dtlsConfigBuilder,
-            EndpointFactory endpointFactory, Map<String, String> additionalAttributes) {
+            EndpointFactory endpointFactory, Map<String, String> additionalAttributes, LwM2mNodeEncoder encoder,
+            LwM2mNodeDecoder decoder) {
 
         Validate.notNull(endpoint);
         Validate.notEmpty(objectEnablers);
@@ -93,8 +94,7 @@ public class LeshanClient implements LwM2mClient {
 
         // Create CoAP resources for each lwm2m Objects.
         for (LwM2mObjectEnabler enabler : objectEnablers) {
-            ObjectResource clientObject = new ObjectResource(enabler, bootstrapHandler, new DefaultLwM2mNodeEncoder(),
-                    new DefaultLwM2mNodeDecoder());
+            ObjectResource clientObject = new ObjectResource(enabler, bootstrapHandler, encoder, decoder);
             clientSideServer.add(clientObject);
         }
 
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
index 857e2b29..0dc1bd9a 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
@@ -34,6 +34,11 @@ import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.californium.DefaultEndpointFactory;
 import org.eclipse.leshan.core.californium.EndpointFactory;
+import org.eclipse.leshan.core.node.LwM2mNode;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.util.Validate;
 
@@ -50,6 +55,9 @@ public class LeshanClientBuilder {
     private NetworkConfig coapConfig;
     private Builder dtlsConfigBuilder;
 
+    private LwM2mNodeEncoder encoder;
+    private LwM2mNodeDecoder decoder;
+
     private EndpointFactory endpointFactory;
     private Map<String, String> additionalAttributes;
 
@@ -97,6 +105,26 @@ public class LeshanClientBuilder {
         return this;
     }
 
+    /**
+     * Set the {@link LwM2mNodeEncoder} which will encode {@link LwM2mNode} with supported content format.
+     * <p>
+     * By default the {@link DefaultLwM2mNodeEncoder} is used. It supports Text, Opaque, TLV and JSON format.
+     */
+    public LeshanClientBuilder setEncoder(LwM2mNodeEncoder encoder) {
+        this.encoder = encoder;
+        return this;
+    }
+
+    /**
+     * Set the {@link LwM2mNodeDecoder} which will decode data in supported content format to create {@link LwM2mNode}.
+     * <p>
+     * By default the {@link DefaultLwM2mNodeDecoder} is used. It supports Text, Opaque, TLV and JSON format.
+     */
+    public LeshanClientBuilder setDecoder(LwM2mNodeDecoder decoder) {
+        this.decoder = decoder;
+        return this;
+    }
+
     /**
      * Set the Californium/CoAP {@link NetworkConfig}.
      */
@@ -162,6 +190,10 @@ public class LeshanClientBuilder {
             initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", "model12345", "12345", "U"));
             objectEnablers = initializer.createAll();
         }
+        if (encoder == null)
+            encoder = new DefaultLwM2mNodeEncoder();
+        if (decoder == null)
+            decoder = new DefaultLwM2mNodeDecoder();
         if (coapConfig == null) {
             coapConfig = createDefaultNetworkConfig();
         }
@@ -210,6 +242,6 @@ public class LeshanClientBuilder {
         }
 
         return new LeshanClient(endpoint, localAddress, objectEnablers, coapConfig, dtlsConfigBuilder, endpointFactory,
-                additionalAttributes);
+                additionalAttributes, encoder, decoder);
     }
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeDecoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeDecoder.java
index 05b1235a..6e2af28e 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeDecoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeDecoder.java
@@ -36,10 +36,41 @@ import org.eclipse.leshan.util.Validate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * A default {@link LwM2mNodeDecoder} which support default {@link ContentFormat} :
+ * <p>
+ * <ul>
+ * <li>{@link ContentFormat#TLV}</li>
+ * <li>{@link ContentFormat#JSON}</li>
+ * <li>{@link ContentFormat#TEXT}</li>
+ * <li>{@link ContentFormat#OPAQUE}</li>
+ * </ul>
+ */
 public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultLwM2mNodeDecoder.class);
 
+    protected final boolean supportDeprecatedContentFormat;
+
+    /**
+     * Create {@link DefaultLwM2mNodeDecoder} without support of old TLV and JSON code.
+     */
+    public DefaultLwM2mNodeDecoder() {
+        this(false);
+    }
+
+    /**
+     * Create {@link DefaultLwM2mNodeDecoder} allowing to enable support for old TLV and JSON code.
+     * <p>
+     * Those old codes was used by the LWM2M specification before the official v1.0.0 release and could still be needed
+     * for backward compatibility.
+     * 
+     * @param supportDeprecatedContentFormat True to accept to decode old code.
+     */
+    public DefaultLwM2mNodeDecoder(boolean supportDeprecatedContentFormat) {
+        this.supportDeprecatedContentFormat = supportDeprecatedContentFormat;
+    }
+
     @Override
     public LwM2mNode decode(byte[] content, ContentFormat format, LwM2mPath path, LwM2mModel model)
             throws CodecException {
@@ -58,6 +89,10 @@ public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
             throw new CodecException("Content format is mandatory. [%s]", path);
         }
 
+        if (!isSupported(format)) {
+            throw new CodecException("Content format %s is not supported [%s]", format, path);
+        }
+
         // Decode content.
         switch (format.getCode()) {
         case ContentFormat.TEXT_CODE:
@@ -70,8 +105,6 @@ public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
         case ContentFormat.JSON_CODE:
         case ContentFormat.OLD_JSON_CODE:
             return LwM2mNodeJsonDecoder.decode(content, path, model, nodeClass);
-        case ContentFormat.LINK_CODE:
-            throw new CodecException("Content format %s not yet implemented [%s]", format, path);
         default:
             throw new CodecException("Content format %s is not supported [%s]", format, path);
         }
@@ -87,6 +120,10 @@ public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
             throw new CodecException("Content format is mandatory. [%s]", path);
         }
 
+        if (!isSupported(format)) {
+            throw new CodecException("Content format %s is not supported [%s]", format, path);
+        }
+
         // Decode content.
         switch (format.getCode()) {
         case ContentFormat.TEXT_CODE:
@@ -99,8 +136,6 @@ public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
         case ContentFormat.JSON_CODE:
         case ContentFormat.OLD_JSON_CODE:
             return LwM2mNodeJsonDecoder.decodeTimestamped(content, path, model, nodeClassFromPath(path));
-        case ContentFormat.LINK_CODE:
-            throw new CodecException("Content format %s not yet implemented [%s]", format, path);
         default:
             throw new CodecException("Content format %s is not supported [%s]", format, path);
         }
@@ -131,11 +166,12 @@ public class DefaultLwM2mNodeDecoder implements LwM2mNodeDecoder {
         switch (format.getCode()) {
         case ContentFormat.TEXT_CODE:
         case ContentFormat.TLV_CODE:
-        case ContentFormat.OLD_TLV_CODE:
         case ContentFormat.OPAQUE_CODE:
         case ContentFormat.JSON_CODE:
-        case ContentFormat.OLD_JSON_CODE:
             return true;
+        case ContentFormat.OLD_TLV_CODE:
+        case ContentFormat.OLD_JSON_CODE:
+            return supportDeprecatedContentFormat;
         default:
             return false;
         }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeEncoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeEncoder.java
index 0a9c7db9..e83f5c0b 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeEncoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/DefaultLwM2mNodeEncoder.java
@@ -31,18 +31,49 @@ import org.eclipse.leshan.util.Validate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * A default {@link LwM2mNodeEncoder} which support default {@link ContentFormat} :
+ * <p>
+ * <ul>
+ * <li>{@link ContentFormat#TLV}</li>
+ * <li>{@link ContentFormat#JSON}</li>
+ * <li>{@link ContentFormat#TEXT}</li>
+ * <li>{@link ContentFormat#OPAQUE}</li>
+ * </ul>
+ */
 public class DefaultLwM2mNodeEncoder implements LwM2mNodeEncoder {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultLwM2mNodeEncoder.class);
 
-    private final LwM2mValueConverter converter;
+    protected final LwM2mValueConverter converter;
+    protected final boolean supportDeprecatedContentFormat;
 
+    /**
+     * Create {@link DefaultLwM2mNodeEncoder} without support of old TLV and JSON code.
+     */
     public DefaultLwM2mNodeEncoder() {
         this(new DefaultLwM2mValueConverter());
     }
 
     public DefaultLwM2mNodeEncoder(LwM2mValueConverter converter) {
+        this(converter, false);
+    }
+
+    /**
+     * Create {@link DefaultLwM2mNodeEncoder} allowing to enable support for old TLV and JSON code.
+     * <p>
+     * Those old codes was used by the LWM2M specification before the official v1.0.0 release and could still be needed
+     * for backward compatibility.
+     * 
+     * @param supportDeprecatedContentFormat True to accept to encode old code.
+     */
+    public DefaultLwM2mNodeEncoder(boolean supportDeprecatedContentFormat) {
+        this(new DefaultLwM2mValueConverter(), supportDeprecatedContentFormat);
+    }
+
+    public DefaultLwM2mNodeEncoder(LwM2mValueConverter converter, boolean supportDeprecatedContentFormat) {
         this.converter = converter;
+        this.supportDeprecatedContentFormat = supportDeprecatedContentFormat;
     }
 
     @Override
@@ -53,8 +84,11 @@ public class DefaultLwM2mNodeEncoder implements LwM2mNodeEncoder {
             throw new CodecException("Content format is mandatory. [%s]", path);
         }
 
-        LOG.debug("Encoding node {} for path {} and format {}", node, path, format);
+        if (!isSupported(format)) {
+            throw new CodecException("Content format %s is not supported [%s]", format, path);
+        }
 
+        LOG.trace("Encoding node {} for path {} and format {}", node, path, format);
         byte[] encoded;
         switch (format.getCode()) {
         case ContentFormat.TLV_CODE:
@@ -72,7 +106,7 @@ public class DefaultLwM2mNodeEncoder implements LwM2mNodeEncoder {
             encoded = LwM2mNodeJsonEncoder.encode(node, path, model, converter);
             break;
         default:
-            throw new CodecException("Cannot encode %s:%s with format %s.", path, node, format);
+            throw new CodecException("Content format %s is not supported [%s]", format, path);
         }
         LOG.trace("Encoded node {}: {}", node, encoded);
         return encoded;
@@ -86,11 +120,15 @@ public class DefaultLwM2mNodeEncoder implements LwM2mNodeEncoder {
             throw new CodecException("Content format is mandatory. [%s]", path);
         }
 
-        LOG.debug("Encoding time-stamped nodes for path {} and format {}", timestampedNodes, path, format);
+        if (!isSupported(format)) {
+            throw new CodecException("Content format %s is not supported [%s]", format, path);
+        }
 
+        LOG.trace("Encoding time-stamped nodes for path {} and format {}", timestampedNodes, path, format);
         byte[] encoded;
         switch (format.getCode()) {
         case ContentFormat.JSON_CODE:
+        case ContentFormat.OLD_JSON_CODE:
             encoded = LwM2mNodeJsonEncoder.encodeTimestampedData(timestampedNodes, path, model, converter);
             break;
         default:
@@ -106,11 +144,12 @@ public class DefaultLwM2mNodeEncoder implements LwM2mNodeEncoder {
         switch (format.getCode()) {
         case ContentFormat.TEXT_CODE:
         case ContentFormat.TLV_CODE:
-        case ContentFormat.OLD_TLV_CODE:
         case ContentFormat.OPAQUE_CODE:
         case ContentFormat.JSON_CODE:
-        case ContentFormat.OLD_JSON_CODE:
             return true;
+        case ContentFormat.OLD_TLV_CODE:
+        case ContentFormat.OLD_JSON_CODE:
+            return supportDeprecatedContentFormat;
         default:
             return false;
         }
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanBootstrapServerBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanBootstrapServerBuilder.java
index 21426425..410996a1 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanBootstrapServerBuilder.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanBootstrapServerBuilder.java
@@ -36,6 +36,11 @@ import org.eclipse.leshan.core.californium.DefaultEndpointFactory;
 import org.eclipse.leshan.core.californium.EndpointFactory;
 import org.eclipse.leshan.core.model.LwM2mModel;
 import org.eclipse.leshan.core.model.ObjectLoader;
+import org.eclipse.leshan.core.node.LwM2mNode;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
@@ -72,6 +77,9 @@ public class LeshanBootstrapServerBuilder {
     private NetworkConfig coapConfig;
     private Builder dtlsConfigBuilder;
 
+    private LwM2mNodeEncoder encoder;
+    private LwM2mNodeDecoder decoder;
+
     private PublicKey publicKey;
     private PrivateKey privateKey;
     private X509Certificate[] certificateChain;
@@ -292,6 +300,28 @@ public class LeshanBootstrapServerBuilder {
         return this;
     }
 
+    /**
+     * <p>
+     * Set the {@link LwM2mNodeEncoder} which will encode {@link LwM2mNode} with supported content format.
+     * </p>
+     * By default the {@link DefaultLwM2mNodeEncoder} is used. It supports Text, Opaque, TLV and JSON format.
+     */
+    public LeshanBootstrapServerBuilder setEncoder(LwM2mNodeEncoder encoder) {
+        this.encoder = encoder;
+        return this;
+    }
+
+    /**
+     * <p>
+     * Set the {@link LwM2mNodeDecoder} which will decode data in supported content format to create {@link LwM2mNode}.
+     * </p>
+     * By default the {@link DefaultLwM2mNodeDecoder} is used. It supports Text, Opaque, TLV and JSON format.
+     */
+    public LeshanBootstrapServerBuilder setDecoder(LwM2mNodeDecoder decoder) {
+        this.decoder = decoder;
+        return this;
+    }
+
     /**
      * Set the CoAP/Californium {@link NetworkConfig}.
      * <p>
@@ -398,6 +428,10 @@ public class LeshanBootstrapServerBuilder {
         if (endpointFactory == null) {
             endpointFactory = new DefaultEndpointFactory();
         }
+        if (encoder == null)
+            encoder = new DefaultLwM2mNodeEncoder();
+        if (decoder == null)
+            decoder = new DefaultLwM2mNodeDecoder();
 
         // handle dtlsConfig
         DtlsConnectorConfig dtlsConfig = null;
@@ -525,7 +559,7 @@ public class LeshanBootstrapServerBuilder {
         }
 
         return createBootstrapServer(unsecuredEndpoint, securedEndpoint, configStore, securityStore, sessionManager,
-                bootstrapHandlerFactory, model, coapConfig);
+                bootstrapHandlerFactory, model, coapConfig, encoder, decoder);
     }
 
     /**
@@ -550,13 +584,15 @@ public class LeshanBootstrapServerBuilder {
      * @param bsHandlerFactory the factory used to create {@link BootstrapHandler}.
      * @param model the LWM2M model used mainly used for data encoding.
      * @param coapConfig the CoAP configuration.
+     * @param decoder decoder used to decode response payload.
+     * @param encoder encode used to encode request payload.
      * @return the LWM2M Bootstrap server.
      */
     protected LeshanBootstrapServer createBootstrapServer(CoapEndpoint unsecuredEndpoint, CoapEndpoint securedEndpoint,
             BootstrapConfigStore bsStore, BootstrapSecurityStore bsSecurityStore,
             BootstrapSessionManager bsSessionManager, BootstrapHandlerFactory bsHandlerFactory, LwM2mModel model,
-            NetworkConfig coapConfig) {
+            NetworkConfig coapConfig, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
         return new LeshanBootstrapServer(unsecuredEndpoint, securedEndpoint, bsStore, bsSecurityStore, bsSessionManager,
-                bsHandlerFactory, model, coapConfig);
+                bsHandlerFactory, model, coapConfig, encoder, decoder);
     }
 }
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/CaliforniumLwM2mBootstrapRequestSender.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/CaliforniumLwM2mBootstrapRequestSender.java
index 4d05a578..ac379341 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/CaliforniumLwM2mBootstrapRequestSender.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/CaliforniumLwM2mBootstrapRequestSender.java
@@ -22,8 +22,6 @@ import org.eclipse.californium.core.network.Endpoint;
 import org.eclipse.leshan.core.californium.AsyncRequestObserver;
 import org.eclipse.leshan.core.californium.SyncRequestObserver;
 import org.eclipse.leshan.core.model.LwM2mModel;
-import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
-import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
 import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
 import org.eclipse.leshan.core.request.DownlinkRequest;
@@ -42,14 +40,16 @@ public class CaliforniumLwM2mBootstrapRequestSender implements LwM2mBootstrapReq
     private final Endpoint nonSecureEndpoint;
     private final Endpoint secureEndpoint;
     private final LwM2mModel model;
-    private final LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder();
-    private final LwM2mNodeEncoder encoder = new DefaultLwM2mNodeEncoder();
+    private final LwM2mNodeDecoder decoder;
+    private final LwM2mNodeEncoder encoder;
 
-    public CaliforniumLwM2mBootstrapRequestSender(Endpoint secureEndpoint, Endpoint nonSecureEndpoint,
-            LwM2mModel model) {
+    public CaliforniumLwM2mBootstrapRequestSender(Endpoint secureEndpoint, Endpoint nonSecureEndpoint, LwM2mModel model,
+            LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
         this.secureEndpoint = secureEndpoint;
         this.nonSecureEndpoint = nonSecureEndpoint;
         this.model = model;
+        this.encoder = encoder;
+        this.decoder = decoder;
     }
 
     @Override
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/LeshanBootstrapServer.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/LeshanBootstrapServer.java
index e61d4c3e..a2483809 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/LeshanBootstrapServer.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/impl/LeshanBootstrapServer.java
@@ -23,6 +23,8 @@ import org.eclipse.californium.core.network.CoapEndpoint;
 import org.eclipse.californium.core.network.Endpoint;
 import org.eclipse.californium.core.network.config.NetworkConfig;
 import org.eclipse.leshan.core.model.LwM2mModel;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.LwM2mNodeEncoder;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
@@ -53,7 +55,8 @@ public class LeshanBootstrapServer implements LwM2mBootstrapServer {
 
     public LeshanBootstrapServer(CoapEndpoint unsecuredEndpoint, CoapEndpoint securedEndpoint, BootstrapConfigStore bsStore,
             BootstrapSecurityStore bsSecurityStore, BootstrapSessionManager bsSessionManager,
-            BootstrapHandlerFactory bsHandlerFactory, LwM2mModel model, NetworkConfig coapConfig) {
+            BootstrapHandlerFactory bsHandlerFactory, LwM2mModel model, NetworkConfig coapConfig,
+            LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
 
         Validate.notNull(bsStore, "bootstrap store must not be null");
         Validate.notNull(bsSessionManager, "session manager must not be null");
@@ -77,7 +80,8 @@ public class LeshanBootstrapServer implements LwM2mBootstrapServer {
             coapServer.addEndpoint(securedEndpoint);
 
         // create request sender
-        LwM2mBootstrapRequestSender requestSender = createRequestSender(securedEndpoint, unsecuredEndpoint, model);
+        LwM2mBootstrapRequestSender requestSender = createRequestSender(securedEndpoint, unsecuredEndpoint, model,
+                encoder, decoder);
 
         // create bootstrap resource
         CoapResource bsResource = createBootstrapResource(
@@ -90,8 +94,8 @@ public class LeshanBootstrapServer implements LwM2mBootstrapServer {
     }
 
     protected LwM2mBootstrapRequestSender createRequestSender(Endpoint securedEndpoint, Endpoint unsecuredEndpoint,
-            LwM2mModel model) {
-        return new CaliforniumLwM2mBootstrapRequestSender(securedEndpoint, unsecuredEndpoint, model);
+            LwM2mModel model, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
+        return new CaliforniumLwM2mBootstrapRequestSender(securedEndpoint, unsecuredEndpoint, model, encoder, decoder);
     }
 
     protected CoapResource createBootstrapResource(BootstrapHandler handler) {


Test patch:
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
index 57123cad..d4e5edb5 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
@@ -43,6 +43,8 @@ import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.model.ResourceModel;
 import org.eclipse.leshan.core.model.ResourceModel.Operations;
 import org.eclipse.leshan.core.model.ResourceModel.Type;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
@@ -157,6 +159,7 @@ public class IntegrationTestHelper {
 
         // Build Client
         LeshanClientBuilder builder = new LeshanClientBuilder(currentEndpointIdentifier.get());
+        builder.setDecoder(new DefaultLwM2mNodeDecoder(true));
         builder.setAdditionalAttributes(additionalAttributes);
         builder.setObjects(objects);
         client = builder.build();
@@ -171,6 +174,7 @@ public class IntegrationTestHelper {
 
     protected LeshanServerBuilder createServerBuilder() {
         LeshanServerBuilder builder = new LeshanServerBuilder();
+        builder.setEncoder(new DefaultLwM2mNodeEncoder(true));
         builder.setObjectModelProvider(new StaticModelProvider(createObjectModels()));
         builder.setLocalAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
         builder.setLocalSecureAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));