/*
* Copyright (c) 2004 University of Murcia. All rights reserved.
* --------------------------------------------------------------
* For more information, please see .
*/
package org.umu.cops.prpep;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.umu.cops.common.COPSDebug;
import org.umu.cops.stack.COPSClientCloseMsg;
import org.umu.cops.stack.COPSContext;
import org.umu.cops.stack.COPSDecision;
import org.umu.cops.stack.COPSDecisionMsg;
import org.umu.cops.stack.COPSError;
import org.umu.cops.stack.COPSException;
import org.umu.cops.stack.COPSHandle;
import org.umu.cops.stack.COPSHeader;
import org.umu.cops.stack.COPSKAMsg;
import org.umu.cops.stack.COPSMsg;
import org.umu.cops.stack.COPSSyncStateMsg;
import org.umu.cops.stack.COPSTransceiver;
/**
* COPSPepConnection represents a PEP-PDP Connection Manager.
* Responsible for processing messages received from PDP.
*/
public class COPSPepConnection implements Runnable {
/** Socket connected to PDP */
protected Socket _sock;
/** Time to wait responses (milliseconds), default is 10 seconds */
protected int _responseTime;
/** COPS Client-type */
protected short _clientType;
/**
Accounting timer value (secs)
*/
protected short _acctTimer;
/**
Keep-alive timer value (secs)
*/
protected short _kaTimer;
/**
* Time of the latest keep-alive received
*/
protected Date _lastRecKa;
/**
Opcode of the latest message sent
*/
protected byte _lastmessage;
/**
Maps a COPS Client Handle to a Request State Manager
*/
protected Hashtable _managerMap;
// map < String(COPSHandle), COPSPepReqStateMan>;
/**
COPS error returned by PDP
*/
protected COPSError _error;
/**
* Creates a new PEP connection
* @param clientType PEP's client-type
* @param sock Socket connected to PDP
*/
public COPSPepConnection(short clientType, Socket sock) {
_clientType = clientType;
_sock = sock;
// Timers
_acctTimer = 0;
_kaTimer = 0;
_responseTime = 10000;
_lastmessage = COPSHeader.COPS_OP_CAT;
_managerMap = new Hashtable(20);
}
/**
* Gets the response time
* @return Response time value (msecs)
*/
public int getResponseTime() {
return _responseTime;
}
/**
* Gets the socket connected to the PDP
* @return Socket connected to PDP
*/
public Socket getSocket() {
return _sock;
}
/**
* Gets keep-alive timer
* @return Keep-alive timer value (secs)
*/
public short getKaTimer () {
return _kaTimer;
}
/**
* Gets accounting timer
* @return Accounting timer value (secs)
*/
public short getAcctTimer () {
return _acctTimer;
}
/**
* Gets active COPS handles
* @return An Enumeration holding all active handles
*/
protected Enumeration getHandles() {
return _managerMap.keys();
}
/**
* Gets all request state managers
* @return A Hashatable holding all request state managers
*/
protected Hashtable getReqStateMans() {
return _managerMap;
}
/**
* Checks whether the socket to the PDP is closed or not
* @return true if the socket is closed, false otherwise
*/
public boolean isClosed() {
return _sock.isClosed();
}
/**
* Closes the socket
*
* @throws java.io.IOException
*/
protected void close()
throws IOException {
_sock.close();
}
/**
* Gets the opcode of the lastest message sent
* @return Message opcode
*/
public byte getLastmessage() {
return _lastmessage;
}
/**
* Sets response time
* @param respTime Response time value (msecs)
*/
public void setResponseTime(int respTime) {
_responseTime = respTime;
};
/**
* Sets keep-alive timer
* @param kaTimer Keep-alive timer value (secs)
*/
public void setKaTimer (short kaTimer) {
_kaTimer = kaTimer;
}
/**
* Sets accounting timer
* @param acctTimer Accounting timer value (secs)
*/
public void setAcctTimer (short acctTimer) {
_acctTimer = acctTimer;
}
/**
* Message-processing loop
*/
public void run () {
Date _lastSendKa = new Date();
Date _lastSendAcc = new Date();
_lastRecKa = new Date();
try {
while (!_sock.isClosed()) {
if (_sock.getInputStream().available() != 0) {
_lastmessage = processMessage(_sock);
_lastRecKa = new Date();
}
// Keep Alive
if (_kaTimer > 0) {
// Timeout at PDP
int _startTime = (int) (_lastRecKa.getTime());
int cTime = (int) (new Date().getTime());
if ((int)(cTime - _startTime) > _kaTimer*1000) {
_sock.close();
// Notify all Request State Managers
notifyNoKAAllReqStateMan();
}
// Send to PEP
_startTime = (int) (_lastSendKa.getTime());
cTime = (int) (new Date().getTime());
if ((int)(cTime - _startTime) > ((_kaTimer*3/4) * 1000)) {
COPSHeader hdr = new COPSHeader(COPSHeader.COPS_OP_KA);
COPSKAMsg msg = new COPSKAMsg();
msg.add(hdr);
COPSTransceiver.sendMsg(msg, _sock);
_lastSendKa = new Date();
}
}
// Accounting
if (_acctTimer > 0) {
int _startTime = (int) (_lastSendAcc.getTime());
int cTime = (int) (new Date().getTime());
if ((int)(cTime - _startTime) > ((_acctTimer*3/4)*1000)) {
// Notify all Request State Managers
notifyAcctAllReqStateMan();
_lastSendAcc = new Date();
}
}
try {
Thread.sleep(500);
} catch (Exception e) {};
}
} catch (Exception e) {
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_SOCKET, e);
}
// connection closed by server
// COPSDebug.out(getClass().getName(),"Connection closed by server");
try {
_sock.close();
} catch (IOException e) {};
// Notify all Request State Managers
try {
notifyCloseAllReqStateMan();
} catch (COPSPepException e) {};
}
/**
* Gets a COPS message from the socket and processes it
* @param conn Socket connected to the PDP
* @return COPS message type
* @throws COPSPepException
* @throws COPSException
* @throws IOException
*/
protected byte processMessage(Socket conn)
throws COPSPepException, COPSException, IOException {
COPSMsg msg = COPSTransceiver.receiveMsg(conn);
if (msg.getHeader().isAClientClose()) {
handleClientCloseMsg(conn, msg);
return COPSHeader.COPS_OP_CC;
} else if (msg.getHeader().isADecision()) {
handleDecisionMsg(conn, msg);
return COPSHeader.COPS_OP_DEC;
} else if (msg.getHeader().isASyncStateReq()) {
handleSyncStateReqMsg(conn, msg);
return COPSHeader.COPS_OP_SSQ;
} else if (msg.getHeader().isAKeepAlive()) {
handleKeepAliveMsg(conn, msg);
return COPSHeader.COPS_OP_KA;
} else {
throw new COPSPepException("Message not expected (" + msg.getHeader().getOpCode() + ").");
}
}
/**
* Handle Client Close Message, close the passed connection
*
* @param conn a Socket
* @param msg a COPSMsg
*
*
* ::=
*
* []
*
* Not support []
*
*/
private void handleClientCloseMsg(Socket conn, COPSMsg msg) {
COPSClientCloseMsg cMsg = (COPSClientCloseMsg) msg;
_error = cMsg.getError();
// COPSDebug.out(getClass().getName(),"Got close request, closing connection " +
// conn.getInetAddress() + ":" + conn.getPort() + ":[Error " + _error.getDescription() + "]");
try {
// Support
if (cMsg.getIntegrity() != null) {
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOSUPPORTED,
"Unsupported objects (Integrity) to connection " + conn.getInetAddress());
}
conn.close();
} catch (Exception unae) { };
}
/**
* Method getError
*
* @return a COPSError
*
*/
protected COPSError getError() {
return _error;
}
/**
* Handle Keep Alive Message
*
* ::=
* []
*
* Not support []
*
* @param conn a Socket
* @param msg a COPSMsg
*
*/
private void handleKeepAliveMsg(Socket conn, COPSMsg msg) {
COPSKAMsg cMsg = (COPSKAMsg) msg;
// COPSDebug.out(getClass().getName(),"Get KAlive Msg");
try {
// Support
if (cMsg.getIntegrity() != null) {
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOSUPPORTED,
"Unsupported objects (Integrity) to connection " + conn.getInetAddress());
}
// should we do anything else?? ....
} catch (Exception unae) { };
}
/**
* Method handleDecisionMsg
*
* ::=
*
* *() |
* []
* ::=
*
* []
* ::= NULLFlag
* ::= NULLDecision | Install | Remove
* ::= < | >
* ::= *( )
* ::= *( | )
*
* Very important, this is actually being treated like this:
* ::= |
* ::= |
*
* @param conn a Socket
* @param msg a COPSMsg
*
*/
private void handleDecisionMsg(Socket conn, COPSMsg msg)
throws COPSPepException {
COPSDecisionMsg dMsg = (COPSDecisionMsg) msg;
COPSHandle handle = dMsg.getClientHandle();
Hashtable decisions = dMsg.getDecisions();
for (Enumeration e = decisions.keys() ; e.hasMoreElements() ;) {
COPSContext context = (COPSContext) e.nextElement();
Vector v = (Vector) decisions.get(context);
Enumeration ee = v.elements();
if (ee.hasMoreElements()) {
COPSDecision decision = (COPSDecision) ee.nextElement();
// Get the associated manager
COPSPepReqStateMan manager = (COPSPepReqStateMan) _managerMap.get(handle.getId().str());
if (manager == null)
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOEXPECTEDMSG);
// Check message type
if (decision.getFlags() == COPSDecision.F_REQSTATE) {
if (decision.isRemoveDecision())
// Delete Request State
manager.processDeleteRequestState(dMsg);
else
// Open new Request State
handleOpenNewRequestStateMsg(conn, handle);
} else
// Decision
manager.processDecision(dMsg);
}
}
}
/**
* Method handleOpenNewRequestStateMsg
*
* @param conn a Socket
* @param handle a COPSHandle
*
*/
private void handleOpenNewRequestStateMsg(Socket conn, COPSHandle handle)
throws COPSPepException {
COPSPepReqStateMan manager = (COPSPepReqStateMan) _managerMap.get(handle.getId().str());
if (manager == null)
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOEXPECTEDMSG);
manager.processOpenNewRequestState();
}
/**
* Method handleSyncStateReqMsg
*
* ::=
* []
* []
*
* @param conn a Socket
* @param msg a COPSMsg
*
*/
private void handleSyncStateReqMsg(Socket conn, COPSMsg msg)
throws COPSPepException {
COPSSyncStateMsg cMsg = (COPSSyncStateMsg) msg;
// COPSHandle handle = cMsg.getClientHandle();
// COPSHeader header = cMsg.getHeader();
// Support
if (cMsg.getIntegrity() != null) {
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOSUPPORTED,
"Unsupported objects (Integrity) to connection " + conn.getInetAddress());
}
COPSPepReqStateMan manager = (COPSPepReqStateMan) _managerMap.get(cMsg.getClientHandle().getId().str());
if (manager == null) {
COPSDebug.err(getClass().getName(), COPSDebug.ERROR_NOEXPECTEDMSG);
} else {
manager.processSyncStateRequest(cMsg);
}
}
/**
* Method createRequestState
*
* @param clientHandle a String
* @param process a COPSPepDataProcess
*
* @return a COPSPepmanager
*
* @throws COPSException
* @throws COPSPepException
*
*/
protected COPSPepReqStateMan addRequestState(String clientHandle, COPSPepDataProcess process)
throws COPSException, COPSPepException {
COPSPepReqStateMan manager = new COPSPepReqStateMan(_clientType,clientHandle);
if (_managerMap.get(clientHandle) != null)
throw new COPSPepException("Duplicate Handle, rejecting " + clientHandle);
manager.setDataProcess(process);
_managerMap.put(clientHandle,manager);
manager.initRequestState(_sock);
return manager;
}
/**
* Method deleteRequestState
*
* @param manager a COPSPepReqStateMan
*
* @throws COPSException
* @throws COPSPepException
*
*/
protected void deleteRequestState(COPSPepReqStateMan manager)
throws COPSException, COPSPepException {
manager.finalizeRequestState();
}
private void notifyCloseAllReqStateMan()
throws COPSPepException {
if (_managerMap.size() > 0) {
for (Enumeration e = _managerMap.keys() ; e.hasMoreElements() ;) {
String handle = (String) e.nextElement();
COPSPepReqStateMan man = (COPSPepReqStateMan) _managerMap.get(handle);
man.processClosedConnection(_error);
}
}
}
private void notifyNoKAAllReqStateMan()
throws COPSPepException {
if (_managerMap.size() > 0) {
for (Enumeration e = _managerMap.keys() ; e.hasMoreElements() ;) {
String handle = (String) e.nextElement();
COPSPepReqStateMan man = (COPSPepReqStateMan) _managerMap.get(handle);
man.processNoKAConnection();
}
}
}
private void notifyAcctAllReqStateMan()
throws COPSPepException {
if (_managerMap.size() > 0) {
for (Enumeration e = _managerMap.keys() ; e.hasMoreElements() ;) {
String handle = (String) e.nextElement();
COPSPepReqStateMan man = (COPSPepReqStateMan) _managerMap.get(handle);
man.processAcctReport();
}
}
}
}