8839c1a632e236c90abc28575a1a459db388b297
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / stack / COPSDecisionMsg.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.COPSHeader.ClientType;
10 import org.umu.cops.stack.COPSHeader.Flag;
11 import org.umu.cops.stack.COPSHeader.OPCode;
12
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.net.Socket;
16 import java.util.*;
17
18 /**
19  * COPS Decision Message  (RFC 2748 page. 23)
20  *
21  * The PDP responds to the REQ with a DEC message that includes the
22  * associated client handle and one or more decision objects grouped
23  * relative to a Context object and Decision Flags object type pair. If
24  * there was a protocol error an error object is returned instead.
25  *
26  * It is required that the first decision message for a new/updated
27  * request will have the solicited message flag set (value = 1) in the
28  * COPS header. This avoids the issue of keeping track of which updated
29  * request (that is, a request reissued for the same handle) a
30  * particular decision corresponds. It is important that, for a given
31  * handle, there be at most one outstanding solicited decision per
32  * request. This essentially means that the PEP SHOULD NOT issue more
33  * than one REQ (for a given handle) before it receives a corresponding
34  * DEC with the solicited message flag set. The PDP MUST always issue
35  * decisions for requests on a particular handle in the order they
36  * arrive and all requests MUST have a corresponding decision.
37  *
38  * To avoid deadlock, the PEP can always timeout after issuing a request
39  * that does not receive a decision. It MUST then delete the timed-out
40  * handle, and may try again using a new handle.
41  *
42  * The format of the Decision message is as follows:
43  *
44  * <Decision Message> ::= <Common Header>
45  * <Client Handle>
46  * <Decision(s)> | <Error>
47  * [<Integrity>]
48  *
49  * <Decision(s)> ::= <Decision> | <Decision(s)> <Decision>
50  *
51  * <Decision> ::= <Context>
52  * <Decision: Flags>
53  * [<Decision: Stateless Data>]
54  * [<Decision: Replacement Data>]
55  * [<Decision: ClientSI Data>]
56  * [<Decision: Named Data>]
57  *
58  * The Decision message may include either an Error object or one or
59  * more context plus associated decision objects. COPS protocol problems
60  * are reported in the Error object (e.g. an error with the format of
61  * the original request including malformed request messages, unknown
62  * COPS objects in the Request, etc.). The applicable Decision object(s)
63  * depend on the context and the type of client. The only ordering
64  * requirement for decision objects is that the required Decision Flags
65  * object type MUST precede the other Decision object types per context
66  * binding.
67  */
68 public class COPSDecisionMsg extends COPSMsg {
69
70     // Required
71     private final COPSHandle _clientHandle;
72
73     // Optional
74     private final COPSError _error;
75     private final Map<COPSContext, Set<COPSDecision>> _decisions;
76     private final COPSIntegrity _integrity;
77
78     /**
79      * Constructor for Decision messages containing a COPS Error.
80      * As this has been deprecated, the constructor containing the version and Flag should be used going forward.
81      * @param clientType - the client type (required)
82      * @param clientHandle - the handle (required)
83      * @param error - the error (required)
84      * @param integrity - the integrity (optional)
85      */
86     @Deprecated
87     public COPSDecisionMsg(final ClientType clientType, final COPSHandle clientHandle,
88                            final COPSError error, final COPSIntegrity integrity) {
89         this(new COPSHeader(OPCode.DEC, clientType), clientHandle, error, null, integrity);
90     }
91
92     /**
93      * Constructor for Decision messages containing a COPS Error.
94      * @param clientType - the client type (required)
95      * @param clientHandle - the handle (required)
96      * @param error - the error (required)
97      * @param integrity - the integrity (optional)
98      */
99     public COPSDecisionMsg(final int version, final Flag flag, final ClientType clientType, final COPSHandle clientHandle,
100                            final COPSError error, final COPSIntegrity integrity) {
101         this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, error, null, integrity);
102     }
103
104     /**
105      * Constructor for Decision messages containing decisions
106      * As this has been deprecated, the constructor containing the version and Flag should be used going forward.
107      * @param clientType - the client type (required)
108      * @param clientHandle - the handle (required)
109      * @param decisions - the decisions (required)
110      * @param integrity - the integrity (optional)
111      */
112     @Deprecated
113     public COPSDecisionMsg(final ClientType clientType, final COPSHandle clientHandle,
114                            final Map<COPSContext, Set<COPSDecision>> decisions, final COPSIntegrity integrity) {
115         this(new COPSHeader(OPCode.DEC, clientType), clientHandle, null, decisions, integrity);
116     }
117
118     /**
119      * Constructor for Decision messages containing decisions
120      * @param clientType - the client type (required)
121      * @param clientHandle - the handle (required)
122      * @param decisions - the decisions (required)
123      * @param integrity - the integrity (optional)
124      */
125     public COPSDecisionMsg(final int version, final Flag flag, final ClientType clientType, final COPSHandle clientHandle,
126                            final Map<COPSContext, Set<COPSDecision>> decisions, final COPSIntegrity integrity) {
127         this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, null, decisions, integrity);
128     }
129
130     /**
131      * Constructor generally designed for Decision messages being parsed from a byte array.
132      * @param hdr - the header
133      * @param clientHandle - the handle
134      * @param error - the error (if null, decisions must not be null or empty)
135      * @param decisions - the decisions (must be empty or null if error is not)
136      * @param integrity - the integrity (optional)
137      */
138     protected COPSDecisionMsg(final COPSHeader hdr, final COPSHandle clientHandle,
139                            final COPSError error, final Map<COPSContext, Set<COPSDecision>> decisions,
140                            final COPSIntegrity integrity) {
141         super(hdr);
142         if (!hdr.getOpCode().equals(OPCode.DEC))
143             throw new IllegalArgumentException("OPCode must be of type - " + OPCode.DEC);
144         if (clientHandle == null) throw new IllegalArgumentException("Client handle must not be null");
145         if (error == null && (decisions == null || decisions.isEmpty()))
146             throw new IllegalArgumentException("Must contain either an COPSError or at least one decision");
147         if (error != null && (decisions != null && !decisions.isEmpty()))
148             throw new IllegalArgumentException("Must not contain a COPSError and decisions");
149
150         if(decisions == null) _decisions = Collections.unmodifiableMap(new HashMap<COPSContext, Set<COPSDecision>>());
151         else _decisions = Collections.unmodifiableMap(decisions);
152
153         for (Set<COPSDecision> decSet: _decisions.values()) {
154             if (decSet == null || decSet.isEmpty())
155                 throw new IllegalArgumentException("Decisions are empty");
156         }
157
158         _clientHandle = clientHandle;
159         _error = error;
160         _integrity = integrity;
161
162     }
163
164     // Getters
165     public COPSHandle getClientHandle() {
166         return _clientHandle;
167     }
168     public COPSError getError() {
169         return _error;
170     }
171     public Map<COPSContext, Set<COPSDecision>> getDecisions() {
172         return _decisions;
173     }
174     public COPSIntegrity getIntegrity() {
175         return _integrity;
176     }
177
178     @Override
179     protected int getDataLength() {
180         int out = 0;
181         out += _clientHandle.getDataLength() + _clientHandle.getHeader().getHdrLength();
182         if (_error != null) out += _error.getDataLength() + _error.getHeader().getHdrLength();
183
184         for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
185             out += entry.getKey().getDataLength() + entry.getKey().getHeader().getHdrLength();
186             for (final COPSDecision decision : entry.getValue()) {
187                 out += decision.getDataLength() + decision.getHeader().getHdrLength();
188             }
189         }
190
191         if (_integrity != null) out += _integrity.getDataLength() + _integrity.getHeader().getHdrLength();
192
193         return out;
194     }
195
196     @Override
197     protected void writeBody(final Socket socket) throws IOException {
198         _clientHandle.writeData(socket);
199         if (_error != null) _error.writeData(socket);
200
201         //Display decisions
202         //Display any local decisions
203         for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
204             entry.getKey().writeData(socket);
205             for (final COPSDecision decision : entry.getValue()) {
206                 decision.writeData(socket);
207             }
208         }
209
210         if (_integrity != null) _integrity.writeData(socket);
211     }
212
213     @Override
214     protected void dumpBody(final OutputStream os) throws IOException {
215         if (_clientHandle != null)
216             _clientHandle.dump(os);
217         if (_error != null)
218             _error.dump(os);
219
220         //Display any local decisions
221         for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
222             entry.getKey().dump(os);
223             for (final COPSDecision decision : entry.getValue()) {
224                 decision.dump(os);
225             }
226         }
227         if (_integrity != null) {
228             _integrity.dump(os);
229         }
230     }
231
232     @Override
233     public boolean equals(final Object o) {
234         if (this == o) {
235             return true;
236         }
237         if (!(o instanceof COPSDecisionMsg)) {
238             return false;
239         }
240         if (!super.equals(o)) {
241             return false;
242         }
243
244         final COPSDecisionMsg that = (COPSDecisionMsg) o;
245
246         for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : this._decisions.entrySet()) {
247             final Set<COPSDecision> thatDecisions = that._decisions.get(entry.getKey());
248             if (thatDecisions == null) return false;
249
250             for (final COPSDecision thisDecision : entry.getValue()) {
251                 boolean found = false;
252                 for (final COPSDecision thatDecision: thatDecisions) {
253                     if (thisDecision.equals(thatDecision)) {
254                         found = true;
255                         break;
256                     }
257                 }
258                 if (! found) return false;
259             }
260         }
261
262         return _clientHandle.equals(that._clientHandle) &&
263                 !(_error != null ? !_error.equals(that._error) : that._error != null) &&
264                 !(_integrity != null ? !_integrity.equals(that._integrity) : that._integrity != null);
265
266     }
267
268     @Override
269     public int hashCode() {
270         int result = super.hashCode();
271         result = 31 * result + _clientHandle.hashCode();
272         result = 31 * result + (_error != null ? _error.hashCode() : 0);
273         result = 31 * result + _decisions.hashCode();
274         result = 31 * result + (_integrity != null ? _integrity.hashCode() : 0);
275         return result;
276     }
277
278     /**
279      * Responsible for parsing a byte array to create a COPSDecisionMsg object
280      * @param hdrData - the object's header data
281      * @param data - the byte array to parse
282      * @return - the message object
283      * @throws COPSException
284      */
285     public static COPSDecisionMsg parse(final COPSHeaderData hdrData, final byte[] data) throws COPSException {
286         // Variables for constructor
287         COPSHandle clientHandle = null;
288         COPSContext context = null;
289         COPSError error = null;
290         COPSIntegrity integrity = null;
291         final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
292
293         int dataStart = 0;
294         while (dataStart < data.length) {
295             final byte[] buf = new byte[data.length - dataStart];
296             System.arraycopy(data, dataStart, buf, 0, data.length - dataStart);
297
298             final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf);
299             switch (objHdrData.header.getCNum()) {
300                 case HANDLE:
301                     clientHandle = COPSHandle.parse(objHdrData, buf);
302                     break;
303                 case CONTEXT:
304                     if (context == null) {
305                         context = COPSContext.parse(objHdrData, buf);
306                     } else context = COPSContext.parse(objHdrData, buf);
307                     break;
308                 case ERROR:
309                     error = COPSError.parse(objHdrData, buf);
310                     break;
311                 case DEC:
312                     if (decisionMap.get(context) != null)
313                         decisionMap.get(context).add(COPSDecision.parse(objHdrData, buf));
314                     else {
315                         final Set<COPSDecision> decisions = new HashSet<>();
316                         decisions.add(COPSDecision.parse(objHdrData, buf));
317                         decisionMap.put(context, decisions);
318                     }
319                     break;
320                 case MSG_INTEGRITY:
321                     integrity = COPSIntegrity.parse(objHdrData, buf);
322                     break;
323                 default:
324                     throw new COPSException("Bad Message format, unknown object type");
325             }
326             dataStart += objHdrData.msgByteCount;
327         }
328
329         return new COPSDecisionMsg(hdrData.header, clientHandle, error, decisionMap, integrity);
330     }
331
332 }