Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update BindingMode to be LWM2M 1.1 compliant and support Q parameter. #908

Merged
merged 9 commits into from
Nov 6, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
import org.eclipse.leshan.server.bootstrap.demo.json.BindingModeTypeAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -69,6 +72,9 @@ public JSONFileBootstrapStore(String filename) {
Validate.notEmpty(filename);
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
builder.registerTypeAdapter(new TypeToken<EnumSet<BindingMode>>() {
}.getType(), new BindingModeTypeAdapter());

this.gson = builder.create();
this.gsonType = new TypeToken<Map<String, BootstrapConfig>>() {
}.getType();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* 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.demo.json;

import java.io.IOException;
import java.util.EnumSet;

import org.eclipse.leshan.core.request.BindingMode;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

public class BindingModeTypeAdapter extends TypeAdapter<EnumSet<BindingMode>> {

@Override
public void write(JsonWriter out, EnumSet<BindingMode> value) throws IOException {
out.value(BindingMode.toString(value));
}

@Override
public EnumSet<BindingMode> read(JsonReader in) throws IOException {
return BindingMode.parse(in.nextString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
import org.eclipse.leshan.server.bootstrap.demo.json.BindingModeTypeAdapter;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Expand All @@ -41,6 +44,7 @@
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

/**
* Servlet for REST API in charge of adding bootstrap information to the bootstrap server.
Expand Down Expand Up @@ -70,8 +74,10 @@ public JsonElement serialize(Byte src, Type typeOfSrc, JsonSerializationContext
public BootstrapServlet(EditableBootstrapConfigStore bsStore) {
this.bsStore = bsStore;

this.gson = new GsonBuilder().registerTypeHierarchyAdapter(Byte.class, new SignedByteUnsignedByteAdapter())
.create();
this.gson = new GsonBuilder()//
.registerTypeAdapter(new TypeToken<EnumSet<BindingMode>>() {
}.getType(), new BindingModeTypeAdapter()) //
.registerTypeHierarchyAdapter(Byte.class, new SignedByteUnsignedByteAdapter()).create();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.net.InetSocketAddress;
import java.security.cert.Certificate;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -160,6 +161,7 @@ public LeshanClientBuilder setDtlsConfig(DtlsConnectorConfig.Builder config) {

/**
* Set optional trust store for verifying X.509 server certificates.
*
* @param trustStore List of trusted CA certificates
*/
public LeshanClientBuilder setTrustStore(List<Certificate> trustStore) {
Expand Down Expand Up @@ -207,6 +209,7 @@ public LeshanClientBuilder setAdditionalAttributes(Map<String, String> additiona

/**
* Set the additionalAttributes for {@link BootstrapRequest}
*
* @since 1.1
*/
public LeshanClientBuilder setBootstrapAdditionalAttributes(Map<String, String> additionalAttributes) {
Expand Down Expand Up @@ -253,8 +256,9 @@ public LeshanClient build() {
ObjectsInitializer initializer = new ObjectsInitializer();
initializer.setInstancesForObject(LwM2mId.SECURITY,
Security.noSec("coap://leshan.eclipseprojects.io: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"));
initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, 5 * 60));
initializer.setInstancesForObject(LwM2mId.DEVICE,
new Device("Eclipse Leshan", "model12345", "12345", EnumSet.of(BindingMode.U)));
objectEnablers = initializer.createAll();
}
if (encoder == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*******************************************************************************/
package org.eclipse.leshan.client.californium.request;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map.Entry;

Expand Down Expand Up @@ -85,12 +86,20 @@ public void visit(RegisterRequest request) {
if (lwVersion != null)
attributes.put("lwm2m", lwVersion);

BindingMode bindingMode = request.getBindingMode();
EnumSet<BindingMode> bindingMode = request.getBindingMode();
if (bindingMode != null)
attributes.put("b", bindingMode.toString());
attributes.put("b", BindingMode.toString(bindingMode));

Boolean queueMode = request.getQueueMode();
if (queueMode != null && queueMode)
attributes.put("Q", null);

for (Entry<String, String> attr : attributes.entrySet()) {
coapRequest.getOptions().addUriQuery(attr.getKey() + "=" + attr.getValue());
if (attr.getValue() != null) {
coapRequest.getOptions().addUriQuery(attr.getKey() + "=" + attr.getValue());
} else {
coapRequest.getOptions().addUriQuery(attr.getKey());
}
}

Link[] objectLinks = request.getObjectLinks();
Expand All @@ -113,9 +122,9 @@ public void visit(UpdateRequest request) {
if (smsNumber != null)
coapRequest.getOptions().addUriQuery("sms=" + smsNumber);

BindingMode bindingMode = request.getBindingMode();
EnumSet<BindingMode> bindingMode = request.getBindingMode();
if (bindingMode != null)
coapRequest.getOptions().addUriQuery("b=" + bindingMode.toString());
coapRequest.getOptions().addUriQuery("b=" + BindingMode.toString(bindingMode));

Link[] linkObjects = request.getObjectLinks();
if (linkObjects != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*******************************************************************************/
package org.eclipse.leshan.client;

import java.util.EnumSet;
import java.util.Map;

import org.eclipse.leshan.core.Link;
Expand All @@ -27,12 +28,12 @@ public class RegistrationUpdate {

private final Long lifeTimeInSec;
private final String smsNumber;
private final BindingMode bindingMode;
private final EnumSet<BindingMode> bindingMode;
private final Link[] objectLinks;
private final Map<String, String> additionalAttributes;

public RegistrationUpdate(Long lifeTimeInSec, String smsNumber, BindingMode bindingMode, Link[] objectLinks,
Map<String, String> additionalAttributes) {
public RegistrationUpdate(Long lifeTimeInSec, String smsNumber, EnumSet<BindingMode> bindingMode,
Link[] objectLinks, Map<String, String> additionalAttributes) {
this.lifeTimeInSec = lifeTimeInSec;
this.smsNumber = smsNumber;
this.bindingMode = bindingMode;
Expand All @@ -52,7 +53,7 @@ public RegistrationUpdate(String smsNumber) {
this(null, smsNumber, null, null, null);
}

public RegistrationUpdate(BindingMode bindingMode) {
public RegistrationUpdate(EnumSet<BindingMode> bindingMode) {
this(null, null, bindingMode, null, null);
}

Expand All @@ -72,7 +73,7 @@ public String getSmsNumber() {
return smsNumber;
}

public BindingMode getBindingMode() {
public EnumSet<BindingMode> getBindingMode() {
return bindingMode;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*******************************************************************************/
package org.eclipse.leshan.client;

import java.util.EnumSet;

import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
import org.eclipse.leshan.client.engine.RegistrationEngine;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
Expand Down Expand Up @@ -74,27 +76,35 @@ public void objectAdded(LwM2mObjectEnabler object) {
public void resourceChanged(LwM2mObjectEnabler object, int instanceId, int... resourceIds) {
if (!bsHandler.isBootstrapping())
if (object.getId() == LwM2mId.SERVER) {
Long lifetime = null;
BindingMode bindingMode = null;
// handle lifetime changes
for (int i = 0; i < resourceIds.length; i++) {
if (resourceIds[i] == LwM2mId.SRV_LIFETIME) {
lifetime = ServersInfoExtractor.getLifeTime(object, instanceId);
} else if (resourceIds[i] == LwM2mId.SRV_BINDING) {
bindingMode = ServersInfoExtractor.getBindingMode(object, instanceId);
Long lifetime = ServersInfoExtractor.getLifeTime(object, instanceId);
Long serverId = ServersInfoExtractor.getServerId(object, instanceId);
if (lifetime != null && serverId != null) {
ServerIdentity server = engine.getRegisteredServer(serverId);
if (server != null) {
engine.triggerRegistrationUpdate(server,
new RegistrationUpdate(lifetime, null, null, null, null));
return;
}
}
}
}

if (bindingMode != null || lifetime != null) {
Long serverId = null;
serverId = ServersInfoExtractor.getServerId(object, instanceId);
if (serverId != null) {
ServerIdentity server = engine.getRegisteredServer(serverId);
if (server != null)
engine.triggerRegistrationUpdate(server,
new RegistrationUpdate(lifetime, null, bindingMode, null, null));
} else if (object.getId() == LwM2mId.DEVICE) {
// handle supported binding changes
EnumSet<BindingMode> bindingMode = null;
for (int i = 0; i < resourceIds.length; i++) {
if (resourceIds[i] == LwM2mId.DVC_SUPPORTED_BINDING) {
bindingMode = ServersInfoExtractor.getDeviceSupportedBindingMode(object, instanceId);
}
}

if (bindingMode != null) {
engine.triggerRegistrationUpdate(
new RegistrationUpdate(null, null, bindingMode, null, null));
return;
}
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.eclipse.leshan.client.engine;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -41,7 +42,9 @@
import org.eclipse.leshan.client.servers.ServersInfoExtractor;
import org.eclipse.leshan.client.util.LinkFormatHelper;
import org.eclipse.leshan.core.LwM2m.Version;
import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.DeregisterRequest;
import org.eclipse.leshan.core.request.Identity;
Expand Down Expand Up @@ -88,6 +91,8 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
private boolean reconnectOnUpdate;
// True if client should try to resume connection if possible.
private boolean resumeOnConnect;
// True if client use queueMode : for now this just add Q parameter on register request.
private final boolean queueMode;

private static enum Status {
SUCCESS, FAILURE, TIMEOUT
Expand Down Expand Up @@ -124,7 +129,7 @@ public DefaultRegistrationEngine(String endpoint, LwM2mObjectTree objectTree, En
Integer communicationPeriodInMs, boolean reconnectOnUpdate, boolean resumeOnConnect) {
this(endpoint, objectTree, endpointsManager, requestSender, bootstrapState, observer, additionalAttributes,
null, executor, requestTimeoutInMs, deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec,
retryWaitingTimeInMs, communicationPeriodInMs, reconnectOnUpdate, resumeOnConnect);
retryWaitingTimeInMs, communicationPeriodInMs, reconnectOnUpdate, resumeOnConnect, false);
}

/** @since 1.1 */
Expand All @@ -133,7 +138,7 @@ public DefaultRegistrationEngine(String endpoint, LwM2mObjectTree objectTree, En
Map<String, String> additionalAttributes, Map<String, String> bsAdditionalAttributes,
ScheduledExecutorService executor, long requestTimeoutInMs, long deregistrationTimeoutInMs,
int bootstrapSessionTimeoutInSec, int retryWaitingTimeInMs, Integer communicationPeriodInMs,
boolean reconnectOnUpdate, boolean resumeOnConnect) {
boolean reconnectOnUpdate, boolean resumeOnConnect, boolean useQueueMode) {
this.endpoint = endpoint;
this.objectEnablers = objectTree.getObjectEnablers();
this.bootstrapHandler = bootstrapState;
Expand All @@ -151,6 +156,7 @@ public DefaultRegistrationEngine(String endpoint, LwM2mObjectTree objectTree, En
this.communicationPeriodInMs = communicationPeriodInMs;
this.reconnectOnUpdate = reconnectOnUpdate;
this.resumeOnConnect = resumeOnConnect;
this.queueMode = useQueueMode;

if (executor == null) {
schedExecutor = createScheduledExecutor();
Expand Down Expand Up @@ -298,8 +304,11 @@ private Status register(ServerIdentity server) throws InterruptedException {
LOG.info("Trying to register to {} ...", server.getUri());
RegisterRequest request = null;
try {
request = new RegisterRequest(endpoint, dmInfo.lifetime, Version.lastSupported().toString(), dmInfo.binding,
null, LinkFormatHelper.getClientDescription(objectEnablers.values(), null), additionalAttributes);
EnumSet<BindingMode> supportedBindingMode = ServersInfoExtractor
.getDeviceSupportedBindingMode(objectEnablers.get(LwM2mId.DEVICE), 0);
request = new RegisterRequest(endpoint, dmInfo.lifetime, Version.lastSupported().toString(),
supportedBindingMode, queueMode, null,
LinkFormatHelper.getClientDescription(objectEnablers.values(), null), additionalAttributes);
if (observer != null) {
observer.onRegistrationStarted(server, request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class DefaultRegistrationEngineFactory implements RegistrationEngineFacto
private Integer communicationPeriodInMs = null;
private boolean reconnectOnUpdate = false;
private boolean resumeOnConnect = true;
private boolean queueMode = false;

public DefaultRegistrationEngineFactory() {
}
Expand All @@ -50,7 +51,7 @@ public RegistrationEngine createRegistratioEngine(String endpoint, LwM2mObjectTr
return new DefaultRegistrationEngine(endpoint, objectTree, endpointsManager, requestSender, bootstrapState,
observer, additionalAttributes, bsAdditionalAttributes, sharedExecutor, requestTimeoutInMs,
deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec, retryWaitingTimeInMs, communicationPeriodInMs,
reconnectOnUpdate, resumeOnConnect);
reconnectOnUpdate, resumeOnConnect, queueMode);
}

/**
Expand Down Expand Up @@ -150,4 +151,17 @@ public DefaultRegistrationEngineFactory setResumeOnConnect(boolean resumeOnConne
this.resumeOnConnect = resumeOnConnect;
return this;
}

/**
* Configure client to use queueMode.
* <p>
* Default value is false
*
* @param enable True if client must use queueMode
* @return this for fluent API
*/
public DefaultRegistrationEngineFactory setQueueMode(boolean enable) {
this.queueMode = enable;
return this;
}
}
Loading