Ensure padding data cannot be written when empty.
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / stack / COPSHandle.java
1 /*
2  * Copyright (c) 2003 University of Murcia.  All rights reserved.
3  * --------------------------------------------------------------
4  * For more information, please see <http://www.umu.euro6ix.org/>.
5  */
6
7 package org.umu.cops.stack;
8
9 import org.umu.cops.stack.COPSObjHeader.CNum;
10 import org.umu.cops.stack.COPSObjHeader.CType;
11
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.net.Socket;
15
16 /**
17  * COPS Handle Object (RFC 2748 pag. 9)
18  *
19  *   The Handle Object encapsulates a unique value that identifies an
20  *   installed state. This identification is used by most COPS operations.
21  *
22  *           C-Num = 1
23  *
24  *           C-Type = 1, Client Handle.
25  *
26  *   Variable-length field, no implied format other than it is unique from
27  *   other client handles from the same PEP (a.k.a. COPS TCP connection)
28  *   for a particular client-type. It is always initially chosen by the
29  *   PEP and then deleted by the PEP when no longer applicable. The client
30  *   handle is used to refer to a request state initiated by a particular
31  *   PEP and installed at the PDP for a client-type. A PEP will specify a
32  *   client handle in its Request messages, Report messages and Delete
33  *   messages sent to the PDP. In all cases, <b>the client handle is used to
34  *   uniquely identify a particular PEP's request for a client-type</b>.
35  *
36  *   The client handle value is set by the PEP and is opaque to the PDP.
37  *   The PDP simply performs a byte-wise comparison on the value in this
38  *   object with respect to the handle object values of other currently
39  *   installed requests.
40  *
41  */
42 public class COPSHandle extends COPSObjBase {
43
44     /**
45      * The payload data
46      */
47     private final COPSData _data;
48
49     /**
50      * Bytes to add to outbound payload to ensure the length is divisible by 4 bytes
51      */
52     private final COPSData _padding;
53
54     /**
55      * Constructor generally used for sending messages
56      * @param id - the identifier (must not be null)
57      * @throws java.lang.IllegalArgumentException when the id parameter is null
58      */
59     public COPSHandle(final COPSData id) {
60         this(new COPSObjHeader(CNum.HANDLE, CType.DEF), id);
61     }
62
63     /**
64      * Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the
65      * COPSObjHeader information is known
66      * @param objHdr - the object header
67      * @param data - the ID
68      * @throws java.lang.IllegalArgumentException
69      */
70     protected COPSHandle(final COPSObjHeader objHdr, final COPSData data) {
71         super(objHdr);
72         if (!objHdr.getCNum().equals(CNum.HANDLE))
73             throw new IllegalArgumentException("CNum on header must be of type HANDLE");
74         if (!objHdr.getCType().equals(CType.DEF))
75             throw new IllegalArgumentException("Invalid CType value. Must be " + CType.DEF);
76         if (data == null) throw new IllegalArgumentException("COPSData must not be null");
77
78         _data = data;
79
80         if ((_data.length() % 4) != 0) {
81             final int padLen = 4 - (_data.length() % 4);
82             _padding = COPSObjectParser.getPadding(padLen);
83         } else {
84             _padding = new COPSData();
85         }
86     }
87
88     @Override
89     public int getDataLength() {
90         return _data.length() + _padding.length();
91     }
92
93     /**
94      * Get handle value
95      * @return   a COPSData
96      */
97     public COPSData getId() {
98         return _data;
99     }
100
101     @Override
102     protected void writeBody(final Socket socket) throws IOException {
103         COPSUtil.writeData(socket, _data.getData(), _data.length());
104         if (_padding.length() != 0) COPSUtil.writeData(socket, _padding.getData(), _padding.length());
105     }
106
107     @Override
108     public void dumpBody(final OutputStream os) throws IOException {
109         os.write(("client-handle: " + _data.str() + "\n").getBytes());
110     }
111
112     @Override
113     public boolean equals(final Object o) {
114         if (this == o) {
115             return true;
116         }
117         if (!(o instanceof COPSHandle)) {
118             return false;
119         }
120         if (!super.equals(o)) {
121             return false;
122         }
123
124         final COPSHandle that = (COPSHandle) o;
125
126         return _data.equals(that._data) && _padding.equals(that._padding) ||
127                 COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding);
128     }
129
130     @Override
131     public int hashCode() {
132         int result = super.hashCode();
133         result = 31 * result + _data.hashCode();
134         result = 31 * result + _padding.hashCode();
135         return result;
136     }
137
138     /**
139      * Parses bytes to return a COPSHandle object
140      * @param objHdrData - the associated header
141      * @param dataPtr - the data to parse
142      * @return - the object
143      * @throws java.lang.IllegalArgumentException
144      */
145     public static COPSHandle parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) {
146         if (dataPtr == null || dataPtr.length < 5)
147             throw new IllegalArgumentException("Data cannot be null or fewer than 5 bytes");
148
149         //Get the length of data following the obj header
150         final COPSData id = new COPSData(dataPtr, 4, objHdrData.msgByteCount - objHdrData.header.getHdrLength());
151         return new COPSHandle(objHdrData.header, id);
152     }
153
154 }
155