Repository: eclipse/leshan
Pull request #883 (2020-08-31 17:22:38)

Refactor BootstrapConfigStore to use a list of "request"
This aims to implements : #437


sbernard31: First thought about this : 

Currently,  to define what should be done during a bootstrap session, we need to create a [`BootstrapConfig`](https://github.com/eclipse/leshan/blob/leshan-1.1.0/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfig.java).

This is a bean in which you are able to define this actions in this order : 
- delete anything,
- write security objects,
- write server objects,
- write ACLs.

This has several limitation like :
- you can not choose the order of the requests,
- you can not write something else than security/server/acl,
- if models change you need to upgrade/change the `BootstrapConfig` bean and probably serialization. 

So replacing `BootstrapConfig` bean by a list of BootstrapDownlinkRequest sounds to resolve all those limitations, but this brings new ones : 
1. This makes bootstrap config validation very difficult. (it sounds difficult to have an equivalent for [ConfigurationChecker](https://github.com/eclipse/leshan/blob/leshan-1.1.0/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/ConfigurationChecker.java))
2. Creating `LwM2mNode` content for `WriteRequest` is more painful, complicated and error prone than creating `BootstrapConfig`

So even if this is not so much satisfying to me, maybe we should keep both API... 
For issue 2. I also try a kind of experimental [API](https://github.com/eclipse/leshan/blob/bootstrap_refactoring/leshan-core/src/main/java/org/eclipse/leshan/core/node/LwM2mNodeBuilder.java) to make more simple LwM2node creation. 
At usage it looks like this : https://github.com/eclipse/leshan/pull/883/commits/7db537392c9a3381b0cc69f3172f4062788686ee#diff-cdf3b6c29cc5769b5054f2d8a660d7afR465

Working on this makes me think about other issues relative to Bootstrap server : 
1. Object Model Versioning : unlike to registration, devices does not begin bootstrap session by saying which version it supports. This could be a problem to found object models to use. (Actually I'm not sure if this is a real problem, if object models are backward compatible maybe have the last version of the object is enough)
=> If a solution is needed, we could imagine to start session with a Bootstrap discover request to know which version to use.
2. If bootstrap server want to apply a config dynamically  regarding of device states using Discover or Read request this is currently not possible. 
=> Maybe a solution could be that `BootstrapConfigurationStore` can return read/discover requests OR delete/write requests. If read/discover is returned, once we get the result we call again `BootstrapConfigurationStore` with read/discover responses in parameter. If delete/write is returned, once we apply all requests we send a bootstrap finished. 


sbernard31: @dachaac do you have any opinions on this ?
dachaac: @sbernard31 kinda sounds into right direction and there might be a needs in certain installations to do custom object writing during bootstrapping -- at least it gives flexibility. What one need to make sure that all operations has been done successfully before bootstrap finish is issued.

About the implementation you have here -- didn't have time to look at it yet -- I will rebase my work on the master now that unsigned integer is there and push it for you for a preview on how I tried to go in between the bootstrapping process and connecting to lwm2m server. With that done we can see if there is something that should be taken into account and I can hopefully give some more though on this issue then.

dachaac: @sbernard31 rebased my https://github.com/dachaac/leshan/commits/est-support branch.

It still needs tidying up and has some traces of ugliness while hacking in ;) and is missing CoAP-EST server stuff as I am wondering currently how to split it and where and what kind of interfaces would be good -- but I would say that is not related to this issue as such.
sbernard31: > About the implementation you have here -- didn't have time to look at it yet

No problem I was mainly interested about your opinion on the idea itself and the new issues it could bring. (The code is currently no so clean it's more a kind of draft)
sbernard31: I cleaned my code and limit the scope of this PR by letting the `bsserver-demo` untouched.

The new `BootstrapConfigurationStore`  replace the old `BootstrapConfigStore`(deprecated now).
About backward compatibility, `BootstrapConfigurationStoreAdapter` can be used.

About API issues raised in **https://github.com/eclipse/leshan/pull/883#issuecomment-687043565**
If we still want to keep the old `BootstrapConfig` bean for convenience, there is a `BootstrapUtil` utility to facilitate conversion to requests.
Another solution could be to explore a new API  like : this experimental [`LwM2mNodeBuilder`](https://github.com/eclipse/leshan/blob/node_builder/leshan-core/src/main/java/org/eclipse/leshan/core/node/LwM2mNodeBuilder.java) ...

About `bsserver-demo`, I didn't touch it for now, meaning it is using lot of deprecated stuff...
sbernard31: I think this is in reviewable state now.
dachaac: Lots of changes were just change from on way to another "in cosmetic category". Actual change seems to be the FIFO that gets generated from BootstrapConfig which is probably OK but then again you had BootstrapConfiguration which was planned to replace it?

I am not sure if BootstrapConfiguration is actually serializable in case it needs to be stored in database for persistence?

Or should this just be runtime thing and it gets generated from other config/code?
sbernard31: > I am not sure if BootstrapConfiguration is actually serializable in case it needs to be stored in database for persistence?

This is not serializable as java.io.Serializable meaning, but this is just a list of bean so we should be able to serialize this list of requests.

> or should this just be runtime thing and it gets generated from other config/code?

Both are possible.
E.g :  In our case in production we don't persist BootstrapConfig or BootstrapConfiguration we create it from data extracted from different places

> Lots of changes were just change from on way to another "in cosmetic category". Actual change seems to be the FIFO that gets generated from BootstrapConfig which is probably OK but then again you had BootstrapConfiguration which was planned to replace it?

My first idea was to replace BootstrapConfiguration by BootstrapConfig to bring more flexibility.
But this brings new issues (see https://github.com/eclipse/leshan/pull/883#issuecomment-687043565)
So for now I didn't get rid off old `BootstrapConfig` (but if we can I would prefer) or maybe we need both, one for convenience on main bootstrap objects (security, server) and the other for all other objects. 
dachaac: Ok. I don't see problems with the changes in here.

Just a comment on deprecated stuff -- Both LWM2M Server Demo and LWM2M Bootstrap Server Demo has a Web UI. They are built with different frameworks. 

LWM2M Server is utilizing AngularJS which is going for EOL:
https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c

LWM2M Bootstrap server then again is utilizing Riot.

On my other changes I just tried to adapt what ever was used but was a bit puzzled why the difference.

Do you have some plans on this sector?
sbernard31: > Both LWM2M Server Demo and LWM2M Bootstrap Server Demo has a Web UI. They are built with different frameworks.

As demos are not the priority,  we don't invest as many time we would like.
There is a lot of technical debt and the more we add features to it the more it is hard to migrate.
I will create a dedicated issue about current identified problem with our demos.

>  why the difference.

This is just historical reasons + lack of time.
`Angular` was chosen at the beginning, I don't know why (I was not in the project at this time) but the team was really disappointing by it. So when we create the bootstrap server demo we try something else : `Riot`.
My personal feeling is `Riot` is not so bad, but maybe not enough popular and so could be an obstacle for contribution.
sbernard31: see https://github.com/eclipse/leshan/issues/907

Patch:
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
index 317fee80..89f9bdc4 100755
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
@@ -46,6 +46,7 @@ import org.eclipse.leshan.core.model.ObjectLoader;
 import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.model.StaticModel;
 import org.eclipse.leshan.core.util.SecurityUtil;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStoreAdapter;
 import org.eclipse.leshan.server.bootstrap.demo.servlet.BootstrapServlet;
 import org.eclipse.leshan.server.bootstrap.demo.servlet.ServerServlet;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
@@ -330,7 +331,7 @@ public class LeshanBootstrapServerDemo {
         // Prepare and start bootstrap server
         LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
         JSONFileBootstrapStore bsStore = new JSONFileBootstrapStore(configFilename);
-        builder.setConfigStore(bsStore);
+        builder.setConfigStore(new BootstrapConfigurationStoreAdapter(bsStore));
         builder.setSecurityStore(new BootstrapConfigSecurityStore(bsStore));
         builder.setModel(new StaticModel(models));
 
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDeleteRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDeleteRequest.java
index d4643ffb..a3252009 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDeleteRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDeleteRequest.java
@@ -22,7 +22,8 @@ import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
 /**
  * A LWM2M request for deleting object instances during the bootstrap phase.
  */
-public class BootstrapDeleteRequest extends AbstractDownlinkRequest<BootstrapDeleteResponse> {
+public class BootstrapDeleteRequest extends AbstractDownlinkRequest<BootstrapDeleteResponse>
+        implements BootstrapDownlinkRequest<BootstrapDeleteResponse> {
 
     /**
      * Creates a request for deleting all Instances of all Objects in the LwM2M Client (except LwM2M Bootstrap-Server
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 12be1e51..ceda3266 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
@@ -25,7 +25,8 @@ import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
  * 
  * @since 1.1
  */
-public class BootstrapDiscoverRequest extends AbstractDownlinkRequest<BootstrapDiscoverResponse> {
+public class BootstrapDiscoverRequest extends AbstractDownlinkRequest<BootstrapDiscoverResponse>
+        implements BootstrapDownlinkRequest<BootstrapDiscoverResponse> {
 
     /**
      * Creates a request for discovering all objects and instances supported by the client.
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDownlinkRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDownlinkRequest.java
new file mode 100644
index 00000000..961e393c
--- /dev/null
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapDownlinkRequest.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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;
+
+import org.eclipse.leshan.core.response.LwM2mResponse;
+
+public interface BootstrapDownlinkRequest<T extends LwM2mResponse> extends DownlinkRequest<T> {
+}
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapFinishRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapFinishRequest.java
index ba05a6dd..e50df006 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapFinishRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapFinishRequest.java
@@ -21,7 +21,7 @@ import org.eclipse.leshan.core.response.BootstrapFinishResponse;
 /**
  * Request sent when bootstrap session is finished
  */
-public class BootstrapFinishRequest implements DownlinkRequest<BootstrapFinishResponse> {
+public class BootstrapFinishRequest implements BootstrapDownlinkRequest<BootstrapFinishResponse> {
 
     @Override
     public LwM2mPath getPath() {
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapWriteRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapWriteRequest.java
index fa6a4a0e..08259037 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapWriteRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BootstrapWriteRequest.java
@@ -27,7 +27,8 @@ import org.eclipse.leshan.core.response.BootstrapWriteResponse;
 /**
  * A LWM2M request for writing object instances during the bootstrap phase.
  */
-public class BootstrapWriteRequest extends AbstractDownlinkRequest<BootstrapWriteResponse> {
+public class BootstrapWriteRequest extends AbstractDownlinkRequest<BootstrapWriteResponse>
+        implements BootstrapDownlinkRequest<BootstrapWriteResponse> {
 
     private final LwM2mNode node;
     private final ContentFormat contentFormat;
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServer.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServer.java
index fa6b21fc..03e02b35 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServer.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServer.java
@@ -30,7 +30,7 @@ import org.eclipse.leshan.core.util.Validate;
 import org.eclipse.leshan.server.Destroyable;
 import org.eclipse.leshan.server.Startable;
 import org.eclipse.leshan.server.Stoppable;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
@@ -54,7 +54,7 @@ public class LeshanBootstrapServer {
     private final CoapEndpoint securedEndpoint;
 
     // LWM2M attributes
-    private final BootstrapConfigStore bsStore;
+    private final BootstrapConfigurationStore bsStore;
     private final BootstrapSecurityStore bsSecurityStore;
 
     private LwM2mBootstrapRequestSender requestSender;
@@ -76,7 +76,7 @@ public class LeshanBootstrapServer {
      * @param decoder decoder used to decode response payload.
      */
     public LeshanBootstrapServer(CoapEndpoint unsecuredEndpoint, CoapEndpoint securedEndpoint,
-            BootstrapConfigStore bsStore, BootstrapSecurityStore bsSecurityStore,
+            BootstrapConfigurationStore bsStore, BootstrapSecurityStore bsSecurityStore,
             BootstrapSessionManager bsSessionManager, BootstrapHandlerFactory bsHandlerFactory, LwM2mModel model,
             NetworkConfig coapConfig, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
 
@@ -141,9 +141,9 @@ public class LeshanBootstrapServer {
      * Access to the bootstrap configuration store. It's used for sending configuration to the devices initiating a
      * bootstrap.
      * 
-     * @return the {@link BootstrapConfigStore} containing configuration to apply to each devices.
+     * @return the {@link BootstrapConfigurationStore} containing configuration to apply to each devices.
      */
-    public BootstrapConfigStore getBoostrapStore() {
+    public BootstrapConfigurationStore getBoostrapStore() {
         return bsStore;
     }
 
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java
index 0cf45bed..c63cad23 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java
@@ -43,13 +43,14 @@ 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.BootstrapConfigurationStore;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStoreAdapter;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSessionManager;
-import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
+import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigurationStore;
 import org.eclipse.leshan.server.bootstrap.LwM2mBootstrapRequestSender;
 import org.eclipse.leshan.server.security.BootstrapSecurityStore;
 import org.slf4j.Logger;
@@ -67,7 +68,7 @@ public class LeshanBootstrapServerBuilder {
 
     private InetSocketAddress localAddress;
     private InetSocketAddress localAddressSecure;
-    private BootstrapConfigStore configStore;
+    private BootstrapConfigurationStore configStore;
     private BootstrapSecurityStore securityStore;
     private BootstrapSessionManager sessionManager;
     private BootstrapHandlerFactory bootstrapHandlerFactory;
@@ -218,16 +219,18 @@ public class LeshanBootstrapServerBuilder {
     }
 
     /**
-     * Set the {@link BootstrapConfigStore} containing bootstrap configuration to apply to each devices.
+     * Set the {@link BootstrapConfigurationStore} containing bootstrap configuration to apply to each devices.
      * <p>
-     * By default an {@link InMemoryBootstrapConfigStore} is used.
+     * By default an {@link InMemoryBootstrapConfigurationStore} is used.
      * <p>
      * See {@link BootstrapConfig} to see what is could be done during a bootstrap session.
      * 
      * @param configStore the bootstrap configuration store.
      * @return the builder for fluent Bootstrap Server creation.
+     * 
+     * @see BootstrapConfigurationStoreAdapter
      */
-    public LeshanBootstrapServerBuilder setConfigStore(BootstrapConfigStore configStore) {
+    public LeshanBootstrapServerBuilder setConfigStore(BootstrapConfigurationStore configStore) {
         this.configStore = configStore;
         return this;
     }
@@ -264,11 +267,8 @@ public class LeshanBootstrapServerBuilder {
     /**
      * Advanced setter used to customize default bootstrap server behavior.
      * <p>
-     * By default Bootstrap Server is only able to write Security, Server and ACL objects, see
-     * {@link #setConfigStore(BootstrapConfigStore)}. If you need more advanced behavior you can create your own
-     * {@link BootstrapHandler} by inspiring yourself from {@link DefaultBootstrapHandler}. You will probably need to
-     * create a custom {@link BootstrapConfig} and {@link BootstrapConfigStore} and/or change LWM2M model to use, see
-     * {@link #setModel(LwM2mModel)}.
+     * If default bootstrap server behavior is not flexible enough, you can create your own {@link BootstrapHandler} by
+     * inspiring yourself from {@link DefaultBootstrapHandler}.
      * 
      * @param bootstrapHandlerFactory the factory used to create {@link BootstrapHandler}.
      * @return the builder for fluent Bootstrap Server creation.
@@ -279,16 +279,10 @@ public class LeshanBootstrapServerBuilder {
     }
 
     /**
-     * Advanced setter used to customize default the {@link LwM2mModel}. This model is mainly used for data encoding of
+     * Advanced setter used to customize default {@link LwM2mModel}. This model is mainly used for data encoding of
      * Bootstrap write request.
      * <p>
-     * By default, LWM2M object models defined in LWM2M v1.0.x are used. Out of the box, Bootstrap Server is only able
-     * to write Security, Server and ACL objects, see {@link #setConfigStore(BootstrapConfigStore)}.
-     * <p>
-     * Set a different LWM2M model if you want to use a different model version of Security, Server and ACL objects, or
-     * if you need to write objects which are not available by default. For the second case, you need to change
-     * {@link BootstrapHandler} behavior as well, using {@link #setBootstrapHandlerFactory(BootstrapHandlerFactory)} and
-     * probably create a custom {@link BootstrapConfig} and {@link BootstrapConfigStore}.
+     * By default, LWM2M object models defined in LWM2M v1.1.x are used.
      * <p>
      * WARNING: Only 1 version by object is supported for now.
      * 
@@ -408,14 +402,14 @@ public class LeshanBootstrapServerBuilder {
         if (localAddress == null)
             localAddress = new InetSocketAddress(LwM2m.DEFAULT_COAP_PORT);
         if (configStore == null)
-            configStore = new InMemoryBootstrapConfigStore();
+            configStore = new InMemoryBootstrapConfigurationStore();
 
         if (sessionManager == null)
             sessionManager = new DefaultBootstrapSessionManager(securityStore);
         if (bootstrapHandlerFactory == null)
             bootstrapHandlerFactory = new BootstrapHandlerFactory() {
                 @Override
-                public BootstrapHandler create(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
+                public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
                         BootstrapSessionManager sessionManager) {
                     return new DefaultBootstrapHandler(store, sender, sessionManager);
                 }
@@ -589,7 +583,7 @@ public class LeshanBootstrapServerBuilder {
      * @return the LWM2M Bootstrap server.
      */
     protected LeshanBootstrapServer createBootstrapServer(CoapEndpoint unsecuredEndpoint, CoapEndpoint securedEndpoint,
-            BootstrapConfigStore bsStore, BootstrapSecurityStore bsSecurityStore,
+            BootstrapConfigurationStore bsStore, BootstrapSecurityStore bsSecurityStore,
             BootstrapSessionManager bsSessionManager, BootstrapHandlerFactory bsHandlerFactory, LwM2mModel model,
             NetworkConfig coapConfig, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder) {
         return new LeshanBootstrapServer(unsecuredEndpoint, securedEndpoint, bsStore, bsSecurityStore, bsSessionManager,
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigStore.java
index 2d43b1a7..c22cd2b9 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigStore.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigStore.java
@@ -19,7 +19,11 @@ import org.eclipse.leshan.core.request.Identity;
 
 /**
  * A store containing the bootstrap information to be sent to the devices.
+ * 
+ * @deprecated use {@link BootstrapConfigurationStore} instead or see {@link BootstrapConfigurationStoreAdapter} or
+ *             {@link BootstrapUtil#toRequests(BootstrapConfig, org.eclipse.leshan.core.request.ContentFormat)}
  */
+@Deprecated
 public interface BootstrapConfigStore {
 
     /**
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfiguration.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfiguration.java
new file mode 100644
index 00000000..9764e9a6
--- /dev/null
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfiguration.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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.server.bootstrap;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
+import org.eclipse.leshan.core.response.LwM2mResponse;
+
+public class BootstrapConfiguration {
+
+    private List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests;
+
+    public BootstrapConfiguration() {
+        this.requests = Collections.emptyList();
+    }
+
+    @SafeVarargs
+    public BootstrapConfiguration(BootstrapDownlinkRequest<? extends LwM2mResponse>... requests) {
+        this.requests = Arrays.asList(requests);
+    }
+
+    public BootstrapConfiguration(List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests) {
+        this.requests = requests;
+    }
+
+    public List<BootstrapDownlinkRequest<? extends LwM2mResponse>> getRequests() {
+        return requests;
+    }
+}
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStore.java
new file mode 100644
index 00000000..2093fc7b
--- /dev/null
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStore.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.server.bootstrap;
+
+import org.eclipse.leshan.core.request.Identity;
+
+/**
+ * A store containing the bootstrap information to be sent to the devices.
+ */
+public interface BootstrapConfigurationStore {
+
+    /**
+     * Get the bootstrap configuration to apply to the device identified by the given parameters.
+     * 
+     * @param endpoint the endpoint of the device.
+     * @param deviceIdentity the {@link Identity} the device.
+     * @param session the current {@link BootstrapSession}.
+     * @return the {@link BootstrapConfig} to apply.
+     */
+    BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session);
+}
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStoreAdapter.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStoreAdapter.java
new file mode 100644
index 00000000..563fa81a
--- /dev/null
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfigurationStoreAdapter.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.server.bootstrap;
+
+import org.eclipse.leshan.core.request.Identity;
+
+/**
+ * It allows to adapt the deprecated {@link BootstrapConfigStore} to the new API {@link BootstrapConfigurationStore}.
+ * You can use it to adapt but it is clearly recommanded to rather directly implements
+ * {@link BootstrapConfigurationStore} and maybe used {@link BootstrapUtil} if you still want to handle
+ * {@link BootstrapConfig}.
+ */
+@SuppressWarnings("deprecation")
+public class BootstrapConfigurationStoreAdapter implements BootstrapConfigurationStore {
+
+    private BootstrapConfigStore internalStore;
+
+    public BootstrapConfigurationStoreAdapter(BootstrapConfigStore store) {
+        this.internalStore = store;
+    }
+
+    @Override
+    public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+        BootstrapConfig bootstrapConfig = internalStore.get(endpoint, deviceIdentity, session);
+        if (bootstrapConfig == null)
+            return null;
+
+        return new BootstrapConfiguration(BootstrapUtil.toRequests(bootstrapConfig, session.getContentFormat()));
+    }
+}
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapFailureCause.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapFailureCause.java
index 99ce1150..77909bcf 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapFailureCause.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapFailureCause.java
@@ -28,21 +28,9 @@ public enum BootstrapFailureCause {
      */
     NO_BOOTSTRAP_CONFIG,
     /**
-     * A delete request failed
+     * A request failed
      */
-    DELETE_FAILED,
-    /**
-     * Object 2 (ACL) could not be written on the device
-     */
-    WRITE_ACL_FAILED,
-    /**
-     * Object 1 (Server) could not be written on the device
-     */
-    WRITE_SERVER_FAILED,
-    /**
-     * Object 0 (Security) could not be written on the device
-     */
-    WRITE_SECURITY_FAILED,
+    REQUEST_FAILED,
     /**
      * 'Bootstrap Finish' failed
      */
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerFactory.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerFactory.java
index db1a6266..4cf9aace 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerFactory.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapHandlerFactory.java
@@ -30,6 +30,6 @@ public interface BootstrapHandlerFactory {
      * @param sessionManager the manager responsible to handle bootstrap session.
      * @return the new {@link BootstrapHandler}.
      */
-    BootstrapHandler create(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
+    BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
             BootstrapSessionManager sessionManager);
 }
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
index c9244d41..275dca2c 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
@@ -17,17 +17,27 @@ package org.eclipse.leshan.server.bootstrap;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
 
+import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.node.LwM2mMultipleResource;
+import org.eclipse.leshan.core.node.LwM2mNode;
 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
+import org.eclipse.leshan.core.node.LwM2mPath;
 import org.eclipse.leshan.core.node.LwM2mResource;
 import org.eclipse.leshan.core.node.LwM2mSingleResource;
+import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
+import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
+import org.eclipse.leshan.core.request.BootstrapWriteRequest;
+import org.eclipse.leshan.core.request.ContentFormat;
+import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ACLConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerSecurity;
 
 public class BootstrapUtil {
-    public static LwM2mObjectInstance convertToSecurityInstance(int instanceId, ServerSecurity securityConfig) {
+    public static LwM2mObjectInstance toSecurityInstance(int instanceId, ServerSecurity securityConfig) {
         Collection<LwM2mResource> resources = new ArrayList<>();
 
         if (securityConfig.uri != null)
@@ -59,7 +69,14 @@ public class BootstrapUtil {
         return new LwM2mObjectInstance(instanceId, resources);
     }
 
-    public static LwM2mObjectInstance convertToServerInstance(int instanceId, ServerConfig serverConfig) {
+    public static BootstrapWriteRequest toWriteRequest(int instanceId, ServerSecurity securityConfig,
+            ContentFormat contentFormat) {
+        LwM2mPath path = new LwM2mPath(LwM2mId.SECURITY, instanceId);
+        final LwM2mNode securityInstance = BootstrapUtil.toSecurityInstance(instanceId, securityConfig);
+        return new BootstrapWriteRequest(path, securityInstance, contentFormat);
+    }
+
+    public static LwM2mObjectInstance toServerInstance(int instanceId, ServerConfig serverConfig) {
         Collection<LwM2mResource> resources = new ArrayList<>();
 
         resources.add(LwM2mSingleResource.newIntegerResource(0, serverConfig.shortId));
@@ -77,7 +94,14 @@ public class BootstrapUtil {
         return new LwM2mObjectInstance(instanceId, resources);
     }
 
-    public static LwM2mObjectInstance convertToAclInstance(int instanceId, ACLConfig aclConfig) {
+    public static BootstrapWriteRequest toWriteRequest(int instanceId, ServerConfig serverConfig,
+            ContentFormat contentFormat) {
+        LwM2mPath path = new LwM2mPath(LwM2mId.SERVER, instanceId);
+        final LwM2mNode securityInstance = BootstrapUtil.toServerInstance(instanceId, serverConfig);
+        return new BootstrapWriteRequest(path, securityInstance, contentFormat);
+    }
+
+    public static LwM2mObjectInstance toAclInstance(int instanceId, ACLConfig aclConfig) {
         Collection<LwM2mResource> resources = new ArrayList<>();
 
         resources.add(LwM2mSingleResource.newIntegerResource(0, aclConfig.objectId));
@@ -89,4 +113,38 @@ public class BootstrapUtil {
 
         return new LwM2mObjectInstance(instanceId, resources);
     }
-}
+
+    public static BootstrapWriteRequest toWriteRequest(int instanceId, ACLConfig aclConfig,
+            ContentFormat contentFormat) {
+        LwM2mPath path = new LwM2mPath(LwM2mId.ACCESS_CONTROL, instanceId);
+        final LwM2mNode securityInstance = BootstrapUtil.toAclInstance(instanceId, aclConfig);
+        return new BootstrapWriteRequest(path, securityInstance, contentFormat);
+    }
+
+    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(
+            BootstrapConfig bootstrapConfig) {
+        return toRequests(bootstrapConfig, ContentFormat.TLV);
+    }
+
+    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(
+            BootstrapConfig bootstrapConfig, ContentFormat contentFormat) {
+        List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>();
+        // handle delete
+        for (String path : bootstrapConfig.toDelete) {
+            requests.add(new BootstrapDeleteRequest(path));
+        }
+        // handle security
+        for (Entry<Integer, ServerSecurity> security : bootstrapConfig.security.entrySet()) {
+            requests.add(toWriteRequest(security.getKey(), security.getValue(), contentFormat));
+        }
+        // handle server
+        for (Entry<Integer, ServerConfig> server : bootstrapConfig.servers.entrySet()) {
+            requests.add(toWriteRequest(server.getKey(), server.getValue(), contentFormat));
+        }
+        // handle acl
+        for (Entry<Integer, ACLConfig> acl : bootstrapConfig.acls.entrySet()) {
+            requests.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat));
+        }
+        return (requests);
+    }
+}
\ No newline at end of file
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 f2a223e4..ed46b20f 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
@@ -21,25 +21,16 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.eclipse.leshan.core.node.LwM2mNode;
-import org.eclipse.leshan.core.node.LwM2mPath;
-import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
+import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
 import org.eclipse.leshan.core.request.BootstrapFinishRequest;
 import org.eclipse.leshan.core.request.BootstrapRequest;
-import org.eclipse.leshan.core.request.BootstrapWriteRequest;
-import org.eclipse.leshan.core.request.DownlinkRequest;
 import org.eclipse.leshan.core.request.Identity;
-import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
 import org.eclipse.leshan.core.response.BootstrapFinishResponse;
 import org.eclipse.leshan.core.response.BootstrapResponse;
-import org.eclipse.leshan.core.response.BootstrapWriteResponse;
 import org.eclipse.leshan.core.response.ErrorCallback;
 import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.core.response.ResponseCallback;
 import org.eclipse.leshan.core.response.SendableResponse;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ACLConfig;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerConfig;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerSecurity;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager.BootstrapPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,7 +46,7 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
     // send a Confirmable message to the time when an acknowledgement is no longer expected.
     public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000l; // 2min in ms
 
-    protected final BootstrapConfigStore store;
+    protected final BootstrapConfigurationStore store;
 
     protected final LwM2mBootstrapRequestSender sender;
     protected final long requestTimeout;
@@ -63,13 +54,25 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
     protected final ConcurrentHashMap<String, BootstrapSession> onGoingSession = new ConcurrentHashMap<>();
     protected final BootstrapSessionManager sessionManager;
 
+    @Deprecated
     public DefaultBootstrapHandler(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
             BootstrapSessionManager sessionManager) {
-        this(store, sender, sessionManager, DEFAULT_TIMEOUT);
+        this(new BootstrapConfigurationStoreAdapter(store), sender, sessionManager, DEFAULT_TIMEOUT);
     }
 
+    @Deprecated
     public DefaultBootstrapHandler(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
             BootstrapSessionManager sessionManager, long requestTimeout) {
+        this(new BootstrapConfigurationStoreAdapter(store), sender, sessionManager, requestTimeout);
+    }
+
+    public DefaultBootstrapHandler(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
+            BootstrapSessionManager sessionManager) {
+        this(store, sender, sessionManager, DEFAULT_TIMEOUT);
+    }
+
+    public DefaultBootstrapHandler(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
+            BootstrapSessionManager sessionManager, long requestTimeout) {
         this.store = store;
         this.sender = sender;
         this.sessionManager = sessionManager;
@@ -101,7 +104,7 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
 
         try {
             // Get the desired bootstrap config for the endpoint
-            final BootstrapConfig cfg = store.get(endpoint, sender, session);
+            final BootstrapConfiguration cfg = store.get(endpoint, sender, session);
             if (cfg == null) {
                 LOG.debug("No bootstrap config for {}", session);
                 stopSession(session, NO_BOOTSTRAP_CONFIG);
@@ -124,8 +127,8 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
         }
     }
 
-    protected void startBootstrap(BootstrapSession session, BootstrapConfig cfg) {
-        delete(session, cfg, new ArrayList<>(cfg.toDelete));
+    protected void startBootstrap(BootstrapSession session, BootstrapConfiguration cfg) {
+        sendRequest(session, cfg, new ArrayList<>(cfg.getRequests()));
     }
 
     protected void stopSession(BootstrapSession session, BootstrapFailureCause cause) {
@@ -142,242 +145,32 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
         }
     }
 
-    protected void delete(final BootstrapSession session, final BootstrapConfig cfg, final List<String> pathToDelete) {
-        if (!pathToDelete.isEmpty()) {
-            // get next Security configuration
-            String path = pathToDelete.get(0);
-
-            final BootstrapDeleteRequest deleteRequest = new BootstrapDeleteRequest(path);
-            send(session, deleteRequest, new SafeResponseCallback<BootstrapDeleteResponse>(session) {
-                @Override
-                public void safeOnResponse(BootstrapDeleteResponse response) {
-                    if (response.isSuccess()) {
-                        LOG.trace("{} receives {} for {}", session, response, deleteRequest);
-                        sessionManager.onResponseSuccess(session, deleteRequest);
-                        afterDelete(session, cfg, pathToDelete, BootstrapPolicy.CONTINUE);
-                    } else {
-                        LOG.debug("{} receives {} for {}", session, response, deleteRequest);
-                        BootstrapPolicy policy = sessionManager.onResponseError(session, deleteRequest, response);
-                        afterDelete(session, cfg, pathToDelete, policy);
-                    }
-                }
-            }, new SafeErrorCallback(session) {
-                @Override
-                public void safeOnError(Exception e) {
-                    LOG.debug("Error for {} while sending {} ", session, deleteRequest, e);
-                    BootstrapPolicy policy = sessionManager.onRequestFailure(session, deleteRequest, e);
-                    afterDelete(session, cfg, pathToDelete, policy);
-                }
-            });
-        } else {
-            // we are done, write the securities now
-            List<Integer> securityInstancesToWrite = new ArrayList<>(cfg.security.keySet());
-            writeSecurities(session, cfg, securityInstancesToWrite);
-        }
-    }
-
-    protected void afterDelete(BootstrapSession session, BootstrapConfig cfg, List<String> pathToDelete,
-            BootstrapPolicy policy) {
-        if (session.isCancelled()) {
-            stopSession(session, CANCELLED);
-            return;
-        }
-        switch (policy) {
-        case CONTINUE:
-            pathToDelete.remove(0);
-            delete(session, cfg, pathToDelete);
-            break;
-        case RETRY:
-            delete(session, cfg, pathToDelete);
-            break;
-        case RETRYALL:
-            startBootstrap(session, cfg);
-            break;
-        case SEND_FINISHED:
-            bootstrapFinished(session, cfg);
-            break;
-        case STOP:
-            stopSession(session, DELETE_FAILED);
-            break;
-        default:
-            throw new IllegalStateException("unknown policy :" + policy);
-        }
-    }
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected void sendRequest(final BootstrapSession session, final BootstrapConfiguration cfg,
+            final List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestToSend) {
+        if (!requestToSend.isEmpty()) {
+            // get next request
+            final BootstrapDownlinkRequest request = requestToSend.get(0);
 
-    protected void writeSecurities(final BootstrapSession session, final BootstrapConfig cfg,
-            final List<Integer> securityInstancesToWrite) {
-        if (!securityInstancesToWrite.isEmpty()) {
-            // get next Security configuration
-            Integer key = securityInstancesToWrite.get(0);
-            ServerSecurity securityConfig = cfg.security.get(key);
-
-            // create write request from it
-            LwM2mPath path = new LwM2mPath(0, key);
-            final LwM2mNode securityInstance = BootstrapUtil.convertToSecurityInstance(key, securityConfig);
-            final BootstrapWriteRequest writeBootstrapRequest = new BootstrapWriteRequest(path, securityInstance,
-                    session.getContentFormat());
-
-            // sent it
-            send(session, writeBootstrapRequest, new SafeResponseCallback<BootstrapWriteResponse>(session) {
+            send(session, request, new SafeResponseCallback(session) {
                 @Override
-                public void safeOnResponse(BootstrapWriteResponse response) {
+                public void safeOnResponse(LwM2mResponse response) {
                     if (response.isSuccess()) {
-                        LOG.trace("{} receives {} for {}", session, response, writeBootstrapRequest);
-                        sessionManager.onResponseSuccess(session, writeBootstrapRequest);
-                        afterWriteSecurities(session, cfg, securityInstancesToWrite, BootstrapPolicy.CONTINUE);
+                        LOG.trace("{} receives {} for {}", session, response, request);
+                        sessionManager.onResponseSuccess(session, request);
+                        afterRequest(session, cfg, requestToSend, BootstrapPolicy.CONTINUE);
                     } else {
-                        LOG.debug("{} receives {} for {}", session, response, writeBootstrapRequest);
-                        BootstrapPolicy policy = sessionManager.onResponseError(session, writeBootstrapRequest,
-                                response);
-                        afterWriteSecurities(session, cfg, securityInstancesToWrite, policy);
+                        LOG.debug("{} receives {} for {}", session, response, request);
+                        BootstrapPolicy policy = sessionManager.onResponseError(session, request, response);
+                        afterRequest(session, cfg, requestToSend, policy);
                     }
                 }
             }, new SafeErrorCallback(session) {
                 @Override
                 public void safeOnError(Exception e) {
-                    LOG.debug("Error for {} while sending {} ", session, writeBootstrapRequest, e);
-                    BootstrapPolicy policy = sessionManager.onRequestFailure(session, writeBootstrapRequest, e);
-                    afterWriteSecurities(session, cfg, securityInstancesToWrite, policy);
-                }
-            });
-        } else {
-            // we are done, write the servers now
-            List<Integer> serverInstancesToWrite = new ArrayList<>(cfg.servers.keySet());
-            writeServers(session, cfg, serverInstancesToWrite);
-        }
-    }
-
-    protected void afterWriteSecurities(BootstrapSession session, BootstrapConfig cfg,
-            List<Integer> securityInstancesToWrite, BootstrapPolicy policy) {
-        if (session.isCancelled()) {
-            stopSession(session, CANCELLED);
-            return;
-        }
-        switch (policy) {
-        case CONTINUE:
-            securityInstancesToWrite.remove(0);
-            writeSecurities(session, cfg, securityInstancesToWrite);
-            break;
-        case RETRY:
-            writeSecurities(session, cfg, securityInstancesToWrite);
-            break;
-        case RETRYALL:
-            startBootstrap(session, cfg);
-            break;
-        case SEND_FINISHED:
-            bootstrapFinished(session, cfg);
-            break;
-        case STOP:
-            stopSession(session, WRITE_SECURITY_FAILED);
-            break;
-        default:
-            throw new IllegalStateException("unknown policy :" + policy);
-        }
-    }
-
-    protected void writeServers(final BootstrapSession session, final BootstrapConfig cfg,
-            final List<Integer> serverInstancesToWrite) {
-        if (!serverInstancesToWrite.isEmpty()) {
-            // get next Server configuration
-            Integer key = serverInstancesToWrite.get(0);
-            ServerConfig serverConfig = cfg.servers.get(key);
-
-            // create write request from it
-            LwM2mPath path = new LwM2mPath(1, key);
-            final LwM2mNode serverInstance = BootstrapUtil.convertToServerInstance(key, serverConfig);
-            final BootstrapWriteRequest writeServerRequest = new BootstrapWriteRequest(path, serverInstance,
-                    session.getContentFormat());
-
-            // sent it
-            send(session, writeServerRequest, new SafeResponseCallback<BootstrapWriteResponse>(session) {
-                @Override
-                public void safeOnResponse(BootstrapWriteResponse response) {
-                    if (response.isSuccess()) {
-                        LOG.trace("{} receives {} for {}", session, response, writeServerRequest);
-                        sessionManager.onResponseSuccess(session, writeServerRequest);
-                        afterWriteServers(session, cfg, serverInstancesToWrite, BootstrapPolicy.CONTINUE);
-                    } else {
-                        LOG.debug("{} receives {} for {}", session, response, writeServerRequest);
-                        BootstrapPolicy policy = sessionManager.onResponseError(session, writeServerRequest, response);
-                        afterWriteServers(session, cfg, serverInstancesToWrite, policy);
-                    }
-                }
-            }, new SafeErrorCallback(session) {
-                @Override
-                public void safeOnError(Exception e) {
-                    LOG.debug("Error for {} while sending {} ", session, writeServerRequest, e);
-                    BootstrapPolicy policy = sessionManager.onRequestFailure(session, writeServerRequest, e);
-                    afterWriteServers(session, cfg, serverInstancesToWrite, policy);
-                }
-            });
-        } else {
-            // we are done, write ACLs now
-            List<Integer> aclInstancesToWrite = new ArrayList<>(cfg.acls.keySet());
-            writedAcls(session, cfg, aclInstancesToWrite);
-        }
-    }
-
-    protected void afterWriteServers(BootstrapSession session, BootstrapConfig cfg,
-            List<Integer> serverInstancesToWrite, BootstrapPolicy policy) {
-        if (session.isCancelled()) {
-            stopSession(session, CANCELLED);
-            return;
-        }
-        switch (policy) {
-        case CONTINUE:
-            serverInstancesToWrite.remove(0);
-            writeServers(session, cfg, serverInstancesToWrite);
-            break;
-        case RETRY:
-            writeServers(session, cfg, serverInstancesToWrite);
-            break;
-        case RETRYALL:
-            startBootstrap(session, cfg);
-            break;
-        case SEND_FINISHED:
-            bootstrapFinished(session, cfg);
-            break;
-        case STOP:
-            stopSession(session, WRITE_SERVER_FAILED);
-            break;
-        default:
-            throw new IllegalStateException("unknown policy :" + policy);
-        }
-    }
-
-    protected void writedAcls(final BootstrapSession session, final BootstrapConfig cfg,
-            final List<Integer> aclInstancesToWrite) {
-        if (!aclInstancesToWrite.isEmpty()) {
-            // get next ACL configuration
-            Integer key = aclInstancesToWrite.get(0);
-            ACLConfig aclConfig = cfg.acls.get(key);
-
-            // create write request from it
-            LwM2mPath path = new LwM2mPath(2, key);
-            final LwM2mNode aclInstance = BootstrapUtil.convertToAclInstance(key, aclConfig);
-            final BootstrapWriteRequest writeACLRequest = new BootstrapWriteRequest(path, aclInstance,
-                    session.getContentFormat());
-
-            // sent it
-            send(session, writeACLRequest, new SafeResponseCallback<BootstrapWriteResponse>(session) {
-                @Override
-                public void safeOnResponse(BootstrapWriteResponse response) {
-                    if (response.isSuccess()) {
-                        LOG.trace("{} receives {} for {}", session, response, writeACLRequest);
-                        sessionManager.onResponseSuccess(session, writeACLRequest);
-                        afterWritedAcls(session, cfg, aclInstancesToWrite, BootstrapPolicy.CONTINUE);
-                    } else {
-                        LOG.debug("{} receives {} for {}", session, response, writeACLRequest);
-                        BootstrapPolicy policy = sessionManager.onResponseError(session, writeACLRequest, response);
-                        afterWritedAcls(session, cfg, aclInstancesToWrite, policy);
-                    }
-                }
-            }, new SafeErrorCallback(session) {
-                @Override
-                public void safeOnError(Exception e) {
-                    LOG.debug("Error for {} while sending {} ", session, writeACLRequest, e);
-                    BootstrapPolicy policy = sessionManager.onRequestFailure(session, writeACLRequest, e);
-                    afterWritedAcls(session, cfg, aclInstancesToWrite, policy);
+                    LOG.debug("Error for {} while sending {} ", session, request, e);
+                    BootstrapPolicy policy = sessionManager.onRequestFailure(session, request, e);
+                    afterRequest(session, cfg, requestToSend, policy);
                 }
             });
         } else {
@@ -386,19 +179,19 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
         }
     }
 
-    protected void afterWritedAcls(BootstrapSession session, BootstrapConfig cfg, List<Integer> aclInstancesToWrite,
-            BootstrapPolicy policy) {
+    protected void afterRequest(BootstrapSession session, BootstrapConfiguration cfg,
+            final List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestToSend, BootstrapPolicy policy) {
         if (session.isCancelled()) {
             stopSession(session, CANCELLED);
             return;
         }
         switch (policy) {
         case CONTINUE:
-            aclInstancesToWrite.remove(0);
-            writedAcls(session, cfg, aclInstancesToWrite);
+            requestToSend.remove(0);
+            sendRequest(session, cfg, requestToSend);
             break;
         case RETRY:
-            writedAcls(session, cfg, aclInstancesToWrite);
+            sendRequest(session, cfg, requestToSend);
             break;
         case RETRYALL:
             startBootstrap(session, cfg);
@@ -407,14 +200,14 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
             bootstrapFinished(session, cfg);
             break;
         case STOP:
-            stopSession(session, WRITE_ACL_FAILED);
+            stopSession(session, REQUEST_FAILED);
             break;
         default:
             throw new IllegalStateException("unknown policy :" + policy);
         }
     }
 
-    protected void bootstrapFinished(final BootstrapSession session, final BootstrapConfig cfg) {
+    protected void bootstrapFinished(final BootstrapSession session, final BootstrapConfiguration cfg) {
 
         final BootstrapFinishRequest finishBootstrapRequest = new BootstrapFinishRequest();
         send(session, finishBootstrapRequest, new SafeResponseCallback<BootstrapFinishResponse>(session) {
@@ -440,7 +233,8 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
         });
     }
 
-    protected void afterBootstrapFinished(BootstrapSession session, BootstrapConfig cfg, BootstrapPolicy policy) {
+    protected void afterBootstrapFinished(BootstrapSession session, BootstrapConfiguration cfg,
+            BootstrapPolicy policy) {
         if (session.isCancelled()) {
             stopSession(session, CANCELLED);
             return;
@@ -466,7 +260,7 @@ public class DefaultBootstrapHandler implements BootstrapHandler {
         }
     }
 
-    protected <T extends LwM2mResponse> void send(BootstrapSession session, DownlinkRequest<T> request,
+    protected <T extends LwM2mResponse> void send(BootstrapSession session, BootstrapDownlinkRequest<T> request,
             ResponseCallback<T> responseCallback, ErrorCallback errorCallback) {
         synchronized (session) {
             if (!session.isCancelled()) {
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigStore.java
index 4a7f1973..a5ff04c7 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigStore.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigStore.java
@@ -19,7 +19,13 @@ import java.util.Map;
 
 /**
  * An editable {@link BootstrapConfigStore}.
+ * 
+ * @deprecated use {@link EditableBootstrapConfigurationStore} instead or see {@link BootstrapConfigurationStoreAdapter}
+ *             or
+ *             {@link BootstrapUtil#toRequests(BootstrapConfig, org.eclipse.leshan.core.request.ContentFormat)}
+ * 
  */
+@Deprecated
 public interface EditableBootstrapConfigStore extends BootstrapConfigStore {
 
     /**
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigurationStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigurationStore.java
new file mode 100644
index 00000000..0d3e6082
--- /dev/null
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/EditableBootstrapConfigurationStore.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.server.bootstrap;
+
+/**
+ * An editable {@link BootstrapConfigurationStore}.
+ */
+public interface EditableBootstrapConfigurationStore extends BootstrapConfigurationStore {
+
+    /**
+     * Add a new bootstrap configuration for a client end-point.
+     * 
+     * @param endpoint The client endpoint name to which we want to set the given bootstrap configuration.
+     * @param config The configuration to apply for the given client.
+     * @throws InvalidConfigurationException if {@link BootstrapConfiguration} is not valid.
+     */
+    void add(String endpoint, BootstrapConfiguration config) throws InvalidConfigurationException;
+
+    /**
+     * Removes the bootstrap Configuration for a given end-point.
+     * 
+     * @param endpoint The client endpoint to which we want to remove the configuration.
+     * @return the removed {@link BootstrapConfiguration} or <code>null</code> if no bootstrap configuration for this
+     *         client.
+     */
+    BootstrapConfiguration remove(String endpoint);
+}
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigStore.java
index b6e74a7b..c89b808b 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigStore.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigStore.java
@@ -25,7 +25,12 @@ import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerSecurity;
 
 /**
  * Simple bootstrap store implementation storing bootstrap configuration information in memory.
+ * 
+ * * @deprecated use {@link InMemoryBootstrapConfigurationStore} instead or see *
+ * {@link BootstrapConfigurationStoreAdapter} or
+ * {@link BootstrapUtil#toRequests(BootstrapConfig, org.eclipse.leshan.core.request.ContentFormat)}
  */
+@Deprecated
 public class InMemoryBootstrapConfigStore implements EditableBootstrapConfigStore {
 
     protected final ConfigurationChecker configChecker = new ConfigurationChecker();
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigurationStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigurationStore.java
new file mode 100644
index 00000000..2275af64
--- /dev/null
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/InMemoryBootstrapConfigurationStore.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.server.bootstrap;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.leshan.core.request.Identity;
+
+/**
+ * Simple bootstrap store implementation storing bootstrap configuration information in memory.
+ */
+public class InMemoryBootstrapConfigurationStore implements EditableBootstrapConfigurationStore {
+    protected final ConfigurationChecker configChecker = new ConfigurationChecker();
+
+    protected final Map<String /* endpoint */, BootstrapConfiguration> bootstrapByEndpoint = new ConcurrentHashMap<>();
+
+    @Override
+    public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+        return bootstrapByEndpoint.get(endpoint);
+    }
+
+    @Override
+    public synchronized void add(String endpoint, BootstrapConfiguration config) throws InvalidConfigurationException {
+        bootstrapByEndpoint.put(endpoint, config);
+    }
+
+    @Override
+    public synchronized BootstrapConfiguration remove(String enpoint) {
+        BootstrapConfiguration bootstrapConfig = bootstrapByEndpoint.remove(enpoint);
+        return bootstrapConfig;
+    }
+}
\ No newline at end of file


Test patch:
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
index 71b84ee4..bbe1f175 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
@@ -52,12 +52,14 @@ import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ACLConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerSecurity;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfiguration;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSession;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
+import org.eclipse.leshan.server.bootstrap.BootstrapUtil;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSessionManager;
@@ -112,7 +114,7 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
     }
 
     private LeshanBootstrapServerBuilder createBootstrapBuilder(BootstrapSecurityStore securityStore,
-            BootstrapConfigStore bootstrapStore) {
+            BootstrapConfigurationStore bootstrapStore) {
         if (bootstrapStore == null) {
             bootstrapStore = unsecuredBootstrapStore();
         }
@@ -138,11 +140,12 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         return builder;
     }
 
-    public void createBootstrapServer(BootstrapSecurityStore securityStore, BootstrapConfigStore bootstrapStore) {
+    public void createBootstrapServer(BootstrapSecurityStore securityStore,
+            BootstrapConfigurationStore bootstrapStore) {
         bootstrapServer = createBootstrapBuilder(securityStore, bootstrapStore).build();
     }
 
-    public void createBootstrapServer(BootstrapSecurityStore securityStore, BootstrapConfigStore bootstrapStore,
+    public void createBootstrapServer(BootstrapSecurityStore securityStore, BootstrapConfigurationStore bootstrapStore,
             final BootstrapDiscoverRequest request) {
         LeshanBootstrapServerBuilder builder = createBootstrapBuilder(securityStore, bootstrapStore);
 
@@ -150,18 +153,18 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         builder.setBootstrapHandlerFactory(new BootstrapHandlerFactory() {
 
             @Override
-            public BootstrapHandler create(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
+            public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
                     BootstrapSessionManager sessionManager) {
                 return new DefaultBootstrapHandler(store, sender, sessionManager) {
 
                     @Override
-                    protected void startBootstrap(final BootstrapSession session, final BootstrapConfig cfg) {
+                    protected void startBootstrap(final BootstrapSession session, final BootstrapConfiguration cfg) {
                         send(session, request, new SafeResponseCallback<BootstrapDiscoverResponse>(session) {
 
                             @Override
                             public void safeOnResponse(BootstrapDiscoverResponse response) {
                                 lastDiscoverAnswer = response;
-                                delete(session, cfg, new ArrayList<>(cfg.toDelete));
+                                sendRequest(session, cfg, new ArrayList<>(cfg.getRequests()));
                             }
                         }, new SafeErrorCallback(session) {
                             @Override
@@ -301,11 +304,11 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         };
     }
 
-    public BootstrapConfigStore unsecuredBootstrapStore() {
-        return new BootstrapConfigStore() {
+    public BootstrapConfigurationStore unsecuredBootstrapStore() {
+        return new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
 
                 BootstrapConfig bsConfig = new BootstrapConfig();
 
@@ -331,12 +334,12 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
                 dmConfig.shortId = 2222;
                 bsConfig.servers.put(0, dmConfig);
 
-                return bsConfig;
+                return new BootstrapConfiguration(BootstrapUtil.toRequests(bsConfig));
             }
         };
     }
 
-    public BootstrapConfigStore deleteSecurityStore(Integer... objectToDelete) {
+    public BootstrapConfigurationStore deleteSecurityStore(Integer... objectToDelete) {
         String[] pathToDelete = new String[objectToDelete.length];
         for (int i = 0; i < pathToDelete.length; i++) {
             pathToDelete[i] = "/" + objectToDelete[i];
@@ -345,24 +348,24 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         return deleteSecurityStore(pathToDelete);
     }
 
-    public BootstrapConfigStore deleteSecurityStore(final String... pathToDelete) {
-        return new BootstrapConfigStore() {
+    public BootstrapConfigurationStore deleteSecurityStore(final String... pathToDelete) {
+        return new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
 
                 BootstrapConfig bsConfig = new BootstrapConfig();
                 bsConfig.toDelete = Arrays.asList(pathToDelete);
-                return bsConfig;
+                return new BootstrapConfiguration(BootstrapUtil.toRequests(bsConfig));
             }
         };
     }
 
-    public BootstrapConfigStore unsecuredWithAclBootstrapStore() {
-        return new BootstrapConfigStore() {
+    public BootstrapConfigurationStore unsecuredWithAclBootstrapStore() {
+        return new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
 
                 BootstrapConfig bsConfig = new BootstrapConfig();
 
@@ -404,16 +407,16 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
                 aclConfig.AccessControlOwner = 2222;
                 bsConfig.acls.put(1, aclConfig);
 
-                return bsConfig;
+                return new BootstrapConfiguration(BootstrapUtil.toRequests(bsConfig));
             }
         };
     }
 
-    public BootstrapConfigStore pskBootstrapStore() {
-        return new BootstrapConfigStore() {
+    public BootstrapConfigurationStore pskBootstrapStore() {
+        return new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
 
                 BootstrapConfig bsConfig = new BootstrapConfig();
 
@@ -441,16 +444,16 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
                 dmConfig.shortId = 2222;
                 bsConfig.servers.put(0, dmConfig);
 
-                return bsConfig;
+                return new BootstrapConfiguration(BootstrapUtil.toRequests(bsConfig));
             }
         };
     }
 
-    public BootstrapConfigStore rpkBootstrapStore() {
-        return new BootstrapConfigStore() {
+    public BootstrapConfigurationStore rpkBootstrapStore() {
+        return new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
 
                 BootstrapConfig bsConfig = new BootstrapConfig();
 
@@ -479,7 +482,7 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
                 dmConfig.shortId = 2222;
                 bsConfig.servers.put(0, dmConfig);
 
-                return bsConfig;
+                return new BootstrapConfiguration(BootstrapUtil.toRequests(bsConfig));
             }
         };
     }
diff --git a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
index 9ad1e1fc..8287aadc 100644
--- a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
+++ b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
@@ -20,8 +20,8 @@ import static org.junit.Assert.*;
 import java.util.Iterator;
 
 import org.eclipse.leshan.core.request.Identity;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfiguration;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapSession;
 import org.eclipse.leshan.server.security.BootstrapSecurityStore;
 import org.eclipse.leshan.server.security.SecurityInfo;
@@ -36,9 +36,10 @@ public class LeshanBootstrapServerBuilderTest {
     @Before
     public void start() {
         builder = new LeshanBootstrapServerBuilder();
-        builder.setConfigStore(new BootstrapConfigStore() {
+        builder.setConfigStore(new BootstrapConfigurationStore() {
+
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
                 return null;
             }
         });
diff --git a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerTest.java b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerTest.java
index 63d8b8d5..365856e6 100644
--- a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerTest.java
+++ b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerTest.java
@@ -19,12 +19,13 @@ import static org.junit.Assert.assertEquals;
 
 import java.net.InetSocketAddress;
 
+import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
 import org.eclipse.leshan.core.request.BootstrapRequest;
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.response.BootstrapResponse;
 import org.eclipse.leshan.core.response.SendableResponse;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
-import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfiguration;
+import org.eclipse.leshan.server.bootstrap.BootstrapConfigurationStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSession;
@@ -43,22 +44,21 @@ public class LeshanBootstrapServerTest {
         builder.setBootstrapHandlerFactory(new BootstrapHandlerFactory() {
 
             @Override
-            public BootstrapHandler create(BootstrapConfigStore store, LwM2mBootstrapRequestSender sender,
+            public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrapRequestSender sender,
                     BootstrapSessionManager sessionManager) {
                 bsHandler = new DefaultBootstrapHandler(store, sender, sessionManager);
                 return bsHandler;
             }
         });
-        builder.setConfigStore(new BootstrapConfigStore() {
+        builder.setConfigStore(new BootstrapConfigurationStore() {
 
             @Override
-            public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
-                BootstrapConfig config = new BootstrapConfig();
-                config.toDelete.add("/");
-                return config;
+            public BootstrapConfiguration get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
+                return new BootstrapConfiguration(new BootstrapDeleteRequest());
             }
         });
         return builder.build();
+
     }
 
     @Test
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 6f7d875e..67ce5ae7 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
@@ -48,7 +48,8 @@ public class BootstrapHandlerTest {
     public void error_if_not_authorized() {
         // prepare bootstrapHandler with a session manager which does not authorized any session
         BootstrapSessionManager bsSessionManager = new MockBootstrapSessionManager(false);
-        BootstrapHandler bsHandler = new DefaultBootstrapHandler(null, null, bsSessionManager);
+        BootstrapHandler bsHandler = new DefaultBootstrapHandler((BootstrapConfigurationStore) null, null,
+                bsSessionManager);
 
         // Try to bootstrap
         BootstrapResponse response = bsHandler
@@ -66,8 +67,8 @@ public class BootstrapHandlerTest {
         // and a config store with and an empty config for the expected endpoint
         MockBootstrapSessionManager bsSessionManager = new MockBootstrapSessionManager(true);
         LwM2mBootstrapRequestSender requestSender = new MockRequestSender(Mode.ALWAYS_SUCCESS);
-        EditableBootstrapConfigStore bsStore = new InMemoryBootstrapConfigStore();
-        bsStore.add("endpoint", new BootstrapConfig());
+        EditableBootstrapConfigurationStore bsStore = new InMemoryBootstrapConfigurationStore();
+        bsStore.add("endpoint", new BootstrapConfiguration());
         BootstrapHandler bsHandler = new DefaultBootstrapHandler(bsStore, requestSender, bsSessionManager);
 
         // Try to bootstrap
@@ -87,8 +88,8 @@ public class BootstrapHandlerTest {
         // and a config store with and an empty config for the expected endpoint
         MockBootstrapSessionManager bsSessionManager = new MockBootstrapSessionManager(true);
         LwM2mBootstrapRequestSender requestSender = new MockRequestSender(Mode.ALWAYS_FAILURE);
-        EditableBootstrapConfigStore bsStore = new InMemoryBootstrapConfigStore();
-        bsStore.add("endpoint", new BootstrapConfig());
+        EditableBootstrapConfigurationStore bsStore = new InMemoryBootstrapConfigurationStore();
+        bsStore.add("endpoint", new BootstrapConfiguration());
         BootstrapHandler bsHandler = new DefaultBootstrapHandler(bsStore, requestSender, bsSessionManager);
 
         // Try to bootstrap
@@ -110,8 +111,8 @@ public class BootstrapHandlerTest {
         // and a config store with and an empty config for the expected endpoint
         MockBootstrapSessionManager bsSessionManager = new MockBootstrapSessionManager(true);
         MockRequestSender requestSender = new MockRequestSender(Mode.NO_RESPONSE);
-        EditableBootstrapConfigStore bsStore = new InMemoryBootstrapConfigStore();
-        bsStore.add("endpoint", new BootstrapConfig());
+        EditableBootstrapConfigurationStore bsStore = new InMemoryBootstrapConfigurationStore();
+        bsStore.add("endpoint", new BootstrapConfiguration());
         BootstrapHandler bsHandler = new DefaultBootstrapHandler(bsStore, requestSender, bsSessionManager,
                 DefaultBootstrapHandler.DEFAULT_TIMEOUT);