/* * Copyright (c) 2003 University of Murcia. All rights reserved. * -------------------------------------------------------------- * For more information, please see . */ package org.umu.cops.stack; import org.umu.cops.stack.COPSObjHeader.CNum; import org.umu.cops.stack.COPSObjHeader.CType; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * COPS Client Specific Information Object (RFC 2748) * * The various types of this object are required for requests, and used * in reports and opens when required. It contains client-type specific * information. * * C-Num = 9, * * C-Type = 1, Signaled ClientSI. * * Variable-length field. All objects/attributes specific to a client's * signaling protocol or internal state are encapsulated within one or * more signaled Client Specific Information Objects. The format of the * data encapsulated in the ClientSI object is determined by the * client-type. * * C-Type = 2, Named ClientSI. * * Variable-length field. Contains named configuration information * useful for relaying specific information about the PEP, a request, or * configured state to the PDP server. */ public class COPSClientSI extends COPSObjBase { private final static Map VAL_TO_CSI = new ConcurrentHashMap<>(); static { VAL_TO_CSI.put(CSIType.NA.ordinal(), CSIType.NA); VAL_TO_CSI.put(CSIType.SIGNALED.ordinal(), CSIType.SIGNALED); VAL_TO_CSI.put(CSIType.NAMED.ordinal(), CSIType.NAMED); } /** * This value is not being used here but stored only for clarity as it is being mapped directly to the * ordinal value of the CType */ private final CSIType _csiType; /** * The payload data */ private final COPSData _data; /** * Bytes to add to outbound payload to ensure the length is divisible by 4 bytes */ private final COPSData _padding; /** * Constructor generally used for sending messages * @param ctype - the CSIType * @param data - the data * @throws java.lang.IllegalArgumentException */ public COPSClientSI(final CNum cnum, final CType ctype, final COPSData data) { /* The CSIType does not map directly to the CType, therefore the hook to map to a CType below is required to ensure the header value outputs the correct value when streamed */ this(new COPSObjHeader(cnum, ctype), data); } /** * Constructor generally used for sending messages * @param csitype - the CSIType * @param data - the data * @throws java.lang.IllegalArgumentException */ public COPSClientSI(final CSIType csitype, final COPSData data) { /* The CSIType does not map directly to the CType, therefore the hook to map to a CType below is required to ensure the header value outputs the correct value when streamed */ this(new COPSObjHeader(CNum.CSI, COPSObjHeader.VAL_TO_CTYPE.get(csitype.ordinal())), data); } /** * Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the * COPSObjHeader information is known * @param hdr - the object header * @param data - the data * @throws java.lang.IllegalArgumentException */ protected COPSClientSI(final COPSObjHeader hdr, final COPSData data) { super(hdr); if (VAL_TO_CSI.get(hdr.getCType().ordinal()) == null) { // TODO - determine if this is a good default value??? _csiType = CSIType.NAMED; } else { _csiType = VAL_TO_CSI.get(hdr.getCType().ordinal()); } if (!hdr.getCNum().equals(CNum.CSI) && !hdr.getCNum().equals(CNum.DEC)) throw new IllegalArgumentException("CNum must be equal to " + CNum.CSI + " or " + CNum.DEC); if (_csiType == null || _csiType.equals(CSIType.NA)) throw new IllegalArgumentException("Invalid CSIType"); if (data == null) throw new IllegalArgumentException("Data must not be null"); _data = data; if ((_data.length() % 4) != 0) { final int padLen = 4 - (_data.length() % 4); _padding = COPSObjectParser.getPadding(padLen); } else { _padding = new COPSData(); } } /** * Returns the CSIType * @return - the type */ public CSIType getCsiType() { return _csiType; } /** * Returns the data * @return - the data */ public COPSData getData() { return _data; } @Override /* The super says protected but this needs to be public due to usage in COPSDecisionMsgEX.java which is currently calling this method. */ public int getDataLength() { return _data.length() + _padding.length(); } @Override public void writeBody(final Socket socket) throws IOException { COPSUtil.writeData(socket, _data.getData(), _data.length()); if (_padding.length() > 0) COPSUtil.writeData(socket, _padding.getData(), _padding.length()); } @Override public void dumpBody(final OutputStream os) throws IOException { os.write(("CSI-type: " + _csiType + "\n").getBytes()); os.write(("client-SI: " + _data.str() + "\n").getBytes()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof COPSClientSI)) { return false; } if (!super.equals(o)) { return false; } final COPSClientSI that = (COPSClientSI) o; return _data.equals(that._data) && _padding.equals(that._padding) || COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (_data != null ? _data.hashCode() : 0); result = 31 * result + (_padding != null ? _padding.hashCode() : 0); return result; } /** * Parses bytes to return a COPSClientSI object * @param objHdrData - the associated header * @param dataPtr - the data to parse * @return - the object * @throws java.lang.IllegalArgumentException */ public static COPSClientSI parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) { short dLen = (short) (objHdrData.msgByteCount - 4); return new COPSClientSI(objHdrData.header, new COPSData(dataPtr, 4, dLen)); } /** * The different CSI types. NA does not exist but is a placeholder for 0 as the ordinal values will be used * to determine which type for marshalling */ public enum CSIType { NA, SIGNALED, NAMED } }