Repository: eclipse/leshan
Pull request #642 (2019-01-18 14:47:39)

Enhance ObjectInitializer
Add a `createAll` which will create enabler for each configured object.
Stop to create instance or dummy/demo instance implicitly. 


Patch:
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
index d9f6402b..546c072e 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
@@ -154,7 +154,7 @@ public class LeshanClientBuilder {
                     Security.noSec("coap://leshan.eclipse.org:5683", 12345));
             initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, 5 * 60, BindingMode.U, false));
             initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", "model12345", "12345", "U"));
-            objectEnablers = initializer.createMandatory();
+            objectEnablers = initializer.createAll();
         }
         if (coapConfig == null) {
             coapConfig = createDefaultNetworkConfig();
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
index 923e4a4a..8ea611e7 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
@@ -49,6 +49,10 @@ public class Device extends BaseInstanceEnabler {
     private String timezone = TimeZone.getDefault().getID();
     private String utcOffset = new SimpleDateFormat("X").format(Calendar.getInstance().getTime());
 
+    public Device() {
+        // should never be used
+    }
+
     public Device(String manufacturer, String modelNumber, String serialNumber, String supportedBinding) {
         this.manufacturer = manufacturer;
         this.modelNumber = modelNumber;
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/DummyInstanceEnabler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/DummyInstanceEnabler.java
new file mode 100644
index 00000000..8aaae343
--- /dev/null
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/DummyInstanceEnabler.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * 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 v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * 
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.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
+ *     Kai Hudalla (Bosch Software Innovations GmbH) - check resource ID when executing resources
+ *     Achim Kraus (Bosch Software Innovations GmbH) - add reset() for 
+ *                                                     REPLACE/UPDATE implementation
+ *******************************************************************************/
+package org.eclipse.leshan.client.resource;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.leshan.client.request.ServerIdentity;
+import org.eclipse.leshan.core.model.ObjectModel;
+import org.eclipse.leshan.core.model.ResourceModel;
+import org.eclipse.leshan.core.node.LwM2mMultipleResource;
+import org.eclipse.leshan.core.response.ExecuteResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DummyInstanceEnabler extends SimpleInstanceEnabler {
+
+    private static Logger LOG = LoggerFactory.getLogger(DummyInstanceEnabler.class);
+
+    public DummyInstanceEnabler() {
+    }
+
+    public DummyInstanceEnabler(int id) {
+        super(id);
+    }
+
+    @Override
+    public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
+        if (getModel().resources.containsKey(resourceid)) {
+            LOG.info("Executing resource [{}] with params [{}]", resourceid, params);
+            return ExecuteResponse.success();
+        } else {
+            return ExecuteResponse.notFound();
+        }
+    }
+
+    @Override
+    public void onDelete(ServerIdentity identity) {
+        LOG.info("Instance {} from object {} ({}) deleted.", getId(), getModel().name, getModel().id);
+    }
+
+    @Override
+    protected LwM2mMultipleResource initializeMultipleResource(ObjectModel objectModel, ResourceModel resourceModel) {
+        Map<Integer, Object> values = new HashMap<>();
+        switch (resourceModel.type) {
+        case STRING:
+            values.put(0, createDefaultStringValueFor(objectModel, resourceModel));
+            break;
+        case BOOLEAN:
+            values.put(0, createDefaultBooleanValueFor(objectModel, resourceModel));
+            values.put(1, createDefaultBooleanValueFor(objectModel, resourceModel));
+            break;
+        case INTEGER:
+            values.put(0, createDefaultIntegerValueFor(objectModel, resourceModel));
+            values.put(1, createDefaultIntegerValueFor(objectModel, resourceModel));
+            break;
+        case FLOAT:
+            values.put(0, createDefaultFloatValueFor(objectModel, resourceModel));
+            values.put(1, createDefaultFloatValueFor(objectModel, resourceModel));
+            break;
+        case TIME:
+            values.put(0, createDefaultDateValueFor(objectModel, resourceModel));
+            break;
+        case OPAQUE:
+            values.put(0, createDefaultOpaqueValueFor(objectModel, resourceModel));
+            break;
+        default:
+            // this should not happened
+            values = null;
+            break;
+        }
+        if (values != null)
+            return LwM2mMultipleResource.newResource(resourceModel.id, values, resourceModel.type);
+        else
+            return null;
+    }
+
+    @Override
+    protected String createDefaultStringValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return resourceModel.name;
+    }
+
+    @Override
+    protected long createDefaultIntegerValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return (long) (Math.random() * 100 % 101);
+    }
+
+    @Override
+    protected boolean createDefaultBooleanValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return Math.random() * 100 % 2 == 0;
+    }
+
+    @Override
+    protected Date createDefaultDateValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return new Date();
+    }
+
+    @Override
+    protected double createDefaultFloatValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return Math.random() * 100;
+    }
+
+    @Override
+    protected byte[] createDefaultOpaqueValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return ("Default " + resourceModel.name).getBytes();
+    }
+}
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectsInitializer.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectsInitializer.java
index 621059b0..7b934d7f 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectsInitializer.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectsInitializer.java
@@ -16,10 +16,11 @@
 package org.eclipse.leshan.client.resource;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.leshan.core.model.LwM2mModel;
 import org.eclipse.leshan.core.model.ObjectLoader;
@@ -29,13 +30,6 @@ import org.eclipse.leshan.util.Validate;
 
 public class ObjectsInitializer {
 
-    protected LwM2mInstanceEnablerFactory defaultFactory = new BaseInstanceEnablerFactory() {
-        @Override
-        public LwM2mInstanceEnabler create() {
-            return new SimpleInstanceEnabler();
-        }
-    };
-
     protected Map<Integer, LwM2mInstanceEnablerFactory> factories = new HashMap<>();
     protected Map<Integer, LwM2mInstanceEnabler[]> instances = new HashMap<>();
     protected Map<Integer, ContentFormat> defaultContentFormat = new HashMap<>();
@@ -95,24 +89,83 @@ public class ObjectsInitializer {
         this.instances.put(objectId, instances);
     }
 
+    /**
+     * Add dummy instance for each given <code>objectId</code>. ObjectId can be repeated to create several dummy
+     * instances. A dummy instance is just a very simple instance implementation which respect the object model and
+     * return some random values. A good way to begin to test Leshan client but not adapted to production environment.
+     * 
+     * @param objectId
+     */
+    public void setDummyInstancesForObject(int... objectIds) {
+        // create a map (id => nb instances)
+        Map<Integer, Integer> idToNbInstance = new HashMap<>();
+        for (int objectid : objectIds) {
+            // get current number of instance
+            Integer nbInstance = idToNbInstance.get(objectid);
+            if (nbInstance == null)
+                nbInstance = 0;
+
+            // add a new instance
+            idToNbInstance.put(objectid, nbInstance + 1);
+        }
+
+        // create dummy instances for each object
+        for (Map.Entry<Integer, Integer> entry : idToNbInstance.entrySet()) {
+            int objectid = entry.getKey();
+
+            // create instance Array;
+            Integer nbInstances = entry.getValue();
+            DummyInstanceEnabler[] instances = new DummyInstanceEnabler[nbInstances];
+            for (int i = 0; i < instances.length; i++) {
+                instances[i] = new DummyInstanceEnabler();
+            }
+
+            // set instances for current object id
+            setInstancesForObject(objectid, instances);
+        }
+    }
+
     public void setDefaultContentFormat(int objectId, ContentFormat format) {
         defaultContentFormat.put(objectId, format);
     }
 
-    public List<LwM2mObjectEnabler> createMandatory() {
-        Collection<ObjectModel> objectModels = model.getObjectModels();
+    /**
+     * Create an {@link LwM2mObjectEnabler} for each object to which you associated an "instances", "object class" or
+     * "factory".
+     * 
+     * @return a list of LwM2MObjectEnabler
+     * 
+     * @see ObjectsInitializer#setInstancesForObject(int, LwM2mInstanceEnabler...)
+     * @see ObjectsInitializer#setClassForObject(int, Class)
+     * @see ObjectsInitializer#setFactoryForObject(int, LwM2mInstanceEnablerFactory)
+     */
+    public List<LwM2mObjectEnabler> createAll() {
+        // collect object ids which is set
+        Set<Integer> ids = new HashSet<>();
+        ids.addAll(factories.keySet());
+        ids.addAll(instances.keySet());
 
-        List<LwM2mObjectEnabler> enablers = new ArrayList<>();
-        for (ObjectModel objectModel : objectModels) {
-            if (objectModel.mandatory) {
-                ObjectEnabler objectEnabler = createNodeEnabler(objectModel);
-                if (objectEnabler != null)
-                    enablers.add(objectEnabler);
-            }
+        // create objects
+        int[] idArray = new int[ids.size()];
+        int i = 0;
+        for (Integer id : ids) {
+            idArray[i] = id;
+            i++;
         }
-        return enablers;
+        return create(idArray);
     }
 
+    /**
+     * Create an {@link LwM2mObjectEnabler} for the given <code>objectId</code>.
+     * 
+     * An "instances", "object class" or "factory" MUST have been associated before.
+     * 
+     * @return a LwM2MObjectEnabler
+     * 
+     * @see ObjectsInitializer#setInstancesForObject(int, LwM2mInstanceEnabler...)
+     * @see ObjectsInitializer#setClassForObject(int, Class)
+     * @see ObjectsInitializer#setFactoryForObject(int, LwM2mInstanceEnablerFactory)
+     */
     public LwM2mObjectEnabler create(int objectId) {
         ObjectModel objectModel = model.getObjectModel(objectId);
         if (objectModel == null) {
@@ -122,6 +175,17 @@ public class ObjectsInitializer {
         return createNodeEnabler(objectModel);
     }
 
+    /**
+     * Create an {@link LwM2mObjectEnabler} for each given <code>objectId</code>.
+     * 
+     * An "instances", "object class" or "factory" MUST have been associated before.
+     * 
+     * @return a list of LwM2MObjectEnabler
+     * 
+     * @see ObjectsInitializer#setClassForObject(int, Class)
+     * @see ObjectsInitializer#setFactoryForObject(int, LwM2mInstanceEnablerFactory)
+     * @see ObjectsInitializer#setInstancesForObject(int, LwM2mInstanceEnabler...)
+     */
     public List<LwM2mObjectEnabler> create(int... objectId) {
         List<LwM2mObjectEnabler> enablers = new ArrayList<>();
         for (int anObjectId : objectId) {
@@ -152,8 +216,9 @@ public class ObjectsInitializer {
                 }
             }
         }
-        // default class :
-        return defaultFactory;
+        throw new IllegalStateException(String.format(
+                "Unable to create factory for %s object (%d) : a factory, a class or an instance with a default constructor should be associated to",
+                objectModel.name, objectModel.id));
     }
 
     protected ObjectEnabler createNodeEnabler(ObjectModel objectModel) {
@@ -184,12 +249,6 @@ public class ObjectsInitializer {
         LwM2mInstanceEnabler[] newInstances = new LwM2mInstanceEnabler[0];
         if (instances.containsKey(objectModel.id)) {
             newInstances = instances.get(objectModel.id);
-        } else {
-            // we create instance from class only for single object
-            if (!objectModel.multiple) {
-                LwM2mInstanceEnablerFactory instanceFactory = getFactoryFor(objectModel);
-                newInstances = new LwM2mInstanceEnabler[] { instanceFactory.create(objectModel, 0, null) };
-            }
         }
         return newInstances;
     }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/SimpleInstanceEnabler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/SimpleInstanceEnabler.java
index 3bd979ad..9f376d00 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/SimpleInstanceEnabler.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/SimpleInstanceEnabler.java
@@ -18,6 +18,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.client.resource;
 
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -31,12 +32,9 @@ import org.eclipse.leshan.core.node.LwM2mSingleResource;
 import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class SimpleInstanceEnabler extends BaseInstanceEnabler {
 
-    private static Logger LOG = LoggerFactory.getLogger(SimpleInstanceEnabler.class);
     protected Map<Integer, LwM2mResource> resources = new HashMap<>();
 
     public SimpleInstanceEnabler() {
@@ -64,17 +62,7 @@ public class SimpleInstanceEnabler extends BaseInstanceEnabler {
 
     @Override
     public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
-        if (getModel().resources.containsKey(resourceid)) {
-            LOG.info("Executing resource [{}] with params [{}]", resourceid, params);
-            return ExecuteResponse.success();
-        } else {
-            return ExecuteResponse.notFound();
-        }
-    }
-
-    @Override
-    public void onDelete(ServerIdentity identity) {
-        LOG.info("Instance {} from object {} ({}) deleted.", getId(), getModel().name, getModel().id);
+        return ExecuteResponse.notFound();
     }
 
     @Override
@@ -89,7 +77,7 @@ public class SimpleInstanceEnabler extends BaseInstanceEnabler {
         // initialize resources
         for (ResourceModel resourceModel : objectModel.resources.values()) {
             if (resourceModel.operations.isReadable()) {
-                LwM2mResource newResource = createResource(objectModel, resourceModel);
+                LwM2mResource newResource = initializeResource(objectModel, resourceModel);
                 if (newResource != null) {
                     resources.put(newResource.getId(), newResource);
                 }
@@ -97,87 +85,67 @@ public class SimpleInstanceEnabler extends BaseInstanceEnabler {
         }
     }
 
-    protected LwM2mResource createResource(ObjectModel objectModel, ResourceModel resourceModel) {
+    protected LwM2mResource initializeResource(ObjectModel objectModel, ResourceModel resourceModel) {
         if (!resourceModel.multiple) {
-            switch (resourceModel.type) {
-            case STRING:
-                return LwM2mSingleResource.newStringResource(resourceModel.id,
-                        createDefaultStringValue(objectModel, resourceModel));
-            case BOOLEAN:
-                return LwM2mSingleResource.newBooleanResource(resourceModel.id,
-                        createDefaultBooleanValue(objectModel, resourceModel));
-            case INTEGER:
-                return LwM2mSingleResource.newIntegerResource(resourceModel.id,
-                        createDefaultIntegerValue(objectModel, resourceModel));
-            case FLOAT:
-                return LwM2mSingleResource.newFloatResource(resourceModel.id,
-                        createDefaultFloatValue(objectModel, resourceModel));
-            case TIME:
-                return LwM2mSingleResource.newDateResource(resourceModel.id,
-                        createDefaultDateValue(objectModel, resourceModel));
-            case OPAQUE:
-                return LwM2mSingleResource.newBinaryResource(resourceModel.id,
-                        createDefaultOpaqueValue(objectModel, resourceModel));
-            default:
-                // this should not happened
-                return null;
-            }
+            return initializeSingleResource(objectModel, resourceModel);
         } else {
-            Map<Integer, Object> values = new HashMap<>();
-            switch (resourceModel.type) {
-            case STRING:
-                values.put(0, createDefaultStringValue(objectModel, resourceModel));
-                break;
-            case BOOLEAN:
-                values.put(0, createDefaultBooleanValue(objectModel, resourceModel));
-                values.put(1, createDefaultBooleanValue(objectModel, resourceModel));
-                break;
-            case INTEGER:
-                values.put(0, createDefaultIntegerValue(objectModel, resourceModel));
-                values.put(1, createDefaultIntegerValue(objectModel, resourceModel));
-                break;
-            case FLOAT:
-                values.put(0, createDefaultFloatValue(objectModel, resourceModel));
-                values.put(1, createDefaultFloatValue(objectModel, resourceModel));
-                break;
-            case TIME:
-                values.put(0, createDefaultDateValue(objectModel, resourceModel));
-                break;
-            case OPAQUE:
-                values.put(0, createDefaultOpaqueValue(objectModel, resourceModel));
-                break;
-            default:
-                // this should not happened
-                values = null;
-                break;
-            }
-            if (values != null)
-                return LwM2mMultipleResource.newResource(resourceModel.id, values, resourceModel.type);
+            return initializeMultipleResource(objectModel, resourceModel);
+        }
+    }
+
+    protected LwM2mSingleResource initializeSingleResource(ObjectModel objectModel, ResourceModel resourceModel) {
+        switch (resourceModel.type) {
+        case STRING:
+            return LwM2mSingleResource.newStringResource(resourceModel.id,
+                    createDefaultStringValueFor(objectModel, resourceModel));
+        case BOOLEAN:
+            return LwM2mSingleResource.newBooleanResource(resourceModel.id,
+                    createDefaultBooleanValueFor(objectModel, resourceModel));
+        case INTEGER:
+            return LwM2mSingleResource.newIntegerResource(resourceModel.id,
+                    createDefaultIntegerValueFor(objectModel, resourceModel));
+        case FLOAT:
+            return LwM2mSingleResource.newFloatResource(resourceModel.id,
+                    createDefaultFloatValueFor(objectModel, resourceModel));
+        case TIME:
+            return LwM2mSingleResource.newDateResource(resourceModel.id,
+                    createDefaultDateValueFor(objectModel, resourceModel));
+        case OPAQUE:
+            return LwM2mSingleResource.newBinaryResource(resourceModel.id,
+                    createDefaultOpaqueValueFor(objectModel, resourceModel));
+        default:
+            // this should not happened
+            return null;
         }
-        return null;
     }
 
-    protected String createDefaultStringValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return resourceModel.name;
+    protected LwM2mMultipleResource initializeMultipleResource(ObjectModel objectModel, ResourceModel resourceModel) {
+        // no default value
+        Map<Integer, ?> emptyMap = Collections.emptyMap();
+        return LwM2mMultipleResource.newResource(resourceModel.id, emptyMap, resourceModel.type);
+    }
+
+    protected String createDefaultStringValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return "";
     }
 
-    protected long createDefaultIntegerValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return (long) (Math.random() * 100 % 101);
+    protected long createDefaultIntegerValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return 0;
     }
 
-    protected boolean createDefaultBooleanValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return Math.random() * 100 % 2 == 0;
+    protected boolean createDefaultBooleanValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return false;
     }
 
-    protected Date createDefaultDateValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return new Date();
+    protected Date createDefaultDateValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return new Date(0);
     }
 
-    protected double createDefaultFloatValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return Math.random() * 100;
+    protected double createDefaultFloatValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return 0;
     }
 
-    protected byte[] createDefaultOpaqueValue(ObjectModel objectModel, ResourceModel resourceModel) {
-        return ("Default " + resourceModel.name).getBytes();
+    protected byte[] createDefaultOpaqueValueFor(ObjectModel objectModel, ResourceModel resourceModel) {
+        return new byte[0];
     }
 }
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 b981f0ed..85c6ba2f 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
@@ -361,8 +361,7 @@ public class LeshanClientDemo {
         initializer.setClassForObject(DEVICE, MyDevice.class);
         initializer.setInstancesForObject(LOCATION, locationInstance);
         initializer.setInstancesForObject(OBJECT_ID_TEMPERATURE_SENSOR, new RandomTemperatureSensor());
-        List<LwM2mObjectEnabler> enablers = initializer.create(SECURITY, SERVER, DEVICE, LOCATION,
-                OBJECT_ID_TEMPERATURE_SENSOR);
+        List<LwM2mObjectEnabler> enablers = initializer.createAll();
 
         // Create CoAP Config
         NetworkConfig coapConfig;


Test patch:
diff --git a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/BaseInstanceEnablerFactoryTest.java b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/BaseInstanceEnablerFactoryTest.java
index 44d1c79e..9cc53478 100644
--- a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/BaseInstanceEnablerFactoryTest.java
+++ b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/BaseInstanceEnablerFactoryTest.java
@@ -24,7 +24,7 @@ import java.util.List;
 import org.eclipse.leshan.LwM2mId;
 import org.eclipse.leshan.client.resource.BaseInstanceEnablerFactory;
 import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
-import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.core.model.LwM2mModel;
 import org.eclipse.leshan.core.model.ObjectLoader;
 import org.junit.Test;
@@ -43,7 +43,7 @@ public class BaseInstanceEnablerFactoryTest {
             @Override
             public LwM2mInstanceEnabler create() {
                 // do no respect the contract and set a bad id;
-                return new SimpleInstanceEnabler(id + 1);
+                return new DummyInstanceEnabler(id + 1);
             }
         };
 
@@ -56,7 +56,7 @@ public class BaseInstanceEnablerFactoryTest {
 
             @Override
             public LwM2mInstanceEnabler create() {
-                return new SimpleInstanceEnabler();
+                return new DummyInstanceEnabler();
             }
         };
 
diff --git a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
index 1b85bf56..dbf6db51 100644
--- a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
+++ b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
@@ -28,7 +28,7 @@ import org.eclipse.leshan.client.resource.BaseInstanceEnablerFactory;
 import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectEnabler;
-import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.core.model.ObjectLoader;
 import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.request.ContentFormat;
@@ -175,7 +175,7 @@ public class LinkFormatHelperTest {
         BaseInstanceEnablerFactory factory = new BaseInstanceEnablerFactory() {
             @Override
             public LwM2mInstanceEnabler create() {
-                return new SimpleInstanceEnabler();
+                return new DummyInstanceEnabler();
             }
         };
 
diff --git a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/ObjectEnablerTest.java b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/ObjectEnablerTest.java
index 15a2933f..2cf05643 100644
--- a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/ObjectEnablerTest.java
+++ b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/ObjectEnablerTest.java
@@ -43,7 +43,7 @@ public class ObjectEnablerTest {
         assertTrue("callback delete should have been called", instanceEnabler.waitForDelete(2, TimeUnit.SECONDS));
     }
 
-    private class TestInstanceEnabler extends BaseInstanceEnabler {
+    public static class TestInstanceEnabler extends BaseInstanceEnabler {
 
         CountDownLatch onDelete = new CountDownLatch(1);
 
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 c2d2eab5..61b52c87 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
@@ -39,6 +39,7 @@ import org.eclipse.leshan.SecurityMode;
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
 import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.request.Identity;
@@ -185,8 +186,9 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         initializer.setInstancesForObject(LwM2mId.SECURITY, security);
         initializer.setInstancesForObject(LwM2mId.DEVICE,
                 new Device("Eclipse Leshan", IntegrationTestHelper.MODEL_NUMBER, "12345", "U"));
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.add(initializer.create(2));
+        initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
+        initializer.setClassForObject(LwM2mId.SERVER, DummyInstanceEnabler.class);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Create Leshan Client
         LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
index 200a2b80..d6b90155 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
@@ -34,6 +34,7 @@ import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
 import org.eclipse.leshan.client.request.ServerIdentity;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.model.LwM2mModel;
@@ -122,6 +123,26 @@ public class IntegrationTestHelper {
         createClient(null);
     }
 
+    public static class TestDevice extends Device {
+
+        public TestDevice() {
+            super();
+        }
+
+        public TestDevice(String manufacturer, String modelNumber, String serialNumber, String supportedBinding) {
+            super(manufacturer, modelNumber, serialNumber, supportedBinding);
+        }
+
+        @Override
+        public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
+            if (resourceid == 4) {
+                return ExecuteResponse.success();
+            } else {
+                return super.execute(identity, resourceid, params);
+            }
+        }
+    }
+
     public void createClient(Map<String, String> additionalAttributes) {
         // Create objects Enabler
         ObjectsInitializer initializer = new ObjectsInitializer(new LwM2mModel(createObjectModels()));
@@ -129,18 +150,10 @@ public class IntegrationTestHelper {
                 "coap://" + server.getUnsecuredAddress().getHostString() + ":" + server.getUnsecuredAddress().getPort(),
                 12345));
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U") {
-            @Override
-            public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
-                if (resourceid == 4) {
-                    return ExecuteResponse.success();
-                } else {
-                    return super.execute(identity, resourceid, params);
-                }
-            }
-        });
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.addAll(initializer.create(2, 2000));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
+        initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
+        initializer.setDummyInstancesForObject(2000);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Build Client
         LeshanClientBuilder builder = new LeshanClientBuilder(currentEndpointIdentifier.get());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
index d05ba0a9..1f7c301f 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
@@ -24,15 +24,13 @@ import java.util.concurrent.TimeoutException;
 
 import org.eclipse.leshan.LwM2mId;
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
-import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
-import org.eclipse.leshan.client.request.ServerIdentity;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.model.LwM2mModel;
 import org.eclipse.leshan.core.request.BindingMode;
-import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
 import org.eclipse.leshan.server.queue.StaticClientAwakeTimeProvider;
@@ -68,18 +66,11 @@ public class QueueModeIntegrationTestHelper extends IntegrationTestHelper {
                 "coap://" + server.getUnsecuredAddress().getHostString() + ":" + server.getUnsecuredAddress().getPort(),
                 12345));
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.UQ, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "UQ") {
-            @Override
-            public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
-                if (resourceid == 4) {
-                    return ExecuteResponse.success();
-                } else {
-                    return super.execute(identity, resourceid, params);
-                }
-            }
-        });
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.addAll(initializer.create(2, 2000));
+        initializer.setInstancesForObject(LwM2mId.DEVICE,
+                new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345", "UQ"));
+        initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
+        initializer.setDummyInstancesForObject(2000);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Build Client
         LeshanClientBuilder builder = new LeshanClientBuilder(currentEndpointIdentifier.get());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
index 4c40f3f4..30598948 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
@@ -50,6 +50,7 @@ import org.eclipse.leshan.client.californium.LeshanClientBuilder;
 import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
+import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.californium.EndpointFactory;
@@ -191,8 +192,8 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                         12345, GOOD_PSK_ID.getBytes(StandardCharsets.UTF_8), GOOD_PSK_KEY));
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
         initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.add(initializer.create(2));
+        initializer.setDummyInstancesForObject(LwM2mId.ACCESS_CONTROL);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
         LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint());
@@ -248,8 +249,8 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                 useServerCertificate ? serverX509Cert.getPublicKey().getEncoded() : serverPublicKey.getEncoded()));
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
         initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.add(initializer.create(2));
+        initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
         LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint());
@@ -286,8 +287,8 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                         serverCertificate.getEncoded()));
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
         initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
-        List<LwM2mObjectEnabler> objects = initializer.createMandatory();
-        objects.add(initializer.create(2));
+        initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
+        List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
         LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint());