Skip to content

Commit

Permalink
Add VersionedModelProvider, it adds object versioning support for server
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Aug 12, 2019
1 parent fe8512e commit c8553cf
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import org.eclipse.leshan.core.model.ObjectLoader;

/**
* A static model provider which uses the default model embedded in Leshan.
* A versioned model provider which uses the default model embedded in Leshan.
*
* @see VersionedModelProvider
*/
public class StandardModelProvider extends StaticModelProvider {
public class StandardModelProvider extends VersionedModelProvider {

public StandardModelProvider() {
super(ObjectLoader.loadDefault());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
import org.eclipse.leshan.server.registration.Registration;

/**
* A LwM2mModelProvider which uses only one model for all registered clients.
* A LwM2mModelProvider which uses only one model for all registered clients without taking into account object version
* provided at registration.
*/
public class StaticModelProvider implements LwM2mModelProvider {
private final LwM2mModel model;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright (c) 2019 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
*******************************************************************************/
package org.eclipse.leshan.server.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.LwM2mModelRepository;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.server.registration.Registration;

/**
* A LwM2mModelProvider which supports object versioning. It returns a LwM2mModel taking into account object version
* given in registration.
*/
public class VersionedModelProvider implements LwM2mModelProvider {

private LwM2mModelRepository repository;

public VersionedModelProvider(Collection<ObjectModel> objectModels) {
this.repository = new LwM2mModelRepository(objectModels);
}

public VersionedModelProvider(LwM2mModelRepository repository) {
this.repository = repository;
}

@Override
public LwM2mModel getObjectModel(Registration registration) {
return new DynamicModel(registration);
}

private class DynamicModel implements LwM2mModel {

private final Registration registration;

public DynamicModel(Registration registration) {
this.registration = registration;
}

@Override
public ResourceModel getResourceModel(int objectId, int resourceId) {
ObjectModel objectModel = getObjectModel(objectId);
if (objectModel != null)
return objectModel.resources.get(resourceId);
else
return null;
}

@Override
public ObjectModel getObjectModel(int objectId) {
String version = registration.getSupportedVersion(objectId);
if (version != null) {
return repository.getObjectModel(objectId, version);
}
return null;
}

@Override
public Collection<ObjectModel> getObjectModels() {
Map<Integer, String> supportedObjects = registration.getSupportedObject();
Collection<ObjectModel> result = new ArrayList<>(supportedObjects.size());
for (Entry<Integer, String> supportedObject : supportedObjects.entrySet()) {
ObjectModel objectModel = repository.getObjectModel(supportedObject.getKey(),
supportedObject.getValue());
if (objectModel != null)
result.add(objectModel);
}
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.leshan.Link;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.util.StringUtils;
Expand Down Expand Up @@ -69,6 +74,10 @@ public class Registration implements Serializable {

private final Link[] objectLinks;

// Lazy Loaded map of supported object (object id => version)
// built from objectLinks
private final AtomicReference<Map<Integer, String>> supportedObjects;

private final Map<String, String> additionalRegistrationAttributes;

/** The location where LWM2M objects are hosted on the device */
Expand All @@ -78,9 +87,8 @@ public class Registration implements Serializable {

protected Registration(String id, String endpoint, Identity identity, String lwM2mVersion, Long lifetimeInSec,
String smsNumber, BindingMode bindingMode, Link[] objectLinks,
InetSocketAddress registrationEndpointAddress,

Date registrationDate, Date lastUpdate, Map<String, String> additionalRegistrationAttributes) {
InetSocketAddress registrationEndpointAddress, Date registrationDate, Date lastUpdate,
Map<String, String> additionalRegistrationAttributes, Map<Integer, String> supportedObjects) {

Validate.notNull(id);
Validate.notEmpty(endpoint);
Expand All @@ -94,7 +102,7 @@ protected Registration(String id, String endpoint, Identity identity, String lwM
this.registrationEndpointAddress = registrationEndpointAddress;

this.objectLinks = objectLinks;
// extract the root objects path from the object links
// Parse object link to extract root path.
String rootPath = "/";
if (objectLinks != null) {
for (Link link : objectLinks) {
Expand All @@ -104,8 +112,10 @@ protected Registration(String id, String endpoint, Identity identity, String lwM
}
}
}
if (!rootPath.endsWith("/"))
rootPath = rootPath + "/";
this.rootPath = rootPath;

this.supportedObjects = new AtomicReference<Map<Integer, String>>(supportedObjects);
this.lifeTimeInSec = lifetimeInSec == null ? DEFAULT_LIFETIME_IN_SEC : lifetimeInSec;
this.lwM2mVersion = lwM2mVersion == null ? DEFAULT_LWM2M_VERSION : lwM2mVersion;
this.bindingMode = bindingMode == null ? BindingMode.U : bindingMode;
Expand Down Expand Up @@ -305,6 +315,26 @@ public boolean usesQueueMode() {
return bindingMode.equals(BindingMode.UQ) || bindingMode.equals(BindingMode.UQS);
}

/**
* @return the supported version of the object with the id {@code objectid}. If the object is not supported return
* {@code null}
*/
public String getSupportedVersion(Integer objectid) {
return getSupportedObject().get(objectid);
}

/**
* @return a map from {@code objectId} => {@code supportedVersion} for each supported objects. supported.
*/
public Map<Integer, String> getSupportedObject() {
Map<Integer, String> objects = supportedObjects.get();
if (objects != null)
return objects;

supportedObjects.compareAndSet(null, Collections.unmodifiableMap(getSupportedObject(rootPath, objectLinks)));
return supportedObjects.get();
}

@Override
public String toString() {
return String.format(
Expand Down Expand Up @@ -339,6 +369,46 @@ public boolean equals(Object obj) {
}
}

/**
* Build a Map (object Id => object Version) from root path and registration object links.
*/
public static Map<Integer, String> getSupportedObject(String rootPath, Link[] objectLinks) {
Map<Integer, String> objects = new HashMap<>();
for (Link link : objectLinks) {
if (link != null) {
Pattern p = Pattern.compile("^\\Q" + rootPath + "\\E(\\d+)(?:/\\d+)*$");
Matcher m = p.matcher(link.getUrl());
if (m.matches()) {
try {
// extract object id and version
int objectId = Integer.parseInt(m.group(1));
Object version = link.getAttributes().get(Attribute.OBJECT_VERSION);
String currentVersion = objects.get(objectId);

// store it in map
if (currentVersion == null) {
// we never find version for this object add it
if (version instanceof String) {
objects.put(objectId, (String) version);
} else {
objects.put(objectId, ObjectModel.DEFAULT_VERSION);
}
} else {
// if version is already set, we override it only if new version is not DEFAULT_VERSION
if (version instanceof String && !version.equals(ObjectModel.DEFAULT_VERSION)) {
objects.put(objectId, (String) version);
}
}
} catch (NumberFormatException e) {
// This should not happened except maybe if the number in url is too long...
// In this case we just ignore it because this is not an object id.
}
}
}
}
return objects;
}

public static class Builder {
private final String registrationId;
private final String endpoint;
Expand All @@ -352,6 +422,7 @@ public static class Builder {
private BindingMode bindingMode;
private String lwM2mVersion;
private Link[] objectLinks;
private Map<Integer, String> supportedObjects;
private Map<String, String> additionalRegistrationAttributes;

public Builder(String registrationId, String endpoint, Identity identity,
Expand Down Expand Up @@ -403,6 +474,11 @@ public Builder objectLinks(Link[] objectLinks) {
return this;
}

public Builder supportedObjects(Map<Integer, String> supportedObjects) {
this.supportedObjects = Collections.unmodifiableMap(supportedObjects);
return this;
}

public Builder additionalRegistrationAttributes(Map<String, String> additionalRegistrationAttributes) {
this.additionalRegistrationAttributes = additionalRegistrationAttributes;
return this;
Expand All @@ -412,9 +488,8 @@ public Registration build() {
return new Registration(Builder.this.registrationId, Builder.this.endpoint, Builder.this.identity,
Builder.this.lwM2mVersion, Builder.this.lifeTimeInSec, Builder.this.smsNumber, this.bindingMode,
this.objectLinks, this.registrationEndpointAddress, this.registrationDate, this.lastUpdate,
this.additionalRegistrationAttributes);
this.additionalRegistrationAttributes, this.supportedObjects);
}

}

}
Loading

0 comments on commit c8553cf

Please sign in to comment.