/*******************************************************************************
* Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Californium (Cf) CoAP framework.
******************************************************************************/
package ch.ethz.inf.vs.californium.server;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import ch.ethz.inf.vs.californium.coap.CoAP.Code;
import ch.ethz.inf.vs.californium.coap.CoAP.ResponseCode;
import ch.ethz.inf.vs.californium.coap.Request;
import ch.ethz.inf.vs.californium.coap.Response;
import ch.ethz.inf.vs.californium.network.Exchange;
import ch.ethz.inf.vs.californium.observe.ObserveManager;
import ch.ethz.inf.vs.californium.observe.ObserveRelation;
import ch.ethz.inf.vs.californium.observe.ObservingEndpoint;
import ch.ethz.inf.vs.californium.server.resources.Resource;
/**
* The ServerMessageDeliverer delivers requests to corresponding resources and
* responses to corresponding requests.
*/
public class ServerMessageDeliverer implements MessageDeliverer {
private final static Logger LOGGER = Logger.getLogger(ServerMessageDeliverer.class.getCanonicalName());
/* The root of all resources */
private final Resource root;
/* The manager of the observe mechanism for this server */
private ObserveManager observeManager = new ObserveManager();
/**
* Constructs a default message deliverer that delivers requests to the
* resources rooted at the specified root.
*/
public ServerMessageDeliverer(Resource root) {
this.root = root;
}
/* (non-Javadoc)
* @see ch.inf.vs.californium.MessageDeliverer#deliverRequest(ch.inf.vs.californium.network.Exchange)
*/
@Override
public void deliverRequest(final Exchange exchange) {
Request request = exchange.getRequest();
List<String> path = request.getOptions().getURIPaths();
final Resource resource = findResource(path);
if (resource != null) {
checkForObserveOption(exchange, resource);
// Get the executor and let it process the request
Executor executor = resource.getExecutor();
if (executor != null) {
executor.execute(new Runnable() {
public void run() {
resource.handleRequest(exchange);
} });
} else {
resource.handleRequest(exchange);
}
} else {
LOGGER.info("Did not find resource " + path.toString());
exchange.sendResponse(new Response(ResponseCode.NOT_FOUND));
}
}
/**
* Checks whether an observe relationship has to be established or canceled.
* This is done here to have a server-global observeManager that holds the
* set of remote endpoints for all resources. This global knowledge is required
* for efficient orphan handling.
*
* @param exchange
* the exchange of the current request
* @param resource
* the target resource
* @param path
* the path to the resource
*/
private void checkForObserveOption(Exchange exchange, Resource resource) {
Request request = exchange.getRequest();
if (request.getCode() != Code.GET) return;
InetSocketAddress source = new InetSocketAddress(request.getSource(), request.getSourcePort());
if (request.getOptions().hasObserve() && resource.isObservable()) {
if (request.getOptions().getObserve()==0) {
// Requests wants to observe and resource allows it :-)
LOGGER.info("Initiate an observe relation between " + request.getSource() + ":" + request.getSourcePort() + " and resource " + resource.getURI());
ObservingEndpoint remote = observeManager.findObservingEndpoint(source);
ObserveRelation relation = new ObserveRelation(remote, resource, exchange);
remote.addObserveRelation(relation);
exchange.setRelation(relation);
// all that's left is to add the relation to the resource which
// the resource must do itself if the response is successful
} else if (request.getOptions().getObserve()==1) {
ObserveRelation relation = observeManager.getRelation(source, request.getToken());
if (relation!=null) relation.cancel();
}
}
}
/**
* Searches in the resource tree for the specified path. A parent resource
* may accept requests to subresources, e.g., to allow addresses with
* wildcards like <code>coap://example.com:5683/devices/*</code>
*
* @param list the path as list of resource names
* @return the resource or null if not found
*/
private Resource findResource(List<String> list) {
LinkedList<String> path = new LinkedList<String>(list);
Resource current = root;
while (!path.isEmpty() && current != null) {
String name = path.removeFirst();
current = current.getChild(name);
}
return current;
}
/* (non-Javadoc)
* @see ch.inf.vs.californium.MessageDeliverer#deliverResponse(ch.inf.vs.californium.network.Exchange, ch.inf.vs.californium.coap.Response)
*/
@Override
public void deliverResponse(Exchange exchange, Response response) {
if (response == null) throw new NullPointerException();
if (exchange == null) throw new NullPointerException();
if (exchange.getRequest() == null) throw new NullPointerException();
exchange.getRequest().setResponse(response);
}
}