/*
* Copyright (c) 2004 University of Murcia. All rights reserved.
* --------------------------------------------------------------
* For more information, please see .
*/
package org.umu.cops.prpep;
import org.pcmm.gates.impl.GateID;
import org.pcmm.gates.impl.PCMMGateReq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.umu.cops.COPSStateMan;
import org.umu.cops.stack.*;
import org.umu.cops.stack.COPSDecision.DecisionFlag;
import org.umu.cops.stack.COPSObjHeader.CNum;
import org.umu.cops.stack.COPSObjHeader.CType;
import org.umu.cops.stack.COPSReportType.ReportType;
import java.io.IOException;
import java.net.Socket;
import java.util.*;
/**
* COPSPepReqStateMan manages Request State using Client Handle (RFC 2748 pag. 21)
* in PEP.
*
* The client handle is used to identify a unique request state for a
* single PEP per client-type. Client handles are chosen by the PEP and
* are opaque to the PDP. The PDP simply uses the request handle to
* uniquely identify the request state for a particular Client-Type over
* a particular TCP connection and generically tie its decisions to a
* corresponding request. Client handles are initiated in request
* messages and are then used by subsequent request, decision, and
* report messages to reference the same request state. When the PEP is
* ready to remove a local request state, it will issue a delete message
* to the PDP for the corresponding client handle. A handle MUST be
* explicitly deleted by the PEP before it can be used by the PEP to
* identify a new request state. Handles referring to different request
* states MUST be unique within the context of a particular TCP
* connection and client-type.
*
* @version COPSPepReqStateMan.java, v 2.00 2004
*
*/
public class COPSPepReqStateMan extends COPSStateMan {
private final static Logger logger = LoggerFactory.getLogger(COPSPepReqStateMan.class);
/**
The PolicyDataProcess is used to process policy data in the PEP
*/
protected final COPSPepDataProcess _process;
/**
The Msg Sender is used to send COPS messages
*/
protected transient COPSPepMsgSender _sender;
/**
* Sync State
*/
protected transient boolean _syncState;
/**
* Create a State Request Manager
*
* @param clientHandle a Client Handle
*
*/
public COPSPepReqStateMan(final short clientType, final COPSHandle clientHandle, final COPSPepDataProcess process) {
super(clientType, clientHandle);
this._process = process;
_syncState = true;
}
/**
* Init Request State
*
* @throws COPSPepException
*
*/
protected void initRequestState(final Socket sock) throws COPSException {
// Inits an object for sending COPS messages to the PDP
_sender = new COPSPepMsgSender(_clientType, _handle, sock);
// If an object for retrieving PEP features exists,
// use it for retrieving them
final Map clientSIs;
if (_process != null)
clientSIs = _process.getClientData(this);
else
clientSIs = new HashMap<>();
// Send the request
// TODO - do we really want to send when this is empty???
_sender.sendRequest(clientSIs);
// Initial state
_status = Status.ST_INIT;
}
/**
* Finalize Request State
*
* @throws COPSPepException
*
*/
protected void finalizeRequestState() throws COPSException {
_sender.sendDeleteRequest();
_status = Status.ST_FINAL;
}
/**
* Process the message Decision
*
* @param dMsg a COPSDecisionMsg
*
* @throws COPSPepException
*
*/
protected void processDecision(final COPSDecisionMsg dMsg, final Socket socket) throws COPSException {
logger.info("Processing decision message - " + dMsg);
final Map> decisions = dMsg.getDecisions();
final Map removeDecs = new HashMap<>();
final Map installDecs = new HashMap<>();
for (final Set copsDecisions: decisions.values()) {
final COPSDecision cmddecision = copsDecisions.iterator().next();
String prid = "";
switch (cmddecision.getCommand()) {
case INSTALL:
// TODO - break up this block
for (final COPSDecision decision : copsDecisions) {
if (decision.getData().getData().length != 0) {
final COPSPrObjBase obj = new COPSPrObjBase(decision.getData().getData());
switch (obj.getSNum()) {
case COPSPrObjBase.PR_PRID:
prid = obj.getData().str();
break;
case COPSPrObjBase.PR_EPD:
installDecs.put(prid, obj.getData().str());
break;
}
}
if (decision.getFlag().equals(DecisionFlag.REQERROR)) {
// This is assuming a gate set right or wrong
if (dMsg.getDecisions().size() == 1 && dMsg.getDecSI() != null) {
final PCMMGateReq gateReq = new PCMMGateReq(dMsg.getDecSI().getData().getData());
// TODO - Check and/or Set state here
// Gate ADD gateReq.getTrafficProfile() != null
// Gate REMOVE gateReq.getTrafficProfile() == null
// final String gateName = trafficProfile.getData().str();
// final Direction gateDir = gateReq.getGateSpec().getDirection();
final boolean success = true;
// Set response
final List data = new ArrayList<>();
for (final byte val : gateReq.getTransactionID().getAsBinaryArray())
data.add(val);
for (final byte val : gateReq.getAMID().getAsBinaryArray())
data.add(val);
for (final byte val : gateReq.getSubscriberID().getAsBinaryArray())
data.add(val);
// Assign a gate ID
final GateID gateID = new GateID();
gateID.setGateID(UUID.randomUUID().hashCode());
for (final byte val : gateID.getAsBinaryArray())
data.add(val);
final byte[] csiArr = new byte[data.size()];
for (int i = 0; i < data.size(); i++) {
csiArr[i] = data.get(i);
}
final COPSClientSI si = new COPSClientSI(CNum.CSI, CType.DEF, new COPSData(csiArr, 0, csiArr.length));
final COPSReportMsg reportMsg;
// TODO FIXME - success is always true
if (success) {
reportMsg = new COPSReportMsg(_clientType, getClientHandle(),
new COPSReportType(ReportType.SUCCESS), si, null);
} else {
reportMsg = new COPSReportMsg(_clientType, getClientHandle(),
new COPSReportType(ReportType.FAILURE), si, null);
}
try {
reportMsg.writeData(socket);
} catch (IOException e) {
throw new COPSPepException("Error writing gate set SUCCESS Report", e);
}
}
}
}
break;
case REMOVE:
for (final COPSDecision decision : copsDecisions) {
final COPSPrObjBase obj = new COPSPrObjBase(decision.getData().getData());
switch (obj.getSNum()) {
case COPSPrObjBase.PR_PRID:
prid = obj.getData().str();
break;
case COPSPrObjBase.PR_EPD:
removeDecs.put(prid, obj.getData().str());
break;
default:
break;
}
}
break;
}
}
//** Apply decisions to the configuration
// TODO - why is this collection never getting populated???
final Map errorDecs = new HashMap<>();
_process.setDecisions(this, removeDecs, installDecs, errorDecs);
_status = Status.ST_DECS;
if (_process.isFailReport(this)) {
// COPSDebug.out(getClass().getName(),"Sending FAIL Report\n");
_sender.sendFailReport(_process.getReportData(this));
} else {
// COPSDebug.out(getClass().getName(),"Sending SUCCESS Report\n");
_sender.sendSuccessReport(_process.getReportData(this));
}
_status = Status.ST_REPORT;
if (!_syncState) {
_sender.sendSyncComplete();
_syncState = true;
_status = Status.ST_SYNCALL;
}
}
/**
* Process the message NewRequestState
*
* @throws COPSPepException
*
*/
protected void processOpenNewRequestState() throws COPSPepException {
if (_process != null)
_process.newRequestState(this);
_status = Status.ST_NEW;
}
/**
* Process the message DeleteRequestState
*
* @param dMsg a COPSDecisionMsg
*
* @throws COPSPepException
*
*/
protected void processDeleteRequestState(final COPSDecisionMsg dMsg) throws COPSPepException {
if (_process != null)
_process.closeRequestState(this);
_status = Status.ST_DEL;
}
/**
* Process the message SycnStateRequest.
* The message SycnStateRequest indicates that the remote PDP
* wishes the client (which appears in the common header)
* to re-send its state.
*
* @param ssMsg a COPSSyncStateMsg
*
* @throws COPSPepException
*
*/
protected void processSyncStateRequest(final COPSSyncStateMsg ssMsg) throws COPSPepException {
_syncState = false;
// If an object for retrieving PEP features exists,
// use it for retrieving them
final Map clientSIs;
if (_process != null)
clientSIs = _process.getClientData(this);
else
clientSIs = new HashMap<>();
// Send request
// TODO - do we really want to send the request when the map is empty???
_sender.sendRequest(clientSIs);
_status = Status.ST_SYNC;
}
protected void processClosedConnection(final COPSError error) throws COPSPepException {
if (_process != null)
_process.notifyClosedConnection(this, error);
_status = Status.ST_CCONN;
}
protected void processNoKAConnection() throws COPSPepException {
if (_process != null)
_process.notifyNoKAliveReceived(this);
_status = Status.ST_NOKA;
}
protected void processAcctReport() throws COPSPepException {
final Map report;
if (_process != null) report = _process.getAcctData(this);
else report = new HashMap<>();
// TODO - do we really want to send when the map is empty???
_sender.sendAcctReport(report);
_status = Status.ST_ACCT;
}
}