Commit 04264b32 authored by Björn Butzin's avatar Björn Butzin
Browse files

Soundness and Usability improvements; Added feature: Arbitrary Resource Tags

---
fixed jcoap-example & jcoap-applications classpath to remain at Java 1.6
removed outdated gradle build to maintain consistency
added license information to maven pom of jcoap
removed coapcache test implementation

added CoapClientChannel.createRequest() signature to consume all mandatory information required for a sound request message.
Thus it is not possible to create an unfinished request with this call.
old createRequest(reliable?, CoapRequestCode); is marked as deprecated as it allows creating unsound requests.
reordered the parameters for better human readability

Added Method signature CoapMessage.setPayload(CoapData data) to soundly set the payload including corresponding media type.

Changed the CoapResource interface by adding functions to add and remove arbitrary tags
Changed BasicCoapResource to implement the interface changes. If a resource is set to be observeble a tag(flag) "obs" is added automatically
Changed the core resource to append arbitrary tags to the resources

further classes have been adapted to the changes mentioned above
parent 5f24e3e7
......@@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
......
......@@ -12,8 +12,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
......@@ -82,7 +82,7 @@
<dependency>
<groupId>org.ws4d.jcoap</groupId>
<artifactId>jcoap-core</artifactId>
<version>1.1.2</version>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
......
package org.ws4d.coap.cache;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.Constants;
import org.ws4d.coap.core.CoapClient;
import org.ws4d.coap.core.CoapConstants;
import org.ws4d.coap.core.connection.BasicCoapChannelManager;
import org.ws4d.coap.core.connection.api.CoapChannelManager;
import org.ws4d.coap.core.connection.api.CoapClientChannel;
import org.ws4d.coap.core.enumerations.CoapRequestCode;
import org.ws4d.coap.core.messages.api.CoapRequest;
import org.ws4d.coap.core.messages.api.CoapResponse;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
/**
* 5.6. Caching
*
* CoAP endpoints MAY cache responses in order to reduce the response time and
* network bandwidth consumption on future, equivalent requests.
*
* The goal of caching in CoAP is to reuse a prior response message to satisfy a
* current request. In some cases, a stored response can be reused without the
* need for a network request, reducing latency and network round-trips; a
* "freshness" mechanism is used for this purpose (see Section 5.6.1). Even when
* a new request is required, it is often possible to reuse the payload of a
* prior response to satisfy the request, thereby reducing network bandwidth
* usage; a "validation" mechanism is used for this purpose (see Section 5.6.2).
*
* @author Björn Butzin <bjoern.butzin@uni-rostock.de>
*
*/
public class CoapCache2 implements CoapClient {
private static Cache cache;
private static final Logger logger = LogManager.getLogger();
public CoapCache2() {
cache = new Cache("Coap Cache", 0, null, false, null, false, 0, 0, false, 0, null, null, 0, 0, false, false,
false);
CacheManager.create().addCache(cache);
}
public List<CoapResponse> request(CoapRequest request) {
List<TimedCacheEntry<CoapResponse>> cacheHits = getFromCache(request);
List<CoapResponse> result = new ArrayList<>();
if (cacheHits.size() > 0) {
for (TimedCacheEntry<CoapResponse> cacheHit : cacheHits) {
// fresh?
if (cacheHit.getExpires() < System.currentTimeMillis()) {
CoapResponse response = cacheHit.getValue();
response.setMaxAge((int) (cacheHit.getExpires() - System.currentTimeMillis()));
result.add(response);
}
// revalidate
}
// if(Multicast){
// send request to server
// add results to cache
// }
} else {
// cache miss
// send request to server
// add results to cache
}
return result;
}
/**
* 5.6.1. Freshness Model
*
* When a response is "fresh" in the cache, it can be used to satisfy
* subsequent requests without contacting the origin server, thereby
* improving efficiency.
*
* The mechanism for determining freshness is for an origin server to
* provide an explicit expiration time in the future, using the Max-Age
* Option (see Section 5.10.5). The Max-Age Option indicates that the
* response is to be considered not fresh after its age is greater than the
* specified number of seconds.
*
* The Max-Age Option defaults to a value of 60. Thus, if it is not present
* in a cacheable response, then the response is considered not fresh after
* its age is greater than 60 seconds. If an origin server wishes to prevent
* caching, it MUST explicitly include a Max-Age Option with a value of zero
* seconds.
*
* If a client has a fresh stored response and makes a new request matching
* the request for that stored response, the new response invalidates the
* old response.
*/
private boolean isFresh(CoapResponse response) {
return false;
}
/**
* Unlike HTTP, the cacheability of CoAP responses does not depend on the
* request method, but it depends on the Response Code. The cacheability of
* each Response Code is defined along the Response Code definitions in
* Section 5.9. Response Codes that indicate success and are unrecognized by
* an endpoint MUST NOT be cached.
*
* For a presented request, a CoAP endpoint MUST NOT use a stored response,
* unless:
*
* the presented request method and that used to obtain the stored response
* match,
*
* all options match between those in the presented request and those of the
* request used to obtain the stored response (which includes the request
* URI), except that there is no need for a match of any request options
* marked as NoCacheKey (Section 5.4) or recognized by the Cache and fully
* interpreted with respect to its specified cache behavior (such as the
* ETag request option described in Section 5.10.6; see also Section 5.4.2),
* and
*
* the stored response is either fresh or successfully revalidated
*/
private boolean isCacheable(CoapResponse response) {
return false;
}
private List<TimedCacheEntry<CoapResponse>> getFromCache(CoapRequest request) {
return null;
}
private boolean addToCache(CoapRequest request, CoapResponse response) {
if (!isCacheable(response)) {
return false;
}
long expires = System.currentTimeMillis();
expires += (-1 == response.getMaxAge()) ? CoapConstants.COAP_DEFAULT_MAX_AGE_MS : response.getMaxAge();
TimedCacheEntry<CoapResponse> e = new TimedCacheEntry<>(expires, response);
if (null == getFromCache(request)) {
// add to cache
} else {
// update response
}
return true;
}
/**
* 5.6.2. Validation Model
*
* When an endpoint has one or more stored responses for a GET request, but
* cannot use any of them (e.g., because they are not fresh), it can use the
* ETag Option (Section 5.10.6) in the GET request to give the origin server
* an opportunity both to select a stored response to be used, and to update
* its freshness. This process is known as "validating" or "revalidating"
* the stored response.
*
* When sending such a request, the endpoint SHOULD add an ETag Option
* specifying the entity-tag of each stored response that is applicable.
*
* A 2.03 (Valid) response indicates the stored response identified by the
* entity-tag given in the response's ETag Option can be reused after
* updating it as described in Section 5.9.1.3.
*
* Any other Response Code indicates that none of the stored responses
* nominated in the request is suitable. Instead, the response SHOULD be
* used to satisfy the request and MAY replace the stored response.
*/
private void revalidate(CoapResponse response) {
if (null != response.getETag()) {
// FIXME where to get them
String sAddress = null;
int sPort = CoapConstants.COAP_DEFAULT_PORT;
CoapClientChannel clientChannel;
try {
clientChannel = BasicCoapChannelManager.getInstance().connect(this, InetAddress.getByName(sAddress),
sPort);
CoapRequest request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/statistic");
request.addETag(response.getETag());
clientChannel.sendMessage(request);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onResponse(CoapClientChannel channel, CoapResponse response) {
// TODO match request and response
// add to cache
}
public void onMCResponse(CoapClientChannel channel, CoapResponse response, InetAddress srcAddress, int srcPort) {
// TODO match request and response
// add to cache
}
public void onConnectionFailed(CoapClientChannel channel, boolean notReachable, boolean resetByServer) {
if (notReachable) {
logger.warn("Target not reachable");
} else if (resetByServer) {
logger.warn("Connection reset by the server");
}
logger.warn("Connection failed for unknown reason");
}
private class TimedCacheEntry<A> {
private final Long expires;
private final A value;
TimedCacheEntry(Long expires, A value) {
this.expires = expires;
this.value = value;
}
public Long getExpires() {
return this.expires;
}
public A getValue() {
return this.value;
}
}
}
......@@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
......
......@@ -12,8 +12,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
......@@ -27,7 +27,7 @@
<dependency>
<groupId>org.ws4d.jcoap</groupId>
<artifactId>jcoap-core</artifactId>
<version>1.1.2</version>
<version>1.1.4</version>
</dependency>
</dependencies>
<url>https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap</url>
......
......@@ -28,6 +28,7 @@ import org.ws4d.coap.core.enumerations.CoapMediaType;
import org.ws4d.coap.core.enumerations.CoapRequestCode;
import org.ws4d.coap.core.messages.api.CoapRequest;
import org.ws4d.coap.core.messages.api.CoapResponse;
import org.ws4d.coap.core.rest.CoapData;
import org.ws4d.coap.core.tools.Encoder;
/**
......@@ -72,95 +73,76 @@ public class Client implements CoapClient {
return;
}
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/temperature");
request = clientChannel.createRequest(CoapRequestCode.GET, "/temperature", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/multiType");
request = clientChannel.createRequest(CoapRequestCode.GET, "/multiType", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/multiType");
request = clientChannel.createRequest(CoapRequestCode.GET, "/multiType", true);
request.addAccept(CoapMediaType.exi);
request.addAccept(CoapMediaType.json);
request.addAccept(CoapMediaType.xml);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/window");
request = clientChannel.createRequest(CoapRequestCode.GET, "/window", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.POST);
request.setUriPath("/window");
request.setContentType(CoapMediaType.text_plain);
request.setPayload("true");
request = clientChannel.createRequest(CoapRequestCode.POST, "/window", true);
request.setPayload(new CoapData("true", CoapMediaType.text_plain));
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/window");
request = clientChannel.createRequest(CoapRequestCode.GET, "/window", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/.well-known/core");
request = clientChannel.createRequest(CoapRequestCode.GET, "/.well-known/core", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.POST);
request.setUriPath("/newResource");
request.setContentType(CoapMediaType.text_plain);
request.setPayload("newValue");
request = clientChannel.createRequest(CoapRequestCode.POST, "/newResource", true);
request.setPayload(new CoapData("newValue", CoapMediaType.text_plain));
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/newResource");
request = clientChannel.createRequest(CoapRequestCode.GET, "/newResource", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.PUT);
request.setUriPath("/newResource");
request.setContentType(CoapMediaType.text_plain);
request.setPayload("veryNewValue");
request = clientChannel.createRequest(CoapRequestCode.PUT, "/newResource", true);
request.setPayload(new CoapData("veryNewValue", CoapMediaType.text_plain));
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/newResource");
request = clientChannel.createRequest(CoapRequestCode.GET, "/newResource", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/.well-known/core");
request = clientChannel.createRequest(CoapRequestCode.GET, "/.well-known/core", true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.DELETE);
request.setUriPath("/newResource");
request = clientChannel.createRequest(CoapRequestCode.DELETE, "/newResource", true);
request.setContentType(CoapMediaType.text_plain);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/.well-known/core");
request = clientChannel.createRequest(CoapRequestCode.GET, "/.well-known/core", true);
printRequest(request);
clientChannel.sendMessage(request);
String longpath = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234";
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/sub/" + longpath);
request = clientChannel.createRequest(CoapRequestCode.GET, "/sub/" + longpath, true);
printRequest(request);
clientChannel.sendMessage(request);
request = clientChannel.createRequest(true, CoapRequestCode.GET);
request.setUriPath("/ns:device/ns:service/operation/parameter");
request = clientChannel.createRequest(CoapRequestCode.GET, "/ns:device/ns:service/operation/parameter", true);
printRequest(request);
clientChannel.sendMessage(request);
}
......
......@@ -24,7 +24,6 @@ import org.ws4d.coap.core.rest.CoapData;
import org.ws4d.coap.core.rest.CoapResourceServer;
import org.ws4d.coap.core.rest.MultiTypeResource;
import org.ws4d.coap.core.rest.api.ResourceHandler;
import org.ws4d.coap.core.tools.Encoder;
import org.ws4d.coap.example.basics.resources.ObservableResource;
import org.ws4d.coap.example.basics.resources.Window;
......@@ -90,12 +89,12 @@ public class Server {
@Override
public CoapData handleGet() {
return new CoapData(Encoder.StringToByte("<XML>Content</XML>"), this.getMediaType());
return new CoapData("<XML>Content</XML>", this.getMediaType());
}
@Override
public CoapData handleGet(List<String> queryString) {
return new CoapData(Encoder.StringToByte("<XML>Content</XML>"), this.getMediaType());
return new CoapData("<XML>Content</XML>", this.getMediaType());
}
@Override
......@@ -122,12 +121,12 @@ public class Server {
@Override
public CoapData handleGet() {
return new CoapData(Encoder.StringToByte("JSON{content:content}"), this.getMediaType());
return new CoapData("JSON{content:content}", this.getMediaType());
}
@Override
public CoapData handleGet(List<String> queryString) {
return new CoapData(Encoder.StringToByte("JSON{content:content}"), this.getMediaType());
return new CoapData("JSON{content:content}", this.getMediaType());
}
@Override
......
......@@ -35,8 +35,7 @@ public class BasicCoapBlockClient implements CoapClient {
try {
this.clientChannel = this.channelManager.connect(this, InetAddress.getByName(SERVER_ADDRESS), PORT);
CoapRequest coapRequest = this.clientChannel.createRequest(true, CoapRequestCode.GET);
coapRequest.setUriPath("/large");
CoapRequest coapRequest = this.clientChannel.createRequest(CoapRequestCode.GET, "/large", true);
this.clientChannel.setMaxReceiveBlocksize(CoapBlockSize.BLOCK_64);
this.clientChannel.sendMessage(coapRequest);
System.out.println("Sent Request");
......
......@@ -24,6 +24,7 @@ import org.ws4d.coap.core.enumerations.CoapMediaType;
import org.ws4d.coap.core.enumerations.CoapResponseCode;
import org.ws4d.coap.core.messages.api.CoapRequest;
import org.ws4d.coap.core.messages.api.CoapResponse;
import org.ws4d.coap.core.rest.CoapData;
/**
* @author Christian Lerche <christian.lerche@uni-rostock.de>
......@@ -82,8 +83,7 @@ public class SeparateResponseCoapServer implements CoapServer {
public void run()
{
this.preparedResponse.setContentType(CoapMediaType.text_plain);
this.preparedResponse.setPayload("payload...".getBytes());
this.preparedResponse.setPayload(new CoapData("payload...", CoapMediaType.text_plain));
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
......
......@@ -11,6 +11,7 @@ import org.ws4d.coap.core.enumerations.CoapMediaType;
import org.ws4d.coap.core.enumerations.CoapRequestCode;
import org.ws4d.coap.core.messages.api.CoapRequest;
import org.ws4d.coap.core.messages.api.CoapResponse;
import org.ws4d.coap.core.rest.CoapData;
/**
* Client Application for Plugtest 2012, Paris, France Execute with argument
......@@ -37,100 +38,76 @@ public class PlugtestClient implements CoapClient {
this.ip = serverAddress;
this.port = serverPort;
init(false, CoapRequestCode.GET);
this.request.setUriPath("/.well-known/core");
init(CoapRequestCode.GET, "/.well-known/core", false);
System.out.println("QueryPath: " + this.request.getUriPath());
//reliable GET
// reliable GET
if (testcase.equals("TD_COAP_CORE_01")) {
init(true, CoapRequestCode.GET);
this.request.setUriPath("/test");
//reliable POST
init(CoapRequestCode.GET, "/test", true);
// reliable POST
} else if (testcase.equals("TD_COAP_CORE_02")) {
init(true, CoapRequestCode.POST);
this.request.setUriPath("/test");
this.request.setPayload("Content of new resource /test");
this.request.setContentType(CoapMediaType.text_plain);
//reliable PUT
init(CoapRequestCode.POST, "/test", true);
this.request.setPayload(new CoapData("Content of new resource /test", CoapMediaType.text_plain));
// reliable PUT
} else if (testcase.equals("TD_COAP_CORE_03")) {
init(true, CoapRequestCode.PUT);
this.request.setUriPath("/test");
this.request.setPayload("Content of new resource /test");
this.request.setContentType(CoapMediaType.text_plain);
//reliable DELETE
init(CoapRequestCode.PUT, "/test", true);
this.request.setPayload(new CoapData("Content of new resource /test", CoapMediaType.text_plain));
// reliable DELETE
} else if (testcase.equals("TD_COAP_CORE_04")) {
init(true, CoapRequestCode.DELETE);
this.request.setUriPath("/test");
//UNreliable GET
init(CoapRequestCode.DELETE, "/test", true);
// UNreliable GET
} else if (testcase.equals("TD_COAP_CORE_05")) {
init(false, CoapRequestCode.GET);
this.request.setUriPath("/test");
//UNreliable POST
init(CoapRequestCode.GET, "/test", false);
// UNreliable POST
} else if (testcase.equals("TD_COAP_CORE_06")) {
init(false, CoapRequestCode.POST);
this.request.setUriPath("/test");
this.request.setPayload("Content of new resource /test");
this.request.setContentType(CoapMediaType.text_plain);
//UNreliable PUT
init(CoapRequestCode.POST, "/test", false);
this.request.setPayload(new CoapData("Content of new resource /test", CoapMediaType.text_plain));
// UNreliable PUT
} else if (testcase.equals("TD_COAP_CORE_07")) {
init(false, CoapRequestCode.PUT);
this.request.setUriPath("/test");
this.request.setPayload("Content of new resource /test");
this.request.setContentType(CoapMediaType.text_plain);
//UNreliable DELETE
init(CoapRequestCode.PUT, "/test", false);
this.request.setPayload(new CoapData("Content of new resource /test", CoapMediaType.text_plain));
// UNreliable DELETE
} else if (testcase.equals("TD_COAP_CORE_08")) {
init(false, CoapRequestCode.DELETE);
this.request.setUriPath("/test");
init(CoapRequestCode.DELETE, "/test", false);
} else if (testcase.equals("TD_COAP_CORE_09")) {
init(true, CoapRequestCode.GET);
this.request.setUriPath("/separate");