Repository: eclipse/leshan
Pull request #868 (2020-07-30 08:12:36)

Remove deprecated/backward compatibility code from leshan v2.0.0


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 06939c31..cff8d85d 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,7 +36,6 @@ import org.eclipse.leshan.client.californium.object.ObjectResource;
 import org.eclipse.leshan.client.californium.request.CaliforniumLwM2mRequestSender;
 import org.eclipse.leshan.client.engine.RegistrationEngine;
 import org.eclipse.leshan.client.engine.RegistrationEngineFactory;
-import org.eclipse.leshan.client.engine.RegistrationEngineFactory2;
 import org.eclipse.leshan.client.observer.LwM2mClientObserver;
 import org.eclipse.leshan.client.observer.LwM2mClientObserverDispatcher;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
@@ -94,14 +93,8 @@ public class LeshanClient implements LwM2mClient {
         bootstrapHandler = createBoostrapHandler(objectTree);
         endpointsManager = createEndpointsManager(localAddress, coapConfig, dtlsConfigBuilder, endpointFactory);
         requestSender = createRequestSender(endpointsManager, sharedExecutor);
-        if (engineFactory instanceof RegistrationEngineFactory2) {
-            engine = ((RegistrationEngineFactory2) engineFactory).createRegistratioEngine(endpoint, objectTree,
-                    endpointsManager, requestSender, bootstrapHandler, observers, additionalAttributes,
-                    bsAdditionalAttributes, sharedExecutor);
-        } else {
-            engine = engineFactory.createRegistratioEngine(endpoint, objectTree, endpointsManager, requestSender,
-                    bootstrapHandler, observers, additionalAttributes, sharedExecutor);
-        }
+        engine = engineFactory.createRegistratioEngine(endpoint, objectTree, endpointsManager, requestSender,
+                bootstrapHandler, observers, additionalAttributes, bsAdditionalAttributes, sharedExecutor);
 
         coapServer = createCoapServer(coapConfig, sharedExecutor);
         coapServer.add(createBootstrapResource(engine, bootstrapHandler));
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java
index f60d433b..5762c1ff 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java
@@ -30,7 +30,6 @@ import org.eclipse.californium.core.server.resources.Resource;
 import org.eclipse.leshan.client.californium.LwM2mClientCoapResource;
 import org.eclipse.leshan.client.engine.RegistrationEngine;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
-import org.eclipse.leshan.client.resource.LwM2mObjectEnabler2;
 import org.eclipse.leshan.client.resource.listener.ObjectListener;
 import org.eclipse.leshan.client.servers.ServerIdentity;
 import org.eclipse.leshan.core.Link;
@@ -70,16 +69,12 @@ import org.eclipse.leshan.core.response.ObserveResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteAttributesResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * A CoAP {@link Resource} in charge of handling requests targeting a lwM2M Object.
  */
 public class ObjectResource extends LwM2mClientCoapResource implements ObjectListener {
 
-    private static final Logger LOG = LoggerFactory.getLogger(ObjectResource.class);
-
     protected final LwM2mObjectEnabler nodeEnabler;
     protected final LwM2mNodeEncoder encoder;
     protected final LwM2mNodeDecoder decoder;
@@ -103,34 +98,27 @@ public class ObjectResource extends LwM2mClientCoapResource implements ObjectLis
         String URI = exchange.getRequestOptions().getUriPathString();
 
         if (exchange.getRequestOptions().getAccept() == MediaTypeRegistry.APPLICATION_LINK_FORMAT) {
-            // Manage Bootstrap Discover Request
             if (identity.isLwm2mBootstrapServer()) {
-                if (nodeEnabler instanceof LwM2mObjectEnabler2) {
-                    BootstrapDiscoverResponse response = ((LwM2mObjectEnabler2) nodeEnabler).discover(identity,
-                            new BootstrapDiscoverRequest(URI));
-                    if (response.getCode().isError()) {
-                        exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
-                    } else {
-                        exchange.respond(toCoapResponseCode(response.getCode()),
-                                Link.serialize(response.getObjectLinks()), MediaTypeRegistry.APPLICATION_LINK_FORMAT);
-                    }
-                    return;
+                // Manage Bootstrap Discover Request
+                BootstrapDiscoverResponse response = nodeEnabler.discover(identity, new BootstrapDiscoverRequest(URI));
+                if (response.getCode().isError()) {
+                    exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
+                } else {
+                    exchange.respond(toCoapResponseCode(response.getCode()), Link.serialize(response.getObjectLinks()),
+                            MediaTypeRegistry.APPLICATION_LINK_FORMAT);
                 }
+                return;
             } else {
-                LOG.warn(
-                        "Your LwM2mObjectEnabler2 {} does not support Bootstrap discover request, you should consider to make it implements LwM2mObjectEnabler2",
-                        nodeEnabler.getClass().getName());
-                // continue with classic discover for backward compatibility.
-            }
-            // Manage Discover Request
-            DiscoverResponse response = nodeEnabler.discover(identity, new DiscoverRequest(URI));
-            if (response.getCode().isError()) {
-                exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
-            } else {
-                exchange.respond(toCoapResponseCode(response.getCode()), Link.serialize(response.getObjectLinks()),
-                        MediaTypeRegistry.APPLICATION_LINK_FORMAT);
+                // Manage Discover Request
+                DiscoverResponse response = nodeEnabler.discover(identity, new DiscoverRequest(URI));
+                if (response.getCode().isError()) {
+                    exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
+                } else {
+                    exchange.respond(toCoapResponseCode(response.getCode()), Link.serialize(response.getObjectLinks()),
+                            MediaTypeRegistry.APPLICATION_LINK_FORMAT);
+                }
+                return;
             }
-            return;
         } else {
             // handle content format for Read and Observe Request
             ContentFormat requestedContentFormat = null;
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
index 1aba2e67..2b888b96 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
@@ -23,17 +23,13 @@ import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.client.observer.LwM2mClientObserver;
 import org.eclipse.leshan.client.request.LwM2mRequestSender;
 import org.eclipse.leshan.client.resource.LwM2mObjectTree;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
- * A default implementation of {@link RegistrationEngineFactory2}.
+ * A default implementation of {@link RegistrationEngineFactory}.
  * <p>
  * It create a {@link DefaultRegistrationEngine} which could be configured. Look at all setter available in this class.
  */
-public class DefaultRegistrationEngineFactory implements RegistrationEngineFactory2 {
-
-    private static final Logger LOG = LoggerFactory.getLogger(DefaultRegistrationEngineFactory.class);
+public class DefaultRegistrationEngineFactory implements RegistrationEngineFactory {
 
     private long requestTimeoutInMs = 2 * 60 * 1000l; // 2min in ms
     private long deregistrationTimeoutInMs = 1000; // 1s in ms
@@ -46,32 +42,15 @@ public class DefaultRegistrationEngineFactory implements RegistrationEngineFacto
     public DefaultRegistrationEngineFactory() {
     }
 
-    @Override
-    @Deprecated
-    public RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTree objectTree,
-            EndpointsManager endpointsManager, LwM2mRequestSender requestSender, BootstrapHandler bootstrapState,
-            LwM2mClientObserver observer, Map<String, String> additionalAttributes,
-            ScheduledExecutorService sharedExecutor) {
-        return null;
-    }
-
     @Override
     public RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTree objectTree,
             EndpointsManager endpointsManager, LwM2mRequestSender requestSender, BootstrapHandler bootstrapState,
             LwM2mClientObserver observer, Map<String, String> additionalAttributes,
             Map<String, String> bsAdditionalAttributes, ScheduledExecutorService sharedExecutor) {
-
-        RegistrationEngine engine = createRegistratioEngine(endpoint, objectTree, endpointsManager, requestSender,
-                bootstrapState, observer, additionalAttributes, sharedExecutor);
-        if (engine != null) {
-            LOG.warn("It seems you override a deprecated createRegistrationEngine method, you should use the new one");
-            return engine;
-        } else {
-            return new DefaultRegistrationEngine(endpoint, objectTree, endpointsManager, requestSender, bootstrapState,
-                    observer, additionalAttributes, bsAdditionalAttributes, sharedExecutor, requestTimeoutInMs,
-                    deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec, retryWaitingTimeInMs,
-                    communicationPeriodInMs, reconnectOnUpdate, resumeOnConnect);
-        }
+        return new DefaultRegistrationEngine(endpoint, objectTree, endpointsManager, requestSender, bootstrapState,
+                observer, additionalAttributes, bsAdditionalAttributes, sharedExecutor, requestTimeoutInMs,
+                deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec, retryWaitingTimeInMs, communicationPeriodInMs,
+                reconnectOnUpdate, resumeOnConnect);
     }
 
     /**
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory.java
index dc7a4268..e5fcd3f7 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory.java
@@ -26,13 +26,11 @@ import org.eclipse.leshan.client.resource.LwM2mObjectTree;
 
 /**
  * A factory for {@link RegistrationEngine}
- * 
- * Since 1.1 {@link RegistrationEngineFactory2} should be preferred.
  */
 public interface RegistrationEngineFactory {
 
     RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTree objectTree,
             EndpointsManager endpointsManager, LwM2mRequestSender requestSender, BootstrapHandler bootstrapState,
             LwM2mClientObserver observer, Map<String, String> additionalAttributes,
-            ScheduledExecutorService sharedExecutor);
+            Map<String, String> bsAdditionalAttributes, ScheduledExecutorService sharedExecutor);
 }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory2.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory2.java
deleted file mode 100644
index 51c156b7..00000000
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/RegistrationEngineFactory2.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2020 Sierra Wireless and others.
- * 
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- * 
- * The Eclipse Public License is available at
- *    http://www.eclipse.org/legal/epl-v20.html
- * and the Eclipse Distribution License is available at
- *    http://www.eclipse.org/org/documents/edl-v10.html.
- * 
- * Contributors:
- *     Sierra Wireless - initial API and implementation
- *******************************************************************************/
-package org.eclipse.leshan.client.engine;
-
-import java.util.Map;
-import java.util.concurrent.ScheduledExecutorService;
-
-import org.eclipse.leshan.client.EndpointsManager;
-import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
-import org.eclipse.leshan.client.observer.LwM2mClientObserver;
-import org.eclipse.leshan.client.request.LwM2mRequestSender;
-import org.eclipse.leshan.client.resource.LwM2mObjectTree;
-
-/**
- * A new version of {@link RegistrationEngineFactory}
- *
- * @since 1.1
- */
-public interface RegistrationEngineFactory2 extends RegistrationEngineFactory {
-
-    @Override
-    @Deprecated
-    RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTree objectTree,
-            EndpointsManager endpointsManager, LwM2mRequestSender requestSender, BootstrapHandler bootstrapState,
-            LwM2mClientObserver observer, Map<String, String> additionalAttributes,
-            ScheduledExecutorService sharedExecutor);
-
-    RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTree objectTree,
-            EndpointsManager endpointsManager, LwM2mRequestSender requestSender, BootstrapHandler bootstrapState,
-            LwM2mClientObserver observer, Map<String, String> additionalAttributes,
-            Map<String, String> bsAdditionalAttributes, ScheduledExecutorService sharedExecutor);
-}
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/BaseObjectEnabler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/BaseObjectEnabler.java
index a26738d9..c1399262 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/BaseObjectEnabler.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/BaseObjectEnabler.java
@@ -64,10 +64,10 @@ import org.eclipse.leshan.core.response.WriteAttributesResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
 
 /**
- * A abstract implementation of {@link LwM2mObjectEnabler2}. It could be use as base for any {@link LwM2mObjectEnabler2}
+ * A abstract implementation of {@link LwM2mObjectEnabler}. It could be use as base for any {@link LwM2mObjectEnabler}
  * implementation.
  */
