2 * Copyright (c) 2003 University of Murcia. All rights reserved.
3 * --------------------------------------------------------------
4 * For more information, please see <http://www.umu.euro6ix.org/>.
7 package org.umu.cops.stack;
9 import org.umu.cops.stack.COPSHeader.Flag;
10 import org.umu.cops.stack.COPSHeader.OPCode;
11 import org.umu.cops.stack.COPSObjHeader.CType;
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.net.Socket;
19 * COPS Decision Message (RFC 2748 page. 23)
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.
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.
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.
42 * The format of the Decision message is as follows:
44 * <Decision Message> ::= <Common Header>
46 * <Decision(s)> | <Error>
49 * <Decision(s)> ::= <Decision> | <Decision(s)> <Decision>
51 * <Decision> ::= <Context>
53 * [<Decision: Stateless Data>]
54 * [<Decision: Replacement Data>]
55 * [<Decision: ClientSI Data>]
56 * [<Decision: Named Data>]
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
68 public class COPSDecisionMsg extends COPSMsg {
71 private final COPSHandle _clientHandle;
74 private final COPSError _error;
75 private final Map<COPSContext, Set<COPSDecision>> _decisions;
76 private final COPSIntegrity _integrity;
77 private final COPSClientSI _decSI;
80 * Constructor for Decision messages containing a COPS Error.
81 * As this has been deprecated, the constructor containing the version and Flag should be used going forward.
82 * @param clientType - the client type (required)
83 * @param clientHandle - the handle (required)
84 * @param error - the error (required)
85 * @param integrity - the integrity (optional)
86 * @param decSI - the client SI for the description(optional)
89 public COPSDecisionMsg(final short clientType, final COPSHandle clientHandle, final COPSError error,
90 final COPSIntegrity integrity, final COPSClientSI decSI) {
91 this(new COPSHeader(OPCode.DEC, clientType), clientHandle, error, null, integrity, decSI);
95 * Constructor for Decision messages containing a COPS Error.
96 * @param clientType - the client type (required)
97 * @param clientHandle - the handle (required)
98 * @param error - the error (required)
99 * @param integrity - the integrity (optional)
100 * @param decSI - the client SI for the description(optional)
102 public COPSDecisionMsg(final int version, final Flag flag, final short clientType, final COPSHandle clientHandle,
103 final COPSError error, final COPSIntegrity integrity, final COPSClientSI decSI) {
104 this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, error, null, integrity, decSI);
108 * Constructor for Decision messages containing decisions
109 * As this has been deprecated, the constructor containing the version and Flag should be used going forward.
110 * @param clientType - the client type (required)
111 * @param clientHandle - the handle (required)
112 * @param decisions - the decisions (required)
113 * @param integrity - the integrity (optional)
114 * @param decSI - the client SI for the description(optional)
117 public COPSDecisionMsg(final short clientType, final COPSHandle clientHandle,
118 final Map<COPSContext, Set<COPSDecision>> decisions, final COPSIntegrity integrity,
119 final COPSClientSI decSI) {
120 this(new COPSHeader(OPCode.DEC, clientType), clientHandle, null, decisions, integrity, decSI);
124 * Constructor for Decision messages containing decisions
125 * @param clientType - the client type (required)
126 * @param clientHandle - the handle (required)
127 * @param decisions - the decisions (required)
128 * @param integrity - the integrity (optional)
129 * @param decSI - the client SI for the description(optional)
131 public COPSDecisionMsg(final int version, final Flag flag, final short clientType, final COPSHandle clientHandle,
132 final Map<COPSContext, Set<COPSDecision>> decisions, final COPSIntegrity integrity,
133 final COPSClientSI decSI) {
134 this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, null, decisions, integrity, decSI);
138 * Constructor generally designed for Decision messages being parsed from a byte array.
139 * @param hdr - the header
140 * @param clientHandle - the handle
141 * @param error - the error (if null, decisions must not be null or empty)
142 * @param decisions - the decisions (must be empty or null if error is not)
143 * @param integrity - the integrity (optional)
144 * @param decSI - the client SI for the description(optional)
146 protected COPSDecisionMsg(final COPSHeader hdr, final COPSHandle clientHandle,
147 final COPSError error, final Map<COPSContext, Set<COPSDecision>> decisions,
148 final COPSIntegrity integrity, final COPSClientSI decSI) {
150 if (!hdr.getOpCode().equals(OPCode.DEC))
151 throw new IllegalArgumentException("OPCode must be of type - " + OPCode.DEC);
152 if (clientHandle == null) throw new IllegalArgumentException("Client handle must not be null");
153 if (error == null && (decisions == null || decisions.isEmpty()))
154 throw new IllegalArgumentException("Must contain either an COPSError or at least one decision");
155 if (error != null && (decisions != null && !decisions.isEmpty()))
156 throw new IllegalArgumentException("Must not contain a COPSError and decisions");
158 if(decisions == null) _decisions = Collections.unmodifiableMap(new HashMap<COPSContext, Set<COPSDecision>>());
159 else _decisions = Collections.unmodifiableMap(decisions);
161 for (Set<COPSDecision> decSet: _decisions.values()) {
162 if (decSet == null || decSet.isEmpty())
163 throw new IllegalArgumentException("Decisions are empty");
166 _clientHandle = clientHandle;
168 _integrity = integrity;
173 public COPSHandle getClientHandle() {
174 return _clientHandle;
176 public COPSError getError() {
179 public Map<COPSContext, Set<COPSDecision>> getDecisions() {
182 public COPSIntegrity getIntegrity() {
185 public COPSClientSI getDecSI() {
190 protected int getDataLength() {
192 out += _clientHandle.getDataLength() + _clientHandle.getHeader().getHdrLength();
193 if (_error != null) out += _error.getDataLength() + _error.getHeader().getHdrLength();
195 for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
196 out += entry.getKey().getDataLength() + entry.getKey().getHeader().getHdrLength();
197 for (final COPSDecision decision : entry.getValue()) {
198 out += decision.getDataLength() + decision.getHeader().getHdrLength();
202 if (_integrity != null) out += _integrity.getDataLength() + _integrity.getHeader().getHdrLength();
203 if (_decSI != null) out += _decSI.getDataLength() + _decSI.getHeader().getHdrLength();
209 protected void writeBody(final Socket socket) throws IOException {
210 _clientHandle.writeData(socket);
211 if (_error != null) _error.writeData(socket);
214 //Display any local decisions
215 for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
216 entry.getKey().writeData(socket);
217 for (final COPSDecision decision : entry.getValue()) {
218 decision.writeData(socket);
222 if (_integrity != null) _integrity.writeData(socket);
223 if (_decSI != null) _decSI.writeData(socket);
227 protected void dumpBody(final OutputStream os) throws IOException {
228 if (_clientHandle != null)
229 _clientHandle.dump(os);
233 //Display any local decisions
234 for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : _decisions.entrySet()) {
235 entry.getKey().dump(os);
236 for (final COPSDecision decision : entry.getValue()) {
240 if (_integrity != null) {
243 if (_decSI != null) {
249 public boolean equals(final Object o) {
253 if (!(o instanceof COPSDecisionMsg)) {
256 if (!super.equals(o)) {
260 final COPSDecisionMsg that = (COPSDecisionMsg) o;
262 for (final Map.Entry<COPSContext, Set<COPSDecision>> entry : this._decisions.entrySet()) {
263 final Set<COPSDecision> thatDecisions = that._decisions.get(entry.getKey());
264 if (thatDecisions == null) return false;
266 for (final COPSDecision thisDecision : entry.getValue()) {
267 boolean found = false;
268 for (final COPSDecision thatDecision: thatDecisions) {
269 if (thisDecision.equals(thatDecision)) {
274 if (! found) return false;
278 return _clientHandle.equals(that._clientHandle) &&
279 !(_error != null ? !_error.equals(that._error) : that._error != null) &&
280 !(_integrity != null ? !_integrity.equals(that._integrity) : that._integrity != null) &&
281 !(_decSI != null ? !_decSI.equals(that._decSI) : that._decSI != null);
286 public int hashCode() {
287 int result = super.hashCode();
288 result = 31 * result + _clientHandle.hashCode();
289 result = 31 * result + (_error != null ? _error.hashCode() : 0);
290 result = 31 * result + _decisions.hashCode();
291 result = 31 * result + (_integrity != null ? _integrity.hashCode() : 0);
292 result = 31 * result + (_decSI != null ? _decSI.hashCode() : 0);
297 * Responsible for parsing a byte array to create a COPSDecisionMsg object
298 * @param hdrData - the object's header data
299 * @param data - the byte array to parse
300 * @return - the message object
301 * @throws COPSException
303 public static COPSDecisionMsg parse(final COPSHeaderData hdrData, final byte[] data) throws COPSException {
304 // Variables for constructor
305 COPSHandle clientHandle = null;
306 COPSContext context = null;
307 COPSError error = null;
308 COPSIntegrity integrity = null;
309 COPSClientSI descSi = null;
310 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
313 while (dataStart < data.length) {
314 final byte[] buf = new byte[data.length - dataStart];
315 System.arraycopy(data, dataStart, buf, 0, data.length - dataStart);
317 final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf);
318 switch (objHdrData.header.getCNum()) {
320 clientHandle = COPSHandle.parse(objHdrData, buf);
323 if (context == null) {
324 context = COPSContext.parse(objHdrData, buf);
325 } else context = COPSContext.parse(objHdrData, buf);
328 error = COPSError.parse(objHdrData, buf);
332 if (objHdrData.header.getCType().equals(CType.CSI)) {
333 // TODO - Revisit, this is pretty darn clunky
335 dec = COPSDecision.parse(objHdrData, buf);
336 } catch (IllegalArgumentException e) {
337 descSi = COPSClientSI.parse(objHdrData, buf);
341 dec = COPSDecision.parse(objHdrData, buf);
344 if (decisionMap.get(context) != null)
345 decisionMap.get(context).add(dec);
347 final Set<COPSDecision> decisions = new HashSet<>();
349 decisionMap.put(context, decisions);
354 descSi = COPSClientSI.parse(objHdrData, buf);
357 integrity = COPSIntegrity.parse(objHdrData, buf);
360 throw new COPSException("Bad Message format, unknown object type with CNum - "
361 + objHdrData.header.getCNum());
363 dataStart += objHdrData.msgByteCount;
366 return new COPSDecisionMsg(hdrData.header, clientHandle, error, decisionMap, integrity, descSi);