() {
+ @Override
+ public boolean accept(Principal principal) {
+ if (principal != null) {
+ for (SecurityInfo info : infos) {
+ if (info != null) {
+ // PSK
+ if (info.usePSK() && principal instanceof PreSharedKeyIdentity) {
+ String identity = ((PreSharedKeyIdentity) principal).getIdentity();
+ if (info.getPskIdentity().equals(identity)) {
+ return true;
+ }
+ }
+ // RPK
+ else if (info.useRPK() && principal instanceof RawPublicKeyIdentity) {
+ PublicKey publicKey = ((RawPublicKeyIdentity) principal).getKey();
+ if (info.getRawPublicKey().equals(publicKey)) {
+ return true;
+ }
+ }
+ // x509
+ else if (info.useX509Cert() && principal instanceof X500Principal
+ || principal instanceof X509CertPath) {
+ // Extract common name
+ String x509CommonName = X509CertUtil.extractCN(principal.getName());
+ if (x509CommonName.equals(info.getEndpoint())) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ });
+ }
+}
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/LwM2mBootstrapOscoreStore.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mBootstrapOscoreStore.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/LwM2mBootstrapOscoreStore.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mBootstrapOscoreStore.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/LwM2mBootstrapPskStore.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mBootstrapPskStore.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/LwM2mBootstrapPskStore.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mBootstrapPskStore.java
diff --git a/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mOscoreStore.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mOscoreStore.java
new file mode 100644
index 0000000000..188b034d45
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mOscoreStore.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2022 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.transport.californium.server;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.eclipse.californium.cose.AlgorithmID;
+import org.eclipse.californium.cose.CoseException;
+import org.eclipse.leshan.core.peer.LwM2mIdentity;
+import org.eclipse.leshan.core.peer.OscoreIdentity;
+import org.eclipse.leshan.core.util.Validate;
+import org.eclipse.leshan.server.registration.Registration;
+import org.eclipse.leshan.server.registration.RegistrationStore;
+import org.eclipse.leshan.server.security.SecurityInfo;
+import org.eclipse.leshan.server.security.SecurityStore;
+import org.eclipse.leshan.transport.californium.oscore.cf.OscoreParameters;
+import org.eclipse.leshan.transport.californium.oscore.cf.OscoreStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.upokecenter.cbor.CBORObject;
+
+/**
+ * An {@link OscoreStore} which search {@link OscoreParameters} in LWM2M {@link SecurityStore}
+ */
+public class LwM2mOscoreStore implements OscoreStore {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LwM2mOscoreStore.class);
+
+ private final SecurityStore securityStore;
+ private final RegistrationStore registrationStore;
+
+ public LwM2mOscoreStore(SecurityStore securityStore, RegistrationStore registrationStore) {
+ Validate.notNull(securityStore);
+ Validate.notNull(registrationStore);
+ this.securityStore = securityStore;
+ this.registrationStore = registrationStore;
+ }
+
+ @Override
+ public OscoreParameters getOscoreParameters(byte[] recipientID) {
+ OscoreIdentity oscoreIdentity = new OscoreIdentity(recipientID);
+ SecurityInfo securityInfo = securityStore.getByOscoreIdentity(oscoreIdentity);
+ if (securityInfo == null || !securityInfo.useOSCORE())
+ return null;
+
+ try {
+ return new OscoreParameters(//
+ securityInfo.getOscoreSetting().getSenderId(), //
+ securityInfo.getOscoreSetting().getRecipientId(), //
+ securityInfo.getOscoreSetting().getMasterSecret(), //
+ // TODO OSCORE we maybe need an API without the need to create a CBOR Object
+ AlgorithmID.FromCBOR(
+ CBORObject.FromObject(securityInfo.getOscoreSetting().getAeadAlgorithm().getValue())), //
+ AlgorithmID.FromCBOR(
+ CBORObject.FromObject(securityInfo.getOscoreSetting().getHkdfAlgorithm().getValue())), //
+ // TODO OSCORE kind of hack because californium doesn't support an empty byte[] array for salt ?
+ securityInfo.getOscoreSetting().getMasterSalt().length == 0 ? null
+ : securityInfo.getOscoreSetting().getMasterSalt());
+ } catch (CoseException e) {
+ LOG.error("Unable to create OscoreParameters from OoscoreSetting %s", securityInfo.getOscoreSetting(), e);
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getRecipientId(String uri) {
+ try {
+ URI foreignPeerUri = new URI(uri);
+ InetSocketAddress foreignPeerAddress = new InetSocketAddress(foreignPeerUri.getHost(),
+ foreignPeerUri.getPort());
+ Registration registration = registrationStore.getRegistrationByAdress(foreignPeerAddress);
+ LwM2mIdentity identity = registration.getClientTransportData().getIdentity();
+ if (identity instanceof OscoreIdentity) {
+ return ((OscoreIdentity) identity).getRecipientId();
+ }
+ } catch (URISyntaxException | SecurityException | IllegalArgumentException e) {
+ LOG.error("Unable to extract InetScocketAddress from uri %s", uri, e);
+ return null;
+ }
+ return null;
+ }
+}
diff --git a/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mPskStore.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mPskStore.java
new file mode 100644
index 0000000000..a2bd5346f9
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/LwM2mPskStore.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2015 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.transport.californium.server;
+
+import java.net.InetSocketAddress;
+
+import javax.crypto.SecretKey;
+
+import org.eclipse.californium.scandium.dtls.ConnectionId;
+import org.eclipse.californium.scandium.dtls.PskPublicInformation;
+import org.eclipse.californium.scandium.dtls.PskSecretResult;
+import org.eclipse.californium.scandium.dtls.pskstore.AdvancedPskStore;
+import org.eclipse.californium.scandium.util.SecretUtil;
+import org.eclipse.californium.scandium.util.ServerNames;
+import org.eclipse.leshan.server.registration.Registration;
+import org.eclipse.leshan.server.registration.RegistrationStore;
+import org.eclipse.leshan.server.security.SecurityInfo;
+import org.eclipse.leshan.server.security.SecurityStore;
+
+/**
+ * A {@link AdvancedPskStore} which retrieve PSK information from Leshan {@link SecurityStore}.
+ */
+public class LwM2mPskStore implements AdvancedPskStore {
+
+ private SecurityStore securityStore;
+ private RegistrationStore registrationStore;
+
+ public LwM2mPskStore(SecurityStore securityStore) {
+ this(securityStore, null);
+ }
+
+ public LwM2mPskStore(SecurityStore securityStore, RegistrationStore registrationStore) {
+ this.securityStore = securityStore;
+ this.registrationStore = registrationStore;
+ }
+
+ @Override
+ public boolean hasEcdhePskSupported() {
+ return true;
+ }
+
+ @Override
+ public PskSecretResult requestPskSecretResult(ConnectionId cid, ServerNames serverName,
+ PskPublicInformation identity, String hmacAlgorithm, SecretKey otherSecret, byte[] seed,
+ boolean useExtendedMasterSecret) {
+ if (securityStore == null)
+ return null;
+
+ SecurityInfo info = securityStore.getByIdentity(identity.getPublicInfoAsString());
+ if (info == null || info.getPreSharedKey() == null) {
+ return new PskSecretResult(cid, identity, null);
+ } else {
+ // defensive copy
+ return new PskSecretResult(cid, identity, SecretUtil.create(info.getPreSharedKey(), "PSK"));
+ }
+ }
+
+ @Override
+ public void setResultHandler(org.eclipse.californium.scandium.dtls.HandshakeResultHandler resultHandler) {
+ // we don't use async mode.
+ }
+
+ @Override
+ public PskPublicInformation getIdentity(InetSocketAddress peerAddress, ServerNames virtualHost) {
+ if (registrationStore == null)
+ return null;
+
+ Registration registration = registrationStore.getRegistrationByAdress(peerAddress);
+ if (registration != null) {
+ SecurityInfo securityInfo = securityStore.getByEndpoint(registration.getEndpoint());
+ if (securityInfo != null) {
+ return new PskPublicInformation(securityInfo.getPskIdentity());
+ }
+ return null;
+ }
+ return null;
+ }
+}
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/OscoreBootstrapListener.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/OscoreBootstrapListener.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/OscoreBootstrapListener.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/OscoreBootstrapListener.java
diff --git a/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/OscoreContextCleaner.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/OscoreContextCleaner.java
new file mode 100644
index 0000000000..c11b77819a
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/OscoreContextCleaner.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2022 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.transport.californium.server;
+
+import java.util.Collection;
+
+import org.eclipse.californium.oscore.OSCoreCtx;
+import org.eclipse.californium.oscore.OSCoreCtxDB;
+import org.eclipse.leshan.core.observation.Observation;
+import org.eclipse.leshan.core.peer.OscoreIdentity;
+import org.eclipse.leshan.server.registration.Registration;
+import org.eclipse.leshan.server.registration.RegistrationListener;
+import org.eclipse.leshan.server.registration.RegistrationUpdate;
+import org.eclipse.leshan.server.security.EditableSecurityStore;
+import org.eclipse.leshan.server.security.SecurityInfo;
+import org.eclipse.leshan.server.security.SecurityStoreListener;
+
+/**
+ * This class is responsible to remove {@link OSCoreCtx} from {@link OSCoreCtxDB} on some events.
+ *
+ * {@link OSCoreCtx} is removed when :
+ *
+ * - a {@link Registration} using OSCORE is removed.
+ *
- an OSCORE {@link SecurityInfo} is removed from {@link EditableSecurityStore}.
+ *
+ *
+ */
+public class OscoreContextCleaner implements RegistrationListener, SecurityStoreListener {
+
+ private final OSCoreCtxDB oscoreCtxDB;
+
+ public OscoreContextCleaner(OSCoreCtxDB oscoreCtxDB) {
+ this.oscoreCtxDB = oscoreCtxDB;
+ }
+
+ @Override
+ public void registered(Registration registration, Registration previousReg,
+ Collection previousObsersations) {
+ }
+
+ @Override
+ public void updated(RegistrationUpdate update, Registration updatedReg, Registration previousReg) {
+ }
+
+ @Override
+ public void unregistered(Registration registration, Collection observations, boolean expired,
+ Registration newReg) {
+ if (registration.getClientTransportData().getIdentity() instanceof OscoreIdentity) {
+ removeContext(((OscoreIdentity) registration.getClientTransportData().getIdentity()).getRecipientId());
+ }
+ }
+
+ @Override
+ public void securityInfoRemoved(boolean infosAreCompromised, SecurityInfo... infos) {
+ for (SecurityInfo securityInfo : infos) {
+ if (securityInfo.useOSCORE()) {
+ removeContext(securityInfo.getOscoreSetting().getRecipientId());
+ }
+ }
+ }
+
+ private void removeContext(byte[] rid) {
+ OSCoreCtx context = oscoreCtxDB.getContext(rid);
+ if (context != null)
+ oscoreCtxDB.removeContext(context);
+ }
+}
diff --git a/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/RootResource.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/RootResource.java
new file mode 100644
index 0000000000..a3ea746cb1
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/RootResource.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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 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.transport.californium.server;
+
+import org.eclipse.californium.core.CoapResource;
+import org.eclipse.californium.core.coap.CoAP.ResponseCode;
+import org.eclipse.californium.core.server.resources.CoapExchange;
+
+/**
+ * A default root resource.
+ */
+public class RootResource extends CoapResource {
+
+ public RootResource() {
+ super("");
+ setVisible(false);
+ }
+
+ @Override
+ public void handleGET(CoapExchange exchange) {
+ exchange.respond(ResponseCode.NOT_FOUND);
+ }
+}
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/BootstrapServerCoapMessageTranslator.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/BootstrapServerCoapMessageTranslator.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/BootstrapServerCoapMessageTranslator.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/BootstrapServerCoapMessageTranslator.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/BootstrapServerProtocolProvider.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/BootstrapServerProtocolProvider.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/BootstrapServerProtocolProvider.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/BootstrapServerProtocolProvider.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpoint.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpoint.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpoint.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpoint.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpointFactory.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpointFactory.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpointFactory.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpointFactory.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpointsProvider.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpointsProvider.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/CaliforniumBootstrapServerEndpointsProvider.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/CaliforniumBootstrapServerEndpointsProvider.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerEndpointFactory.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerEndpointFactory.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerEndpointFactory.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerEndpointFactory.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerEndpointFactoryBuilder.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerEndpointFactoryBuilder.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerEndpointFactoryBuilder.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerEndpointFactoryBuilder.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerProtocolProvider.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerProtocolProvider.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapBootstrapServerProtocolProvider.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapBootstrapServerProtocolProvider.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapOscoreBootstrapServerEndpointFactory.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapOscoreBootstrapServerEndpointFactory.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coap/CoapOscoreBootstrapServerEndpointFactory.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coap/CoapOscoreBootstrapServerEndpointFactory.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactoryBuilder.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerEndpointFactoryBuilder.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactoryBuilder.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerEndpointFactoryBuilder.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerProtocolProvider.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerProtocolProvider.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/endpoint/coaps/CoapsBootstrapServerProtocolProvider.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/endpoint/coaps/CoapsBootstrapServerProtocolProvider.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/request/CoapRequestBuilder.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/request/CoapRequestBuilder.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/request/CoapRequestBuilder.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/request/CoapRequestBuilder.java
diff --git a/leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/request/LwM2mResponseBuilder.java b/leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/request/LwM2mResponseBuilder.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/main/java/org/eclipse/leshan/transport/californium/server/bootstrap/request/LwM2mResponseBuilder.java
rename to leshan-tl-cf-bsserver-coap/src/main/java/org/eclipse/leshan/transport/californium/bsserver/request/LwM2mResponseBuilder.java
diff --git a/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/DummyDecoder.java b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/DummyDecoder.java
new file mode 100644
index 0000000000..7e68e57512
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/DummyDecoder.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Orange.
+ *
+ * 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:
+ * Michał Wadowski (Orange) - Add Observe-Composite feature.
+ *******************************************************************************/
+package org.eclipse.leshan.transport.californium.server;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.leshan.core.model.LwM2mModel;
+import org.eclipse.leshan.core.node.LwM2mNode;
+import org.eclipse.leshan.core.node.LwM2mPath;
+import org.eclipse.leshan.core.node.LwM2mSingleResource;
+import org.eclipse.leshan.core.node.TimestampedLwM2mNode;
+import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
+import org.eclipse.leshan.core.node.codec.CodecException;
+import org.eclipse.leshan.core.node.codec.LwM2mDecoder;
+import org.eclipse.leshan.core.request.ContentFormat;
+
+public class DummyDecoder implements LwM2mDecoder {
+ @Override
+ public LwM2mNode decode(byte[] content, ContentFormat format, LwM2mPath path, LwM2mModel model)
+ throws CodecException {
+ return LwM2mSingleResource.newResource(15, "Example");
+ }
+
+ @Override
+ public T decode(byte[] content, ContentFormat format, LwM2mPath path, LwM2mModel model,
+ Class nodeClass) throws CodecException {
+ return null;
+ }
+
+ @Override
+ public Map decodeNodes(byte[] content, ContentFormat format, List paths,
+ LwM2mModel model) throws CodecException {
+ return null;
+ }
+
+ @Override
+ public List decodeTimestampedData(byte[] content, ContentFormat format, LwM2mPath path,
+ LwM2mModel model) throws CodecException {
+ return Collections.singletonList(new TimestampedLwM2mNode(null, decode(null, null, null, null)));
+ }
+
+ @Override
+ public TimestampedLwM2mNodes decodeTimestampedNodes(byte[] content, ContentFormat format, List paths,
+ LwM2mModel model) throws CodecException {
+ return null;
+ }
+
+ @Override
+ public List decodePaths(byte[] content, ContentFormat format) throws CodecException {
+ return null;
+ }
+
+ @Override
+ public boolean isSupported(ContentFormat format) {
+ return false;
+ }
+
+ @Override
+ public Set getSupportedContentFormat() {
+ return null;
+ }
+}
diff --git a/leshan-tl-cf-server-coap/src/test/java/org/eclipse/leshan/transport/californium/server/bootstrap/LeshanBootstrapServerBuilderTest.java b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/LeshanBootstrapServerBuilderTest.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/test/java/org/eclipse/leshan/transport/californium/server/bootstrap/LeshanBootstrapServerBuilderTest.java
rename to leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/LeshanBootstrapServerBuilderTest.java
diff --git a/leshan-tl-cf-server-coap/src/test/java/org/eclipse/leshan/transport/californium/server/bootstrap/LeshanBootstrapServerTest.java b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/LeshanBootstrapServerTest.java
similarity index 100%
rename from leshan-tl-cf-server-coap/src/test/java/org/eclipse/leshan/transport/californium/server/bootstrap/LeshanBootstrapServerTest.java
rename to leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/LeshanBootstrapServerTest.java
diff --git a/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/CoapRequestBuilderTest.java b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/CoapRequestBuilderTest.java
new file mode 100644
index 0000000000..8fc8e6c0f2
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/CoapRequestBuilderTest.java
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2015 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
+ * Michał Wadowski (Orange) - Improved compliance with rfc6690.
+ *******************************************************************************/
+package org.eclipse.leshan.transport.californium.server.request;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.californium.core.coap.CoAP;
+import org.eclipse.californium.core.coap.MediaTypeRegistry;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.leshan.core.LwM2m.LwM2mVersion;
+import org.eclipse.leshan.core.endpoint.EndpointUriUtil;
+import org.eclipse.leshan.core.link.Link;
+import org.eclipse.leshan.core.link.attributes.ResourceTypeAttribute;
+import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute;
+import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
+import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;
+import org.eclipse.leshan.core.model.LwM2mModel;
+import org.eclipse.leshan.core.model.ObjectLoader;
+import org.eclipse.leshan.core.model.StaticModel;
+import org.eclipse.leshan.core.node.LwM2mObjectInstance;
+import org.eclipse.leshan.core.node.LwM2mSingleResource;
+import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder;
+import org.eclipse.leshan.core.node.codec.LwM2mEncoder;
+import org.eclipse.leshan.core.peer.IpPeer;
+import org.eclipse.leshan.core.request.ContentFormat;
+import org.eclipse.leshan.core.request.CreateRequest;
+import org.eclipse.leshan.core.request.DeleteRequest;
+import org.eclipse.leshan.core.request.DiscoverRequest;
+import org.eclipse.leshan.core.request.ExecuteRequest;
+import org.eclipse.leshan.core.request.ObserveRequest;
+import org.eclipse.leshan.core.request.ReadRequest;
+import org.eclipse.leshan.core.request.WriteAttributesRequest;
+import org.eclipse.leshan.core.request.WriteRequest;
+import org.eclipse.leshan.core.request.WriteRequest.Mode;
+import org.eclipse.leshan.core.tlv.Tlv;
+import org.eclipse.leshan.core.tlv.Tlv.TlvType;
+import org.eclipse.leshan.core.tlv.TlvDecoder;
+import org.eclipse.leshan.server.registration.DefaultRegistrationDataExtractor;
+import org.eclipse.leshan.server.registration.Registration;
+import org.eclipse.leshan.server.registration.Registration.Builder;
+import org.eclipse.leshan.server.registration.RegistrationDataExtractor.RegistrationData;
+import org.eclipse.leshan.transport.californium.identity.DefaultCoapIdentityHandler;
+import org.eclipse.leshan.transport.californium.identity.IdentityHandler;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link CoapRequestBuilder}
+ */
+public class CoapRequestBuilderTest {
+
+ private static LwM2mModel model;
+ private static LwM2mEncoder encoder;
+ private static IdentityHandler identityHandler;
+
+ @BeforeAll
+ public static void loadModel() {
+ model = new StaticModel(ObjectLoader.loadDefault());
+ encoder = new DefaultLwM2mEncoder();
+ identityHandler = new DefaultCoapIdentityHandler();
+ }
+
+ private Registration newRegistration() throws UnknownHostException {
+ return newRegistration(null);
+ }
+
+ private Registration newRegistration(String rootpath) throws UnknownHostException {
+ Builder b = new Registration.Builder("regid", "endpoint",
+ new IpPeer(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 12354)),
+ EndpointUriUtil.createUri("coap://localhost:5683"));
+
+ if (rootpath != null) {
+ Link[] links = new Link[] { new Link(rootpath, new ResourceTypeAttribute("oma.lwm2m")) };
+ b.objectLinks(links);
+
+ RegistrationData dataFromObjectLinks = new DefaultRegistrationDataExtractor()
+ .extractDataFromObjectLinks(links, LwM2mVersion.V1_0);
+ b.rootPath(dataFromObjectLinks.getAlternatePath());
+ b.supportedContentFormats(dataFromObjectLinks.getSupportedContentFormats());
+ b.supportedObjects(dataFromObjectLinks.getSupportedObjects());
+ b.availableInstances(dataFromObjectLinks.getAvailableInstances());
+ }
+ return b.build();
+ }
+
+ @Test
+ public void build_read_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ ReadRequest request = new ReadRequest(3, 0);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.GET, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/3/0", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_read_request_with_non_default_object_path() throws Exception {
+ Registration reg = newRegistration("/lwm2m");
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ ReadRequest request = new ReadRequest(3, 0, 1);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals("coap://127.0.0.1:12354/lwm2m/3/0/1", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_read_request_with_root_path() throws Exception {
+ Registration reg = newRegistration("/");
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ ReadRequest request = new ReadRequest(3);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals("coap://127.0.0.1:12354/3", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_discover_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ DiscoverRequest request = new DiscoverRequest(3, 0);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.GET, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals(MediaTypeRegistry.APPLICATION_LINK_FORMAT, coapRequest.getOptions().getAccept());
+ assertEquals("coap://127.0.0.1:12354/3/0", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_write_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ WriteRequest request = new WriteRequest(Mode.UPDATE, 3, 0, LwM2mSingleResource.newStringResource(15, "value"));
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.POST, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals(ContentFormat.TLV.getCode(), coapRequest.getOptions().getContentFormat());
+ assertNotNull(coapRequest.getPayload());
+ // assert it is encoded as array of resources TLV
+ Tlv[] tlvs = TlvDecoder.decode(ByteBuffer.wrap(coapRequest.getPayload()));
+ assertEquals(TlvType.RESOURCE_VALUE, tlvs[0].getType());
+ assertEquals("value", TlvDecoder.decodeString(tlvs[0].getValue()));
+ assertEquals("coap://127.0.0.1:12354/3/0", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_write_request_replace() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ WriteRequest request = new WriteRequest(3, 0, 14, "value");
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.PUT, coapRequest.getCode());
+ }
+
+ @Test
+ public void build_write_attribute_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ LwM2mAttributeSet attributes = new LwM2mAttributeSet(
+ new LwM2mAttribute(LwM2mAttributes.MINIMUM_PERIOD, 10L),
+ new LwM2mAttribute(LwM2mAttributes.MAXIMUM_PERIOD, 100L));
+ WriteAttributesRequest request = new WriteAttributesRequest(3, 0, 14, attributes);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.PUT, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/3/0/14?pmin=10&pmax=100", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_unset_write_attribute_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ LwM2mAttributeSet attributes = new LwM2mAttributeSet(new LwM2mAttribute(LwM2mAttributes.MINIMUM_PERIOD),
+ new LwM2mAttribute(LwM2mAttributes.MAXIMUM_PERIOD));
+ WriteAttributesRequest request = new WriteAttributesRequest(3, 0, 14, attributes);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.PUT, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/3/0/14?pmin&pmax", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_execute_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ ExecuteRequest request = new ExecuteRequest(3, 0, 12, "0='params'");
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.POST, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/3/0/12", coapRequest.getURI());
+ assertEquals("0='params'", coapRequest.getPayloadString());
+ }
+
+ @Test
+ public void build_create_request__without_instance_id() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ CreateRequest request = new CreateRequest(12, LwM2mSingleResource.newStringResource(0, "value"));
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.POST, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/12", coapRequest.getURI());
+ assertEquals(ContentFormat.TLV.getCode(), coapRequest.getOptions().getContentFormat());
+ assertNotNull(coapRequest.getPayload());
+ // assert it is encoded as array of resources TLV
+ Tlv[] tlvs = TlvDecoder.decode(ByteBuffer.wrap(coapRequest.getPayload()));
+ assertEquals(TlvType.RESOURCE_VALUE, tlvs[0].getType());
+ }
+
+ @Test
+ public void build_create_request__with_instance_id() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ CreateRequest request = new CreateRequest(12,
+ new LwM2mObjectInstance(26, LwM2mSingleResource.newStringResource(0, "value")));
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.POST, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/12", coapRequest.getURI());
+ assertEquals(ContentFormat.TLV.getCode(), coapRequest.getOptions().getContentFormat());
+ assertNotNull(coapRequest.getPayload());
+ // assert it is encoded as array of instance TLV
+ Tlv[] tlvs = TlvDecoder.decode(ByteBuffer.wrap(coapRequest.getPayload()));
+ assertEquals(TlvType.OBJECT_INSTANCE, tlvs[0].getType());
+ assertEquals(26, tlvs[0].getIdentifier());
+ }
+
+ @Test
+ public void build_delete_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ DeleteRequest request = new DeleteRequest(12, 0);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.DELETE, coapRequest.getCode());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/12/0", coapRequest.getURI());
+ }
+
+ @Test
+ public void build_observe_request() throws Exception {
+ Registration reg = newRegistration();
+
+ // test
+ CoapRequestBuilder builder = new CoapRequestBuilder(reg.getClientTransportData(), reg.getRootPath(),
+ reg.getId(), reg.getEndpoint(), model, encoder, false, null, identityHandler);
+ ObserveRequest request = new ObserveRequest(12, 0);
+ builder.visit(request);
+
+ // verify
+ Request coapRequest = builder.getRequest();
+ assertEquals(CoAP.Code.GET, coapRequest.getCode());
+ assertEquals(0, coapRequest.getOptions().getObserve().intValue());
+ assertEquals("127.0.0.1", coapRequest.getDestinationContext().getPeerAddress().getAddress().getHostAddress());
+ assertEquals(12354, coapRequest.getDestinationContext().getPeerAddress().getPort());
+ assertEquals("coap://127.0.0.1:12354/12/0", coapRequest.getURI());
+ }
+}
diff --git a/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/LwM2mResponseBuilderTest.java b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/LwM2mResponseBuilderTest.java
new file mode 100644
index 0000000000..5502e11c9a
--- /dev/null
+++ b/leshan-tl-cf-bsserver-coap/src/test/java/org/eclipse/leshan/transport/californium/bsserver/request/LwM2mResponseBuilderTest.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Orange.
+ *
+ * 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:
+ * Michał Wadowski (Orange) - Add Observe-Composite feature.
+ * Michał Wadowski (Orange) - Improved compliance with rfc6690.
+ *******************************************************************************/
+package org.eclipse.leshan.transport.californium.server.request;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.californium.core.coap.CoAP;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Response;
+import org.eclipse.californium.core.coap.Token;
+import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser;
+import org.eclipse.leshan.core.link.lwm2m.LwM2mLinkParser;
+import org.eclipse.leshan.core.node.LwM2mPath;
+import org.eclipse.leshan.core.observation.CompositeObservation;
+import org.eclipse.leshan.core.observation.SingleObservation;
+import org.eclipse.leshan.core.request.ObserveCompositeRequest;
+import org.eclipse.leshan.core.request.ObserveRequest;
+import org.eclipse.leshan.core.response.ObserveCompositeResponse;
+import org.eclipse.leshan.core.response.ObserveResponse;
+import org.eclipse.leshan.transport.californium.ObserveUtil;
+import org.eclipse.leshan.transport.californium.server.DummyDecoder;
+import org.junit.jupiter.api.Test;
+
+public class LwM2mResponseBuilderTest {
+
+ private final DummyDecoder decoder = new DummyDecoder();
+ private final LwM2mLinkParser linkParser = new DefaultLwM2mLinkParser();
+
+ @Test
+ public void visit_observe_request() {
+ // given
+ String examplePath = "/1/2/3";
+
+ ObserveRequest observeRequest = new ObserveRequest(null, examplePath);
+
+ Map userContext = ObserveUtil.createCoapObserveRequestContext(null, null, observeRequest);
+
+ Request coapRequest = new Request(null);
+ coapRequest.setToken(Token.EMPTY);
+ coapRequest.setUserContext(userContext);
+
+ Response coapResponse = new Response(CoAP.ResponseCode.CONTENT);
+ coapResponse.getOptions().setObserve(1);
+
+ LwM2mResponseBuilder responseBuilder = new LwM2mResponseBuilder<>(coapRequest, coapResponse,
+ null, null, decoder, linkParser);
+ // when
+ responseBuilder.visit(observeRequest);
+
+ // then
+ ObserveResponse response = responseBuilder.getResponse();
+ assertNotNull(response);
+ assertNotNull(response.getObservation());
+
+ SingleObservation observation = response.getObservation();
+ assertEquals(examplePath, observation.getPath().toString());
+ }
+
+ @Test
+ public void visit_observe_composite_request() {
+ // given
+ List examplePaths = Arrays.asList(new LwM2mPath("/1/2/3"), new LwM2mPath("/4/5/6"));
+
+ ObserveCompositeRequest observeRequest = new ObserveCompositeRequest(null, null, examplePaths);
+
+ Map userContext = ObserveUtil.createCoapObserveCompositeRequestContext(null, null,
+ observeRequest);
+
+ Request coapRequest = new Request(null);
+ coapRequest.setToken(Token.EMPTY);
+ coapRequest.setUserContext(userContext);
+
+ Response coapResponse = new Response(CoAP.ResponseCode.CONTENT);
+ coapResponse.getOptions().setObserve(1);
+
+ LwM2mResponseBuilder responseBuilder = new LwM2mResponseBuilder<>(coapRequest,
+ coapResponse, null, null, decoder, linkParser);
+ // when
+ responseBuilder.visit(observeRequest);
+
+ // then
+ ObserveCompositeResponse response = responseBuilder.getResponse();
+ assertNotNull(response);
+ assertNotNull(response.getObservation());
+
+ CompositeObservation observation = response.getObservation();
+ assertEquals(examplePaths, observation.getPaths());
+ }
+
+}