Repository: eclipse/leshan
Pull request #1018 (2021-06-03 09:35:40)

Add a way to add application Data 
Some Application Data can be attached to the Registration at Authorizing time using :
```
return new Registration.Builder(registration).applicationData(myAppData).build();
````
in `Authorizer`.


Patch:
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
index 3a268739..798eac75 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
@@ -39,6 +39,7 @@ import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.util.StringUtils;
 import org.eclipse.leshan.core.util.Validate;
+import org.eclipse.leshan.server.security.Authorizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -88,6 +89,8 @@ public class Registration {
 
     private final Date lastUpdate;
 
+    private final Map<String, String> applicationData;
+
     protected Registration(Builder builder) {
 
         Validate.notNull(builder.registrationId);
@@ -116,6 +119,7 @@ public class Registration {
         smsNumber = builder.smsNumber;
         additionalRegistrationAttributes = builder.additionalRegistrationAttributes;
 
+        applicationData = builder.applicationData;
     }
 
     public String getId() {
@@ -332,13 +336,20 @@ public class Registration {
         return supportedObjects;
     }
 
+    /**
+     * @return Some application data which could have been added at Registration by the {@link Authorizer}
+     */
+    public Map<String, String> getApplicationData() {
+        return applicationData;
+    }
+
     @Override
     public String toString() {
         return String.format(
-                "Registration [registrationDate=%s, identity=%s, lifeTimeInSec=%s, smsNumber=%s, lwM2mVersion=%s, bindingMode=%s, queueMode=%s, endpoint=%s, id=%s, objectLinks=%s, additionalRegistrationAttributes=%s, rootPath=%s, supportedContentFormats=%s, supportedObjects=%s, availableInstances=%s, lastUpdate=%s]",
+                "Registration [registrationDate=%s, identity=%s, lifeTimeInSec=%s, smsNumber=%s, lwM2mVersion=%s, bindingMode=%s, queueMode=%s, endpoint=%s, id=%s, objectLinks=%s, additionalRegistrationAttributes=%s, rootPath=%s, supportedContentFormats=%s, supportedObjects=%s, availableInstances=%s, lastUpdate=%s, applicationData=%s]",
                 registrationDate, identity, lifeTimeInSec, smsNumber, lwM2mVersion, bindingMode, queueMode, endpoint,
                 id, Arrays.toString(objectLinks), additionalRegistrationAttributes, rootPath, supportedContentFormats,
-                supportedObjects, availableInstances, lastUpdate);
+                supportedObjects, availableInstances, lastUpdate, applicationData);
     }
 
     @Override
@@ -347,6 +358,7 @@ public class Registration {
         int result = 1;
         result = prime * result
                 + ((additionalRegistrationAttributes == null) ? 0 : additionalRegistrationAttributes.hashCode());
+        result = prime * result + ((applicationData == null) ? 0 : applicationData.hashCode());
         result = prime * result + ((availableInstances == null) ? 0 : availableInstances.hashCode());
         result = prime * result + ((bindingMode == null) ? 0 : bindingMode.hashCode());
         result = prime * result + ((endpoint == null) ? 0 : endpoint.hashCode());
@@ -379,6 +391,11 @@ public class Registration {
                 return false;
         } else if (!additionalRegistrationAttributes.equals(other.additionalRegistrationAttributes))
             return false;
+        if (applicationData == null) {
+            if (other.applicationData != null)
+                return false;
+        } else if (!applicationData.equals(other.applicationData))
+            return false;
         if (availableInstances == null) {
             if (other.availableInstances != null)
                 return false;
@@ -469,10 +486,38 @@ public class Registration {
         private Map<Integer, String> supportedObjects;
         private Set<LwM2mPath> availableInstances;
         private Map<String, String> additionalRegistrationAttributes;
+        private Map<String, String> applicationData;
 
         // builder setting
         private boolean extractData; // if true extract data from objectLinks
 
+        public Builder(Registration registration) {
+
+            // mandatory params
+            registrationId = registration.id;
+            identity = registration.identity;
+            endpoint = registration.endpoint;
+
+            // object links related params
+            objectLinks = registration.objectLinks;
+            rootPath = registration.rootPath;
+            supportedContentFormats = registration.supportedContentFormats;
+            supportedObjects = registration.supportedObjects;
+            availableInstances = registration.availableInstances;
+
+            // other params
+            lifeTimeInSec = registration.lifeTimeInSec;
+            lwM2mVersion = registration.lwM2mVersion;
+            bindingMode = registration.bindingMode;
+            queueMode = registration.queueMode;
+            registrationDate = registration.registrationDate;
+            lastUpdate = registration.lastUpdate;
+            smsNumber = registration.smsNumber;
+            additionalRegistrationAttributes = registration.additionalRegistrationAttributes;
+
+            applicationData = registration.applicationData;
+        }
+
         public Builder(String registrationId, String endpoint, Identity identity) {
 
             Validate.notNull(registrationId);
@@ -561,6 +606,11 @@ public class Registration {
             return this;
         }
 
+        public Builder applicationData(Map<String, String> applicationData) {
+            this.applicationData = applicationData;
+            return this;
+        }
+
         private void extractDataFromObjectLinks() {
             if (objectLinks != null) {
                 // Define default RootPath;
@@ -696,6 +746,11 @@ public class Registration {
                 additionalRegistrationAttributes = Collections
                         .unmodifiableMap(new HashMap<>(additionalRegistrationAttributes));
             }
+            if (applicationData == null || applicationData.isEmpty()) {
+                applicationData = Collections.emptyMap();
+            } else {
+                applicationData = Collections.unmodifiableMap(new HashMap<>(applicationData));
+            }
 
             // Create Registration
             return new Registration(this);
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/Authorizer.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/Authorizer.java
index be56cd84..efff4386 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/Authorizer.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/Authorizer.java
@@ -26,9 +26,15 @@ import org.eclipse.leshan.server.registration.Registration;
 public interface Authorizer {
 
     /**
-     * Return the registration if this request should be handle by the LWM2M Server. When <code>null</code> is returned
+     * Return the registration if this request should be handled by the LWM2M Server. When <code>null</code> is returned
      * the LWM2M server will stop to handle this request and will respond with a {@link ResponseCode#FORBIDDEN} or
      * {@link ResponseCode#BAD_REQUEST}.
+     * <p>
+     * Some Application Data could be attached to the Registration using :
+     * 
+     * <pre>
+     * return new Registration.Builder(registration).applicationData(myAppData).build();
+     * </pre>
      * 
      * @param request the request received
      * @param registration the registration linked to the received request.<br>
diff --git a/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java b/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
index 3dd634ab..b056fdd3 100644
--- a/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
+++ b/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
@@ -92,6 +92,13 @@ public class RegistrationSerDes {
             so.add(supportedObject.getKey().toString(), supportedObject.getValue());
         }
         o.add("suppObjs", so);
+
+        // handle application data
+        JsonObject ad = Json.object();
+        for (Entry<String, String> appData : r.getApplicationData().entrySet()) {
+            ad.add(appData.getKey(), appData.getValue());
+        }
+        o.add("appdata", ad);
         return o;
     }
 
@@ -146,6 +153,8 @@ public class RegistrationSerDes {
             linkObjs[i] = o;
         }
         b.objectLinks(linkObjs);
+
+        // additional attributes
         Map<String, String> addAttr = new HashMap<>();
         JsonObject o = (JsonObject) jObj.get("addAttr");
         for (String k : o.names()) {
@@ -178,6 +187,19 @@ public class RegistrationSerDes {
             b.supportedObjects(supportedObject);
         }
 
+        // app data
+        Map<String, String> appData = new HashMap<>();
+        JsonObject oap = (JsonObject) jObj.get("appdata");
+        for (String k : oap.names()) {
+            JsonValue jv = oap.get(k);
+            if (jv.isNull()) {
+                appData.put(k, null);
+            } else {
+                appData.put(k, jv.asString());
+            }
+        }
+        b.applicationData(appData);
+
         return b.build();
     }
 


Test patch:
diff --git a/leshan-server-redis/src/test/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDesTest.java b/leshan-server-redis/src/test/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDesTest.java
index a0215e6b..86b8f6c1 100644
--- a/leshan-server-redis/src/test/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDesTest.java
+++ b/leshan-server-redis/src/test/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDesTest.java
@@ -53,4 +53,32 @@ public class RegistrationSerDesTest {
 
         assertEquals(r, r2);
     }
+
+    @Test
+    public void ser_and_des_are_equals_with_app_data() throws Exception {
+        Link[] objs = new Link[2];
+        Map<String, Object> att = new HashMap<>();
+        att.put("ts", 12);
+        att.put("rt", "test");
+        att.put("hb", null);
+        objs[0] = new Link("/0/1024/2", att, Object.class);
+        objs[1] = new Link("/0/2");
+
+        Map<String, String> appData = new HashMap<>();
+        appData.put("string", "string test");
+        appData.put("null", null);
+
+        Registration.Builder builder = new Registration.Builder("registrationId", "endpoint",
+                Identity.unsecure(Inet4Address.getLoopbackAddress(), 1)).objectLinks(objs).rootPath("/")
+                        .supportedContentFormats(ContentFormat.TLV, ContentFormat.TEXT).applicationData(appData);
+
+        builder.registrationDate(new Date(100L));
+        builder.lastUpdate(new Date(101L));
+        Registration r = builder.build();
+
+        byte[] ser = RegistrationSerDes.bSerialize(r);
+        Registration r2 = RegistrationSerDes.deserialize(ser);
+
+        assertEquals(r, r2);
+    }
 }