@Override
  public void handleMessage(CoapMessage message) {
    /* message MUST be a request */
    if (message.isEmpty()) {
      return;
    }

    if (!message.isRequest()) {
      return;
      // throw new IllegalStateException("Incomming server message is not a request");
    }

    BasicCoapRequest request = (BasicCoapRequest) message;
    CoapChannel channel = request.getChannel();
    /* TODO make this cast safe */
    server.onRequest((CoapServerChannel) channel, request);
  }
    public boolean addBlock(CoapMessage msg, CoapBlockOption block) {
      int blockPos = block.getBytePosition();
      int blockLength = msg.getPayloadLength();
      int bufSize = payload.size();

      /*TODO: check if payload length = blocksize (except for the last block)*/
      if (blockPos > bufSize) {
        /* data is missing before this block */
        return false;
      } else if ((blockPos + blockLength) <= bufSize) {
        /* data already received */
        return false;
      }
      int offset = bufSize - blockPos;
      payload.write(msg.getPayload(), offset, blockLength - offset);

      if (block.isLast()) {
        /* was this the last block */
        finished = true;
      }

      return true;
    }
  @Override
  public BasicCoapResponse createResponse(
      CoapMessage request, CoapResponseCode responseCode, CoapMediaType contentType) {
    BasicCoapResponse response;
    if (request.getPacketType() == CoapPacketType.CON) {
      response =
          new BasicCoapResponse(
              CoapPacketType.ACK, responseCode, request.getMessageID(), request.getToken());
    } else if (request.getPacketType() == CoapPacketType.NON) {
      response =
          new BasicCoapResponse(
              CoapPacketType.NON, responseCode, request.getMessageID(), request.getToken());
    } else {
      throw new IllegalStateException(
          "Create Response failed, Request is neither a CON nor a NON packet");
    }
    if (contentType != null && contentType != CoapMediaType.UNKNOWN) {
      response.setContentType(contentType);
    }

    response.setChannel(this);
    return response;
  }
  public void handleMessage(CoapMessage message) {
    if (message.isRequest()) {
      /* this is a client channel, no requests allowed */
      message
          .getChannel()
          .sendMessage(new CoapEmptyMessage(CoapPacketType.RST, message.getMessageID()));
      return;
    }

    if (message.isEmpty() && message.getPacketType() == CoapPacketType.ACK) {
      /* this is the ACK of a separate response */
      // TODO: implement a handler or listener, that informs a client when a sep. resp. ack was
      // received
      return;
    }

    if (message.getPacketType() == CoapPacketType.CON) {
      /* this is a separate response */
      /* send ACK */
      this.sendMessage(new CoapEmptyMessage(CoapPacketType.ACK, message.getMessageID()));
    }

    /* check for blockwise transfer */
    CoapBlockOption block2 = message.getBlock2();
    if (blockContext == null && block2 != null) {
      /* initiate blockwise transfer */
      blockContext = new ClientBlockContext(block2, maxReceiveBlocksize);
      blockContext.setFirstRequest(lastRequest);
      blockContext.setFirstResponse((CoapResponse) message);
    }

    if (blockContext != null) {
      /*blocking option*/
      if (!blockContext.addBlock(message, block2)) {
        /*this was not a correct block*/
        /* TODO: implement either a RST or ignore this packet */
      }

      if (!blockContext.isFinished()) {
        /* TODO: implement a counter to avoid an infinity req/resp loop:
         *  		if the same block is received more than x times -> rst the connection
         *  implement maxPayloadSize to avoid an infinity payload */
        CoapBlockOption newBlock = blockContext.getNextBlock();
        if (lastRequest == null) {
          /*TODO: this should never happen*/
          System.out.println("ERROR: client channel: lastRequest == null");
        } else {
          /* create a new request for the next block */
          BasicCoapRequest request =
              new BasicCoapRequest(
                  lastRequest.getPacketType(),
                  lastRequest.getRequestCode(),
                  channelManager.getNewMessageID());
          request.copyHeaderOptions((BasicCoapRequest) blockContext.getFirstRequest());
          request.setBlock2(newBlock);
          sendMessage(request);
        }
        /* TODO: implement handler, inform the client that a block (but not the complete message) was received*/
        return;
      }
      /* blockwise transfer finished */
      message.setPayload(blockContext.getPayload());
      /* TODO: give the payload separately and leave the original message as they is*/
    }

    /* normal or separate response */
    client.onResponse(this, (BasicCoapResponse) message);
  }
  @Override
  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);
  }