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, null);
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, null);
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, null);
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");
185 if (data == null) _data = new COPSData();
188 if ((_data.length() % 4) != 0) {
189 final int padLen = 4 - (_data.length() % 4);
190 _padding = COPSObjectParser.getPadding(padLen);
192 _padding = new COPSData();
197 * Returns the command
198 * @return - the command
200 public Command getCommand() { return _cmdCode; }
203 public int getDataLength() {
204 return 4 + _data.length() + _padding.length();
208 * Get the associated data if decision object is of cType 2 or higher
211 public COPSData getData() {
216 * If cType == 1 , get the flags associated
219 public DecisionFlag getFlag() {
227 public String getTypeStr() {
228 switch (this.getHeader().getCType()) {
232 return "Stateless data";
234 return "Replacement data";
236 return "Client specific decision data";
238 return "Named decision data";
245 protected void writeBody(final Socket socket) throws IOException {
246 final byte[] buf = new byte[4];
247 buf[0] = (byte) (_cmdCode.ordinal() >> 8);
248 buf[1] = (byte) _cmdCode.ordinal();
249 buf[2] = (byte) (_flags.ordinal() >> 8);
250 buf[3] = (byte) _flags.ordinal();
251 COPSUtil.writeData(socket, buf, 4);
253 COPSUtil.writeData(socket, _data.getData(), _data.length());
254 if (_padding != null) {
255 COPSUtil.writeData(socket, _padding.getData(), _padding.length());
260 protected void dumpBody(final OutputStream os) throws IOException {
261 if (this.getHeader().getCType().equals(CType.DEF)) {
262 os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
263 os.write(("Command code: " + _cmdCode + "\n").getBytes());
264 os.write(("Command flags: " + _flags + "\n").getBytes());
266 os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
267 os.write(("Data: " + _data.str() + "\n").getBytes());
272 public boolean equals(final Object o) {
276 if (!(o instanceof COPSDecision)) {
279 if (!super.equals(o)) {
283 final COPSDecision that = (COPSDecision) o;
285 return _cmdCode == that._cmdCode && _flags == that._flags && _data.equals(that._data) &&
286 _padding.equals(that._padding) ||
287 COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding);
291 public int hashCode() {
292 int result = super.hashCode();
293 result = 31 * result + _data.hashCode();
294 result = 31 * result + _cmdCode.hashCode();
295 result = 31 * result + _flags.hashCode();
296 result = 31 * result + _padding.hashCode();
301 * Parses bytes to return a COPSDecision object
302 * @param objHdrData - the associated header
303 * @param dataPtr - the data to parse
304 * @return - the object
305 * @throws java.lang.IllegalArgumentException
307 public static COPSDecision parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) {
309 _cmdCode |= ((short) dataPtr[4]) << 8;
310 _cmdCode |= ((short) dataPtr[5]) & 0xFF;
313 _flags |= ((short) dataPtr[6]) << 8;
314 _flags |= ((short) dataPtr[7]) & 0xFF;
317 if (objHdrData.header.getCType().equals(CType.DEF)) {
320 d = new COPSData(dataPtr, 8, objHdrData.msgByteCount - 8);
322 return new COPSDecision(objHdrData.header, COPSDecision.VAL_TO_CMD.get(_cmdCode),
323 COPSDecision.VAL_TO_FLAG.get(_flags), d);
327 * Supported command types
329 public enum Command {
330 NULL, // No configuration data available
331 INSTALL, // Admit request/install configuration
332 REMOVE // Remove request/remove configuration
335 public enum DecisionFlag {
337 REQERROR, // = Trigger error