| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.mina.coap; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| |
| import org.apache.mina.filter.query.Request; |
| import org.apache.mina.filter.query.Response; |
| |
| /** |
| * A representation of CoAP message following the CoAP RFC. |
| * |
| * @author <a href="http://mina.apache.org">Apache MINA Project</a> |
| */ |
| public class CoapMessage implements Request, Response { |
| |
| public static final CoapMessage get(String url, boolean confimable) { |
| |
| return new CoapMessage(1, confimable ? MessageType.CONFIRMABLE : MessageType.NON_CONFIRMABLE, |
| CoapCode.GET.getCode(), (int) (System.nanoTime() % 65536), null, optionsForUrl(url), null); |
| } |
| |
| public static final CoapMessage post(String url, boolean confimable, byte[] payload) { |
| |
| return new CoapMessage(1, confimable ? MessageType.CONFIRMABLE : MessageType.NON_CONFIRMABLE, |
| CoapCode.GET.getCode(), (int) (System.nanoTime() % 65536), null, optionsForUrl(url), payload); |
| } |
| |
| private static final CoapOption[] optionsForUrl(String url) { |
| String[] parts = url.split("\\?"); |
| |
| String[] paths = parts[0].split("\\/"); |
| |
| String[] params = new String[] {}; |
| |
| if (parts.length > 1) { |
| params = parts[1].split("\\&"); |
| } |
| CoapOption[] opt = new CoapOption[paths.length + params.length]; |
| for (int i = 0; i < paths.length; i++) { |
| try { |
| opt[i] = new CoapOption(CoapOptionType.URI_PATH, paths[i].getBytes("UTF-8")); |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| for (int i = 0; i < params.length; i++) { |
| try { |
| opt[paths.length + i] = new CoapOption(CoapOptionType.URI_QUERY, params[i].getBytes("UTF-8")); |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| return opt; |
| } |
| |
| private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {}; |
| |
| private int version; |
| |
| private MessageType type; |
| |
| private int code; |
| |
| private int id; |
| |
| private byte[] token; |
| |
| private byte[] payload; |
| |
| private CoapOption[] options; |
| |
| /** |
| * Create a CoAP message |
| * |
| * @param version the version (you probably want 1 here) |
| * @param type the type of CoAP message |
| * @param code the message code : {@link CoapCode} |
| * @param id the identifier for this message |
| * @param token the message token (can be <code>null</code>) |
| * @param options list of options for this message (can be <code>null</code>) |
| * @param payload the payload of the message (can be <code>null</code> |
| */ |
| public CoapMessage(int version, MessageType type, int code, int id, byte[] token, CoapOption[] options, |
| byte[] payload) { |
| super(); |
| this.version = version; |
| this.type = type; |
| this.code = code; |
| this.id = id; |
| this.token = token == null ? EMPTY_BYTE_ARRAY : token; |
| this.payload = payload == null ? EMPTY_BYTE_ARRAY : payload; |
| this.options = options == null ? new CoapOption[] {} : options; |
| |
| // sort options by code (easier for delta encoding) |
| Arrays.<CoapOption> sort(this.options, new Comparator<CoapOption>() { |
| @Override |
| public int compare(CoapOption o1, CoapOption o2) { |
| return o1.getType().getCode() < o2.getType().getCode() ? -1 : (o1.getType().getCode() == o2.getType() |
| .getCode() ? 0 : 1); |
| }; |
| }); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Object requestId() { |
| return id; |
| } |
| |
| public int getVersion() { |
| return version; |
| } |
| |
| public int getCode() { |
| return code; |
| } |
| |
| public int getId() { |
| return id; |
| } |
| |
| public byte[] getToken() { |
| return token; |
| } |
| |
| public byte[] getPayload() { |
| return payload; |
| |
| } |
| |
| public CoapOption[] getOptions() { |
| return options; |
| } |
| |
| public MessageType getType() { |
| return type; |
| } |
| |
| /* Utility methods to rebuild some CoAP options */ |
| |
| /** |
| * @return all segments of the absolute path to the resource |
| */ |
| public String[] getUriPath() { |
| return strArrayOptions(CoapOptionType.URI_PATH); |
| } |
| |
| /** |
| * @return all arguments parameterizing the resource |
| */ |
| public String[] getUriQuery() { |
| return strArrayOptions(CoapOptionType.URI_QUERY); |
| } |
| |
| /** |
| * @return the Internet host of the resource being requested |
| */ |
| public String getUriHost() { |
| return strOption(CoapOptionType.URI_HOST); |
| } |
| |
| /** |
| * @return the transport layer port number of the resource |
| */ |
| public Integer getUriPort() { |
| return intOption(CoapOptionType.URI_PORT); |
| } |
| |
| /** |
| * return the absolute URI used to make a request to a forward-proxy |
| */ |
| public String getProxyUri() { |
| return strOption(CoapOptionType.PROXY_URI); |
| } |
| |
| /** |
| * @return the scheme to be used in the proxy URI |
| */ |
| public String getProxyScheme() { |
| return strOption(CoapOptionType.PROXY_SCHEME); |
| } |
| |
| /** |
| * @return the representation format of the message payload |
| */ |
| public Integer getContentFormat() { |
| return intOption(CoapOptionType.CONTENT_FORMAT); |
| } |
| |
| /** |
| * @return which Content-Format is acceptable to the client |
| */ |
| public Integer getAccept() { |
| return intOption(CoapOptionType.ACCEPT); |
| } |
| |
| /** |
| * @return the maximum time a response may be cached before it is considered not fresh |
| */ |
| public Integer getMaxAge() { |
| return intOption(CoapOptionType.MAX_AGE); |
| } |
| |
| /** |
| * @return all segments of the path to the created resource (as the result of a POST request) |
| */ |
| public String[] getLocationPath() { |
| return strArrayOptions(CoapOptionType.LOCATION_PATH); |
| } |
| |
| /** |
| * @return all arguments parameterizing the created resource (as the result of a POST request) |
| */ |
| public String[] getLocationQuery() { |
| return strArrayOptions(CoapOptionType.LOCATION_QUERY); |
| } |
| |
| private String strOption(CoapOptionType type) { |
| if (options != null) { |
| for (CoapOption option : options) { |
| if (type.equals(option.getType())) { |
| try { |
| return new String(option.getData(), "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| private String[] strArrayOptions(CoapOptionType type) { |
| Collection<String> opts = new ArrayList<>(); |
| |
| if (options != null) { |
| for (CoapOption option : options) { |
| if (type.equals(option.getType())) { |
| try { |
| opts.add(new String(option.getData(), "UTF-8")); |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| } |
| } |
| |
| return opts.toArray(new String[0]); |
| } |
| |
| private Integer intOption(CoapOptionType type) { |
| if (options != null) { |
| for (CoapOption option : options) { |
| if (type.equals(option.getType())) { |
| ByteBuffer bb = ByteBuffer.wrap(option.getData()); |
| bb.order(ByteOrder.BIG_ENDIAN); |
| return bb.getInt(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| CoapCode c = CoapCode.fromCode(code); |
| String cstr; |
| if (c == null) { |
| cstr = String.valueOf(code); |
| } else { |
| cstr = c.getText(); |
| } |
| |
| builder.append("CoapMessage [version=").append(version).append(", type=").append(type).append(", code=") |
| .append(cstr).append(", id=").append(id).append(", token=").append(Arrays.toString(token)) |
| .append(", payload=").append(Arrays.toString(payload)).append(", options=") |
| .append(Arrays.toString(options)).append("]"); |
| |
| return builder.toString(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + code; |
| result = prime * result + id; |
| result = prime * result + Arrays.hashCode(options); |
| result = prime * result + Arrays.hashCode(payload); |
| result = prime * result + Arrays.hashCode(token); |
| result = prime * result + ((type == null) ? 0 : type.hashCode()); |
| result = prime * result + version; |
| |
| return result; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| |
| if (obj == null) { |
| return false; |
| } |
| |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| |
| CoapMessage other = (CoapMessage) obj; |
| |
| if (code != other.code) { |
| return false; |
| } |
| |
| if (id != other.id) { |
| return false; |
| } |
| |
| if (!Arrays.equals(options, other.options)) { |
| return false; |
| } |
| |
| if (!Arrays.equals(payload, other.payload)) { |
| return false; |
| } |
| |
| if (!Arrays.equals(token, other.token)) { |
| return false; |
| } |
| |
| if (type != other.type) { |
| return false; |
| } |
| |
| if (version != other.version) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| public void setVersion(int version) { |
| this.version = version; |
| } |
| |
| public void setType(MessageType type) { |
| this.type = type; |
| } |
| |
| public void setCode(int code) { |
| this.code = code; |
| } |
| |
| public void setId(int id) { |
| this.id = id; |
| } |
| |
| public void setToken(byte[] token) { |
| this.token = token; |
| } |
| |
| public void setPayload(byte[] payload) { |
| this.payload = payload; |
| } |
| |
| public void setOptions(CoapOption[] options) { |
| this.options = options; |
| } |
| |
| } |