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;
    21
    22  import java.io.UnsupportedEncodingException;
    23  import java.nio.ByteBuffer;
    24  import java.nio.ByteOrder;
    25  import java.util.ArrayList;
    26  import java.util.Arrays;
    27  import java.util.Collection;
    28  import java.util.Comparator;
    29
    30  import org.apache.mina.filter.query.Request;
    31  import org.apache.mina.filter.query.Response;
    32
    33  /**
    34   * A representation of CoAP message following the CoAP RFC.
    35   *
    36   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    37   */
    38  public class CoapMessage implements Request, Response {
    39
    40      public static final CoapMessage get(String url, boolean confimable) {
    41
    42          return new CoapMessage(1, confimable ? MessageType.CONFIRMABLE : MessageType.NON_CONFIRMABLE,
    43                  CoapCode.GET.getCode(), (int) (System.nanoTime() % 65536), null, optionsForUrl(url), null);
    44      }
    45
    46      public static final CoapMessage post(String url, boolean confimable, byte[] payload) {
    47
    48          return new CoapMessage(1, confimable ? MessageType.CONFIRMABLE : MessageType.NON_CONFIRMABLE,
    49                  CoapCode.GET.getCode(), (int) (System.nanoTime() % 65536), null, optionsForUrl(url), payload);
    50      }
    51
    52      private static final CoapOption[] optionsForUrl(String url) {
    53          String[] parts = url.split("\\?");
    54
    55          String[] paths = parts[0].split("\\/");
    56
    57          String[] params = new String[] {};
    58
    59          if (parts.length > 1) {
    60              params = parts[1].split("\\&");
    61          }
    62          CoapOption[] opt = new CoapOption[paths.length + params.length];
    63          for (int i = 0; i < paths.length; i++) {
    64              try {
    65                  opt[i] = new CoapOption(CoapOptionType.URI_PATH, paths[i].getBytes("UTF-8"));
    66              } catch (UnsupportedEncodingException e) {
    67                  throw new IllegalStateException(e);
    68              }
    69          }
    70
    71          for (int i = 0; i < params.length; i++) {
    72              try {
    73                  opt[paths.length + i] = new CoapOption(CoapOptionType.URI_QUERY, params[i].getBytes("UTF-8"));
    74              } catch (UnsupportedEncodingException e) {
    75                  throw new IllegalStateException(e);
    76              }
    77          }
    78
    79          return opt;
    80      }
    81
    82      private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
    83
    84      private int version;
    85
    86      private MessageType type;
    87
    88      private int code;
    89
    90      private int id;
    91
    92      private byte[] token;
    93
    94      private byte[] payload;
    95
    96      private CoapOption[] options;
    97
    98      /**
    99       * Create a CoAP message
    100      *
    101      * @param version the version (you probably want 1 here)
    102      * @param type the type of CoAP message
    103      * @param code the message code : {@link CoapCode}
    104      * @param id the identifier for this message
    105      * @param token the message token (can be <code>null</code>)
    106      * @param options list of options for this message (can be <code>null</code>)
    107      * @param payload the payload of the message (can be <code>null</code>
    108      */
    109     public CoapMessage(int version, MessageType type, int code, int id, byte[] token, CoapOption[] options,
    110             byte[] payload) {
    111         super();
    112         this.version = version;
    113         this.type = type;
    114         this.code = code;
    115         this.id = id;
    116         this.token = token == null ? EMPTY_BYTE_ARRAY : token;
    117         this.payload = payload == null ? EMPTY_BYTE_ARRAY : payload;
    118         this.options = options == null ? new CoapOption[] {} : options;
    119
    120         // sort options by code (easier for delta encoding)
    121         Arrays.<CoapOption> sort(this.options, new Comparator<CoapOption>() {
    122             @Override
    123             public int compare(CoapOption o1, CoapOption o2) {
    124                 return o1.getType().getCode() < o2.getType().getCode() ? -1 : (o1.getType().getCode() == o2.getType()
    125                         .getCode() ? 0 : 1);
    126             };
    127         });
    128     }
    129
    130     /**
    131      * {@inheritDoc}
    132      */
    133     @Override
    134     public Object requestId() {
    135         return id;
    136     }
    137
    138     public int getVersion() {
    139         return version;
    140     }
    141
    142     public int getCode() {
    143         return code;
    144     }
    145
    146     public int getId() {
    147         return id;
    148     }
    149
    150     public byte[] getToken() {
    151         return token;
    152     }
    153
    154     public byte[] getPayload() {
    155         return payload;
    156
    157     }
    158
    159     public CoapOption[] getOptions() {
    160         return options;
    161     }
    162
    163     public MessageType getType() {
    164         return type;
    165     }
    166
    167     /* Utility methods to rebuild some CoAP options */
    168
    169     /**
    170      * @return all segments of the absolute path to the resource
    171      */
    172     public String[] getUriPath() {
    173         return strArrayOptions(CoapOptionType.URI_PATH);
    174     }
    175
    176     /**
    177      * @return all arguments parameterizing the resource
    178      */
    179     public String[] getUriQuery() {
    180         return strArrayOptions(CoapOptionType.URI_QUERY);
    181     }
    182
    183     /**
    184      * @return the Internet host of the resource being requested
    185      */
    186     public String getUriHost() {
    187         return strOption(CoapOptionType.URI_HOST);
    188     }
    189
    190     /**
    191      * @return the transport layer port number of the resource
    192      */
    193     public Integer getUriPort() {
    194         return intOption(CoapOptionType.URI_PORT);
    195     }
    196
    197     /**
    198      * return the absolute URI used to make a request to a forward-proxy
    199      */
    200     public String getProxyUri() {
    201         return strOption(CoapOptionType.PROXY_URI);
    202     }
    203
    204     /**
    205      * @return the scheme to be used in the proxy URI
    206      */
    207     public String getProxyScheme() {
    208         return strOption(CoapOptionType.PROXY_SCHEME);
    209     }
    210
    211     /**
    212      * @return the representation format of the message payload
    213      */
    214     public Integer getContentFormat() {
    215         return intOption(CoapOptionType.CONTENT_FORMAT);
    216     }
    217
    218     /**
    219      * @return which Content-Format is acceptable to the client
    220      */
    221     public Integer getAccept() {
    222         return intOption(CoapOptionType.ACCEPT);
    223     }
    224
    225     /**
    226      * @return the maximum time a response may be cached before it is considered not fresh
    227      */
    228     public Integer getMaxAge() {
    229         return intOption(CoapOptionType.MAX_AGE);
    230     }
    231
    232     /**
    233      * @return all segments of the path to the created resource (as the result of a POST request)
    234      */
    235     public String[] getLocationPath() {
    236         return strArrayOptions(CoapOptionType.LOCATION_PATH);
    237     }
    238
    239     /**
    240      * @return all arguments parameterizing the created resource (as the result of a POST request)
    241      */
    242     public String[] getLocationQuery() {
    243         return strArrayOptions(CoapOptionType.LOCATION_QUERY);
    244     }
    245
    246     private String strOption(CoapOptionType type) {
    247         if (options != null) {
    248             for (CoapOption option : options) {
    249                 if (type.equals(option.getType())) {
    250                     try {
    251                         return new String(option.getData(), "UTF-8");
    252                     } catch (UnsupportedEncodingException e) {
    253                         throw new IllegalStateException(e);
    254                     }
    255                 }
    256             }
    257         }
    258         return null;
    259     }
    260
    261     private String[] strArrayOptions(CoapOptionType type) {
    262         Collection<String> opts = new ArrayList<>();
    263
    264         if (options != null) {
    265             for (CoapOption option : options) {
    266                 if (type.equals(option.getType())) {
    267                     try {
    268                         opts.add(new String(option.getData(), "UTF-8"));
    269                     } catch (UnsupportedEncodingException e) {
    270                         throw new IllegalStateException(e);
    271                     }
    272                 }
    273             }
    274         }
    275
    276         return opts.toArray(new String[0]);
    277     }
    278
    279     private Integer intOption(CoapOptionType type) {
    280         if (options != null) {
    281             for (CoapOption option : options) {
    282                 if (type.equals(option.getType())) {
    283                     ByteBuffer bb = ByteBuffer.wrap(option.getData());
    284                     bb.order(ByteOrder.BIG_ENDIAN);
    285                     return bb.getInt();
    286                 }
    287             }
    288         }
    289         return null;
    290     }
    291
    292     /**
    293      * {@inheritDoc}
    294      */
    295     @Override
    296     public String toString() {
    297         StringBuilder builder = new StringBuilder();
    298         CoapCode c = CoapCode.fromCode(code);
    299         String cstr;
    300         if (c == null) {
    301             cstr = String.valueOf(code);
    302         } else {
    303             cstr = c.getText();
    304         }
    305
    306         builder.append("CoapMessage [version=").append(version).append(", type=").append(type).append(", code=")
    307                 .append(cstr).append(", id=").append(id).append(", token=").append(Arrays.toString(token))
    308                 .append(", payload=").append(Arrays.toString(payload)).append(", options=")
    309                 .append(Arrays.toString(options)).append("]");
    310
    311         return builder.toString();
    312     }
    313
    314     /**
    315      * {@inheritDoc}
    316      */
    317     @Override
    318     public int hashCode() {
    319         final int prime = 31;
    320         int result = 1;
    321         result = prime * result + code;
    322         result = prime * result + id;
    323         result = prime * result + Arrays.hashCode(options);
    324         result = prime * result + Arrays.hashCode(payload);
    325         result = prime * result + Arrays.hashCode(token);
    326         result = prime * result + ((type == null) ? 0 : type.hashCode());
    327         result = prime * result + version;
    328
    329         return result;
    330     }
    331
    332     /**
    333      * {@inheritDoc}
    334      */
    335     @Override
    336     public boolean equals(Object obj) {
    337         if (this == obj) {
    338             return true;
    339         }
    340
    341         if (obj == null) {
    342             return false;
    343         }
    344
    345         if (getClass() != obj.getClass()) {
    346             return false;
    347         }
    348
    349         CoapMessage other = (CoapMessage) obj;
    350
    351         if (code != other.code) {
    352             return false;
    353         }
    354
    355         if (id != other.id) {
    356             return false;
    357         }
    358
    359         if (!Arrays.equals(options, other.options)) {
    360             return false;
    361         }
    362
    363         if (!Arrays.equals(payload, other.payload)) {
    364             return false;
    365         }
    366
    367         if (!Arrays.equals(token, other.token)) {
    368             return false;
    369         }
    370
    371         if (type != other.type) {
    372             return false;
    373         }
    374
    375         if (version != other.version) {
    376             return false;
    377         }
    378
    379         return true;
    380     }
    381
    382     public void setVersion(int version) {
    383         this.version = version;
    384     }
    385
    386     public void setType(MessageType type) {
    387         this.type = type;
    388     }
    389
    390     public void setCode(int code) {
    391         this.code = code;
    392     }
    393
    394     public void setId(int id) {
    395         this.id = id;
    396     }
    397
    398     public void setToken(byte[] token) {
    399         this.token = token;
    400     }
    401
    402     public void setPayload(byte[] payload) {
    403         this.payload = payload;
    404     }
    405
    406     public void setOptions(CoapOption[] options) {
    407         this.options = options;
    408     }
    409
    410 }