2 * Copyright (c) 2004 University of Murcia. All rights reserved.
3 * --------------------------------------------------------------
4 * For more information, please see <http://www.umu.euro6ix.org/>.
7 package org.umu.cops.prpdp;
9 import org.pcmm.objects.MMVersionInfo;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import org.umu.cops.stack.*;
13 import org.umu.cops.stack.COPSError.ErrorTypes;
14 import org.umu.cops.stack.COPSHeader.OPCode;
16 import java.io.IOException;
17 import java.net.InetAddress;
18 import java.net.Socket;
21 * Core PDP agent for managing the connection to one PDP.
23 public class COPSPdpAgent {
25 private static final Logger logger = LoggerFactory.getLogger(COPSPdpAgent.class);
27 /** Well-known port for COPS */
28 public static final int WELL_KNOWN_PDP_PORT = 3288;
29 /** Default keep-alive timer value (secs) */
30 public static final short KA_TIMER_VALUE = 30;
31 /** Default accounting timer value (secs) */
32 public static final short ACCT_TIMER_VALUE = 0;
37 protected final String _host;
42 protected final int _serverPort;
45 * Client-type of connecting PEP
47 protected final short _clientType;
50 * Accounting timer (secs)
52 protected final short _acctTimer;
55 * Keep-alive timer (secs)
57 protected final short _kaTimer;
60 * Policy data processing object
62 protected final COPSPdpDataProcess _process;
64 // Next two attributes are initialized when connected
66 * The Socket connection to the PEP
68 protected transient Socket _socket;
73 protected transient COPSHandle _handle;
75 // Next three attributes are initialized after the client accepts
77 * Holds the last PEP ID processed
79 protected transient COPSPepId _pepId;
82 * the PDP connection connection
84 protected transient COPSPdpConnection _pdpConn;
87 * The handle to the tread accepting messages from the PDP
89 protected transient Thread _thread;
94 * @param host PDP agent host name
95 * @param port Port to listen to
96 * @param clientType COPS Client-type
97 * @param process Object to perform policy data processing
99 public COPSPdpAgent(final String host, final int port, final short clientType, final COPSPdpDataProcess process) {
101 this._serverPort = port;
103 this._kaTimer = KA_TIMER_VALUE;
104 this._acctTimer = ACCT_TIMER_VALUE;
106 this._clientType = clientType;
107 this._process = process;
111 * Returns handle after connect() has successfully been executed
112 * @return - the handle
114 public COPSHandle getClientHandle() {
119 * Returns handle after connect() has successfully been executed
120 * @return - the handle
122 public Socket getSocket() {
128 * @throws java.net.UnknownHostException
129 * @throws java.io.IOException
130 * @throws COPSException
132 public void connect() throws IOException, COPSException {
133 // Create Socket and send OPN
134 final InetAddress addr = InetAddress.getByName(_host);
135 _socket = new Socket(addr, _serverPort);
136 logger.info("PDP Socket Opened. Waiting to receive client-open message");
137 final COPSMsg msg = COPSTransceiver.receiveMsg(_socket);
138 logger.debug("Message received of type - " + msg.getHeader().getOpCode());
139 if (msg.getHeader().getOpCode().equals(OPCode.OPN)) {
140 handleClientOpenMsg(_socket, msg);
144 } catch (Exception ex) {
145 logger.error("Unexpected error closing socket", ex);
151 * Disconnects a PEP and stops the listener thread
152 * @param error COPS Error to be reported as a reason
153 * @throws COPSException
154 * @throws IOException
156 public void disconnect(final COPSError error) throws COPSException, IOException {
157 if (_pdpConn != null) {
158 sendCloseMessage(_socket, error.getErrCode(), error.getErrSubCode(), "Disconnecting from PDP requested");
161 logger.warn("Unable to locate PDP connection. Cannot close");
163 if (_thread != null) _thread.interrupt();
164 else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
166 if (_socket.isConnected()) _socket.close();
174 * Requests a COPS sync for a PEP
175 * @throws COPSException
176 * @throws COPSPdpException
178 public void sync() throws COPSException {
179 if (_pdpConn != null) _pdpConn.syncAllRequestState();
180 else logger.warn("Unable to sync, not connected to a PEP");
184 * Handles a COPS client-open message and sets the _pepId, _handle, _pdpConn, & _thread objects in the process
185 * as well as starts the PDP connection thread for receiving other COPS messages from the PDP
186 * @param conn Socket to the PEP
187 * @param msg <tt>COPSMsg</tt> holding the client-open message
188 * @throws COPSException
189 * @throws IOException
191 protected void handleClientOpenMsg(final Socket conn, final COPSMsg msg) throws COPSException, IOException {
192 logger.info("Processing client open message");
194 if (_pepId != null) {
195 throw new COPSException("Connection already opened");
198 final COPSClientOpenMsg cMsg = (COPSClientOpenMsg) msg;
199 _pepId = cMsg.getPepId();
201 // Validate Client Type
202 if (msg.getHeader().getClientType() != _clientType) {
203 sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE, ErrorTypes.NA,
204 "Unsupported client type");
207 // PEPId is mandatory
208 if (_pepId == null) {
209 sendCloseMessage(conn, ErrorTypes.MANDATORY_OBJECT_MISSING, ErrorTypes.NA,
210 "Mandatory COPS object missing (PEPId)");
213 // TODO - Determine if I should be checking for the PDPAddress and Integrity objects on the message too???
216 if ( (cMsg.getClientSI() != null) || (cMsg.getPdpAddress() != null) || (cMsg.getIntegrity() != null)) {
217 sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE,
218 "Unsupported objects (ClientSI, PdpAddress, Integrity)");
222 if ((cMsg.getClientSI() == null) ) {
223 sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA,
224 "Unsupported objects (PdpAddress, Integrity)");
226 final MMVersionInfo _mminfo = new MMVersionInfo(cMsg.getClientSI().getData().getData());
227 logger.debug("CMTS sent MMVersion info : major:" + _mminfo.getMajorVersionNB() + " minor:" +
228 _mminfo.getMinorVersionNB());
231 acceptConnection(conn);
233 _handle = handleAcceptResponse(conn);
234 if (_handle != null) {
235 // Connection accepted
236 _pdpConn = setputPdpConnection(conn, _handle);
237 _thread = new Thread(_pdpConn, "PDP Agent for PEP ID " + _pepId.getData().str());
240 throw new COPSException("Unable to connect to PDP");
245 * Creates and sends a client close message
246 * @param conn - the socket connection
247 * @param errorType - the error type to send
248 * @param msg - the error message to log
249 * @throws COPSException
251 private void sendCloseMessage(final Socket conn, final ErrorTypes errorType, final ErrorTypes errorSubType,
253 throws COPSException {
254 final COPSClientCloseMsg closeMsg = new COPSClientCloseMsg(_clientType,
255 new COPSError(errorType, errorSubType), null, null);
257 closeMsg.writeData(conn);
258 } catch (IOException unae) {
259 logger.error("Exception writing data", unae);
262 throw new COPSException(msg);
266 * Sends a client-accept message back to the PDP
267 * @param conn - the socket connection to the PDP
268 * @throws IOException
270 private void acceptConnection(final Socket conn) throws IOException {
271 final COPSClientAcceptMsg acceptMsg;
273 acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer),
274 new COPSAcctTimer(_acctTimer), null);
276 acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer) ,null, null);
277 acceptMsg.writeData(conn);
281 * Waits for the response back from the PDP and handles it appropriately. When successful, the handle to the
282 * client is returned.
283 * @param conn - the socket connection to the PDP
284 * @return - the handle or null if not successful
286 private COPSHandle handleAcceptResponse(final Socket conn) {
288 logger.debug("handleClientOpenMsg() - Waiting to receive message");
289 final COPSMsg rmsg = COPSTransceiver.receiveMsg(conn);
290 logger.debug("Received message of type - " + rmsg.getHeader().getOpCode());
292 if (rmsg.getHeader().getOpCode().equals(OPCode.CC)) {
293 logger.info("Received client-close message");
294 sendCloseMessage(conn, ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA, "Received client-close message");
298 if (rmsg.getHeader().getOpCode().equals(OPCode.REQ)) {
299 final COPSReqMsg rMsg = (COPSReqMsg) rmsg;
300 return rMsg.getClientHandle();
302 sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA, "Received unknown object");
306 } catch (Exception e) {
307 logger.error("Error COPSTransceiver.receiveMsg", e);
313 * Creates the PDP connection object
314 * @param conn - the socket connection to the PDP
315 * @param handle - the client's handle
316 * @return - the PDP connection object
318 protected COPSPdpConnection setputPdpConnection(final Socket conn, final COPSHandle handle) {
319 logger.debug("PDPCOPSConnection");
320 final COPSPdpConnection pdpConn = new COPSPdpConnection(_pepId, conn, _process, _kaTimer, _acctTimer);
322 // XXX - handleRequestMsg
323 // XXX - check handle is valid
324 final COPSPdpReqStateMan man = new COPSPdpReqStateMan(_clientType, handle, _process);
325 pdpConn.addStateMan(handle, man);
327 man.initRequestState(conn);
328 } catch (COPSException unae) {
329 logger.error("Unexpected error initializing state", unae);
331 // XXX - End handleRequestMsg
333 logger.info("Starting PDP connection thread to - " + _host);