Skip to content

Commit

Permalink
#1027: Add TaskProvider which provides requests to send during bootstrap
Browse files Browse the repository at this point in the history
The DefaultBootstrapSessionManager delegates knowledge of requests to
send to BootstrapTaskProvider.
A SimpleBootstrapStoreTaskProvider use BootstrapConfigStore to know
which requests must be sent from BootstrapConfig.
  • Loading branch information
sbernard31 committed Jun 25, 2021
1 parent 0edf18c commit e6f87b2
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSessionManager;
import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.LwM2mBootstrapRequestSender;
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStoreTaskProvider;
import org.eclipse.leshan.server.model.LwM2mBootstrapModelProvider;
import org.eclipse.leshan.server.model.StandardBootstrapModelProvider;
import org.eclipse.leshan.server.security.BootstrapSecurityStore;
Expand Down Expand Up @@ -410,8 +411,8 @@ public BootstrapHandler create(LwM2mBootstrapRequestSender sender,
if (modelProvider == null)
modelProvider = new StandardBootstrapModelProvider();
if (sessionManager == null)
sessionManager = new DefaultBootstrapSessionManager(securityStore, new SecurityChecker(), configStore,
modelProvider);
sessionManager = new DefaultBootstrapSessionManager(securityStore, new SecurityChecker(),
new BootstrapConfigStoreTaskProvider(configStore), modelProvider);
if (coapConfig == null) {
coapConfig = createDefaultNetworkConfig();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2021 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;

import java.util.HashMap;
import java.util.List;

import org.eclipse.leshan.core.response.LwM2mResponse;

/**
* An implementation of {@link BootstrapTaskProvider} which use a {@link BootstrapConfigStore} to know which requests to
* send during a {@link BootstrapSession}.
*/
public class BootstrapConfigStoreTaskProvider implements BootstrapTaskProvider {

private BootstrapConfigStore store;

public BootstrapConfigStoreTaskProvider(BootstrapConfigStore store) {
this.store = store;
}

@Override
public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponse) {

BootstrapConfig config = store.get(session.getEndpoint(), session.getIdentity(), session);
if (config == null)
return null;

Tasks tasks = new Tasks();
// create requests from config
tasks.requestsToSend = BootstrapUtil.toRequests(config, session.getContentFormat());

// We add model for Security(0), Server(0) and ACL(2) which are the only one supported by BootstrapConfig
// We use default 1.0 model as currently BootstrapConfig support only this model version (which should be
// compatible with models of LWM2M v1.1 but without new resources)
tasks.supportedObjects = new HashMap<>();
tasks.supportedObjects.put(0, "1.0");
tasks.supportedObjects.put(1, "1.0");
tasks.supportedObjects.put(2, "1.0");

return tasks;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ public String toString() {
*
* @param bsSession the bootstrap session concerned.
* @param request The request for which we get a successful response.
*
* @param response The response received.
* @return a {@link BootstrapPolicy} given the way to continue the bootstrap session.
*/
public BootstrapPolicy onResponseSuccess(BootstrapSession bsSession,
BootstrapDownlinkRequest<? extends LwM2mResponse> request);
BootstrapDownlinkRequest<? extends LwM2mResponse> request, LwM2mResponse response);

/**
* Called when we receive a error response to a request.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2021 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;

import java.util.List;
import java.util.Map;

import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;
import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
import org.eclipse.leshan.core.request.BootstrapWriteRequest;
import org.eclipse.leshan.core.response.LwM2mResponse;

/**
* A class responsible to return tasks to do during a {@link BootstrapSession}
* <p>
* This class is used by {@link DefaultBootstrapSessionManager}.
* <p>
* {@link #getTasks(BootstrapSession, List)} must return requests to send to the client. During 1 session,
* {@link #getTasks(BootstrapSession, List)} can be called several time. Responses received for first batch of requests
* can be used to determine next request to send. E.g. first batch of Requests could be a
* {@link BootstrapDiscoverRequest} or a BootstrapReadRequest and response can be used to determine which
* {@link BootstrapWriteRequest} to Send.
*
* @see BootstrapConfigStoreTaskProvider
*/
public interface BootstrapTaskProvider {

/**
* a batch of requests to send.
*
*/
public class Tasks {
/**
* the list of request to send
*/
public List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsToSend;

/**
* Object (with version) supported by the client. {@link LwM2mModel} needed to encode/decode payload of request
* will be created from this data
*/
public Map<Integer, String> supportedObjects;

/**
* if true {@link BootstrapTaskProvider#getTasks(BootstrapSession, List)} will not be called again for this
* session
*/
public boolean last = true;
}

/**
* @param previousResponses a list of {@link LwM2mResponse} received from previous executed {@link Tasks}. It can be
* <code>null</code> if this is the first call for this session.
* @return next tasks to do (next requests to send), returning <code>null</code> means there is nothing to do with
* this client.
*/
Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponses);
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ protected void sendRequest(final BootstrapSession session,
public void safeOnResponse(LwM2mResponse response) {
if (response.isSuccess()) {
LOG.trace("{} receives {} for {}", session, response, requestToSend);
BootstrapPolicy policy = sessionManager.onResponseSuccess(session, requestToSend);
BootstrapPolicy policy = sessionManager.onResponseSuccess(session, requestToSend, response);
afterRequest(session, policy, requestToSend);
} else {
LOG.debug("{} receives {} for {}", session, response, requestToSend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ public class DefaultBootstrapSession implements BootstrapSession {
private final ContentFormat contentFormat;
private final long creationTime;
private final BootstrapRequest request;
private LwM2mModel model;
private List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests;

private volatile LwM2mModel model;
private volatile List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests;
private volatile List<LwM2mResponse> responses;
private volatile boolean moreTasks = false;
private volatile boolean cancelled = false;

/**
Expand Down Expand Up @@ -147,6 +149,14 @@ public void setRequests(List<BootstrapDownlinkRequest<? extends LwM2mResponse>>
this.requests = requests;
}

public List<LwM2mResponse> getResponses() {
return responses;
}

public void setResponses(List<LwM2mResponse> responses) {
this.responses = responses;
}

@Override
public LwM2mModel getModel() {
return model;
Expand All @@ -156,6 +166,14 @@ public void setModel(LwM2mModel model) {
this.model = model;
}

public void setMoreTasks(boolean moreTasks) {
this.moreTasks = moreTasks;
}

public boolean hasMoreTasks() {
return moreTasks;
}

@Override
public void cancel() {
cancelled = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*******************************************************************************/
package org.eclipse.leshan.server.bootstrap;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

Expand All @@ -25,6 +25,7 @@
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider.Tasks;
import org.eclipse.leshan.server.model.LwM2mBootstrapModelProvider;
import org.eclipse.leshan.server.model.StandardBootstrapModelProvider;
import org.eclipse.leshan.server.security.BootstrapSecurityStore;
Expand All @@ -46,7 +47,7 @@ public class DefaultBootstrapSessionManager implements BootstrapSessionManager {

private BootstrapSecurityStore bsSecurityStore;
private SecurityChecker securityChecker;
private BootstrapConfigStore configStore;
private BootstrapTaskProvider tasksProvider;
private LwM2mBootstrapModelProvider modelProvider;

/**
Expand All @@ -56,7 +57,8 @@ public class DefaultBootstrapSessionManager implements BootstrapSessionManager {
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}.
*/
public DefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore) {
this(bsSecurityStore, new SecurityChecker(), configStore, new StandardBootstrapModelProvider());
this(bsSecurityStore, new SecurityChecker(), new BootstrapConfigStoreTaskProvider(configStore),
new StandardBootstrapModelProvider());
}

/**
Expand All @@ -66,11 +68,11 @@ public DefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, Bo
* @param securityChecker used to accept or refuse new {@link BootstrapSession}.
*/
public DefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, SecurityChecker securityChecker,
BootstrapConfigStore configStore, LwM2mBootstrapModelProvider modelProvider) {
Validate.notNull(configStore);
BootstrapTaskProvider tasksProvider, LwM2mBootstrapModelProvider modelProvider) {
Validate.notNull(tasksProvider);
this.bsSecurityStore = bsSecurityStore;
this.securityChecker = securityChecker;
this.configStore = configStore;
this.tasksProvider = tasksProvider;
this.modelProvider = modelProvider;
}

Expand All @@ -90,27 +92,28 @@ public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity)

@Override
public boolean hasConfigFor(BootstrapSession session) {
BootstrapConfig configuration = configStore.get(session.getEndpoint(), session.getIdentity(), session);
if (configuration == null)
Tasks firstTasks = tasksProvider.getTasks(session, null);
if (firstTasks == null)
return false;

initSessionFromConfig(session, configuration);
initTasks(session, firstTasks);
return true;
}

protected void initSessionFromConfig(BootstrapSession bssession, BootstrapConfig configuration) {
protected void initTasks(BootstrapSession bssession, Tasks tasks) {
DefaultBootstrapSession session = (DefaultBootstrapSession) bssession;
// set models
HashMap<Integer, String> supportedObjects = new HashMap<>();
supportedObjects.put(0, "1.0");
supportedObjects.put(1, "1.0");
supportedObjects.put(2, "1.0");
session.setModel(modelProvider.getObjectModel(session, supportedObjects));
if (tasks.supportedObjects != null)
session.setModel(modelProvider.getObjectModel(session, tasks.supportedObjects));

// set Requests to Send
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = BootstrapUtil.toRequests(configuration,
session.getContentFormat());
session.setRequests(requests);
session.setRequests(tasks.requestsToSend);

// prepare list where we will store Responses
session.setResponses(new ArrayList<LwM2mResponse>(tasks.requestsToSend.size()));

// is last Tasks ?
session.setMoreTasks(!tasks.last);
}

@Override
Expand All @@ -121,21 +124,37 @@ public BootstrapDownlinkRequest<? extends LwM2mResponse> getFirstRequest(Bootstr
protected BootstrapDownlinkRequest<? extends LwM2mResponse> nextRequest(BootstrapSession bsSession) {
DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession;
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsToSend = session.getRequests();
if (requestsToSend.isEmpty()) {
return new BootstrapFinishRequest();
} else {

if (!requestsToSend.isEmpty()) {
// get next requests
return requestsToSend.remove(0);
} else {
if (session.hasMoreTasks()) {
Tasks nextTasks = tasksProvider.getTasks(session, session.getResponses());
if (nextTasks == null) {
session.setMoreTasks(false);
return new BootstrapFinishRequest();
}

initTasks(session, nextTasks);
return nextRequest(bsSession);
} else {
return new BootstrapFinishRequest();
}
}
}

@Override
public BootstrapPolicy onResponseSuccess(BootstrapSession bsSession,
BootstrapDownlinkRequest<? extends LwM2mResponse> request) {
BootstrapDownlinkRequest<? extends LwM2mResponse> request, LwM2mResponse response) {
if (LOG.isTraceEnabled())
LOG.trace("{} {} receives success response for {} : {}", request.getClass().getSimpleName(),
request.getPath(), bsSession, request);

if (!(request instanceof BootstrapFinishRequest)) {
// store response
DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession;
session.getResponses().add(response);
// on success for NOT bootstrap finish request we send next request
return BootstrapPolicy.continueWith(nextRequest(bsSession));
} else {
Expand All @@ -152,6 +171,10 @@ public BootstrapPolicy onResponseError(BootstrapSession bsSession,
request.getPath(), response, bsSession, request);

if (!(request instanceof BootstrapFinishRequest)) {
// store response
DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession;
session.getResponses().add(response);

// on response error for NOT bootstrap finish request we continue any sending next request
return BootstrapPolicy.continueWith(nextRequest(bsSession));
} else {
Expand Down

0 comments on commit e6f87b2

Please sign in to comment.