package org.ws4d.coap.rest; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.HashMap; import java.util.Vector; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.SimpleLayout; import org.ws4d.coap.Constants; import org.ws4d.coap.connection.BasicCoapChannelManager; import org.ws4d.coap.connection.BasicCoapSocketHandler; import org.ws4d.coap.interfaces.CoapChannelManager; import org.ws4d.coap.interfaces.CoapMessage; import org.ws4d.coap.interfaces.CoapRequest; import org.ws4d.coap.interfaces.CoapServer; import org.ws4d.coap.interfaces.CoapServerChannel; import org.ws4d.coap.messages.CoapRequestCode; import org.ws4d.coap.messages.CoapResponseCode; /** * @author Christian Lerche <christian.lerche@uni-rostock.de> */ public class CoapResourceServer implements CoapServer, ResourceServer { private int port = 0; private final static Logger logger = Logger.getLogger(CoapResourceServer.class); protected HashMap<String, Resource> resources = new HashMap<String, Resource>(); private CoreResource coreResource = new CoreResource(this); public CoapResourceServer(){ logger.addAppender(new ConsoleAppender(new SimpleLayout())); logger.setLevel(Level.WARN); } public HashMap<String, Resource> getResources(){ return resources; } private void addResource(Resource resource){ resource.registerServerListener(this); resources.put(resource.getPath(), resource); coreResource.registerResource(resource); } public boolean createResource(Resource resource) { if (resource==null) return false; if (!resources.containsKey(resource.getPath())) { addResource(resource); logger.info("created ressource: " + resource.getPath()); return true; } else return false; } public boolean updateResource(Resource resource) { if (resource==null) return false; if (resources.containsKey(resource.getPath())) { addResource(resource); logger.info("updated ressource: " + resource.getPath()); return true; } else return false; } public boolean deleteResource(String path) { if (null != resources.remove(path)) { logger.info("deleted ressource: " + path); return true; } else return false; } public final Resource readResource(String path) { logger.info("read ressource: " + path); return resources.get(path); } /*corresponding to the coap spec the put is an update or create (or error)*/ public CoapResponseCode CoapResponseCode(Resource resource) { Resource res = readResource(resource.getPath()); //TODO: check results if (res == null){ createResource(resource); return CoapResponseCode.Created_201; } else { updateResource(resource); return CoapResponseCode.Changed_204; } } public void start() throws Exception { start(Constants.COAP_DEFAULT_PORT); } public void start(int port) throws Exception { resources.put(coreResource.getPath(), coreResource); CoapChannelManager channelManager = BasicCoapChannelManager .getInstance(); this.port = port; channelManager.createServerListener(this, port); } public void stop() { } public int getPort() { return port; } public URI getHostUri() { URI hostUri = null; try { hostUri = new URI("coap://" + this.getLocalIpAddress() + ":" + getPort()); } catch (URISyntaxException e) { e.printStackTrace(); } return hostUri; } public void resourceChanged(Resource resource) { logger.info("Resource changed: " + resource.getPath()); } public CoapServer onAccept(CoapRequest request) { return this; } public void onRequest(CoapServerChannel channel, CoapRequest request) { CoapMessage response = null; CoapRequestCode requestCode = request.getRequestCode(); String targetPath = request.getUriPath(); //TODO make this cast safe (send internal server error if it is not a CoapResource) CoapResource resource = (CoapResource) readResource(targetPath); /* TODO: check return values of create, read, update and delete * TODO: implement forbidden * TODO: implement ETag * TODO: implement If-Match... * TODO: check for well known addresses (do not override well known core) * TODO: check that path begins with "/" */ switch (requestCode) { case GET: if (resource != null) { // URI queries Vector<String> uriQueries = request.getUriQuery(); final byte[] responseValue; if (uriQueries != null) { responseValue = resource.getValue(uriQueries); } else { responseValue = resource.getValue(); } response = channel.createResponse(request, CoapResponseCode.Content_205, resource.getCoapMediaType()); response.setPayload(responseValue); if (request.getObserveOption() != null){ /*client wants to observe this resource*/ if (resource.addObserver(request)){ /* successfully added observer */ response.setObserveOption(resource.getObserveSequenceNumber()); } } } else { response = channel.createResponse(request, CoapResponseCode.Not_Found_404); } break; case DELETE: /* CoAP: "A 2.02 (Deleted) response SHOULD be sent on success or in case the resource did not exist before the request.*/ deleteResource(targetPath); response = channel.createResponse(request, CoapResponseCode.Deleted_202); break; case POST: if (resource != null){ resource.post(request.getPayload()); response = channel.createResponse(request, CoapResponseCode.Changed_204); } else { /* if the resource does not exist, a new resource will be created */ createResource(parseRequest(request)); response = channel.createResponse(request, CoapResponseCode.Created_201); } break; case PUT: if (resource == null){ /* create*/ createResource(parseRequest(request)); response = channel.createResponse(request,CoapResponseCode.Created_201); } else { /*update*/ updateResource(parseRequest(request)); response = channel.createResponse(request, CoapResponseCode.Changed_204); } break; default: response = channel.createResponse(request, CoapResponseCode.Bad_Request_400); break; } channel.sendMessage(response); } private CoapResource parseRequest(CoapRequest request) { CoapResource resource = new BasicCoapResource(request.getUriPath(), request.getPayload(), request.getContentType()); // TODO add content type return resource; } public void onSeparateResponseFailed(CoapServerChannel channel) { logger.error("Separate response failed but server never used separate responses"); } protected String getLocalIpAddress() { try { for (Enumeration<NetworkInterface> en = NetworkInterface .getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf .getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress()) { return inetAddress.getHostAddress().toString(); } } } } catch (SocketException ex) { } return null; } }