X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=packetcable-driver%2Fsrc%2Fmain%2Fjava%2Forg%2Fumu%2Fcops%2Fstack%2FCOPSDecisionMsg.java;fp=packetcable-driver%2Fsrc%2Fmain%2Fjava%2Forg%2Fumu%2Fcops%2Fstack%2FCOPSDecisionMsg.java;h=8839c1a632e236c90abc28575a1a459db388b297;hb=dd830d0bca451a8a11ec30d9a6d2f6d3480624e1;hp=488168b7f36dfc9324513a7686b10ad5cfe21a14;hpb=4f0f343be373c2a8c261a8b951b254ccea7fc022;p=packetcable.git diff --git a/packetcable-driver/src/main/java/org/umu/cops/stack/COPSDecisionMsg.java b/packetcable-driver/src/main/java/org/umu/cops/stack/COPSDecisionMsg.java index 488168b..8839c1a 100644 --- a/packetcable-driver/src/main/java/org/umu/cops/stack/COPSDecisionMsg.java +++ b/packetcable-driver/src/main/java/org/umu/cops/stack/COPSDecisionMsg.java @@ -6,429 +6,327 @@ package org.umu.cops.stack; -import org.umu.cops.stack.COPSDecision.DecisionFlag; -import org.umu.cops.stack.COPSObjHeader.CNum; +import org.umu.cops.stack.COPSHeader.ClientType; +import org.umu.cops.stack.COPSHeader.Flag; +import org.umu.cops.stack.COPSHeader.OPCode; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; +import java.util.*; /** - * COPS Decision Message + * COPS Decision Message (RFC 2748 page. 23) * - * @version COPSDecisionMsg.java, v 1.00 2003 + * The PDP responds to the REQ with a DEC message that includes the + * associated client handle and one or more decision objects grouped + * relative to a Context object and Decision Flags object type pair. If + * there was a protocol error an error object is returned instead. * + * It is required that the first decision message for a new/updated + * request will have the solicited message flag set (value = 1) in the + * COPS header. This avoids the issue of keeping track of which updated + * request (that is, a request reissued for the same handle) a + * particular decision corresponds. It is important that, for a given + * handle, there be at most one outstanding solicited decision per + * request. This essentially means that the PEP SHOULD NOT issue more + * than one REQ (for a given handle) before it receives a corresponding + * DEC with the solicited message flag set. The PDP MUST always issue + * decisions for requests on a particular handle in the order they + * arrive and all requests MUST have a corresponding decision. + * + * To avoid deadlock, the PEP can always timeout after issuing a request + * that does not receive a decision. It MUST then delete the timed-out + * handle, and may try again using a new handle. + * + * The format of the Decision message is as follows: + * + * ::= + * + * | + * [] + * + * ::= | + * + * ::= + * + * [] + * [] + * [] + * [] + * + * The Decision message may include either an Error object or one or + * more context plus associated decision objects. COPS protocol problems + * are reported in the Error object (e.g. an error with the format of + * the original request including malformed request messages, unknown + * COPS objects in the Request, etc.). The applicable Decision object(s) + * depend on the context and the type of client. The only ordering + * requirement for decision objects is that the required Decision Flags + * object type MUST precede the other Decision object types per context + * binding. */ public class COPSDecisionMsg extends COPSMsg { - /* COPSHeader coming from base class */ - private COPSHandle _clientHandle; - private COPSError _error; - private Hashtable _decisions; - private COPSIntegrity _integrity; - private COPSContext _decContext; - private COPSClientSI _decSI; - - /// - public COPSDecisionMsg() { - _clientHandle = null; - _error = null; - _decisions = new Hashtable(20); - _integrity = null; - _decContext = null; - _decSI = null; - } - - /** Checks the sanity of COPS message and throw an - * COPSBadDataException when data is bad. - */ - public void checkSanity() throws COPSException { - if ((_hdr == null) || (_clientHandle == null) || ( (_error == null) && (_decisions.size() == 0))) { - throw new COPSException("Bad message format"); - } - } - - /// - protected COPSDecisionMsg(byte[] data) throws COPSException { - _decisions = new Hashtable(20); - _clientHandle = null; - _error = null; - _integrity = null; - _decContext = null; - _decSI = null; - - parse(data); - } - - /** - * Parses the data and fills COPSDecisionMsg with its constituents - * - * @param data a byte[] - * - * @throws COPSException - * - */ - protected void parse(byte[] data) throws COPSException { - super.parseHeader(data); - - while (_dataStart < _dataLength) { - byte[] buf = new byte[data.length - _dataStart]; - System.arraycopy(data,_dataStart,buf,0,data.length - _dataStart); + // Required + private final COPSHandle _clientHandle; - final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf); - switch (objHdrData.header.getCNum()) { - case HANDLE: - _clientHandle = COPSHandle.parse(objHdrData, buf); - _dataStart += _clientHandle.getDataLength(); - break; - case CONTEXT: - //dec context - _decContext = COPSContext.parse(objHdrData, buf); - _dataStart += _decContext.getDataLength(); - break; - case ERROR: - _error = COPSError.parse(objHdrData, buf); - _dataStart += _error.getDataLength(); - break; - case DEC: - COPSDecision decs = COPSDecision.parse(objHdrData, buf); - _dataStart += decs.getDataLength(); - addDecision(decs, _decContext); - break; - case MSG_INTEGRITY: - _integrity = COPSIntegrity.parse(objHdrData, buf); - _dataStart += _integrity.getDataLength(); - break; - default: { - throw new COPSException("Bad Message format, unknown object type"); - } - } - } - checkSanity(); - } + // Optional + private final COPSError _error; + private final Map> _decisions; + private final COPSIntegrity _integrity; /** - * Parses the data and fills that follows the header hdr and fills COPSDecisionMsg - * - * @param hdr a COPSHeader - * @param data a byte[] - * - * @throws COPSException - * + * Constructor for Decision messages containing a COPS Error. + * As this has been deprecated, the constructor containing the version and Flag should be used going forward. + * @param clientType - the client type (required) + * @param clientHandle - the handle (required) + * @param error - the error (required) + * @param integrity - the integrity (optional) */ - protected void parse(COPSHeader hdr, byte[] data) throws COPSException { - _hdr = hdr; - parse(data); - setMsgLength(); + @Deprecated + public COPSDecisionMsg(final ClientType clientType, final COPSHandle clientHandle, + final COPSError error, final COPSIntegrity integrity) { + this(new COPSHeader(OPCode.DEC, clientType), clientHandle, error, null, integrity); } /** - * Add message header - * - * @param hdr a COPSHeader - * - * @throws COPSException - * + * Constructor for Decision messages containing a COPS Error. + * @param clientType - the client type (required) + * @param clientHandle - the handle (required) + * @param error - the error (required) + * @param integrity - the integrity (optional) */ - public void add (COPSHeader hdr) throws COPSException { - if (hdr == null) - throw new COPSException ("Null Header"); - if (hdr.getOpCode() != COPSHeader.COPS_OP_DEC) - throw new COPSException ("Error Header (no COPS_OP_DEC)"); - _hdr = hdr; - setMsgLength(); + public COPSDecisionMsg(final int version, final Flag flag, final ClientType clientType, final COPSHandle clientHandle, + final COPSError error, final COPSIntegrity integrity) { + this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, error, null, integrity); } /** - * Add client handle to the message - * - * @param handle a COPSHandle - * - * @throws COPSException - * + * Constructor for Decision messages containing decisions + * As this has been deprecated, the constructor containing the version and Flag should be used going forward. + * @param clientType - the client type (required) + * @param clientHandle - the handle (required) + * @param decisions - the decisions (required) + * @param integrity - the integrity (optional) */ - public void add (COPSHandle handle) throws COPSException { - if (handle == null) - throw new COPSException ("Null Handle"); - _clientHandle = handle; - setMsgLength(); + @Deprecated + public COPSDecisionMsg(final ClientType clientType, final COPSHandle clientHandle, + final Map> decisions, final COPSIntegrity integrity) { + this(new COPSHeader(OPCode.DEC, clientType), clientHandle, null, decisions, integrity); } /** - * Add an Error object - * - * @param error a COPSError - * - * @throws COPSException - * + * Constructor for Decision messages containing decisions + * @param clientType - the client type (required) + * @param clientHandle - the handle (required) + * @param decisions - the decisions (required) + * @param integrity - the integrity (optional) */ - public void add (COPSError error) throws COPSException { - if (_decisions.size() != 0) - throw new COPSException ("No null decisions"); - if (_error != null) - throw new COPSException ("No null error"); - //Message integrity object should be the very last one - //If it is already added - if (_integrity != null) - throw new COPSException ("No null integrity"); - _error = error; - setMsgLength(); + public COPSDecisionMsg(final int version, final Flag flag, final ClientType clientType, final COPSHandle clientHandle, + final Map> decisions, final COPSIntegrity integrity) { + this(new COPSHeader(version, flag, OPCode.DEC, clientType), clientHandle, null, decisions, integrity); } /** - * Add one or more local decision object for a given decision context - * the context is optional, if null all decision object are tided to - * message context - * - * @param decision a COPSDecision - * @param context a COPSContext - * - * @throws COPSException - * + * Constructor generally designed for Decision messages being parsed from a byte array. + * @param hdr - the header + * @param clientHandle - the handle + * @param error - the error (if null, decisions must not be null or empty) + * @param decisions - the decisions (must be empty or null if error is not) + * @param integrity - the integrity (optional) */ - public void addDecision(COPSDecision decision, COPSContext context) throws COPSException { - //Either error or decision can be added - //If error is aleady there assert - if (_error != null) - throw new COPSException ("No null error"); - - if (decision.getHeader().getCNum().equals(CNum.LPDP_DEC)) - throw new COPSException ("Is local decision"); - - Vector v = (Vector) _decisions.get(context); - if (v == null) v = new Vector(); - - if (!decision.getFlag().equals(DecisionFlag.NA)) {//Commented out as advised by Felix - //if (v.size() != 0) - //{ - //Only one set of decision flags is allowed - //for each context - // throw new COPSException ("Bad Message format, only one set of decision flags is allowed."); - //} - } else { - if (v.size() == 0) { - //The flags decision must precede any other - //decision message, since the decision is not - //flags throw exception - throw new COPSException ("Bad Message format, flags decision must precede any other decision object."); - } + protected COPSDecisionMsg(final COPSHeader hdr, final COPSHandle clientHandle, + final COPSError error, final Map> decisions, + final COPSIntegrity integrity) { + super(hdr); + if (!hdr.getOpCode().equals(OPCode.DEC)) + throw new IllegalArgumentException("OPCode must be of type - " + OPCode.DEC); + if (clientHandle == null) throw new IllegalArgumentException("Client handle must not be null"); + if (error == null && (decisions == null || decisions.isEmpty())) + throw new IllegalArgumentException("Must contain either an COPSError or at least one decision"); + if (error != null && (decisions != null && !decisions.isEmpty())) + throw new IllegalArgumentException("Must not contain a COPSError and decisions"); + + if(decisions == null) _decisions = Collections.unmodifiableMap(new HashMap>()); + else _decisions = Collections.unmodifiableMap(decisions); + + for (Set decSet: _decisions.values()) { + if (decSet == null || decSet.isEmpty()) + throw new IllegalArgumentException("Decisions are empty"); } - v.add(decision); - _decisions.put(context,v); - setMsgLength(); - } - - /** - * Add integrity object - * - * @param integrity a COPSIntegrity - * - * @throws COPSException - * - */ - public void add (COPSIntegrity integrity) throws COPSException { - if (integrity == null) - throw new COPSException ("Null Integrity"); + _clientHandle = clientHandle; + _error = error; _integrity = integrity; - setMsgLength(); - } - /** - * Add clientSI object - * - * @param clientSI a COPSClientSI - * - * @throws COPSException - * - */ - public void add (COPSClientSI clientSI) throws COPSException { - if (clientSI == null) - throw new COPSException ("Null clientSI"); - /* - if (!integrity.isMessageIntegrity()) - throw new COPSException ("Error Integrity"); - */ - _decSI = clientSI; - setMsgLength(); - } - - /** - * Writes data to given socket - * - * @param id a Socket - * - * @throws IOException - * - */ - public void writeData(Socket id) throws IOException { - // checkSanity(); - if (_hdr != null) _hdr.writeData(id); - if (_clientHandle != null) _clientHandle.writeData(id); - if (_error != null) _error.writeData(id); - - //Display decisions - //Display any local decisions - for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) { - - COPSContext context = (COPSContext) e.nextElement(); - Vector v = (Vector) _decisions.get(context); - context.writeData(id); - for (Enumeration ee = v.elements() ; ee.hasMoreElements() ;) { - COPSDecision decision = (COPSDecision) ee.nextElement(); - decision.writeData(id); - } - } - - if (_decSI != null) _decSI.writeData(id); - if (_integrity != null) _integrity.writeData(id); } - /** - * Method getHeader - * - * @return a COPSHeader - * - */ - public COPSHeader getHeader() { - return _hdr; - } - - /** - * Method getClientHandle - * - * @return a COPSHandle - * - */ + // Getters public COPSHandle getClientHandle() { return _clientHandle; } - - /** - * Returns true if it has error object - * - * @return a boolean - * - */ - public boolean hasError() { - return (_error != null); - } - - /** - * Should check hasError() before calling - * - * @return a COPSError - * - */ public COPSError getError() { return _error; } - - /** - * Returns a map of decision for which is an arry of context and vector - * of associated decision object. - * - * @return a Hashtable - * - */ - public Hashtable getDecisions() { + public Map> getDecisions() { return _decisions; } - - /** - * Returns true if it has integrity object - * - * @return a boolean - * - */ - public boolean hasIntegrity() { - return (_integrity != null); - } - - /** - * Should check hasIntegrity() before calling - * - * @return a COPSIntegrity - * - */ public COPSIntegrity getIntegrity() { return _integrity; } - /** - * Method setMsgLength - * - * @throws COPSException - * - */ - protected void setMsgLength() throws COPSException { - short len = 0; - if (_clientHandle != null) - len += _clientHandle.getDataLength(); - if (_error != null) - len += _error.getDataLength(); + @Override + protected int getDataLength() { + int out = 0; + out += _clientHandle.getDataLength() + _clientHandle.getHeader().getHdrLength(); + if (_error != null) out += _error.getDataLength() + _error.getHeader().getHdrLength(); - //Display any local decisions - for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) { + for (final Map.Entry> entry : _decisions.entrySet()) { + out += entry.getKey().getDataLength() + entry.getKey().getHeader().getHdrLength(); + for (final COPSDecision decision : entry.getValue()) { + out += decision.getDataLength() + decision.getHeader().getHdrLength(); + } + } + + if (_integrity != null) out += _integrity.getDataLength() + _integrity.getHeader().getHdrLength(); + + return out; + } - COPSContext context = (COPSContext) e.nextElement(); - Vector v = (Vector) _decisions.get(context); - len += context.getDataLength(); + @Override + protected void writeBody(final Socket socket) throws IOException { + _clientHandle.writeData(socket); + if (_error != null) _error.writeData(socket); - for (Enumeration ee = v.elements() ; ee.hasMoreElements() ;) { - COPSDecision decision = (COPSDecision) ee.nextElement(); - len += decision.getDataLength(); + //Display decisions + //Display any local decisions + for (final Map.Entry> entry : _decisions.entrySet()) { + entry.getKey().writeData(socket); + for (final COPSDecision decision : entry.getValue()) { + decision.writeData(socket); } } - if (_decSI != null) { - len += _decSI.getDataLength(); - } - if (_integrity != null) { - len += _integrity.getDataLength(); - } - _hdr.setMsgLength((int) len); + if (_integrity != null) _integrity.writeData(socket); } - /** - * Write an object textual description in the output stream - * - * @param os an OutputStream - * - * @throws IOException - * - */ - public void dump(OutputStream os) throws IOException { - _hdr.dump(os); - + @Override + protected void dumpBody(final OutputStream os) throws IOException { if (_clientHandle != null) _clientHandle.dump(os); if (_error != null) _error.dump(os); //Display any local decisions - for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) { - - COPSContext context = (COPSContext) e.nextElement(); - Vector v = (Vector) _decisions.get(context); - context.dump(os); - - for (Enumeration ee = v.elements() ; ee.hasMoreElements() ;) { - COPSDecision decision = (COPSDecision) ee.nextElement(); + for (final Map.Entry> entry : _decisions.entrySet()) { + entry.getKey().dump(os); + for (final COPSDecision decision : entry.getValue()) { decision.dump(os); } } - if (_decSI != null) { - _decSI.dump(os); - } if (_integrity != null) { _integrity.dump(os); } } -} + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof COPSDecisionMsg)) { + return false; + } + if (!super.equals(o)) { + return false; + } + + final COPSDecisionMsg that = (COPSDecisionMsg) o; + + for (final Map.Entry> entry : this._decisions.entrySet()) { + final Set thatDecisions = that._decisions.get(entry.getKey()); + if (thatDecisions == null) return false; + + for (final COPSDecision thisDecision : entry.getValue()) { + boolean found = false; + for (final COPSDecision thatDecision: thatDecisions) { + if (thisDecision.equals(thatDecision)) { + found = true; + break; + } + } + if (! found) return false; + } + } + + return _clientHandle.equals(that._clientHandle) && + !(_error != null ? !_error.equals(that._error) : that._error != null) && + !(_integrity != null ? !_integrity.equals(that._integrity) : that._integrity != null); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + _clientHandle.hashCode(); + result = 31 * result + (_error != null ? _error.hashCode() : 0); + result = 31 * result + _decisions.hashCode(); + result = 31 * result + (_integrity != null ? _integrity.hashCode() : 0); + return result; + } + + /** + * Responsible for parsing a byte array to create a COPSDecisionMsg object + * @param hdrData - the object's header data + * @param data - the byte array to parse + * @return - the message object + * @throws COPSException + */ + public static COPSDecisionMsg parse(final COPSHeaderData hdrData, final byte[] data) throws COPSException { + // Variables for constructor + COPSHandle clientHandle = null; + COPSContext context = null; + COPSError error = null; + COPSIntegrity integrity = null; + final Map> decisionMap = new HashMap<>(); + + int dataStart = 0; + while (dataStart < data.length) { + final byte[] buf = new byte[data.length - dataStart]; + System.arraycopy(data, dataStart, buf, 0, data.length - dataStart); + + final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf); + switch (objHdrData.header.getCNum()) { + case HANDLE: + clientHandle = COPSHandle.parse(objHdrData, buf); + break; + case CONTEXT: + if (context == null) { + context = COPSContext.parse(objHdrData, buf); + } else context = COPSContext.parse(objHdrData, buf); + break; + case ERROR: + error = COPSError.parse(objHdrData, buf); + break; + case DEC: + if (decisionMap.get(context) != null) + decisionMap.get(context).add(COPSDecision.parse(objHdrData, buf)); + else { + final Set decisions = new HashSet<>(); + decisions.add(COPSDecision.parse(objHdrData, buf)); + decisionMap.put(context, decisions); + } + break; + case MSG_INTEGRITY: + integrity = COPSIntegrity.parse(objHdrData, buf); + break; + default: + throw new COPSException("Bad Message format, unknown object type"); + } + dataStart += objHdrData.msgByteCount; + } + + return new COPSDecisionMsg(hdrData.header, clientHandle, error, decisionMap, integrity); + } +} \ No newline at end of file