-public abstract class BaseObjectEnabler implements LwM2mObjectEnabler2 {
+public abstract class BaseObjectEnabler implements LwM2mObjectEnabler {
 
     protected final int id;
     protected final TransactionalObjectListener transactionalListener;
@@ -292,8 +292,7 @@ public abstract class BaseObjectEnabler implements LwM2mObjectEnabler2 {
         return doDelete(identity, request);
     }
 
-    // TODO fix in 2.0 this should be protected not public
-    public BootstrapDeleteResponse doDelete(ServerIdentity identity, BootstrapDeleteRequest request) {
+    protected BootstrapDeleteResponse doDelete(ServerIdentity identity, BootstrapDeleteRequest request) {
         // This should be a not implemented error, but this is not defined in the spec.
         return BootstrapDeleteResponse.internalServerError("not implemented");
     }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler.java
index e4f4234e..20d190d7 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler.java
@@ -23,6 +23,7 @@ import org.eclipse.leshan.client.resource.listener.ObjectListener;
 import org.eclipse.leshan.client.servers.ServerIdentity;
 import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
+import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;
 import org.eclipse.leshan.core.request.BootstrapWriteRequest;
 import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.CreateRequest;
@@ -35,6 +36,7 @@ import org.eclipse.leshan.core.request.ReadRequest;
 import org.eclipse.leshan.core.request.WriteAttributesRequest;
 import org.eclipse.leshan.core.request.WriteRequest;
 import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
+import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
 import org.eclipse.leshan.core.response.BootstrapWriteResponse;
 import org.eclipse.leshan.core.response.CreateResponse;
 import org.eclipse.leshan.core.response.DeleteResponse;
@@ -82,6 +84,8 @@ public interface LwM2mObjectEnabler {
 
     DiscoverResponse discover(ServerIdentity identity, DiscoverRequest request);
 
+    BootstrapDiscoverResponse discover(ServerIdentity identity, BootstrapDiscoverRequest request);
+
     ObserveResponse observe(ServerIdentity identity, ObserveRequest request);
 
     void addListener(ObjectListener listener);
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler2.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler2.java
deleted file mode 100644
index 07e33375..00000000
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/LwM2mObjectEnabler2.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 Sierra Wireless and others.
- * 
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- * 
- * The Eclipse Public License is available at
- *    http://www.eclipse.org/legal/epl-v20.html
- * and the Eclipse Distribution License is available at
- *    http://www.eclipse.org/org/documents/edl-v10.html.
- *
- * Contributors:
- *     Sierra Wireless - initial API and implementation
- *     Achim Kraus (Bosch Software Innovations GmbH) - use ServerIdentity
- *******************************************************************************/
-package org.eclipse.leshan.client.resource;
-
-import org.eclipse.leshan.client.servers.ServerIdentity;
-import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;
-import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
-
-/**
- * A new version of {@link LwM2mObjectEnabler} which support bootstrap discover.
- * 
- * @since 1.1
- */
-public interface LwM2mObjectEnabler2 extends LwM2mObjectEnabler {
-
-    BootstrapDiscoverResponse discover(ServerIdentity identity, BootstrapDiscoverRequest request);
-
-}
diff --git a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
index a73df30c..a0f645f0 100644
--- a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
+++ b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
@@ -26,7 +26,6 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.ECPublicKey;
 import java.util.Arrays;
@@ -476,7 +475,7 @@ public class LeshanClientDemo {
             PrivateKey clientPrivateKey, PublicKey clientPublicKey, PublicKey serverPublicKey,
             X509Certificate clientCertificate, X509Certificate serverCertificate, Float latitude, Float longitude,
             float scaleFactor, boolean supportOldFormat, boolean supportDeprecatedCiphers, boolean reconnectOnUpdate,
-            boolean forceFullhandshake, String modelsFolderPath) throws CertificateEncodingException {
+            boolean forceFullhandshake, String modelsFolderPath) throws Exception {
 
         locationInstance = new MyLocation(latitude, longitude, scaleFactor);
 
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/DDFFileParser.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/DDFFileParser.java
index c70ab9b4..55f7e48e 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/DDFFileParser.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/DDFFileParser.java
@@ -20,7 +20,6 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import javax.xml.parsers.DocumentBuilder;
@@ -64,63 +63,16 @@ public class DDFFileParser {
         this.ddfValidator = ddfValidator;
     }
 
-    /**
-     * @deprecated use {@link #parseEx(File)}
-     */
-    @Deprecated
-    public List<ObjectModel> parse(File ddfFile) {
-        try (InputStream input = new FileInputStream(ddfFile)) {
-            return parse(input, ddfFile.getName());
-        } catch (Exception e) {
-            LOG.error("Could not parse the resource definition file " + ddfFile.getName(), e);
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * @deprecated use {@link #parseEx(InputStream, String)}
-     */
-    @Deprecated
-    public List<ObjectModel> parse(InputStream inputStream, String streamName) {
-        streamName = streamName == null ? "" : streamName;
-
-        LOG.debug("Parsing DDF file {}", streamName);
-
-        try {
-            // Parse XML file
-            DocumentBuilder builder = factory.newDocumentBuilder();
-            Document document = builder.parse(inputStream);
-
-            // Validate XML against Schema
-            if (ddfValidator != null) {
-                ddfValidator.validate(document);
-            }
-
-            // Build list of ObjectModel
-            ArrayList<ObjectModel> objects = new ArrayList<>();
-            NodeList nodeList = document.getDocumentElement().getElementsByTagName("Object");
-            for (int i = 0; i < nodeList.getLength(); i++) {
-                objects.add(parseObject(nodeList.item(i), streamName, false));
-            }
-            return objects;
-        } catch (Exception e) {
-            LOG.error("Could not parse the resource definition file " + streamName, e);
-        }
-        return Collections.emptyList();
-    }
-
     /**
      * Parse a DDF file.
      * 
      * @throws InvalidDDFFileException if DDF file is not a valid.
      * @throws IOException see {@link FileInputStream#FileInputStream(File)} or
      *         {@link DocumentBuilder#parse(InputStream)}
-     * 
-     * @since 1.1
      */
-    public List<ObjectModel> parseEx(File ddfFile) throws InvalidDDFFileException, IOException {
+    public List<ObjectModel> parse(File ddfFile) throws InvalidDDFFileException, IOException {
         try (InputStream input = new FileInputStream(ddfFile)) {
-            return parseEx(input, ddfFile.getName());
+            return parse(input, ddfFile.getName());
         }
     }
 
@@ -130,10 +82,8 @@ public class DDFFileParser {
      * @throws InvalidDDFFileException if DDF file is not a valid.
      * @throws IOException see {@link FileInputStream#FileInputStream(File)} or
      *         {@link DocumentBuilder#parse(InputStream)}
-     * 
-     * @since 1.1
      */
-    public List<ObjectModel> parseEx(InputStream inputStream, String streamName)
+    public List<ObjectModel> parse(InputStream inputStream, String streamName)
             throws InvalidDDFFileException, IOException {
         streamName = streamName == null ? "" : streamName;
 
@@ -162,14 +112,11 @@ public class DDFFileParser {
         }
     }
 
-    private ObjectModel parseObject(Node object, String streamName, boolean raiseException)
-            throws InvalidDDFFileException {
+    private ObjectModel parseObject(Node object, String streamName, boolean validate) throws InvalidDDFFileException {
 
         Node objectType = object.getAttributes().getNamedItem("ObjectType");
-        if (objectType == null || !"MODefinition".equals(objectType.getTextContent())) {
-            // TODO change for v2.0 : we keep it for backward compatibility
-            // test must be done only if validation is needed
-            handleError(raiseException,
+        if (validate && (objectType == null || !"MODefinition".equals(objectType.getTextContent()))) {
+            throw new InvalidDDFFileException(
                     "Object element in %s MUST have a ObjectType attribute equals to 'MODefinition'.", streamName);
         }
 
@@ -177,12 +124,12 @@ public class DDFFileParser {
         String name = null;
         String description = null;
         String version = ObjectModel.DEFAULT_VERSION;
-        boolean multiple = false;
-        boolean mandatory = false;
+        Boolean multiple = null;
+        Boolean mandatory = null;
         List<ResourceModel> resources = new ArrayList<>();
         String urn = null;
         String description2 = null;
-        String lwm2mVersion = null;
+        String lwm2mVersion = ObjectModel.DEFAULT_VERSION;
 
         for (int i = 0; i < object.getChildNodes().getLength(); i++) {
             Node field = object.getChildNodes().item(i);
@@ -208,13 +155,6 @@ public class DDFFileParser {
                     multiple = true;
                 } else if ("Single".equals(field.getTextContent())) {
                     multiple = false;
-                } else {
-                    // TODO remove for v2.0 : we keep it for backward compatibility
-                    // test will be done in DDFFileValidator or ObjectModelValidatorif validation if needed
-                    handleError(raiseException,
-                            "Invalid Value for [MultipleInstances] field in %s. It MUST be 'Multiple' or 'Single' but it was %s.",
-                            field.getTextContent(), streamName);
-                    multiple = false;
                 }
                 break;
             case "Mandatory":
@@ -222,13 +162,6 @@ public class DDFFileParser {
                     mandatory = true;
                 } else if ("Optional".equals(field.getTextContent())) {
                     mandatory = false;
-                } else {
-                    // TODO remove for v2.0 : we keep it for backward compatibility
-                    // test will be done in DDFFileValidator or ObjectModelValidator if validation if needed
-                    handleError(raiseException,
-                            "Invalid Value for [Mandatory] field in %s. It MUST be 'Mandatory' or 'Optional' but it was %s.",
-                            field.getTextContent(), streamName);
-                    mandatory = false;
                 }
                 break;
             case "Resources":
@@ -238,12 +171,7 @@ public class DDFFileParser {
                         continue;
 
                     if (item.getNodeName().equals("Item")) {
-                        resources.add(this.parseResource(item, streamName, raiseException));
-                    } else {
-                        // TODO remove for v2.0 : we keep it for backward compatibility
-                        // test will be done in DDFFileValidator if validation if needed
-                        handleError(raiseException, "Unexpected resources element [%s] in %s : it will be ignored.",
-                                item.getNodeName(), streamName);
+                        resources.add(this.parseResource(item, streamName));
                     }
                 }
                 break;
@@ -251,16 +179,13 @@ public class DDFFileParser {
                 urn = field.getTextContent();
                 break;
             case "LWM2MVersion":
-                lwm2mVersion = field.getTextContent();
+                if (!StringUtils.isEmpty(field.getTextContent()))
+                    lwm2mVersion = field.getTextContent();
                 break;
             case "Description2":
                 description2 = field.getTextContent();
                 break;
             default:
-                // TODO remove for v2.0 : we keep it for backward compatibility
-                // test will be done in DDFFileValidator if validation if needed
-                handleError(raiseException, "Unexpected object element [%s] in %s : it will be ignored.",
-                        field.getNodeName(), streamName);
                 break;
             }
         }
@@ -270,14 +195,13 @@ public class DDFFileParser {
 
     }
 
-    private ResourceModel parseResource(Node item, String streamName, boolean raiseException)
-            throws DOMException, InvalidDDFFileException {
+    private ResourceModel parseResource(Node item, String streamName) throws DOMException, InvalidDDFFileException {
 
         Integer id = Integer.valueOf(item.getAttributes().getNamedItem("ID").getTextContent());
         String name = null;
-        Operations operations = Operations.NONE;
-        boolean multiple = false;
-        boolean mandatory = false;
+        Operations operations = null;
+        Boolean multiple = false;
+        Boolean mandatory = false;
         Type type = null;
         String rangeEnumeration = null;
         String units = null;
@@ -296,6 +220,8 @@ public class DDFFileParser {
                 String strOp = field.getTextContent();
                 if (strOp != null && !strOp.isEmpty()) {
                     operations = Operations.valueOf(strOp);
+                } else {
+                    operations = Operations.NONE;
                 }
                 break;
             case "MultipleInstances":
@@ -303,13 +229,6 @@ public class DDFFileParser {
                     multiple = true;
                 } else if ("Single".equals(field.getTextContent())) {
                     multiple = false;
-                } else {
-                    // TODO remove for v2.0 : we keep it for backward compatibility
-                    // test will be done in DDFFileValidator or ObjectModelValidatorif validation if needed
-                    handleError(raiseException,
-                            "Invalid Value for [MultipleInstances] field in %s. It MUST be 'Multiple' or 'Single' but it was %s : We will consider it as a 'Single' value.",
-                            field.getTextContent(), streamName);
-                    multiple = false;
                 }
                 break;
             case "Mandatory":
@@ -317,13 +236,6 @@ public class DDFFileParser {
                     mandatory = true;
                 } else if ("Optional".equals(field.getTextContent())) {
                     mandatory = false;
-                } else {
-                    // TODO remove for v2.0 : we keep it for backward compatibility
-                    // test will be done in DDFFileValidator or ObjectModelValidatorif validation if needed
-                    handleError(raiseException,
-                            "Invalid Value for [Mandatory] field in %s. It MUST be 'Mandatory' or 'Optional' but it was %s :s We will consider it as a 'Optional' value.",
-                            field.getTextContent(), streamName);
-                    mandatory = false;
                 }
                 break;
             case "Type":
@@ -350,13 +262,9 @@ public class DDFFileParser {
                     type = Type.OBJLNK;
                     break;
                 case "":
-                    type = null;
+                    type = Type.NONE;
                     break;
                 default:
-                    // TODO remove for v2.0 : we keep it for backward compatibility
-                    // test will be done in DDFFileValidator validation if needed
-                    handleError(raiseException, "Unexpected type value [%s] in %s : no type assigned",
-                            field.getTextContent(), streamName);
                     break;
                 }
                 break;
@@ -370,37 +278,9 @@ public class DDFFileParser {
                 description = field.getTextContent();
                 break;
             default:
-                // TODO remove for v2.0 : we keep it for backward compatibility
-                // test will be done in DDFFileValidator validation if needed
-                handleError(raiseException, "Unexpected resource element [%s %s] in %s : it will be ignored.",
-                        field.getNodeName(), field.getNodeType(), streamName);
                 break;
             }
         }
-
-        // TODO remove for v2.0 : we keep it for backward compatibility
-        // test will be done in ObjetModelValidator if validation if needed
-        if (!raiseException) {
-            if (operations.isExecutable() && type != null) {
-                LOG.warn("Model for Resource {}({}) in {} is invalid : an executable resource MUST NOT have a type({})",
-                        name, id, streamName, type);
-            } else if (!operations.isExecutable() && type == null) {
-                LOG.warn(
-                        "Model for Resource {}({}) in {} is invalid : a none executable resource MUST have a type. We will consider it as String.",
-                        name, id, streamName, type);
-                type = Type.STRING;
-            }
-        }
-
         return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description);
     }
-
-    private void handleError(boolean raiseException, String Message, Object... args) throws InvalidDDFFileException {
-        String msg = String.format(Message, args);
-        if (raiseException) {
-            throw new InvalidDDFFileException(msg);
-        } else {
-            LOG.warn(msg);
-        }
-    }
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/DefaultObjectModelValidator.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/DefaultObjectModelValidator.java
index a8720001..1ba6fc72 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/DefaultObjectModelValidator.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/DefaultObjectModelValidator.java
@@ -17,6 +17,8 @@ package org.eclipse.leshan.core.model;
 
 import java.util.List;
 
+import org.eclipse.leshan.core.model.ResourceModel.Type;
+
 public class DefaultObjectModelValidator implements ObjectModelValidator {
 
     @Override
@@ -35,11 +37,15 @@ public class DefaultObjectModelValidator implements ObjectModelValidator {
      * @throws InvalidModelException is raised when {@link ObjectModel} is Invalid
      */
     public void validate(ObjectModel object, String modelName) throws InvalidModelException {
+        // validate name
         if (object.name == null || object.name.isEmpty()) {
             throw new InvalidModelException(
                     "Model for Object (%d) in %s is invalid : Object name MUST NOT be null or empty", object.id,
                     modelName);
         }
+
+        // validate id
+        validateModelFieldNotNull(object, modelName, object.id, "id");
         if (!(0 <= object.id && object.id <= 42768)) {
             throw new InvalidModelException(
                     "Model for Object (%d) in %s is invalid : Object id MUST be between 0 and 42768", object.id,
@@ -50,8 +56,15 @@ public class DefaultObjectModelValidator implements ObjectModelValidator {
                     "Model for Object (%d) in %s is invalid : Object id is in reserved range (1024-2047)", object.id,
                     modelName);
         }
-        validateVersion(object.version, object, modelName);
+
+        // validate others fields
+        validateModelFieldNotNull(object, modelName, object.multiple, "multiple");
+        validateModelFieldNotNull(object, modelName, object.mandatory, "mandatory");
+        validateVersion(object.version, object, "version", modelName);
+        validateVersion(object.lwm2mVersion, object, "lwm2mVersion", modelName);
         validateURN(object.urn, object, modelName);
+
+        // validate resources
         if (object.resources == null || object.resources.isEmpty()) {
             throw new InvalidModelException(
                     "Model for Object %s(%d) in %s is invalid : Resource lists MUST NOT be null or empty", object.name,
@@ -71,22 +84,32 @@ public class DefaultObjectModelValidator implements ObjectModelValidator {
      * @throws InvalidModelException is raised when {@link ResourceModel} is Invalid
      */
     public void validate(ResourceModel resource, String modelName) throws InvalidModelException {
+        // validate name
         if (resource.name == null || resource.name.isEmpty()) {
             throw new InvalidModelException(
                     "Model for Resource (%d) in %s is invalid : resource name MUST NOT be null or empty", resource.id,
                     modelName);
         }
+        // validate id
+        validateResourceFieldNotNull(resource, modelName, resource.id, "id");
         if (!(0 <= resource.id && resource.id <= 32768)) {
             throw new InvalidModelException(
                     "Model for Resource (%d) in %s is invalid : Resource id MUST be between 0 and 32768", resource.id,
                     modelName);
         }
-        // TODO in 2.0 : type must be NONE
-        if (resource.operations.isExecutable() && resource.type != null) {
+
+        // validate others fields
+        validateResourceFieldNotNull(resource, modelName, resource.operations, "operations");
+        validateResourceFieldNotNull(resource, modelName, resource.multiple, "multiple");
+        validateResourceFieldNotNull(resource, modelName, resource.mandatory, "mandatory");
+
+        // validate type
+        validateResourceFieldNotNull(resource, modelName, resource.type, "type");
+        if (resource.operations.isExecutable() && resource.type != Type.NONE) {
             throw new InvalidModelException(
                     "Model for Resource %s(%d) in %s is invalid : an executable resource MUST NOT have a type(%s)",
                     resource.name, resource.id, modelName, resource.type);
-        } else if (!resource.operations.isExecutable() && resource.type == null) {
+        } else if (!resource.operations.isExecutable() && resource.type == Type.NONE) {
             throw new InvalidModelException(
                     "Model for Resource %s(%d) in %s is invalid : a none executable resource MUST have a type.",
                     resource.name, resource.id, modelName, resource.type);
@@ -121,14 +144,14 @@ public class DefaultObjectModelValidator implements ObjectModelValidator {
 
         if (urnParts.length == 6) {
             String version = urnParts[5];
-            String expectedVersion = object.getVersion();
+            String expectedVersion = object.version;
             if (!expectedVersion.equals(version)) {
                 throw new InvalidModelException(
                         "Model for Object %s(%d) in %s is invalid : URN (%s) version MUST be equals to object version (%s)",
                         object.name, object.id, modelName, urn, expectedVersion);
             }
         } else {
-            if (!ObjectModel.DEFAULT_VERSION.equals(object.getVersion())) {
+            if (!ObjectModel.DEFAULT_VERSION.equals(object.version)) {
                 throw new InvalidModelException(
                         "Model for Object %s(%d) in %s is invalid : URN (%s) version MUST be present as object version is not %s",
                         object.name, object.id, modelName, urn, ObjectModel.DEFAULT_VERSION);
@@ -136,28 +159,46 @@ public class DefaultObjectModelValidator implements ObjectModelValidator {
         }
     }
 
-    protected void validateVersion(String version, ObjectModel object, String modelName) throws InvalidModelException {
+    protected void validateVersion(String version, ObjectModel object, String fieldName, String modelName)
+            throws InvalidModelException {
         if (version == null || version.isEmpty())
-            return;
+            throw new InvalidModelException("Model for Object (%d) in %s is invalid : %s  MUST NOT be null or empty",
+                    object.id, modelName, fieldName);
         String[] versionPart = version.split("\\.");
         if (versionPart.length != 2) {
             throw new InvalidModelException(
-                    "Model for Object %s(%d) in %s is invalid : Version (%s) MUST be composed of 2 parts", object.name,
-                    object.id, modelName, version);
+                    "Model for Object %s(%d) in %s is invalid : %s (%s) MUST be composed of 2 parts", object.name,
+                    object.id, modelName, fieldName, version);
         }
         for (int i = 0; i < 2; i++) {
             try {
                 int parsedInt = Integer.parseInt(versionPart[i]);
                 if (parsedInt < 0) {
                     throw new InvalidModelException(
-                            "Model for Object %s(%d) in %s is invalid : Version (%s) part %d (%s) is not a valid integer",
-                            object.name, object.id, modelName, version, i + 1, versionPart[i]);
+                            "Model for Object %s(%d) in %s is invalid : %s (%s) part %d (%s) is not a valid integer",
+                            object.name, object.id, modelName, fieldName, version, i + 1, versionPart[i]);
                 }
             } catch (Exception e) {
                 throw new InvalidModelException(
-                        "Model for Object %s(%d) in %s is invalid : Version (%s) part %d (%s) is not a valid integer",
-                        object.name, object.id, modelName, version, i + 1, versionPart);
+                        "Model for Object %s(%d) in %s is invalid : %s (%s) part %d (%s) is not a valid integer",
+                        object.name, object.id, modelName, fieldName, version, i + 1, versionPart);
             }
         }
     }
+
+    protected void validateResourceFieldNotNull(ResourceModel resource, String modelName, Object fieldValue,
+            String fieldName) throws InvalidModelException {
+        if (fieldValue == null) {
+            throw new InvalidModelException("Model for Resource (%d) in %s is invalid : '%s' field MUST NOT be null",
+                    resource.id, modelName, fieldName);
+        }
+    }
+
+    protected void validateModelFieldNotNull(ObjectModel object, String modelName, Object fieldValue, String fieldName)
+            throws InvalidModelException {
+        if (fieldValue == null) {
+            throw new InvalidModelException("Model for Object (%d) in %s is invalid : '%s' field MUST NOT be null",
+                    object.id, modelName, fieldName);
+        }
+    }
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/LwM2mModelRepository.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/LwM2mModelRepository.java
index fb58a01e..b48f2f68 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/LwM2mModelRepository.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/LwM2mModelRepository.java
@@ -55,7 +55,7 @@ public class LwM2mModelRepository {
                 ObjectModel old = map.put(key, model);
                 if (old != null) {
                     LOG.debug("Model already exists for object {} in version {}. Overriding it.", model.id,
-                            model.getVersion());
+                            model.version);
                 }
             }
             objects = Collections.unmodifiableMap(map);
@@ -70,7 +70,7 @@ public class LwM2mModelRepository {
     }
 
     private String getKey(ObjectModel objectModel) {
-        return getKey(objectModel.id, objectModel.getVersion());
+        return getKey(objectModel.id, objectModel.version);
     }
 
     private String getKey(Integer objectId, String version) {
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectLoader.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectLoader.java
index 88d97d67..6511dbdb 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectLoader.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectLoader.java
@@ -20,11 +20,8 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import org.eclipse.leshan.core.util.StringUtils;
@@ -48,23 +45,14 @@ public class ObjectLoader {
 
         // standard objects
         LOG.debug("Loading OMA standard object models");
-        models.addAll(loadDdfResources("/models/", ddfpaths));
-
+        try {
+            models.addAll(loadDdfResources("/models/", ddfpaths));
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to load models", e);
+        }
         return models;
     }
 
-    /**
-     * Load object definitions from DDF or JSON files.
-     * 
-     * @param modelDir the directory containing the object definition files.
-     * 
-     * @deprecated use {@link #loadObjectsFromDir(File)} instead
-     */
-    @Deprecated
-    public static List<ObjectModel> load(File modelDir) {
-        return loadObjectsFromDir(modelDir);
-    }
-
     /**
      * Load object definition from DDF file.
      * <p>
@@ -74,16 +62,14 @@ public class ObjectLoader {
      * 
      * @param input An inputStream to a DDF file.
      * @param streamName A name for the stream used for logging only
+     * 
+     * @throws InvalidDDFFileException if DDF file is invalid
+     * @throws IOException
      */
-    public static List<ObjectModel> loadDdfFile(InputStream input, String streamName) {
-        try {
-            DDFFileParser ddfFileParser = new DDFFileParser();
-            return ddfFileParser.parseEx(input, streamName);
-        } catch (InvalidDDFFileException | IOException e) {
-            // TODO change for 2.0 : we should raise an IOException
-            LOG.warn("Unable to parse ddf file {}", streamName, e);
-        }
-        return Collections.emptyList();
+    public static List<ObjectModel> loadDdfFile(InputStream input, String streamName)
+            throws InvalidDDFFileException, IOException {
+        DDFFileParser ddfFileParser = new DDFFileParser();
+        return ddfFileParser.parse(input, streamName);
     }
 
     /**
@@ -125,7 +111,7 @@ public class ObjectLoader {
     public static List<ObjectModel> loadDdfFile(InputStream input, String streamName, DDFFileValidator ddfValidator,
             ObjectModelValidator modelValidator) throws InvalidModelException, InvalidDDFFileException, IOException {
         DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
-        List<ObjectModel> models = ddfFileParser.parseEx(input, streamName);
+        List<ObjectModel> models = ddfFileParser.parse(input, streamName);
         if (modelValidator != null) {
             modelValidator.validate(models, streamName);
         }
@@ -138,19 +124,19 @@ public class ObjectLoader {
      * It should be used to load DDF embedded with your application bundle (e.g. jar, war, ...)
      * <p>
      * Models are not validate if you want to ensure that model are valid use
-     * {@link #loadDdfFile(InputStream, String, boolean)} or
-     * {@link #loadDdfFile(InputStream, String, DDFFileValidator, ObjectModelValidator)}
+     * {@link #loadDdfResources(String, String[], boolean)}
+     * {@link #loadDdfResources(String, String[], DDFFileValidator, ObjectModelValidator)}
      * 
      * @param path directory path to the DDF files
      * @param filenames names of all the DDF files
+     * 
+     * @throws InvalidDDFFileException if DDF file is invalid
+     * @throws InvalidModelException if model is invalid
+     * @throws IOException
      */
-    public static List<ObjectModel> loadDdfResources(String path, String[] filenames) {
-        try {
-            return loadDdfResources(path, filenames, false);
-        } catch (IOException | InvalidModelException | InvalidDDFFileException e) {
-            throw new IllegalStateException("Unable to load model", e);
-        }
-
+    public static List<ObjectModel> loadDdfResources(String path, String[] filenames)
+            throws IOException, InvalidModelException, InvalidDDFFileException {
+        return loadDdfResources(path, filenames, false);
     }
 
     /**
@@ -201,9 +187,7 @@ public class ObjectLoader {
             String fullpath = StringUtils.removeEnd(path, "/") + "/" + StringUtils.removeStart(filename, "/");
             InputStream input = ObjectLoader.class.getResourceAsStream(fullpath);
             if (input != null) {
-                try (Reader reader = new InputStreamReader(input)) {
-                    models.addAll(loadDdfFile(input, fullpath, ddfValidator, modelValidator));
-                }
+                models.addAll(loadDdfFile(input, fullpath, ddfValidator, modelValidator));
             } else {
                 throw new FileNotFoundException(String.format("%s not found", fullpath));
             }
@@ -218,23 +202,63 @@ public class ObjectLoader {
      * It should be used to load DDF embedded with your application bundle (e.g. jar, war, ...)
      * <p>
      * Models are not validate if you want to ensure that model are valid use
-     * {@link #loadDdfFile(InputStream, String, boolean)} or
-     * {@link #loadDdfFile(InputStream, String, DDFFileValidator, ObjectModelValidator)}
+     * {@link #loadDdfResources(String[], boolean)} or
+     * {@link #loadDdfResources(String[], DDFFileValidator, ObjectModelValidator)}
      * 
      * @param paths An array of paths to DDF files.
+     * 
+     * @throws InvalidDDFFileException if DDF file is invalid
+     * @throws IOException
+     * @throws InvalidModelException
      */
-    public static List<ObjectModel> loadDdfResources(String[] paths) {
+    public static List<ObjectModel> loadDdfResources(String[] paths)
+            throws InvalidDDFFileException, IOException, InvalidModelException {
+        return loadDdfResources(paths, false);
+    }
+
+    /**
+     * Load object definition from DDF resources following rules of {@link Class#getResourceAsStream(String)}.
+     * <p>
+     * It should be used to load DDF embedded with your application bundle (e.g. jar, war, ...)
+     * <p>
+     * 
+     * @param paths An array of paths to DDF files.
+     * @param validate true if you want model validation. Validation is not free and it could make sense to not validate
+     *        model if you already trust it.
+     * 
+     * @throws InvalidDDFFileException if DDF file is invalid
+     * @throws IOException
+     */
+    public static List<ObjectModel> loadDdfResources(String[] paths, boolean validate)
+            throws IOException, InvalidModelException, InvalidDDFFileException {
+        return loadDdfResources(paths, validate ? new DefaultDDFFileValidator() : null,
+                validate ? new DefaultObjectModelValidator() : null);
+    }
+
+    /**
+     * Load object definition from DDF resources following rules of {@link Class#getResourceAsStream(String)}.
+     * <p>
+     * It should be used to load DDF embedded with your application bundle (e.g. jar, war, ...)
+     * <p>
+     * 
+     * @param paths An array of paths to DDF files.
+     * @param ddfValidator a validator used to validate DDF files, see {@link DefaultDDFFileValidator}. If {@code null}
+     *        then there will be no validation.
+     * @param modelValidator an Object model validator to ensure model is valid, see
+     *        {@link DefaultObjectModelValidator}. If {@code null} then there will be no validation.
+     * 
+     * @throws InvalidDDFFileException if DDF file is invalid
+     * @throws IOException
+     */
+    public static List<ObjectModel> loadDdfResources(String[] paths, DDFFileValidator ddfValidator,
+            ObjectModelValidator modelValidator) throws IOException, InvalidModelException, InvalidDDFFileException {
         List<ObjectModel> models = new ArrayList<>();
         for (String path : paths) {
             InputStream input = ObjectLoader.class.getResourceAsStream(path);
             if (input != null) {
-                try (Reader reader = new InputStreamReader(input)) {
-                    models.addAll(loadDdfFile(input, path));
-                } catch (IOException e) {
-                    throw new IllegalStateException(String.format("Unable to load model %s", path), e);
-                }
+                models.addAll(loadDdfFile(input, path, ddfValidator, modelValidator));
             } else {
-                throw new IllegalStateException(String.format("Unable to load model %s", path));
+                throw new FileNotFoundException(String.format("%s not found", path));
             }
         }
         return models;
@@ -243,9 +267,8 @@ public class ObjectLoader {
     /**
      * Load object definitions from directory.
      * <p>
-     * Models are not validate if you want to ensure that model are valid use
-     * {@link #loadDdfFile(InputStream, String, boolean)} or
-     * {@link #loadDdfFile(InputStream, String, DDFFileValidator, ObjectModelValidator)}
+     * Models are not validate if you want to ensure that model are valid use {@link #loadObjectsFromDir(File, boolean)}
+     * or {@link #loadObjectsFromDir(File, DDFFileValidator, ObjectModelValidator)}
      * 
      * @param modelsDir the directory containing all the ddf file definition.
      */
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectModel.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectModel.java
index 577a9e16..d25309d5 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectModel.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ObjectModel.java
@@ -21,10 +21,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.eclipse.leshan.core.LwM2m;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 /**
  * An object description.
  * 
@@ -33,20 +29,17 @@ import org.slf4j.LoggerFactory;
  */
 public class ObjectModel {
 
-    private static final Logger LOG = LoggerFactory.getLogger(ObjectModel.class);
-
     public static final String DEFAULT_VERSION = "1.0";
 
     private static final int OMA_OBJECT_MIN_ID = 0;
     private static final int OMA_OBJECT_MAX_ID = 1023;
 
-    // TODO in version 2.0 all field should be null-able and ObjectModelValidator should be responsible to validate it.
-    public final int id;
+    public final Integer id;
     public final String name;
     public final String description;
     public final String version;
-    public final boolean multiple;
-    public final boolean mandatory;
+    public final Boolean multiple;
+    public final Boolean mandatory;
     /** @since 1.1 */
     public final String urn;
     /** @since 1.1 */
@@ -56,20 +49,18 @@ public class ObjectModel {
 
     public final Map<Integer, ResourceModel> resources; // resources by ID
 
-    public ObjectModel(int id, String name, String description, String version, boolean multiple, boolean mandatory,
+    public ObjectModel(Integer id, String name, String description, String version, Boolean multiple, Boolean mandatory,
             ResourceModel... resources) {
         this(id, name, description, version, multiple, mandatory, Arrays.asList(resources));
     }
 
-    public ObjectModel(int id, String name, String description, String version, boolean multiple, boolean mandatory,
+    public ObjectModel(Integer id, String name, String description, String version, Boolean multiple, Boolean mandatory,
             Collection<ResourceModel> resources) {
-        this(id, name, description, version, multiple, mandatory, resources, URN.generateURN(id, version), null, "");
+        this(id, name, description, version, multiple, mandatory, resources, URN.generateURN(id, version),
+                DEFAULT_VERSION, "");
     }
 
-    /**
-     * @since 1.1
-     */
-    public ObjectModel(int id, String name, String description, String version, boolean multiple, boolean mandatory,
+    public ObjectModel(Integer id, String name, String description, String version, Boolean multiple, Boolean mandatory,
             Collection<ResourceModel> resources, String urn, String lwm2mVersion, String description2) {
 
         this.id = id;
@@ -86,7 +77,8 @@ public class ObjectModel {
         for (ResourceModel resource : resources) {
             ResourceModel old = resourcesMap.put(resource.id, resource);
             if (old != null) {
-                LOG.debug("Model already exists for resource {} of object {}. Overriding it.", resource.id, id);
+                throw new IllegalStateException(String
+                        .format("Model already exists for resource %d of object %d. Overriding it.", resource.id, id));
             }
             resourcesMap.put(resource.id, resource);
         }
@@ -97,29 +89,6 @@ public class ObjectModel {
         return id >= OMA_OBJECT_MIN_ID && id <= OMA_OBJECT_MAX_ID;
     }
 
-    /**
-     * @return the version and if the version is null or empty return the default value 1.0
-     * @see ObjectModel#DEFAULT_VERSION
-     */
-    public String getVersion() {
-        if (version == null || version.isEmpty()) {
-            return ObjectModel.DEFAULT_VERSION;
-        }
-        return version;
-    }
-
-    /**
-     * @return the LWM2M version and if the LWM2M version is null or empty return the default value 1.0
-     * @see LwM2m#VERSION
-     * @since 1.1
-     */
-    public String getLwM2mVersion() {
-        if (lwm2mVersion == null || lwm2mVersion.isEmpty()) {
-            return LwM2m.VERSION;
-        }
-        return lwm2mVersion;
-    }
-
     @Override
     public String toString() {
         return String.format(
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ResourceModel.java b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ResourceModel.java
index fa9fcd16..e29f36e2 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/model/ResourceModel.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/model/ResourceModel.java
@@ -39,23 +39,21 @@ public class ResourceModel {
         }
     }
 
-    // TODO in version 2.0 : NONE should be added.
     public enum Type {
-        STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK
+        NONE, STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK
     }
 
-    // TODO in version 2.0 all field should be null-able and ObjectModelValidator should be responsible to validate it.
-    public final int id;
+    public final Integer id;
     public final String name;
     public final Operations operations;
-    public final boolean multiple;
-    public final boolean mandatory;
+    public final Boolean multiple;
+    public final Boolean mandatory;
     public final Type type;
     public final String rangeEnumeration;
     public final String units;
     public final String description;
 
-    public ResourceModel(int id, String name, Operations operations, boolean multiple, boolean mandatory, Type type,
+    public ResourceModel(Integer id, String name, Operations operations, Boolean multiple, Boolean mandatory, Type type,
             String rangeEnumeration, String units, String description) {
         this.id = id;
         this.name = name;
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonDecoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonDecoder.java
index 8675ba93..def3e6fd 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonDecoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonDecoder.java
@@ -418,7 +418,7 @@ public class LwM2mNodeJsonDecoder {
     public static Type getResourceType(LwM2mPath rscPath, LwM2mModel model, JsonArrayEntry resourceElt) {
         // Use model type in priority
         ResourceModel rscDesc = model.getResourceModel(rscPath.getObjectId(), rscPath.getResourceId());
-        if (rscDesc != null && rscDesc.type != null)
+        if (rscDesc != null)
             return rscDesc.type;
 
         // Then json type
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonEncoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonEncoder.java
index ddc7c0d5..63b6e02a 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonEncoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/json/LwM2mNodeJsonEncoder.java
@@ -226,7 +226,7 @@ public class LwM2mNodeJsonEncoder {
         private void setResourceValue(Object value, Type type, JsonArrayEntry jsonResource, LwM2mPath resourcePath) {
             LOG.trace("Encoding value {} in JSON", value);
 
-            if (type == null) {
+            if (type == null || type == Type.NONE) {
                 throw new CodecException(
                         "Unable to encode value for resource {} without type(probably a executable one)", resourcePath);
             }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/text/LwM2mNodeTextDecoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/text/LwM2mNodeTextDecoder.java
index 917a96bd..d64227a2 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/text/LwM2mNodeTextDecoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/text/LwM2mNodeTextDecoder.java
@@ -41,7 +41,7 @@ public class LwM2mNodeTextDecoder {
         ResourceModel rDesc = model.getResourceModel(path.getObjectId(), path.getResourceId());
 
         String strValue = content != null ? new String(content, StandardCharsets.UTF_8) : "";
-        if (rDesc != null && rDesc.type != null) {
+        if (rDesc != null) {
             return LwM2mSingleResource.newResource(path.getResourceId(), parseTextValue(strValue, rDesc.type, path),
                     rDesc.type);
         } else {
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvDecoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvDecoder.java
index c3506dc9..6bfe78a1 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvDecoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvDecoder.java
@@ -34,9 +34,9 @@ import org.eclipse.leshan.core.node.LwM2mResource;
 import org.eclipse.leshan.core.node.LwM2mSingleResource;
 import org.eclipse.leshan.core.node.codec.CodecException;
 import org.eclipse.leshan.core.tlv.Tlv;
+import org.eclipse.leshan.core.tlv.Tlv.TlvType;
 import org.eclipse.leshan.core.tlv.TlvDecoder;
 import org.eclipse.leshan.core.tlv.TlvException;
-import org.eclipse.leshan.core.tlv.Tlv.TlvType;
 import org.eclipse.leshan.core.util.Hex;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -262,7 +262,7 @@ public class LwM2mNodeTlvDecoder {
 
     public static Type getResourceType(LwM2mPath rscPath, LwM2mModel model) throws CodecException {
         ResourceModel rscDesc = model.getResourceModel(rscPath.getObjectId(), rscPath.getResourceId());
-        if (rscDesc == null || rscDesc.type == null) {
+        if (rscDesc == null) {
             LOG.trace("unknown type for resource : {}", rscPath);
             // no resource description... opaque
             return Type.OPAQUE;
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvEncoder.java b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvEncoder.java
index 6d9e1a1a..b697f5d0 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvEncoder.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/node/codec/tlv/LwM2mNodeTlvEncoder.java
@@ -170,7 +170,7 @@ public class LwM2mNodeTlvEncoder {
 
         private byte[] encodeTlvValue(Object value, Type type, LwM2mPath path) {
             LOG.trace("Encoding value {} in TLV", value);
-            if (type == null) {
+            if (type == null || type == Type.NONE) {
                 throw new CodecException(
                         "Unable to encode value for resource {} without type(probably a executable one)", path);
             }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDiscoverRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDiscoverRequest.java
index efe8ac53..12be1e51 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDiscoverRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDiscoverRequest.java
@@ -18,8 +18,6 @@ package org.eclipse.leshan.core.request;
 import org.eclipse.leshan.core.node.LwM2mPath;
 import org.eclipse.leshan.core.request.exception.InvalidRequestException;
 import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * A Lightweight M2M request to let LWM2M Bootstrap server discovering which LwM2M Objects and Object Instances are
@@ -29,8 +27,6 @@ import org.slf4j.LoggerFactory;
  */
 public class BootstrapDiscoverRequest extends AbstractDownlinkRequest<BootstrapDiscoverResponse> {
 
-    private static final Logger LOG = LoggerFactory.getLogger(BootstrapDiscoverRequest.class);
-
     /**
      * Creates a request for discovering all objects and instances supported by the client.
      */
@@ -72,12 +68,6 @@ public class BootstrapDiscoverRequest extends AbstractDownlinkRequest<BootstrapD
 
     @Override
     public void accept(DownlinkRequestVisitor visitor) {
-        if (visitor instanceof DownlinkRequestVisitor2) {
-            ((DownlinkRequestVisitor2) visitor).visit(this);
-        } else {
-            LOG.warn(
-                    "Your visitor {} does not support Bootstrap discover request, you should consider to make it implements DownlinkRequestVisitor2",
-                    this.getClass().getName());
-        }
+        visitor.visit(this);
     }
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownLinkRequestVisitorAdapter.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownLinkRequestVisitorAdapter.java
index 83d15d80..f0f6e36c 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownLinkRequestVisitorAdapter.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownLinkRequestVisitorAdapter.java
@@ -57,6 +57,10 @@ public class DownLinkRequestVisitorAdapter implements DownlinkRequestVisitor {
     public void visit(CancelObservationRequest request) {
     }
 
+    @Override
+    public void visit(BootstrapDiscoverRequest request) {
+    }
+
     @Override
     public void visit(BootstrapWriteRequest request) {
     }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor.java
index 900bcd19..17292d66 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor.java
@@ -37,6 +37,8 @@ public interface DownlinkRequestVisitor {
 
     void visit(CancelObservationRequest request);
 
+    void visit(BootstrapDiscoverRequest request);
+
     void visit(BootstrapWriteRequest request);
 
     void visit(BootstrapDeleteRequest request);
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor2.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor2.java
deleted file mode 100644
index 30683f42..00000000
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/DownlinkRequestVisitor2.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2020 Sierra Wireless and others.
- * 
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- * 
- * The Eclipse Public License is available at
- *    http://www.eclipse.org/legal/epl-v20.html
- * and the Eclipse Distribution License is available at
- *    http://www.eclipse.org/org/documents/edl-v10.html.
- * 
- * Contributors:
- *     Sierra Wireless - initial API and implementation
- *******************************************************************************/
-package org.eclipse.leshan.core.request;
-
-/**
- * A new version of {@link DownlinkRequestVisitor} which supports {@link BootstrapDiscoverRequest}
- * 
- * @since 1.1
- */
-public interface DownlinkRequestVisitor2 extends DownlinkRequestVisitor {
-
-    void visit(BootstrapDiscoverRequest request);
-}
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java
index b8ce736b..9b0ddff1 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java
@@ -38,7 +38,7 @@ import org.eclipse.leshan.core.request.CreateRequest;
 import org.eclipse.leshan.core.request.DeleteRequest;
 import org.eclipse.leshan.core.request.DiscoverRequest;
 import org.eclipse.leshan.core.request.DownlinkRequest;
-import org.eclipse.leshan.core.request.DownlinkRequestVisitor2;
+import org.eclipse.leshan.core.request.DownlinkRequestVisitor;
 import org.eclipse.leshan.core.request.ExecuteRequest;
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.request.ObserveRequest;
@@ -53,7 +53,7 @@ import org.eclipse.leshan.server.californium.observation.ObserveUtil;
  * <p>
  * Call <code>CoapRequestBuilder#visit(lwm2mRequest)</code>, then get the result using {@link #getRequest()}
  */
-public class CoapRequestBuilder implements DownlinkRequestVisitor2 {
+public class CoapRequestBuilder implements DownlinkRequestVisitor {
 
     private Request coapRequest;
 
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java
index 0ba8c496..315ee9cb 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java
@@ -37,7 +37,7 @@ import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.CreateRequest;
 import org.eclipse.leshan.core.request.DeleteRequest;
 import org.eclipse.leshan.core.request.DiscoverRequest;
-import org.eclipse.leshan.core.request.DownlinkRequestVisitor2;
+import org.eclipse.leshan.core.request.DownlinkRequestVisitor;
 import org.eclipse.leshan.core.request.ExecuteRequest;
 import org.eclipse.leshan.core.request.LwM2mRequest;
 import org.eclipse.leshan.core.request.ObserveRequest;
@@ -71,7 +71,7 @@ import org.slf4j.LoggerFactory;
  * 
  * @param <T> the type of the response to build.
  */
-public class LwM2mResponseBuilder<T extends LwM2mResponse> implements DownlinkRequestVisitor2 {
+public class LwM2mResponseBuilder<T extends LwM2mResponse> implements DownlinkRequestVisitor {
 
     private static final Logger LOG = LoggerFactory.getLogger(LwM2mResponseBuilder.class);
 
@@ -117,13 +117,8 @@ public class LwM2mResponseBuilder<T extends LwM2mResponse> implements DownlinkRe
             // handle success response:
             Link[] links;
             if (MediaTypeRegistry.APPLICATION_LINK_FORMAT != coapResponse.getOptions().getContentFormat()) {
-                // TODO change this behavior for the 2.0 :
-                // throw new InvalidResponseException("Client [%s] returned unexpected content format [%s] for [%s]",
-                // clientEndpoint, coapResponse.getOptions().getContentFormat(), request);
-                LOG.debug("Expected LWM2M Client [{}] to return application/link-format [{}] content but got [{}]",
-                        clientEndpoint, MediaTypeRegistry.APPLICATION_LINK_FORMAT,
-                        coapResponse.getOptions().getContentFormat());
-                links = new Link[] {}; // empty list
+                throw new InvalidResponseException("Client [%s] returned unexpected content format [%s] for [%s]",
+                        clientEndpoint, coapResponse.getOptions().getContentFormat(), request);
             } else {
                 links = Link.parse(coapResponse.getPayload());
             }
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSession.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSession.java
index 1233d4c1..54d4fbdf 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSession.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSession.java
@@ -15,6 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.server.bootstrap;
 
+import org.eclipse.leshan.core.request.BootstrapRequest;
 import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.Identity;
 
@@ -22,7 +23,7 @@ import org.eclipse.leshan.core.request.Identity;
  * Represent a single Bootstrapping session.
  * 
  * Should be created by {@link BootstrapSessionManager} implementations in
- * {@link BootstrapSessionManager#begin(String,Identity)}.
+ * {@link BootstrapSessionManager#begin(BootstrapRequest, Identity)}.
  */
 public interface BootstrapSession {
 
@@ -36,6 +37,11 @@ public interface BootstrapSession {
      */
     String getEndpoint();
 
+    /**
+     * @return the bootstrap request which initiate the session.
+     */
+    BootstrapRequest getBootstrapRequest();
+
     /**
      * @return the network identity of the LwM2M client.
      */
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager.java
index a02d7b1a..c03d2678 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager.java
@@ -15,6 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.server.bootstrap;
 
+import org.eclipse.leshan.core.request.BootstrapRequest;
 import org.eclipse.leshan.core.request.DownlinkRequest;
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.response.LwM2mResponse;
@@ -25,8 +26,6 @@ import org.eclipse.leshan.core.response.LwM2mResponse;
  * This class is responsible to accept or refuse to start new {@link BootstrapSession}. The session also contain the
  * ContentFormat which will be used to send Bootstrap Write Request.
  * 
- * Since 1.1, you should rather use a {@link BootstrapSessionManager2}
- * 
  * @see DefaultBootstrapSessionManager
  * @see BootstrapSession
  */
@@ -57,14 +56,14 @@ public interface BootstrapSessionManager {
 
     /**
      * Starts a bootstrapping session for an endpoint. In particular, this is responsible for authorizing the endpoint
-     * if applicable
+     * if applicable.
      * 
-     * @param endpoint the endpoint of the client.
+     * @param request the bootstrap request which initiates the session.
      * @param clientIdentity the {@link Identity} of the client.
      * 
      * @return a BootstrapSession, possibly authorized.
      */
-    public BootstrapSession begin(String endpoint, Identity clientIdentity);
+    public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity);
 
     /**
      * Called when we receive a successful response to a request.
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager2.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager2.java
deleted file mode 100644
index e22d8ba0..00000000
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapSessionManager2.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Sierra Wireless and others.
- * 
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- * 
- * The Eclipse Public License is available at
- *    http://www.eclipse.org/legal/epl-v20.html
- * and the Eclipse Distribution License is available at
- *    http://www.eclipse.org/org/documents/edl-v10.html.
- * 
- * Contributors:
- *     Sierra Wireless - initial API and implementation
- *******************************************************************************/
-package org.eclipse.leshan.server.bootstrap;
-
-import org.eclipse.leshan.core.request.BootstrapRequest;
-import org.eclipse.leshan.core.request.Identity;
-
-/**
- * A new version of {@link BootstrapSessionManager}.
- * 
- * @since 1.1
- */
-public interface BootstrapSessionManager2 extends BootstrapSessionManager {
-
-    @Override
-    @Deprecated
-    BootstrapSession begin(String endpoint, Identity clientIdentity);
-
-    /**
-     * Starts a bootstrapping session for an endpoint. In particular, this is responsible for authorizing the endpoint
-     * if applicable.
-     * 
-     * @param request the bootstrap request which initiates the session.
-     * @param clientIdentity the {@link Identity} of the client.
-     * 
-     * @return a BootstrapSession, possibly authorized.
-     * 
-     * @since 1.1
-     */
-    public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity);
-}
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapHandler.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapHandler.java
index c87f02f1..f2a223e4 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapHandler.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapHandler.java
@@ -82,12 +82,7 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
 
         // Start session, checking the BS credentials
         final BootstrapSession session;
-        // @since 1.1
-        if (sessionManager instanceof BootstrapSessionManager2) {
-            session = ((BootstrapSessionManager2) sessionManager).begin(request, sender);
-        } else {
-            session = this.sessionManager.begin(endpoint, sender);
-        }
+        session = sessionManager.begin(request, sender);
 
         if (!session.isAuthorized()) {
             sessionManager.failed(session, UNAUTHORIZED);
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSession.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSession.java
index 41757528..3df6a711 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSession.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSession.java
@@ -36,21 +36,6 @@ public class DefaultBootstrapSession implements BootstrapSession {
 
     private volatile boolean cancelled = false;
 
-    /**
-     * Create a {@link DefaultBootstrapSession} using default {@link ContentFormat#TLV} content format and using
-     * <code>System.currentTimeMillis()</code> to set the creation time.
-     * 
-     * @param endpoint The endpoint of the device.
-     * @param identity The transport layer identity of the device.
-     * @param authorized True if device is authorized to bootstrap.
-     * 
-     * @deprecated use {@link #DefaultBootstrapSession(BootstrapRequest, Identity, boolean)} instead
-     */
-    @Deprecated
-    public DefaultBootstrapSession(String endpoint, Identity identity, boolean authorized) {
-        this(endpoint, identity, authorized, ContentFormat.TLV);
-    }
-
     /**
      * Create a {@link DefaultBootstrapSession} using default {@link ContentFormat#TLV} content format and using
      * <code>System.currentTimeMillis()</code> to set the creation time.
@@ -65,22 +50,6 @@ public class DefaultBootstrapSession implements BootstrapSession {
         this(request, identity, authorized, ContentFormat.TLV);
     }
 
-    /**
-     * Create a {@link DefaultBootstrapSession} using <code>System.currentTimeMillis()</code> to set the creation time.
-     * 
-     * @param endpoint The endpoint of the device.
-     * @param identity The transport layer identity of the device.
-     * @param authorized True if device is authorized to bootstrap.
-     * @param contentFormat The content format to use to write object.
-     * 
-     * @deprecated use {@link #DefaultBootstrapSession(BootstrapRequest, Identity, boolean, ContentFormat)} instead
-     */
-    @Deprecated
-    public DefaultBootstrapSession(String endpoint, Identity identity, boolean authorized,
-            ContentFormat contentFormat) {
-        this(endpoint, identity, authorized, contentFormat, System.currentTimeMillis());
-    }
-
     /**
      * Create a {@link DefaultBootstrapSession} using <code>System.currentTimeMillis()</code> to set the creation time.
      * 
@@ -96,29 +65,6 @@ public class DefaultBootstrapSession implements BootstrapSession {
         this(request, identity, authorized, contentFormat, System.currentTimeMillis());
     }
 
-    /**
-     * Create a {@link DefaultBootstrapSession}.
-     * 
-     * @param endpoint The endpoint of the device.
-     * @param identity The transport layer identity of the device.
-     * @param authorized True if device is authorized to bootstrap.
-     * @param contentFormat The content format to use to write object.
-     * @param creationTime The creation time of this session in ms.
-     * 
-     * @deprecated use {@link #DefaultBootstrapSession(BootstrapRequest, Identity, boolean, ContentFormat, long)}
-     */
-    @Deprecated
-    public DefaultBootstrapSession(String endpoint, Identity identity, boolean authorized, ContentFormat contentFormat,
-            long creationTime) {
-        this.id = RandomStringUtils.random(10, true, true);
-        this.request = null;
-        this.endpoint = endpoint;
-        this.identity = identity;
-        this.authorized = authorized;
-        this.contentFormat = contentFormat;
-        this.creationTime = creationTime;
-    }
-
     /**
      * Create a {@link DefaultBootstrapSession}.
      * 
@@ -172,6 +118,7 @@ public class DefaultBootstrapSession implements BootstrapSession {
         return creationTime;
     }
 
+    @Override
     public BootstrapRequest getBootstrapRequest() {
         return request;
     }
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
index 3c5167c2..c9dbb7de 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
  * <p>
  * Nothing specific is done on session's end.
  */
-public class DefaultBootstrapSessionManager implements BootstrapSessionManager2 {
+public class DefaultBootstrapSessionManager implements BootstrapSessionManager {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultBootstrapSessionManager.class);
 
@@ -63,28 +63,16 @@ public class DefaultBootstrapSessionManager implements BootstrapSessionManager2
         this.securityChecker = securityChecker;
     }
 
-    @Override
-    @Deprecated
-    public BootstrapSession begin(String endpoint, Identity clientIdentity) {
-        return null;
-    }
-
     @Override
     public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
-        BootstrapSession session = begin(request.getEndpointName(), clientIdentity);
-        if (session != null) {
-            LOG.warn("It seems you override a deprecated begin method, you should use the new one");
+        boolean authorized;
+        if (bsSecurityStore != null) {
+            List<SecurityInfo> securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
+            authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
         } else {
-            boolean authorized;
-            if (bsSecurityStore != null) {
-                List<SecurityInfo> securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
-                authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity,
-                        securityInfos);
-            } else {
-                authorized = true;
-            }
-            session = new DefaultBootstrapSession(request, clientIdentity, authorized);
+            authorized = true;
         }
+        DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized);
         LOG.trace("Bootstrap session started : {}", session);
         return session;
     }
diff --git a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/Ddf2JsonGenerator.java b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/Ddf2JsonGenerator.java
index d6da80cd..830d5d63 100644
--- a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/Ddf2JsonGenerator.java
+++ b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/Ddf2JsonGenerator.java
@@ -61,7 +61,7 @@ public class Ddf2JsonGenerator {
         DDFFileParser ddfParser = new DDFFileParser();
         for (File f : files) {
             if (f.canRead()) {
-                objectModels.addAll(ddfParser.parseEx(f));
+                objectModels.addAll(ddfParser.parse(f));
             }
         }
 
diff --git a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/ObjectModelSerDes.java b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/ObjectModelSerDes.java
index 7a2b95a5..745a86ed 100644
--- a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/ObjectModelSerDes.java
+++ b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/model/ObjectModelSerDes.java
@@ -36,13 +36,15 @@ public class ObjectModelSerDes extends JsonSerDes<ObjectModel> {
     @Override
     public JsonObject jSerialize(ObjectModel m) {
         final JsonObject o = Json.object();
-        o.add("name", m.name);
         o.add("id", m.id);
+        o.add("name", m.name);
         o.add("instancetype", m.multiple ? "multiple" : "single");
         o.add("mandatory", m.mandatory);
-        if (!ObjectModel.DEFAULT_VERSION.equals(m.version))
-            o.add("version", m.version);
+        o.add("urn", m.urn);
+        o.add("version", m.version);
+        o.add("lwm2mversion", m.lwm2mVersion);
         o.add("description", m.description);
+        o.add("description2", m.description2);
 
         // sort resources value
         List<ResourceModel> resourceSpecs = new ArrayList<>(m.resources.values());
@@ -76,12 +78,22 @@ public class ObjectModelSerDes extends JsonSerDes<ObjectModel> {
 
         String name = o.getString("name", null);
         String instancetype = o.getString("instancetype", null);
-        boolean mandatory = o.getBoolean("mandatory", false);
-        String description = o.getString("description", null);
+        if (!instancetype.equals("multiple") && !instancetype.equals("single")) {
+            throw new JsonException("Invalid value for 'instancetype' : must be multiple or single");
+        }
+        Boolean mandatory = null;
+        if (o.get("mandatory") != null) {
+            mandatory = o.asBoolean();
+        }
+        String urn = o.getString("urn", null);
         String version = o.getString("version", ObjectModel.DEFAULT_VERSION);
+        String lwm2mVersion = o.getString("lwm2mversion", ObjectModel.DEFAULT_VERSION);
+        String description = o.getString("description", null);
+        String description2 = o.getString("description2", null);
+
         List<ResourceModel> resourceSpecs = resourceModelSerDes.deserialize(o.get("resourcedefs").asArray());
 
         return new ObjectModel(id, name, description, version, "multiple".equals(instancetype), mandatory,
-                resourceSpecs);
+                resourceSpecs, urn, lwm2mVersion, description2);
     }
 }


Test patch:
diff --git a/leshan-server-core/src/test/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerTest.java b/leshan-server-core/src/test/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerTest.java
index 3900494d..6f7d875e 100644
--- a/leshan-server-core/src/test/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerTest.java
+++ b/leshan-server-core/src/test/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerTest.java
@@ -222,8 +222,8 @@ public class BootstrapHandlerTest {
         }
 
         @Override
-        public BootstrapSession begin(String endpoint, Identity clientIdentity) {
-            lastSession = new DefaultBootstrapSession(new BootstrapRequest(endpoint), clientIdentity, authorized);
+        public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
+            lastSession = new DefaultBootstrapSession(request, clientIdentity, authorized);
             return lastSession;
         }