Merge "Changed tests to leverage a dynamic port for testing COPS message marshalling...
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / prpep / COPSPepConnection.java
1 /*
2  * Copyright (c) 2004 University of Murcia.  All rights reserved.
3  * --------------------------------------------------------------
4  * For more information, please see <http://www.umu.euro6ix.org/>.
5  */
6
7 package org.umu.cops.prpep;
8
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.umu.cops.stack.*;
12 import org.umu.cops.stack.COPSDecision.Command;
13 import org.umu.cops.stack.COPSDecision.DecisionFlag;
14 import org.umu.cops.stack.COPSHeader.OPCode;
15
16 import java.io.IOException;
17 import java.net.Socket;
18 import java.util.Date;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentHashMap;
22
23 /**
24  * COPSPepConnection represents a PEP-PDP Connection Manager.
25  * Responsible for processing messages received from PDP.
26  */
27 public class COPSPepConnection implements Runnable {
28
29     public final static Logger logger = LoggerFactory.getLogger(COPSPepConnection.class);
30
31     /** Socket connected to PDP */
32     protected Socket _sock;
33
34     /** Time to wait responses (milliseconds), default is 10 seconds */
35     protected int _responseTime;
36
37     /** COPS Client-type */
38     protected short _clientType;
39
40     /**
41         Accounting timer value (secs)
42      */
43     protected short _acctTimer;
44
45     /**
46         Keep-alive timer value (secs)
47      */
48     protected short _kaTimer;
49
50     /**
51      *  Time of the latest keep-alive received
52      */
53     protected Date _lastRecKa;
54
55     /**
56         Maps a COPS Client Handle to a Request State Manager
57      */
58     protected final Map<String, COPSPepReqStateMan> _managerMap;
59
60     /**
61         COPS error returned by PDP
62      */
63     protected COPSError _error;
64
65     /**
66      * Creates a new PEP connection
67      * @param clientType    PEP's client-type
68      * @param sock          Socket connected to PDP
69      */
70     public COPSPepConnection(final short clientType, final Socket sock) {
71
72         _clientType = clientType;
73         _sock = sock;
74
75         // Timers
76         _acctTimer = 0;
77         _kaTimer = 0;
78         _responseTime = 10000;
79         _managerMap = new ConcurrentHashMap<>();
80     }
81
82     /**
83      * Gets the response time
84      * @return  Response time value (msecs)
85      */
86     public int getResponseTime() {
87         return _responseTime;
88     }
89
90     /**
91      * Gets the socket connected to the PDP
92      * @return  Socket connected to PDP
93      */
94     public Socket getSocket() {
95         return _sock;
96     }
97
98     /**
99      * Gets keep-alive timer
100      * @return  Keep-alive timer value (secs)
101      */
102     public short getKaTimer () {
103         return _kaTimer;
104     }
105
106     /**
107      * Gets accounting timer
108      * @return  Accounting timer value (secs)
109      */
110     public short getAcctTimer () {
111         return _acctTimer;
112     }
113
114     /**
115      * Checks whether the socket to the PDP is closed or not
116      * @return  <tt>true</tt> if the socket is closed, <tt>false</tt> otherwise
117      */
118     public boolean isClosed() {
119         return _sock.isClosed();
120     }
121
122     /**
123      * Closes the socket
124      *
125      * @throws java.io.IOException
126      */
127     protected void close()
128     throws IOException {
129         _sock.close();
130     }
131
132     /**
133      * Sets response time
134      * @param respTime  Response time value (msecs)
135      */
136     public void setResponseTime(int respTime) {
137         _responseTime = respTime;
138     }
139
140     /**
141      * Sets keep-alive timer
142      * @param kaTimer   Keep-alive timer value (secs)
143      */
144     public void setKaTimer (short kaTimer) {
145         _kaTimer = kaTimer;
146     }
147
148     /**
149      * Sets accounting timer
150      * @param acctTimer Accounting timer value (secs)
151      */
152     public void setAcctTimer (short acctTimer) {
153         _acctTimer = acctTimer;
154     }
155
156     /**
157      * Message-processing loop
158      */
159     public void run () {
160         Date _lastSendKa = new Date();
161         Date _lastSendAcc = new Date();
162         _lastRecKa = new Date();
163         try {
164             while (!_sock.isClosed()) {
165                 if (_sock.getInputStream().available() != 0) {
166                     processMessage(_sock);
167                     _lastRecKa = new Date();
168                 }
169
170                 // Keep Alive
171                 if (_kaTimer > 0) {
172                     // Timeout at PDP
173                     int _startTime = (int) (_lastRecKa.getTime());
174                     int cTime = (int) (new Date().getTime());
175
176                     if ((cTime - _startTime) > _kaTimer*1000) {
177                         _sock.close();
178                         // Notify all Request State Managers
179                         notifyNoKAAllReqStateMan();
180                     }
181
182                     // Send to PEP
183                     _startTime = (int) (_lastSendKa.getTime());
184                     cTime = (int) (new Date().getTime());
185
186                     if ((cTime - _startTime) > ((_kaTimer*3/4) * 1000)) {
187                         final COPSKAMsg msg = new COPSKAMsg(null);
188                         COPSTransceiver.sendMsg(msg, _sock);
189                         _lastSendKa = new Date();
190                     }
191                 }
192
193                 // Accounting
194                 if (_acctTimer > 0) {
195                     int _startTime = (int) (_lastSendAcc.getTime());
196                     int cTime = (int) (new Date().getTime());
197
198                     if ((cTime - _startTime) > ((_acctTimer*3/4)*1000)) {
199                         // Notify all Request State Managers
200                         notifyAcctAllReqStateMan();
201                         _lastSendAcc = new Date();
202                     }
203                 }
204
205                 try {
206                     Thread.sleep(500);
207                 } catch (Exception e) {
208                     logger.error("Exception thrown while sleeping", e);
209                 }
210             }
211         } catch (Exception e) {
212             logger.error("Error while processing socket messages", e);
213         }
214
215         // connection closed by server
216         // COPSDebug.out(getClass().getName(),"Connection closed by server");
217         try {
218             _sock.close();
219         } catch (IOException e) {
220             logger.error("Error closing socket", e);
221         }
222
223         // Notify all Request State Managers
224         try {
225             notifyCloseAllReqStateMan();
226         } catch (COPSPepException e) {
227             logger.error("Error closing state managers");
228         }
229     }
230
231     /**
232      * Gets a COPS message from the socket and processes it
233      * @param conn  Socket connected to the PDP
234      * @return COPS message type
235      * @throws COPSPepException
236      * @throws COPSException
237      * @throws IOException
238      */
239     protected byte processMessage(final Socket conn) throws COPSPepException, COPSException, IOException {
240         final COPSMsg msg = COPSTransceiver.receiveMsg(conn);
241
242         switch (msg.getHeader().getOpCode()) {
243             case CC:
244                 handleClientCloseMsg(conn, msg);
245                 return (byte)OPCode.CC.ordinal();
246             case DEC:
247                 handleDecisionMsg(conn, msg);
248                 return (byte)OPCode.DEC.ordinal();
249             case SSQ:
250                 handleSyncStateReqMsg(conn, msg);
251                 return (byte)OPCode.SSQ.ordinal();
252             case KA:
253                 handleKeepAliveMsg(conn, msg);
254                 return (byte)OPCode.KA.ordinal();
255             default:
256                 throw new COPSPepException("Message not expected (" + msg.getHeader().getOpCode() + ").");
257         }
258     }
259
260     /**
261      * Handle Client Close Message, close the passed connection
262      *
263      * @param    conn                a  Socket
264      * @param    msg                 a  COPSMsg
265      *
266      *
267      * <Client-Close> ::= <Common Header>
268      *                      <Error>
269      *                      [<Integrity>]
270      *
271      * Not support [<Integrity>]
272      *
273      */
274     private void handleClientCloseMsg(Socket conn, COPSMsg msg) {
275         COPSClientCloseMsg cMsg = (COPSClientCloseMsg) msg;
276         _error = cMsg.getError();
277
278         // COPSDebug.out(getClass().getName(),"Got close request, closing connection " +
279         //  conn.getInetAddress() + ":" + conn.getPort() + ":[Error " + _error.getDescription() + "]");
280
281         try {
282             // Support
283             if (cMsg.getIntegrity() != null) {
284                 logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
285             }
286
287             conn.close();
288         } catch (Exception unae) {
289             logger.error("Unexpected exception closing connection", unae);
290         }
291     }
292
293     /**
294      * Method getError
295      *
296      * @return   a COPSError
297      *
298      */
299     protected COPSError getError()  {
300         return _error;
301     }
302
303     /**
304      * Handle Keep Alive Message
305      *
306      * <Keep-Alive> ::= <Common Header>
307      *                  [<Integrity>]
308      *
309      * Not support [<Integrity>]
310      *
311      * @param    conn                a  Socket
312      * @param    msg                 a  COPSMsg
313      *
314      */
315     private void handleKeepAliveMsg(Socket conn, COPSMsg msg) {
316         COPSKAMsg cMsg = (COPSKAMsg) msg;
317
318         // COPSDebug.out(getClass().getName(),"Get KAlive Msg");
319
320         try {
321             // Support
322             if (cMsg.getIntegrity() != null) {
323                 logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
324             }
325
326             // should we do anything else?? ....
327
328         } catch (Exception unae) {
329             logger.error("Unexpected exception while writing COPS data", unae);
330         }
331     }
332
333     /**
334      * Method handleDecisionMsg
335      *
336      * <Decision Message> ::= <Common Header: Flag SOLICITED>
337      *                          <Client Handle>
338      *                          *(<Decision>) | <Error>
339      *                          [<Integrity>]
340      * <Decision> ::= <Context>
341      *                  <Decision: Flags>
342      *                  [<Named Decision Data: Provisioning>]
343      * <Decision: Flags> ::= <Command-Code> NULLFlag
344      * <Command-Code> ::= NULLDecision | Install | Remove
345      * <Named Decision Data> ::= <<Install Decision> | <Remove Decision>>
346      * <Install Decision> ::= *(<PRID> <EPD>)
347      * <Remove Decision> ::= *(<PRID> | <PPRID>)
348      *
349      * Very important, this is actually being treated like this:
350      * <Install Decision> ::= <PRID> | <EPD>
351      * <Remove Decision> ::= <PRID> | <PPRID>
352      *
353      * @param    conn                a  Socket
354      * @param    msg                 a  COPSMsg
355      *
356      */
357     private void handleDecisionMsg(final Socket conn, final COPSMsg msg) throws COPSPepException {
358         final COPSDecisionMsg dMsg = (COPSDecisionMsg) msg;
359         final COPSHandle handle = dMsg.getClientHandle();
360         final Map<COPSContext, Set<COPSDecision>> decisions = dMsg.getDecisions();
361
362         for (final Set<COPSDecision> copsDecisions: decisions.values()) {
363             for (final COPSDecision decision : copsDecisions) {
364                 // Get the associated manager
365                 final COPSPepReqStateMan manager = _managerMap.get(handle.getId().str());
366                 if (manager == null)
367                     logger.warn("Unable to find state manager with key - " + handle.getId().str());
368
369                 // Check message type
370                 // TODO FIXME - Use of manager object could result in a NPE
371                 if (decision.getFlag().equals(DecisionFlag.REQSTATE)) {
372                     if (decision.getCommand().equals(Command.REMOVE))
373                         // Delete Request State
374                         manager.processDeleteRequestState(dMsg);
375                     else
376                         // Open new Request State
377                         handleOpenNewRequestStateMsg(conn, handle);
378                 } else
379                     // Decision
380                     manager.processDecision(dMsg);
381             }
382         }
383     }
384
385
386     /**
387      * Method handleOpenNewRequestStateMsg
388      *
389      * @param    conn                a  Socket
390      * @param    handle              a  COPSHandle
391      *
392      */
393     private void handleOpenNewRequestStateMsg(Socket conn, COPSHandle handle) throws COPSPepException {
394
395         COPSPepReqStateMan manager = _managerMap.get(handle.getId().str());
396         if (manager == null)
397             logger.warn("Unable to find state manager with key - " + handle.getId().str());
398
399         // TODO FIXME - Use of manager object could result in a NPE
400         manager.processOpenNewRequestState();
401     }
402
403     /**
404      * Method handleSyncStateReqMsg
405      *
406      *              <Synchronize State> ::= <Common Header>
407      *                                      [<Client Handle>]
408      *                                      [<Integrity>]
409      *
410      * @param    conn                a  Socket
411      * @param    msg                 a  COPSMsg
412      *
413      */
414     private void handleSyncStateReqMsg(Socket conn, COPSMsg msg) throws COPSPepException {
415         COPSSyncStateMsg cMsg = (COPSSyncStateMsg) msg;
416
417         // Support
418         if (cMsg.getIntegrity() != null) {
419             logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
420         }
421
422         COPSPepReqStateMan manager = _managerMap.get(cMsg.getClientHandle().getId().str());
423         if (manager == null) {
424             logger.warn("Unable to find state manager with key - " + cMsg.getClientHandle().getId().str());
425         } else {
426             manager.processSyncStateRequest(cMsg);
427         }
428     }
429
430     /**
431      * Method createRequestState
432      *
433      * @param    clientHandle             a  String
434      * @param    process                  a  COPSPepDataProcess
435      *
436      * @return   a COPSPepmanager
437      *
438      * @throws   COPSException
439      * @throws   COPSPepException
440      *
441      */
442     protected COPSPepReqStateMan addRequestState(String clientHandle, COPSPepDataProcess process) throws COPSException,
443             COPSPepException {
444         COPSPepReqStateMan manager = new COPSPepReqStateMan(_clientType, clientHandle);
445         if (_managerMap.get(clientHandle) != null)
446             throw new COPSPepException("Duplicate Handle, rejecting " + clientHandle);
447
448         manager.setDataProcess(process);
449         _managerMap.put(clientHandle,manager);
450         manager.initRequestState(_sock);
451         return manager;
452     }
453
454     /**
455      * Method deleteRequestState
456      *
457      * @param    manager             a  COPSPepReqStateMan
458      *
459      * @throws   COPSException
460      * @throws   COPSPepException
461      *
462      */
463     protected void deleteRequestState(COPSPepReqStateMan manager) throws COPSException, COPSPepException {
464         manager.finalizeRequestState();
465     }
466
467     private void notifyCloseAllReqStateMan() throws COPSPepException {
468         for (final COPSPepReqStateMan man: _managerMap.values()) {
469             man.processClosedConnection(_error);
470         }
471     }
472
473     private void notifyNoKAAllReqStateMan() throws COPSPepException {
474         for (final COPSPepReqStateMan man: _managerMap.values()) {
475             man.processNoKAConnection();
476         }
477     }
478
479     private void notifyAcctAllReqStateMan() throws COPSPepException {
480         for (final COPSPepReqStateMan man: _managerMap.values()) {
481             man.processAcctReport();
482         }
483     }
484
485 }
486