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