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