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.COPSObjHeader.CNum;
10 import org.umu.cops.stack.COPSObjHeader.CType;
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.net.Socket;
16 import java.util.concurrent.ConcurrentHashMap;
19 * COPS Decision (RFC 2748)
21 * Decision made by the PDP. Appears in replies. The specific non-
22 * mandatory decision objects required in a decision to a particular
23 * request depend on the type of client.
26 * C-Type = 1, Decision Flags (Mandatory)
29 * 0 = NULL Decision (No configuration data available)
30 * 1 = Install (Admit request/Install configuration)
31 * 2 = Remove (Remove request/Remove configuration)
34 * 0x01 = Trigger Error (Trigger error message if set)
35 * Note: Trigger Error is applicable to client-types that
36 * are capable of sending error notifications for signaled
39 * Flag values not applicable to a given context's R-Type or
40 * client-type MUST be ignored by the PEP.
42 * C-Type = 2, Stateless Data
44 * This type of decision object carries additional stateless
45 * information that can be applied by the PEP locally. It is a
46 * variable length object and its internal format SHOULD be
47 * specified in the relevant COPS extension document for the given
48 * client-type. This object is optional in Decision messages and is
49 * interpreted relative to a given context.
51 * It is expected that even outsourcing PEPs will be able to make
52 * some simple stateless policy decisions locally in their LPDP. As
53 * this set is well known and implemented ubiquitously, PDPs are
54 * aware of it as well (either universally, through configuration,
55 * or using the Client-Open message). The PDP may also include this
56 * information in its decision, and the PEP MUST apply it to the
57 * resource allocation event that generated the request.
59 * C-Type = 3, Replacement Data
61 * This type of decision object carries replacement data that is to
62 * replace existing data in a signaled message. It is a variable
63 * length object and its internal format SHOULD be specified in the
64 * relevant COPS extension document for the given client-type. It is
65 * optional in Decision messages and is interpreted relative to a
68 * C-Type = 4, Client Specific Decision Data
70 * Additional decision types can be introduced using the Client
71 * Specific Decision Data Object. It is a variable length object and
72 * its internal format SHOULD be specified in the relevant COPS
73 * extension document for the given client-type. It is optional in
74 * Decision messages and is interpreted relative to a given context.
76 * C-Type = 5, Named Decision Data
78 * Named configuration information is encapsulated in this version
79 * of the decision object in response to configuration requests. It
80 * is a variable length object and its internal format SHOULD be
81 * specified in the relevant COPS extension document for the given
82 * client-type. It is optional in Decision messages and is
83 * interpreted relative to both a given context and decision flags.
85 public class COPSDecision extends COPSObjBase {
87 static Map<Integer, Command> VAL_TO_CMD = new ConcurrentHashMap<>();
89 VAL_TO_CMD.put(Command.NULL.ordinal(), Command.NULL);
90 VAL_TO_CMD.put(Command.INSTALL.ordinal(), Command.INSTALL);
91 VAL_TO_CMD.put(Command.REMOVE.ordinal(), Command.REMOVE);
94 static Map<Integer, DecisionFlag> VAL_TO_FLAG = new ConcurrentHashMap<>();
96 VAL_TO_FLAG.put(DecisionFlag.NA.ordinal(), DecisionFlag.NA);
97 VAL_TO_FLAG.put(DecisionFlag.REQERROR.ordinal(), DecisionFlag.REQERROR);
98 VAL_TO_FLAG.put(DecisionFlag.REQSTATE.ordinal(), DecisionFlag.REQSTATE);
102 * All CTypes are supported except NA
104 private final Command _cmdCode;
105 private final DecisionFlag _flags;
106 private final COPSData _data;
107 private final COPSData _padding;
110 * Constructor generally used for sending messages without any extra data
111 * @param cmdCode - the command
112 * @throws java.lang.IllegalArgumentException
114 public COPSDecision(final Command cmdCode) {
115 this(CType.DEF, cmdCode, DecisionFlag.NA, new COPSData());
119 * Constructor generally used for sending messages with a specific CType and extra data and a NA decision flag
120 * @param cType - the CType
121 * @param data - the data
122 * @throws java.lang.IllegalArgumentException
124 public COPSDecision(final CType cType, final COPSData data) {
125 this(cType, Command.NULL, DecisionFlag.NA, data);
129 * Constructor generally used for sending messages with a specific Command and DecisionFlag
130 * @param cmdCode - the command
131 * @param flags - the flags
132 * @throws java.lang.IllegalArgumentException
134 public COPSDecision(final Command cmdCode, final DecisionFlag flags) {
135 this(CType.DEF, cmdCode, flags, new COPSData());
139 * Constructor generally used for sending messages with a specific, CType, Command and DecisionFlag
140 * @param cType - the CType
141 * @param cmdCode - the command
142 * @param flags - the flags
143 * @throws java.lang.IllegalArgumentException
145 public COPSDecision(final CType cType, final Command cmdCode, final DecisionFlag flags) {
146 this(cType, cmdCode, flags, new COPSData());
150 * Constructor generally used for sending messages with a specific, CType, Command, DecisionFlag and data
151 * @param cType - the CType
152 * @param cmdCode - the command
153 * @param flags - the flags
154 * @param data - the data
155 * @throws java.lang.IllegalArgumentException
157 public COPSDecision(final CType cType, final Command cmdCode, final DecisionFlag flags,
158 final COPSData data) {
159 this(new COPSObjHeader(CNum.DEC, cType), cmdCode, flags, data);
163 * Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the
164 * COPSObjHeader information is known
165 * @param hdr - the object header
166 * @param cmdCode - the command
167 * @param flags - the flags
168 * @param data - the data
169 * @throws java.lang.IllegalArgumentException
171 protected COPSDecision(final COPSObjHeader hdr, final Command cmdCode, final DecisionFlag flags,
172 final COPSData data) {
174 // TODO - find a better way to make this check
175 if (this.getClass().getName().equals("org.umu.cops.stack.COPSDecision") && !hdr.getCNum().equals(CNum.DEC))
176 throw new IllegalArgumentException("CNum must be equal to " + CNum.DEC);
178 if (hdr.getCType().equals(CType.NA)) throw new IllegalArgumentException("CType must not be " + CType.NA);
179 if (cmdCode == null) throw new IllegalArgumentException("Command code must not be null");
180 if (flags == null) throw new IllegalArgumentException("Flags must not be null");
181 if (data == null) throw new IllegalArgumentException("Data object must not be null");
187 if ((_data.length() % 4) != 0) {
188 final int padLen = 4 - (_data.length() % 4);
189 _padding = COPSObjectParser.getPadding(padLen);
191 _padding = new COPSData();
196 * Returns the command
197 * @return - the command
199 public Command getCommand() { return _cmdCode; }
202 public int getDataLength() {
203 return 4 + _data.length() + _padding.length();
207 * Get the associated data if decision object is of cType 2 or higher
210 public COPSData getData() {
215 * If cType == 1 , get the flags associated
218 public DecisionFlag getFlag() {
226 public String getTypeStr() {
227 switch (this.getHeader().getCType()) {
231 return "Stateless data";
233 return "Replacement data";
235 return "Client specific decision data";
237 return "Named decision data";
244 protected void writeBody(final Socket socket) throws IOException {
245 final byte[] buf = new byte[4];
246 buf[0] = (byte) (_cmdCode.ordinal() >> 8);
247 buf[1] = (byte) _cmdCode.ordinal();
248 buf[2] = (byte) (_flags.ordinal() >> 8);
249 buf[3] = (byte) _flags.ordinal();
250 COPSUtil.writeData(socket, buf, 4);
252 COPSUtil.writeData(socket, _data.getData(), _data.length());
253 if (_padding != null) {
254 COPSUtil.writeData(socket, _padding.getData(), _padding.length());
259 protected void dumpBody(final OutputStream os) throws IOException {
260 if (this.getHeader().getCType().equals(CType.DEF)) {
261 os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
262 os.write(("Command code: " + _cmdCode + "\n").getBytes());
263 os.write(("Command flags: " + _flags + "\n").getBytes());
265 os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
266 os.write(("Data: " + _data.str() + "\n").getBytes());
271 public boolean equals(final Object o) {
275 if (!(o instanceof COPSDecision)) {
278 if (!super.equals(o)) {
282 final COPSDecision that = (COPSDecision) o;
284 return _cmdCode == that._cmdCode && _flags == that._flags && _data.equals(that._data) &&
285 _padding.equals(that._padding) ||
286 COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding);
290 public int hashCode() {
291 int result = super.hashCode();
292 result = 31 * result + _data.hashCode();
293 result = 31 * result + _cmdCode.hashCode();
294 result = 31 * result + _flags.hashCode();
295 result = 31 * result + _padding.hashCode();
300 * Parses bytes to return a COPSDecision object
301 * @param objHdrData - the associated header
302 * @param dataPtr - the data to parse
303 * @return - the object
304 * @throws java.lang.IllegalArgumentException
306 public static COPSDecision parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) {
308 _cmdCode |= ((short) dataPtr[4]) << 8;
309 _cmdCode |= ((short) dataPtr[5]) & 0xFF;
312 _flags |= ((short) dataPtr[6]) << 8;
313 _flags |= ((short) dataPtr[7]) & 0xFF;
316 if (objHdrData.header.getCType().equals(CType.DEF)) {
319 d = new COPSData(dataPtr, 8, objHdrData.msgByteCount - 8);
321 return new COPSDecision(objHdrData.header, COPSDecision.VAL_TO_CMD.get(_cmdCode),
322 COPSDecision.VAL_TO_FLAG.get(_flags), d);
326 * Supported command types
328 public enum Command {
329 NULL, // No configuration data available
330 INSTALL, // Admit request/install configuration
331 REMOVE // Remove request/remove configuration
334 public enum DecisionFlag {
336 REQERROR, // = Trigger error