6 changed files with 313 additions and 111 deletions
@ -0,0 +1,242 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.transport.lwm2m.bootstrap.store; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.leshan.core.Link; |
||||
|
import org.eclipse.leshan.core.node.LwM2mObject; |
||||
|
import org.eclipse.leshan.core.node.LwM2mPath; |
||||
|
import org.eclipse.leshan.core.request.*; |
||||
|
import org.eclipse.leshan.core.response.BootstrapDiscoverResponse; |
||||
|
import org.eclipse.leshan.core.response.BootstrapReadResponse; |
||||
|
import org.eclipse.leshan.core.response.LwM2mResponse; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapConfig; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapSession; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapUtil; |
||||
|
|
||||
|
import java.net.InetSocketAddress; |
||||
|
import java.util.*; |
||||
|
|
||||
|
import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvider { |
||||
|
|
||||
|
private BootstrapConfigStore store; |
||||
|
|
||||
|
private Map<Integer, String> supportedObjects; |
||||
|
|
||||
|
/** |
||||
|
* Map<serverId, InstanceId> |
||||
|
*/ |
||||
|
protected Map<Integer, Integer> securityInstances; |
||||
|
protected Map<Integer, Integer> serverInstances; |
||||
|
|
||||
|
public LwM2MBootstrapConfigStoreTaskProvider(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; |
||||
|
} |
||||
|
if (previousResponse == null && shouldStartWithDiscover(config)) { |
||||
|
Tasks tasks = new Tasks(); |
||||
|
tasks.requestsToSend = new ArrayList<>(1); |
||||
|
tasks.requestsToSend.add(new BootstrapDiscoverRequest()); |
||||
|
tasks.last = false; |
||||
|
return tasks; |
||||
|
} else { |
||||
|
Tasks tasks = new Tasks(); |
||||
|
if (this.supportedObjects == null) { |
||||
|
initSupportedObjectsDefault(); |
||||
|
|
||||
|
} |
||||
|
// add supportedObjects
|
||||
|
tasks.supportedObjects = this.supportedObjects; |
||||
|
// handle bootstrap discover response
|
||||
|
if (previousResponse != null) { |
||||
|
if (previousResponse.get(0) instanceof BootstrapDiscoverResponse) { |
||||
|
BootstrapDiscoverResponse discoverResponse = (BootstrapDiscoverResponse) previousResponse.get(0); |
||||
|
if (discoverResponse.isSuccess()) { |
||||
|
this.initAfterBootstrapDiscover(discoverResponse); |
||||
|
findSecurityInstanceId(discoverResponse.getObjectLinks()); |
||||
|
} else { |
||||
|
log.info( |
||||
|
"Bootstrap Discover return error {} : to continue bootstrap session without autoIdForSecurityObject mode. {}", |
||||
|
discoverResponse, session); |
||||
|
} |
||||
|
if (this.securityInstances.get(0) == null) { |
||||
|
log.error( |
||||
|
"Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}", |
||||
|
discoverResponse, session); |
||||
|
return null; |
||||
|
} |
||||
|
tasks.requestsToSend = new ArrayList<>(1); |
||||
|
tasks.requestsToSend.add(new BootstrapReadRequest("/1")); |
||||
|
tasks.last = false; |
||||
|
return tasks; |
||||
|
} |
||||
|
BootstrapReadResponse readResponse = (BootstrapReadResponse) previousResponse.get(0); |
||||
|
findServerInstanceId(readResponse); |
||||
|
// create requests from config
|
||||
|
tasks.requestsToSend = this.toRequests(config, |
||||
|
config.contentFormat != null ? config.contentFormat : session.getContentFormat(), |
||||
|
this.securityInstances.get(0)); |
||||
|
} else { |
||||
|
// create requests from config
|
||||
|
tasks.requestsToSend = BootstrapUtil.toRequests(config, |
||||
|
config.contentFormat != null ? config.contentFormat : session.getContentFormat()); |
||||
|
|
||||
|
} |
||||
|
return tasks; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected boolean shouldStartWithDiscover(BootstrapConfig config) { |
||||
|
return config.autoIdForSecurityObject; |
||||
|
} |
||||
|
|
||||
|
protected void findSecurityInstanceId(Link[] objectLinks) { |
||||
|
this.securityInstances = new HashMap<>(); |
||||
|
for (Link link : objectLinks) { |
||||
|
if (link.getUrl().startsWith("/0/")) { |
||||
|
try { |
||||
|
LwM2mPath path = new LwM2mPath(link.getUrl()); |
||||
|
if (path.isObjectInstance()) { |
||||
|
if (link.getAttributes().containsKey("ssid")) { |
||||
|
int serverId = Integer.valueOf(link.getAttributes().get("ssid")); |
||||
|
if (!this.securityInstances.containsKey(serverId)) { |
||||
|
this.securityInstances.put(serverId, path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
log.error(String.format("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId())); |
||||
|
} |
||||
|
this.securityInstances.put(Integer.valueOf(link.getAttributes().get("ssid")), path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
if (!this.securityInstances.containsKey(0)) { |
||||
|
this.securityInstances.put(0, path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
log.error(String.format("Invalid bootstrapSecurityInstance by [{}]", path.getObjectInstanceId())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
// ignore if this is not a LWM2M path
|
||||
|
log.error("Invalid LwM2MPath starting by \"/0/\""); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void findServerInstanceId(BootstrapReadResponse readResponse) { |
||||
|
serverInstances = new HashMap<>(); |
||||
|
((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> { |
||||
|
serverInstances.put(((Long) instance.getResource(0).getValue()).intValue(), instance.getId()); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public BootstrapConfigStore getStore() { |
||||
|
return this.store; |
||||
|
} |
||||
|
|
||||
|
private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) { |
||||
|
Link[] links = response.getObjectLinks(); |
||||
|
Arrays.stream(links).forEach(link -> { |
||||
|
LwM2mPath path = new LwM2mPath(link.getUrl()); |
||||
|
if (path != null && !path.isRoot() && path.getObjectId() < 3) { |
||||
|
if (path.isObject()) { |
||||
|
String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver") : "1.0"; |
||||
|
this.supportedObjects.put(path.getObjectId(), ver); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig, |
||||
|
ContentFormat contentFormat, int bootstrapSecurityInstanceId) { |
||||
|
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>(); |
||||
|
boolean isBsServer = false; |
||||
|
boolean isLwServer = false; |
||||
|
/** Map<serverId, InstanceId> */ |
||||
|
Map<Integer, Integer> instances = new HashMap<>(); |
||||
|
// handle security
|
||||
|
int id = 0; |
||||
|
for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) { |
||||
|
if (security.bootstrapServer) { |
||||
|
requests.add(toWriteRequest(bootstrapSecurityInstanceId, security, contentFormat)); |
||||
|
isBsServer = true; |
||||
|
instances.put(security.serverId, bootstrapSecurityInstanceId); |
||||
|
} else { |
||||
|
if (id == bootstrapSecurityInstanceId) { |
||||
|
id++; |
||||
|
} |
||||
|
requests.add(toWriteRequest(id, security, contentFormat)); |
||||
|
instances.put(security.serverId, id); |
||||
|
id++; |
||||
|
} |
||||
|
} |
||||
|
// handle server
|
||||
|
for (Map.Entry<Integer, BootstrapConfig.ServerConfig> server : bootstrapConfig.servers.entrySet()) { |
||||
|
int securityInstanceId = instances.get(server.getValue().shortId); |
||||
|
requests.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); |
||||
|
if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)){ |
||||
|
requests.add(new BootstrapDeleteRequest("/0/" + securityInstanceId)); |
||||
|
requests.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId))); |
||||
|
} |
||||
|
isLwServer = true; |
||||
|
} |
||||
|
// handle acl
|
||||
|
for (Map.Entry<Integer, BootstrapConfig.ACLConfig> acl : bootstrapConfig.acls.entrySet()) { |
||||
|
requests.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); |
||||
|
} |
||||
|
// handle delete
|
||||
|
if (isBsServer & isLwServer) { |
||||
|
requests.add(new BootstrapDeleteRequest("/0")); |
||||
|
requests.add(new BootstrapDeleteRequest("/1")); |
||||
|
} |
||||
|
|
||||
|
return (requests); |
||||
|
} |
||||
|
|
||||
|
private InetSocketAddress getSocketAddress(String uri) { |
||||
|
// String uri1 = "coap://localhost:5687";
|
||||
|
if (uri.contains("coap://")) { |
||||
|
uri = uri.replace("coap://", ""); |
||||
|
} else return null; |
||||
|
String[] uris = uri.split(":"); |
||||
|
if (uris.length == 2) { |
||||
|
try { |
||||
|
return new InetSocketAddress(uris[0], Integer.parseInt(uris[1])); |
||||
|
} catch (Exception e) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
} else return null; |
||||
|
} |
||||
|
|
||||
|
private void initSupportedObjectsDefault() { |
||||
|
this.supportedObjects = new HashMap<>(); |
||||
|
this.supportedObjects.put(0, "1.1"); |
||||
|
this.supportedObjects.put(1, "1.1"); |
||||
|
this.supportedObjects.put(2, "1.0"); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue