Skip to content

Commit

Permalink
#732: fix rt and ver attributes of Link format.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Jan 6, 2020
1 parent ed9e8ad commit 8120415
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public static Link[] getClientDescription(Collection<LwM2mObjectEnabler> objectE

// create links for "object"
String rootURL = getPath("/", root);
Map<String, Object> attributes = new HashMap<>();
attributes.put("rt", "oma.lwm2m");
Map<String, String> attributes = new HashMap<>();
attributes.put("rt", "\"oma.lwm2m\"");
links.add(new Link(rootURL, attributes));

// sort resources
Expand All @@ -67,7 +67,7 @@ public int compare(LwM2mObjectEnabler o1, LwM2mObjectEnabler o2) {

List<Integer> availableInstance = objectEnabler.getAvailableInstanceIds();
// Include an object link if there are no instances or there are object attributes (e.g. "ver")
Map<String, ?> objectAttributes = getObjectAttributes(objectEnabler.getObjectModel());
Map<String, String> objectAttributes = getObjectAttributes(objectEnabler.getObjectModel());
if (availableInstance.isEmpty() || (objectAttributes != null)) {
String objectInstanceUrl = getPath("/", root, Integer.toString(objectEnabler.getId()));
links.add(new Link(objectInstanceUrl, objectAttributes));
Expand All @@ -89,7 +89,7 @@ public static Link[] getObjectDescription(LwM2mObjectEnabler objectEnabler, Stri
String rootPath = root == null ? "" : root;

// create link for "object"
Map<String, ?> objectAttributes = getObjectAttributes(objectEnabler.getObjectModel());
Map<String, String> objectAttributes = getObjectAttributes(objectEnabler.getObjectModel());
String objectURL = getPath("/", rootPath, Integer.toString(objectEnabler.getId()));
links.add(new Link(objectURL, objectAttributes));

Expand Down Expand Up @@ -192,7 +192,7 @@ private static String normalize(String input, int len, int off) {
return sb.toString();
}

private static Map<String, ?> getObjectAttributes(ObjectModel objectModel) {
private static Map<String, String> getObjectAttributes(ObjectModel objectModel) {
if (StringUtils.isEmpty(objectModel.version) || ObjectModel.DEFAULT_VERSION.equals(objectModel.version)) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ public void encode_objectModel_to_linkObject_with_version2_0() {
Link[] links = LinkFormatHelper.getObjectDescription(createObjectEnabler(locationModel), "/");
String strLinks = Link.serialize(links);

assertEquals("</6>;ver=\"2.0\",</6/0>,</6/0/0>,</6/0/1>,</6/0/2>,</6/0/3>,</6/0/4>,</6/0/5>,</6/0/6>",
strLinks);
assertEquals("</6>;ver=2.0,</6/0>,</6/0/0>,</6/0/1>,</6/0/2>,</6/0/3>,</6/0/4>,</6/0/5>,</6/0/6>", strLinks);
}

@Test
Expand Down Expand Up @@ -128,7 +127,7 @@ public void encode_client_description_with_version_2_0() {
Link[] links = LinkFormatHelper.getClientDescription(objectEnablers, null);
String strLinks = Link.serialize(links);

assertEquals("</>;rt=\"oma.lwm2m\",</6>;ver=\"2.0\",</6/0>,</6/1>", strLinks);
assertEquals("</>;rt=\"oma.lwm2m\",</6>;ver=2.0,</6/0>,</6/1>", strLinks);
}

@Test
Expand All @@ -142,7 +141,7 @@ public void encode_client_description_with_version_2_0_no_instances() {
Link[] links = LinkFormatHelper.getClientDescription(objectEnablers, null);
String strLinks = Link.serialize(links);

assertEquals("</>;rt=\"oma.lwm2m\",</6>;ver=\"2.0\"", strLinks);
assertEquals("</>;rt=\"oma.lwm2m\",</6>;ver=2.0", strLinks);
}

private ObjectModel getObjectModel(int id) {
Expand Down
101 changes: 81 additions & 20 deletions leshan-core/src/main/java/org/eclipse/leshan/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Map.Entry;

import org.eclipse.leshan.util.StringUtils;
import org.eclipse.leshan.util.Validate;

/**
* A Link as defined in http://tools.ietf.org/html/rfc6690.
Expand All @@ -46,15 +47,15 @@ public class Link implements Serializable {

private final String url;

private final Map<String, Object> attributes;
private final Map<String, String> attributes;

/**
* Creates a new Link without attributes.
*
* @param url the object link URL
*/
public Link(String url) {
this(url, null);
this(url, (Map<String, String>) null);
}

/**
Expand All @@ -63,7 +64,8 @@ public Link(String url) {
* @param url the link URL
* @param attributes the object link attributes or <code>null</code> if the link has no attributes
*/
public Link(String url, Map<String, ?> attributes) {
public <T> Link(String url, Map<String, String> attributes) {
Validate.notNull(url);
this.url = url;
if (attributes != null) {
this.attributes = Collections.unmodifiableMap(new HashMap<>(attributes));
Expand All @@ -72,6 +74,61 @@ public Link(String url, Map<String, ?> attributes) {
}
}

/**
* Creates a new link and with its attributes.
*
* @param url the link URL
* @param attributes the object link attributes or <code>null</code> if the link has no attributes
*/
@SuppressWarnings("unchecked")
public <T> Link(String url, Map<String, T> attributes, Class<T> clazz) {
Validate.notNull(url);
this.url = url;
if (attributes == null || attributes.isEmpty()) {
this.attributes = Collections.emptyMap();
} else {
if (String.class.equals(clazz)) {
this.attributes = Collections.unmodifiableMap((Map<String, String>) new HashMap<>(attributes));
} else {
HashMap<String, String> attributesMap = new HashMap<>();
for (Entry<String, T> attr : attributes.entrySet()) {
if (attr.getValue() == null) {
attributesMap.put(attr.getKey(), null);
} else {
attributesMap.put(attr.getKey(), attr.getValue().toString());
}

}
this.attributes = Collections.unmodifiableMap(attributesMap);
}
}
}

/**
* Creates a new link and with its attributes.
*
* @param url the link URL
* @param attributes the object link attributes. The format is attributeKey1, attributeValue1, attributeKey2,
* attributeValue2. For empty attributes null value should be used.
*/
public Link(String url, String... attributes) {
Validate.notNull(url);
this.url = url;
if (attributes == null || attributes.length == 0) {
this.attributes = Collections.emptyMap();
} else {
if (attributes.length % 2 != 0) {
throw new IllegalArgumentException("Each attributes key must have a value");
}

HashMap<String, String> attributesMap = new HashMap<>();
for (int i = 0; i < attributes.length; i = i + 2) {
attributesMap.put(attributes[i], attributes[i + 1]);
}
this.attributes = Collections.unmodifiableMap(attributesMap);
}
}

public String getUrl() {
return url;
}
Expand All @@ -81,7 +138,7 @@ public String getUrl() {
*
* @return an unmodifiable map containing the link attributes
*/
public Map<String, Object> getAttributes() {
public Map<String, String> getAttributes() {
return attributes;
}

Expand All @@ -92,18 +149,14 @@ public String toString() {
builder.append(getUrl());
builder.append('>');

Map<String, Object> attributes = getAttributes();
Map<String, String> attributes = getAttributes();
if (attributes != null && !attributes.isEmpty()) {
for (Entry<String, Object> entry : attributes.entrySet()) {
for (Entry<String, String> entry : attributes.entrySet()) {
builder.append(";");
builder.append(entry.getKey());
if (entry.getValue() != null) {
builder.append("=");
if (entry.getValue() instanceof String) {
builder.append("\"").append(entry.getValue()).append("\"");
} else {
builder.append(entry.getValue());
}
builder.append(entry.getValue());
}
}
}
Expand Down Expand Up @@ -132,22 +185,16 @@ public static Link[] parse(byte[] content) {
url = StringUtils.removeStart(StringUtils.removeEnd(url, ">"), "<");

// parse attributes
Map<String, Object> attributes = new HashMap<>();
Map<String, String> attributes = new HashMap<>();

if (linkParts.length > 1) {
for (int i = 1; i < linkParts.length; i++) {
String[] attParts = linkParts[i].split("=");
if (attParts.length > 0) {
String key = attParts[0];
Object value = null;
String value = null;
if (attParts.length > 1) {
String rawvalue = attParts[1];
try {
value = Integer.valueOf(rawvalue);
} catch (NumberFormatException e) {

value = rawvalue.replaceFirst("^\"(.*)\"$", "$1");
}
value = attParts[1];
}
attributes.put(key, value);
}
Expand Down Expand Up @@ -210,4 +257,18 @@ public boolean equals(Object obj) {
return false;
return true;
}

/**
* remove quote from string, only if it begins and ends by a quote.
*
* @param string to unquote
* @return unquoted string or the original string if there no quote to remove.
*/
public static String unquote(String string) {
if (string != null && string.length() >= 2 && string.charAt(0) == '"'
&& string.charAt(string.length() - 1) == '"') {
string = string.substring(1, string.length() - 1);
}
return string;
}
}
56 changes: 36 additions & 20 deletions leshan-core/src/test/java/org/eclipse/leshan/LinkObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*******************************************************************************/
package org.eclipse.leshan;

import static org.junit.Assert.assertEquals;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -30,9 +32,9 @@ public void parse_with_some_attributes() {
Assert.assertEquals(5, parse.length);
Assert.assertEquals("/", parse[0].getUrl());

Map<String, Object> attResult = new HashMap<>();
attResult.put("rt", "oma.lwm2m");
attResult.put("ct", 100);
Map<String, String> attResult = new HashMap<>();
attResult.put("rt", "\"oma.lwm2m\"");
attResult.put("ct", "100");
Assert.assertEquals(attResult, parse[0].getAttributes());

Assert.assertEquals("/1/101", parse[1].getUrl());
Expand All @@ -58,7 +60,7 @@ public void parse_with_quoted_attributes() {
Assert.assertEquals("/", parse[0].getUrl());

Map<String, String> attResult = new HashMap<>();
attResult.put("k1", "quotes\"inside");
attResult.put("k1", "\"quotes\"inside\"");
attResult.put("k2", "endwithquotes\"");
attResult.put("k3", "noquotes");
attResult.put("k4", "\"startwithquotes");
Expand All @@ -79,17 +81,9 @@ public void serialyse_without_attribute() {

@Test
public void serialyse_with_attributes() {
HashMap<String, Object> attributesObj1 = new HashMap<>();
attributesObj1.put("number", 12);
Link obj1 = new Link("/1/0/1", attributesObj1);

HashMap<String, Object> attributesObj2 = new HashMap<>();
attributesObj2.put("string", "stringval");
Link obj2 = new Link("/2/1", attributesObj2);

HashMap<String, Object> attributesObj3 = new HashMap<>();
attributesObj3.put("empty", null);
Link obj3 = new Link("/3", attributesObj3);
Link obj1 = new Link("/1/0/1", "number", "12");
Link obj2 = new Link("/2/1", "string", "\"stringval\"");
Link obj3 = new Link("/3", "empty", null);

String res = Link.serialize(obj1, obj2, obj3);

Expand All @@ -99,9 +93,9 @@ public void serialyse_with_attributes() {

@Test
public void serialyse_with_root_url() {
HashMap<String, Object> attributesObj1 = new HashMap<>();
HashMap<String, Integer> attributesObj1 = new HashMap<>();
attributesObj1.put("number", 12);
Link obj1 = new Link("/", attributesObj1);
Link obj1 = new Link("/", attributesObj1, Integer.class);

String res = Link.serialize(obj1);

Expand All @@ -115,11 +109,11 @@ public void serialyse_then_parse_with_severals_attributes() {
attributesObj1.put("number1", 1);
attributesObj1.put("number2", 1);
attributesObj1.put("string1", "stringval1");
Link obj1 = new Link("/1/0", attributesObj1);
Link obj1 = new Link("/1/0", attributesObj1, Object.class);

HashMap<String, Object> attributesObj2 = new HashMap<>();
HashMap<String, Integer> attributesObj2 = new HashMap<>();
attributesObj2.put("number3", 3);
Link obj2 = new Link("/2", attributesObj2);
Link obj2 = new Link("/2", attributesObj2, Integer.class);

Link[] input = new Link[] { obj1, obj2 };
String strObjs = Link.serialize(input);
Expand All @@ -137,4 +131,26 @@ public void parse_then_serialyse_with_rt_attribute() {
Assert.assertEquals(input, output);

}

@Test
public void parse_quoted_ver_attributes() {
String input = "</1>;ver=\"2.2\"";
Link[] objs = Link.parse(input.getBytes());
assertEquals(objs[0].getAttributes().get("ver"), "\"2.2\"");
}

@Test
public void parse_unquoted_ver_attributes() {
String input = "</1>;ver=2.2";
Link[] objs = Link.parse(input.getBytes());
assertEquals(objs[0].getAttributes().get("ver"), "2.2");
}

@Test
public void serialyse_ver_attributes_without_quote() {
Map<String, String> att = new HashMap<>();
att.put("ver", "2.2");
Link link = new Link("/1", att);
assertEquals("</1>;ver=2.2", Link.serialize(link));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private Registration newRegistration(String rootpath) throws UnknownHostExceptio
Identity.unsecure(Inet4Address.getLoopbackAddress(), 12354));
if (rootpath != null) {
Map<String, String> attr = new HashMap<>();
attr.put("rt", "oma.lwm2m");
attr.put("rt", "\"oma.lwm2m\"");
b.objectLinks(new Link[] { new Link(rootpath, attr) });
}
return b.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ protected Registration(String id, String endpoint, Identity identity, String lwM
String rootPath = "/";
if (objectLinks != null) {
for (Link link : objectLinks) {
if (link != null && "oma.lwm2m".equals(link.getAttributes().get("rt"))) {
if (link != null && "oma.lwm2m".equals(Link.unquote(link.getAttributes().get("rt")))) {
rootPath = link.getUrl();
break;
}
Expand Down Expand Up @@ -366,21 +366,23 @@ public static Map<Integer, String> getSupportedObject(String rootPath, Link[] ob
try {
// extract object id and version
int objectId = Integer.parseInt(m.group(1));
Object version = link.getAttributes().get(Attribute.OBJECT_VERSION);
String version = link.getAttributes().get(Attribute.OBJECT_VERSION);
// un-quote version (see https://github.com/eclipse/leshan/issues/732)
version = Link.unquote(version);
String currentVersion = objects.get(objectId);

// store it in map
if (currentVersion == null) {
// we never find version for this object add it
if (version instanceof String) {
objects.put(objectId, (String) version);
if (version != null) {
objects.put(objectId, version);
} else {
objects.put(objectId, ObjectModel.DEFAULT_VERSION);
}
} else {
// if version is already set, we override it only if new version is not DEFAULT_VERSION
if (version instanceof String && !version.equals(ObjectModel.DEFAULT_VERSION)) {
objects.put(objectId, (String) version);
if (version != null && !version.equals(ObjectModel.DEFAULT_VERSION)) {
objects.put(objectId, version);
}
}
} catch (NumberFormatException e) {
Expand Down
Loading

0 comments on commit 8120415

Please sign in to comment.