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

#732: fix rt and ver attributes of Link format. #774

Merged
merged 1 commit into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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