View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.coap.codec;
    21
    22  import java.nio.ByteBuffer;
    23  import java.nio.ByteOrder;
    24
    25  import org.apache.mina.coap.CoapMessage;
    26  import org.apache.mina.coap.CoapOption;
    27  import org.apache.mina.codec.StatelessProtocolEncoder;
    28  import org.slf4j.Logger;
    29  import org.slf4j.LoggerFactory;
    30
    31  /**
    32   * Encode a CoAP message following the RFC.
    33   *
    34   * Encode {@link CoapMessage} into {@link ByteBuffer}
    35   *
    36   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    37   */
    38  public class CoapEncoder implements StatelessProtocolEncoder<CoapMessage, ByteBuffer> {
    39
    40      private static final Logger LOG = LoggerFactory.getLogger(CoapEncoder.class);
    41
    42      private static final int HEADER_SIZE = 4;
    43
    44      /**
    45       * {@inheritDoc}
    46       */
    47      @Override
    48      public Void createEncoderState() {
    49          return null;
    50      }
    51
    52      /**
    53       * {@inheritDoc}
    54       */
    55      @Override
    56      public ByteBuffer encode(CoapMessage message, Void context) {
    57
    58          LOG.debug("encoding {}", message);
    59
    60          // compute size of the needed buffer
    61          int size = HEADER_SIZE + message.getToken().length;
    62
    63          int delta = 0;
    64          for (CoapOption opt : message.getOptions()) {
    65              // compute delta encoding size
    66              int code = opt.getType().getCode();
    67              delta = code - delta;
    68              if (delta < 13) {
    69                  size += 1;
    70              } else if (delta < 256 + 13) {
    71                  size += 2;
    72              } else if (delta < 65536 + 269) {
    73                  size += 3;
    74              }
    75
    76              // compute option length encoding size
    77              int optLength = opt.getData().length;
    78              if (optLength < 13) {
    79                  size += 0;
    80              } else if (optLength < 256 + 13) {
    81                  size += 1;
    82              } else if (optLength < 65536 + 269) {
    83                  size += 2;
    84              }
    85
    86              // compute option data encoding size
    87              size += optLength;
    88          }
    89
    90          // if we have a payload, we place the marker and the payload
    91          if (message.getPayload() != null && message.getPayload().length > 0) {
    92              // payload marker
    93              size += 1;
    94              size += message.getPayload().length;
    95          }
    96
    97          ByteBuffer buffer = ByteBuffer.allocate(size);
    98
    99          buffer.order(ByteOrder.BIG_ENDIAN);
    100
    101         // encode header
    102         buffer.put((byte) (((message.getVersion() & 0x03) << 6) | (message.getType().getCode() & 0x03) << 4 | (message
    103                 .getToken().length & 0x0F)));
    104         buffer.put((byte) message.getCode());
    105         buffer.putShort((short) message.getId());
    106         buffer.put(message.getToken());
    107
    108         // encode options
    109         int lastOptCode = 0;
    110
    111         for (CoapOption opt : message.getOptions()) {
    112             int optionDelta = opt.getType().getCode() - lastOptCode;
    113             int deltaQuartet = getQuartet(optionDelta);
    114             int optionLength = opt.getData().length;
    115
    116             int optionQuartet = getQuartet(optionLength);
    117
    118             buffer.put((byte) ((deltaQuartet << 4) | optionQuartet));
    119
    120             // write extended option delta field (0 - 2 bytes)
    121             if (deltaQuartet == 13) {
    122                 buffer.put((byte) (optionDelta - 13));
    123             } else if (deltaQuartet == 14) {
    124                 buffer.putShort((short) (optionDelta - 269));
    125             }
    126
    127             if (optionQuartet == 13) {
    128                 buffer.put((byte) (optionLength - 13));
    129             } else if (optionQuartet == 14) {
    130                 buffer.putShort((short) (optionLength - 269));
    131             }
    132             buffer.put(opt.getData());
    133
    134             lastOptCode = opt.getType().getCode();
    135         }
    136
    137         if (message.getPayload() != null && message.getPayload().length > 0) {
    138             buffer.put((byte) 0xFF);
    139             buffer.put(message.getPayload());
    140         }
    141
    142         buffer.flip();
    143         return buffer;
    144     }
    145
    146     private int getQuartet(int value) {
    147         if (value <= 12) {
    148             return value;
    149         } else if (value <= 255 + 13) {
    150             return 13;
    151         } else {
    152             return 14;
    153         }
    154     }
    155 }