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.ClientType;
10 import org.umu.cops.stack.COPSHeader.Flag;
11 import org.umu.cops.stack.COPSHeader.OPCode;
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;
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)
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);
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)
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);
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)
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);
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)
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);
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)
138 protected COPSDecisionMsg(final COPSHeader hdr, final COPSHandle clientHandle,
139 final COPSError error, final Map<COPSContext, Set<COPSDecision>> decisions,
140 final COPSIntegrity integrity) {
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");
150 if(decisions == null) _decisions = Collections.unmodifiableMap(new HashMap<COPSContext, Set<COPSDecision>>());
151 else _decisions = Collections.unmodifiableMap(decisions);
153 for (Set<COPSDecision> decSet: _decisions.values()) {
154 if (decSet == null || decSet.isEmpty())
155 throw new IllegalArgumentException("Decisions are empty");
158 _clientHandle = clientHandle;
160 _integrity = integrity;
165 public COPSHandle getClientHandle() {
166 return _clientHandle;
168 public COPSError getError() {
171 public Map<COPSContext, Set<COPSDecision>> getDecisions() {
174 public COPSIntegrity getIntegrity() {
179 protected int getDataLength() {
181 out += _clientHandle.getDataLength() + _clientHandle.getHeader().getHdrLength();
182 if (_error != null) out += _error.getDataLength() + _error.getHeader().getHdrLength();
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();
191 if (_integrity != null) out += _integrity.getDataLength() + _integrity.getHeader().getHdrLength();
197 protected void writeBody(final Socket socket) throws IOException {
198 _clientHandle.writeData(socket);
199 if (_error != null) _error.writeData(socket);
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);
210 if (_integrity != null) _integrity.writeData(socket);
214 protected void dumpBody(final OutputStream os) throws IOException {
215 if (_clientHandle != null)
216 _clientHandle.dump(os);
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()) {
227 if (_integrity != null) {
233 public boolean equals(final Object o) {
237 if (!(o instanceof COPSDecisionMsg)) {
240 if (!super.equals(o)) {
244 final COPSDecisionMsg that = (COPSDecisionMsg) o;
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;
250 for (final COPSDecision thisDecision : entry.getValue()) {
251 boolean found = false;
252 for (final COPSDecision thatDecision: thatDecisions) {
253 if (thisDecision.equals(thatDecision)) {
258 if (! found) return false;
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);
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);
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
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<>();
294 while (dataStart < data.length) {
295 final byte[] buf = new byte[data.length - dataStart];
296 System.arraycopy(data, dataStart, buf, 0, data.length - dataStart);
298 final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf);
299 switch (objHdrData.header.getCNum()) {
301 clientHandle = COPSHandle.parse(objHdrData, buf);
304 if (context == null) {
305 context = COPSContext.parse(objHdrData, buf);
306 } else context = COPSContext.parse(objHdrData, buf);
309 error = COPSError.parse(objHdrData, buf);
312 if (decisionMap.get(context) != null)
313 decisionMap.get(context).add(COPSDecision.parse(objHdrData, buf));
315 final Set<COPSDecision> decisions = new HashSet<>();
316 decisions.add(COPSDecision.parse(objHdrData, buf));
317 decisionMap.put(context, decisions);
321 integrity = COPSIntegrity.parse(objHdrData, buf);
324 throw new COPSException("Bad Message format, unknown object type");
326 dataStart += objHdrData.msgByteCount;
329 return new COPSDecisionMsg(hdrData.header, clientHandle, error, decisionMap, integrity);