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.util.ArrayList;
    24  import java.util.List;
    25
    26  import org.apache.mina.coap.CoapMessage;
    27  import org.apache.mina.coap.CoapOption;
    28  import org.apache.mina.coap.CoapOptionType;
    29  import org.apache.mina.coap.MessageType;
    30  import org.apache.mina.codec.ProtocolDecoderException;
    31  import org.apache.mina.codec.StatelessProtocolDecoder;
    32  import org.slf4j.Logger;
    33  import org.slf4j.LoggerFactory;
    34
    35  /**
    36   * Decoder CoAP messages from the ByteBuffer of a received UDP Datagram.
    37   *
    38   * Decode {@link ByteBuffer} into {@link CoapMessage}.
    39   *
    40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    41   */
    42  public class CoapDecoder implements StatelessProtocolDecoder<ByteBuffer, CoapMessage> {
    43
    44      private static final Logger LOG = LoggerFactory.getLogger(CoapDecoder.class);
    45
    46      private static final CoapOption[] EMPTY_OPTION = new CoapOption[0];
    47
    48      private static final byte[] EMPTY_PAYLOAD = new byte[0];
    49
    50      /**
    51       * {@inheritDoc}
    52       */
    53      @Override
    54      public Void createDecoderState() {
    55          return null;
    56      }
    57
    58      /**
    59       * {@inheritDoc}
    60       */
    61      @Override
    62      public CoapMessage decode(ByteBuffer input, Void context) {
    63          LOG.debug("decode");
    64
    65          if (input.remaining() <= 0) {
    66              LOG.debug("nothing to decode");
    67              return null;
    68          }
    69          int byte0 = input.get() & 0xFF;
    70          int version = (byte0 >> 6) & 0x3;
    71          MessageType type = MessageType.fromCode((byte0 >> 4) & 0x3);
    72          byte[] token = new byte[byte0 & 0xF];
    73          int code = input.get() & 0xFF;
    74          int id = input.getShort() & 0xFFFF;
    75          input.get(token);
    76
    77          // decode options
    78          int optionCode = 0;
    79          byte[] payload = EMPTY_PAYLOAD;
    80          List<CoapOption> options = new ArrayList<CoapOption>();
    81          while (input.hasRemaining()) {
    82              int next = input.get() & 0xFF;
    83
    84              // start of payload ?
    85              if (next == 0xFF) {
    86                  payload = new byte[input.remaining()];
    87                  input.get(payload);
    88                  break;
    89              } else {
    90                  int optionDeltaQuartet = (next >> 4) & 0xF;
    91
    92                  optionCode += optionFromQuartet(optionDeltaQuartet, input);
    93
    94                  // decode the option length
    95                  int optionLenQuartet = next & 0x0F;
    96                  int optionLength = optionFromQuartet(optionLenQuartet, input);
    97
    98                  // create the option DTO
    99                  CoapOptionType optType = CoapOptionType.fromCode(optionCode);
    100                 if (optType == null) {
    101                     throw new ProtocolDecoderException("unknown option code : " + optionCode);
    102                 }
    103
    104                 // get the value
    105                 byte[] optionValue = new byte[optionLength];
    106                 input.get(optionValue);
    107
    108                 options.add(new CoapOption(optType, optionValue));
    109             }
    110         }
    111
    112         if (input.hasRemaining()) {
    113             throw new ProtocolDecoderException("trailling " + input.remaining() + " bytes in the UDP datagram");
    114         }
    115         return new CoapMessage(version, type, code, id, token, options.toArray(EMPTY_OPTION), payload);
    116     }
    117
    118     /**
    119      * {@inheritDoc}
    120      */
    121     @Override
    122     public void finishDecode(Void context) {
    123     }
    124
    125     private int optionFromQuartet(int value, ByteBuffer input) {
    126         if (value < 13) {
    127             return value;
    128         } else if (value == 13) {
    129             return (input.get() & 0xFF) + 13;
    130         } else if (value == 14) {
    131             return (input.getShort() & 0xFFFF) + 269;
    132         } else {
    133             throw new ProtocolDecoderException("illegal option quartet value : " + value);
    134         }
    135     }
    136 }