The second patch of an estimated 4 to complete the COPS message refactoring as descri...
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / stack / COPSClientSI.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
11 import java.io.IOException;
12 import java.io.OutputStream;
13 import java.net.Socket;
14 import java.util.Map;
15 import java.util.concurrent.ConcurrentHashMap;
16
17 /**
18  * COPS Client Specific Information Object (RFC 2748)
19  *
20  * The various types of this object are required for requests, and used
21  * in reports and opens when required. It contains client-type specific
22  * information.
23  *
24  * C-Num = 9,
25  *
26  * C-Type = 1, Signaled ClientSI.
27  *
28  * Variable-length field. All objects/attributes specific to a client's
29  * signaling protocol or internal state are encapsulated within one or
30  * more signaled Client Specific Information Objects. The format of the
31  * data encapsulated in the ClientSI object is determined by the
32  * client-type.
33  *
34  * C-Type = 2, Named ClientSI.
35  *
36  * Variable-length field. Contains named configuration information
37  * useful for relaying specific information about the PEP, a request, or
38  * configured state to the PDP server.
39  */
40 public class COPSClientSI extends COPSObjBase {
41
42     private final static Map<Integer, CSIType> VAL_TO_CSI = new ConcurrentHashMap<>();
43     static {
44         VAL_TO_CSI.put(CSIType.NA.ordinal(), CSIType.NA);
45         VAL_TO_CSI.put(CSIType.SIGNALED.ordinal(), CSIType.SIGNALED);
46         VAL_TO_CSI.put(CSIType.NAMED.ordinal(), CSIType.NAMED);
47     }
48
49     /**
50      * This value is not being used here but stored only for clarity as it is being mapped directly to the
51      * ordinal value of the CType
52      */
53     private final CSIType _csiType;
54
55     /**
56      * The payload data
57      */
58     private final COPSData _data;
59
60     /**
61      * Bytes to add to outbound payload to ensure the length is divisible by 4 bytes
62      */
63     private final COPSData _padding;
64
65     /**
66      * Constructor generally used for sending messages
67      * @param csitype - the CSIType
68      * @param data - the data
69      * @throws java.lang.IllegalArgumentException
70      */
71     public COPSClientSI(final CSIType csitype, final COPSData data) {
72         /* The CSIType does not map directly to the CType, therefore the hook to map to a CType below is
73            required to ensure the header value outputs the correct value when streamed
74          */
75         this(new COPSObjHeader(CNum.CSI, COPSObjHeader.VAL_TO_CTYPE.get(csitype.ordinal())), data);
76     }
77
78     /**
79      * Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the
80      * COPSObjHeader information is known
81      * @param hdr - the object header
82      * @param data - the data
83      * @throws java.lang.IllegalArgumentException
84      */
85     protected COPSClientSI(final COPSObjHeader hdr, final COPSData data) {
86         super(hdr);
87         _csiType = VAL_TO_CSI.get(hdr.getCType().ordinal());
88
89         if (!hdr.getCNum().equals(CNum.CSI))
90             throw new IllegalArgumentException("CNum must be equal to " + CNum.CSI);
91         if (_csiType == null || _csiType.equals(CSIType.NA))
92             throw new IllegalArgumentException("Invalid CSIType");
93         if (_csiType.ordinal() != hdr.getCType().ordinal())
94             throw new IllegalArgumentException("Error mapping CSIType " + _csiType + " to CType" + hdr.getCType());
95         if (data == null) throw new IllegalArgumentException("Data must not be null");
96
97         _data = data;
98         if ((_data.length() % 4) != 0) {
99             final int padLen = 4 - (_data.length() % 4);
100             _padding = COPSObjectParser.getPadding(padLen);
101         } else {
102             _padding = new COPSData();
103         }
104     }
105
106     /**
107      * Returns the CSIType
108      * @return - the type
109      */
110     public CSIType getCsiType() { return _csiType; }
111
112     /**
113      * Returns the data
114      * @return - the data
115      */
116     public COPSData getData() { return _data; }
117
118     @Override
119     /* The super says protected but this needs to be public due to usage in COPSDecisionMsgEX.java which is currently
120        calling this method. */
121     public int getDataLength() {
122         return _data.length() + _padding.length();
123     }
124
125     @Override
126     public void writeBody(final Socket socket) throws IOException {
127         COPSUtil.writeData(socket, _data.getData(), _data.length());
128         COPSUtil.writeData(socket, _padding.getData(), _padding.length());
129     }
130
131     @Override
132     public void dumpBody(final OutputStream os) throws IOException {
133         os.write(("CSI-type: " + _csiType + "\n").getBytes());
134         os.write(("client-SI: " + _data.str() + "\n").getBytes());
135     }
136
137     @Override
138     public boolean equals(Object o) {
139         if (this == o) {
140             return true;
141         }
142         if (!(o instanceof COPSClientSI)) {
143             return false;
144         }
145         if (!super.equals(o)) {
146             return false;
147         }
148
149         final COPSClientSI that = (COPSClientSI) o;
150         return _data.equals(that._data) && _padding.equals(that._padding) ||
151                 COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding);
152     }
153
154     @Override
155     public int hashCode() {
156         int result = super.hashCode();
157         result = 31 * result + (_data != null ? _data.hashCode() : 0);
158         result = 31 * result + (_padding != null ? _padding.hashCode() : 0);
159         return result;
160     }
161
162     /**
163      * Parses bytes to return a COPSClientSI object
164      * @param objHdrData - the associated header
165      * @param dataPtr - the data to parse
166      * @return - the object
167      * @throws java.lang.IllegalArgumentException
168      */
169     public static COPSClientSI parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) {
170         short dLen = (short) (objHdrData.msgByteCount - 4);
171         return new COPSClientSI(objHdrData.header, new COPSData(dataPtr, 4, dLen));
172     }
173
174     /**
175      * The different CSI types. NA does not exist but is a placeholder for 0 as the ordinal values will be used
176      * to determine which type for marshalling
177      */
178     public enum CSIType {
179         NA, SIGNALED, NAMED
180     }
181 }
182