Repository: eclipse/leshan
Pull request #821 (2020-03-24 15:25:26)

Delete all instance on bootstrap delete "/" except device and bs server
5.2.7.5 BOOTSTRAP DELETE, the specification says : 

> When the Delete operation is used without any parameter (i.e. without Object ID parameter), all Instances of all Objects in the LwM2M Client MUST be removed (except for the two cases mentioned above); this functionality could be used for initialization purpose before LwM2MBootstrap-Server sends Write operation(s) to the LwM2MClient.

> The two exceptions are the LwM2MBootstrap-Server Account and the single Instance of the Mandatory Device Object (ID:3) which are not affected by any Delete operation.


Patch:
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/bootstrap/BootstrapHandler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/bootstrap/BootstrapHandler.java
index ddd89a3c..eaec120d 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/bootstrap/BootstrapHandler.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/bootstrap/BootstrapHandler.java
@@ -16,8 +16,6 @@
  *******************************************************************************/
 package org.eclipse.leshan.client.bootstrap;
 
-import static org.eclipse.leshan.LwM2mId.*;
-
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -27,7 +25,6 @@ import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.servers.ServerInfo;
 import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
 import org.eclipse.leshan.core.request.BootstrapFinishRequest;
-import org.eclipse.leshan.core.request.DeleteRequest;
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
 import org.eclipse.leshan.core.response.BootstrapFinishResponse;
@@ -77,21 +74,9 @@ public class BootstrapHandler {
                 return BootstrapDeleteResponse.badRequest("not from a bootstrap server");
             }
 
-            // The spec say that delete on "/" should delete all the existing Object Instances - except LWM2M
-            // Bootstrap Server Account, (see 5.2.5.2 Bootstrap Delete)
-            // For now we only remove security and server object.
-
             // Delete all device management server
-            LwM2mObjectEnabler serverObject = objects.get(SERVER);
-            for (Integer instanceId : serverObject.getAvailableInstanceIds()) {
-                serverObject.delete(identity, new DeleteRequest(SERVER, instanceId));
-            }
-
-            // Delete all security instance (except bootstrap one)
-            // TODO do not delete bootstrap server (see 5.2.5.2 Bootstrap Delete)
-            LwM2mObjectEnabler securityObject = objects.get(SECURITY);
-            for (Integer instanceId : securityObject.getAvailableInstanceIds()) {
-                securityObject.delete(identity, new DeleteRequest(SECURITY, instanceId));
+            for (LwM2mObjectEnabler enabler : objects.values()) {
+                enabler.delete(identity, new BootstrapDeleteRequest(enabler.getId()));
             }
 
             return BootstrapDeleteResponse.success();


Test patch:
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapTest.java
index fcc0e041..693b38c4 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.*;
 import org.eclipse.leshan.LwM2mId;
 import org.eclipse.leshan.SecurityMode;
 import org.eclipse.leshan.client.request.ServerIdentity;
+import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
 import org.eclipse.leshan.core.node.LwM2mObject;
@@ -107,6 +108,46 @@ public class BootstrapTest {
                 ((LwM2mObject) response.getContent()).getInstances().isEmpty());
     }
 
+    @Test
+    public void bootstrapDeleteAll() {
+        // Create DM Server without security & start it
+        helper.createServer();
+        helper.server.start();
+
+        // Create and start bootstrap server
+        helper.createBootstrapServer(null, helper.deleteSecurityStore("/"));
+        helper.bootstrapServer.start();
+
+        // Create Client and check it is not already registered
+        ObjectsInitializer initializer = new ObjectsInitializer();
+        initializer.setInstancesForObject(LwM2mId.ACCESS_CONTROL, new SimpleInstanceEnabler());
+        initializer.setInstancesForObject(LwM2mId.CONNECTIVITY_STATISTICS, new SimpleInstanceEnabler());
+        helper.createClient(helper.withoutSecurity(), initializer);
+        helper.assertClientNotRegisterered();
+
+        // Start it and wait for bootstrap finished
+        helper.client.start();
+        helper.waitForBootstrapFinishedAtClientSide(1);
+
+        // ensure instances are deleted except device instance and bootstrap server
+        for (LwM2mObjectEnabler enabler : helper.client.getObjectTree().getObjectEnablers().values()) {
+            ReadResponse response = enabler.read(ServerIdentity.SYSTEM, new ReadRequest(enabler.getId()));
+            LwM2mObject responseValue = (LwM2mObject) response.getContent();
+            if (enabler.getId() == LwM2mId.DEVICE) {
+                assertTrue("The Device instance should still be here", responseValue.getInstances().size() == 1);
+            } else if (enabler.getId() == LwM2mId.SECURITY) {
+                assertTrue("Only bootstrap security instance should be here",
+                        ((LwM2mObject) response.getContent()).getInstances().size() == 1);
+                LwM2mObjectInstance securityInstance = responseValue.getInstances().values().iterator().next();
+                assertTrue("Only bootstrap security instance should be here",
+                        securityInstance.getResource(1).getValue() == Boolean.TRUE);
+            } else {
+                assertTrue(enabler.getObjectModel().name + " instance is not deleted",
+                        responseValue.getInstances().isEmpty());
+            }
+        }
+    }
+
     @Test
     public void bootstrapWithAcl() {
         // Create DM Server without security & start it