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 }