Created abstract super class for all COPS state managers called COPSStateMan as each...
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / ospep / COPSPepOSConnection.java
1 package org.umu.cops.ospep;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.umu.cops.stack.*;
6 import org.umu.cops.stack.COPSHeader.OPCode;
7
8 import java.io.IOException;
9 import java.net.Socket;
10 import java.util.Date;
11 import java.util.Hashtable;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.concurrent.ConcurrentHashMap;
15
16 /**
17  * COPSPepConnection represents a PEP-PDP Connection Manager.
18  * Responsible for processing messages received from PDP.
19  */
20 public class COPSPepOSConnection implements Runnable {
21
22     public final static Logger logger = LoggerFactory.getLogger(COPSPepOSConnection.class);
23
24     /** Socket connected to PDP */
25     protected Socket _sock;
26
27     /** Time to wait responses (milliseconds), default is 10 seconds */
28     protected final int _responseTime;
29
30     /** COPS Client-type */
31     protected short _clientType;
32
33     /**
34         Accounting timer value (secs)
35      */
36     protected transient short _acctTimer;
37
38     /**
39         Keep-alive timer value (secs)
40      */
41     protected transient short _kaTimer;
42
43     /**
44      *  Time of the latest keep-alive received
45      */
46     protected Date _lastRecKa;
47
48     /**
49         Maps a COPS Client Handle to a Request State Manager
50      */
51     protected final Map<String, COPSPepOSReqStateMan> _managerMap;
52
53     /**
54         COPS error returned by PDP
55      */
56     protected COPSError _error;
57
58     /**
59      * Creates a new PEP connection
60      * @param clientType    PEP's client-type
61      * @param sock          Socket connected to PDP
62      */
63     public COPSPepOSConnection(final short clientType, final Socket sock) {
64         _clientType = clientType;
65         _sock = sock;
66
67         // Timers
68         _acctTimer = 0;
69         _kaTimer = 0;
70         _responseTime = 10000;
71         _managerMap = new ConcurrentHashMap<>();
72     }
73
74     /**
75      * Gets the response time
76      * @return  Response time value (msecs)
77      */
78     public int getResponseTime() {
79         return _responseTime;
80     }
81
82     /**
83      * Gets the socket connected to the PDP
84      * @return  Socket connected to PDP
85      */
86     public Socket getSocket() {
87         return _sock;
88     }
89
90     /**
91      * Gets keep-alive timer
92      * @return  Keep-alive timer value (secs)
93      */
94     public short getKaTimer () {
95         return _kaTimer;
96     }
97
98     /**
99      * Gets accounting timer
100      * @return  Accounting timer value (secs)
101      */
102     public short getAcctTimer () {
103         return _acctTimer;
104     }
105
106     /**
107      * Gets all request state managers
108      * @return  A <tt>Hashatable</tt> holding all request state managers
109      * TODO - change the return to Map
110      */
111     protected Hashtable getReqStateMans() {
112         return new Hashtable(_managerMap);
113     }
114
115     /**
116      * Checks whether the socket to the PDP is closed or not
117      * @return  <tt>true</tt> if the socket is closed, <tt>false</tt> otherwise
118      */
119     public boolean isClosed() {
120         return _sock.isClosed();
121     }
122
123     /**
124      * Closes the socket
125      *
126      * @throws java.io.IOException
127      */
128     protected void close() throws IOException {
129         _sock.close();
130     }
131
132     /**
133      * Sets keep-alive timer
134      * @param kaTimer   Keep-alive timer value (secs)
135      */
136     public void setKaTimer(short kaTimer) {
137         _kaTimer = kaTimer;
138     }
139
140     /**
141      * Sets accounting timer
142      * @param acctTimer Accounting timer value (secs)
143      */
144     public void setAcctTimer(short acctTimer) {
145         _acctTimer = acctTimer;
146     }
147
148     /**
149      * Message-processing loop
150      */
151     public void run () {
152         Date _lastSendKa = new Date();
153         Date _lastSendAcc = new Date();
154         _lastRecKa = new Date();
155
156         try {
157             while (!_sock.isClosed()) {
158                 if (_sock.getInputStream().available() != 0) {
159                     processMessage(_sock);
160                     _lastRecKa = new Date();
161                 }
162
163                 // Keep Alive
164                 if (_kaTimer > 0) {
165                     // Timeout del PDP
166                     int _startTime = (int) (_lastRecKa.getTime());
167                     int cTime = (int) (new Date().getTime());
168
169                     if ((cTime - _startTime) > _kaTimer*1000) {
170                         _sock.close();
171                         // Notify all Request State Managers
172                         notifyNoKAAllReqStateMan();
173                     }
174
175                     // Send to PEP
176                     _startTime = (int) (_lastSendKa.getTime());
177                     cTime = (int) (new Date().getTime());
178
179                     if ((cTime - _startTime) > ((_kaTimer*3/4) * 1000)) {
180                         final COPSKAMsg msg = new COPSKAMsg(null);
181                         COPSTransceiver.sendMsg(msg, _sock);
182                         _lastSendKa = new Date();
183                     }
184                 }
185
186                 // Accounting
187                 if (_acctTimer > 0) {
188                     int _startTime = (int) (_lastSendAcc.getTime());
189                     int cTime = (int) (new Date().getTime());
190
191                     if ((cTime - _startTime) > ((_acctTimer*3/4)*1000)) {
192                         // Notify all Request State Managers
193                         notifyAcctAllReqStateMan();
194                         _lastSendAcc = new Date();
195                     }
196                 }
197
198                 try {
199                     Thread.sleep(500);
200                 } catch (Exception e) {
201                     logger.error("Exception thrown while sleeping", e);
202                 }
203             }
204         } catch (Exception e) {
205             logger.error("Error while processing socket messages", e);
206         }
207
208         // connection closed by server
209         // COPSDebug.out(getClass().getName(),"Connection closed by server");
210         try {
211             _sock.close();
212         } catch (IOException e) {
213             logger.error("Unexpected exception closing the socket", e);
214         }
215
216         // Notify all Request State Managers
217         try {
218             notifyCloseAllReqStateMan();
219         } catch (COPSPepException e) {
220             logger.error("Error closing state managers", e);
221         }
222     }
223
224     /**
225      * Gets a COPS message from the socket and processes it
226      * @param conn  Socket connected to the PDP
227      * @throws COPSPepException
228      * @throws COPSException
229      * @throws IOException
230      */
231     protected void processMessage(Socket conn) throws COPSException, IOException {
232         COPSMsg msg = COPSTransceiver.receiveMsg(conn);
233
234         if (msg.getHeader().getOpCode().equals(OPCode.CC)) {
235             handleClientCloseMsg(conn, msg);
236         } else if (msg.getHeader().getOpCode().equals(OPCode.DEC)) {
237             handleDecisionMsg(/*OJO conn, */msg);
238         } else if (msg.getHeader().getOpCode().equals(OPCode.SSQ)) {
239             handleSyncStateReqMsg(conn, msg);
240         } else if (msg.getHeader().getOpCode().equals(OPCode.KA)) {
241             handleKeepAliveMsg(conn, msg);
242         } else {
243             throw new COPSPepException("Message not expected (" + msg.getHeader().getOpCode() + ").");
244         }
245     }
246
247     /**
248      * Handle Client Close Message, close the passed connection
249      *
250      * @param    conn                a  Socket
251      * @param    msg                 a  COPSMsg
252      *
253      *
254      * <Client-Close> ::= <Common Header>
255      *                      <Error>
256      *                      [<Integrity>]
257      *
258      * Not support [<Integrity>]
259      *
260      */
261     private void handleClientCloseMsg(Socket conn, COPSMsg msg) {
262         COPSClientCloseMsg cMsg = (COPSClientCloseMsg) msg;
263         _error = cMsg.getError();
264
265         // COPSDebug.out(getClass().getName(),"Got close request, closing connection " +
266         //  conn.getInetAddress() + ":" + conn.getPort() + ":[Error " + _error.getDescription() + "]");
267
268         try {
269             // Support
270             if (cMsg.getIntegrity() != null)
271                 logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
272
273             conn.close();
274         } catch (Exception unae) {
275             logger.error("Unexpected exception closing connection", unae);
276         }
277     }
278
279     /**
280      * Gets the COPS error
281      * @return  <tt>COPSError</tt> returned by PDP
282      */
283     protected COPSError getError() {
284         return _error;
285     }
286
287     /**
288      * Handle Keep Alive Message
289      *
290      * <Keep-Alive> ::= <Common Header>
291      *                  [<Integrity>]
292      *
293      * Not support [<Integrity>]
294      *
295      * @param    conn                a  Socket
296      * @param    msg                 a  COPSMsg
297      *
298      */
299     private void handleKeepAliveMsg(Socket conn, COPSMsg msg) {
300         COPSKAMsg cMsg = (COPSKAMsg) msg;
301
302         // COPSDebug.out(getClass().getName(),"Get KAlive Msg");
303
304         // Support
305         if (cMsg.getIntegrity() != null)
306             logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
307
308         // must we do anything else?
309     }
310
311     /**
312      * Method handleDecisionMsg
313      *
314      * <Decision Message> ::= <Common Header: Flag SOLICITED>
315      *                          <Client Handle>
316      *                          *(<Decision>) | <Error>
317      *                          [<Integrity>]
318      * <Decision> ::= <Context>
319      *                  <Decision: Flags>
320      *                  [<ClientSI Decision Data: Outsourcing>]
321      * <Decision: Flags> ::= <Command-Code> NULLFlag
322      * <Command-Code> ::= NULLDecision | Install | Remove
323      * <ClientSI Decision Data> ::= <<Install Decision> | <Remove Decision>>
324      * <Install Decision> ::= *(<PRID> <EPD>)
325      * <Remove Decision> ::= *(<PRID> | <PPRID>)
326      *
327      * @param    msg                 a  COPSMsg
328      *
329      */
330     private void handleDecisionMsg(/*OJO Socket conn, */COPSMsg msg) throws COPSPepException {
331         COPSDecisionMsg dMsg = (COPSDecisionMsg) msg;
332         COPSHandle handle = dMsg.getClientHandle();
333         COPSPepOSReqStateMan manager = _managerMap.get(handle.getId().str());
334         manager.processDecision(dMsg);
335     }
336
337     /**
338      * Method handleSyncStateReqMsg
339      *
340      *              <Synchronize State> ::= <Common Header>
341      *                                      [<Client Handle>]
342      *                                      [<Integrity>]
343      *
344      * @param    conn                a  Socket
345      * @param    msg                 a  COPSMsg
346      *
347      */
348     private void handleSyncStateReqMsg(final Socket conn, final COPSMsg msg) throws COPSException {
349         final COPSSyncStateMsg cMsg = (COPSSyncStateMsg) msg;
350         // COPSHandle handle = cMsg.getClientHandle();
351         // COPSHeader header = cMsg.getHeader();
352
353         // Support
354         if (cMsg.getIntegrity() != null)
355             logger.warn("Unsupported objects (Integrity) to connection " + conn.getInetAddress());
356
357         final COPSPepOSReqStateMan manager = _managerMap.get(cMsg.getClientHandle().getId().str());
358
359         if (manager == null)
360             logger.warn("Unable to find state manager with ID - " + cMsg.getClientHandle().getId().str());
361         else
362             manager.processSyncStateRequest(cMsg);
363     }
364
365     /**
366      * Adds a new request state
367      * @param clientHandle  Client's handle
368      * @param process       Policy data processing object
369      * @param clientSIs     Client data from the outsourcing event
370      * @return              The newly created request state manager
371      * @throws COPSException
372      */
373     protected COPSPepOSReqStateMan addRequestState(final String clientHandle, final COPSPepOSDataProcess process,
374                                                    final List<COPSClientSI> clientSIs) throws COPSException {
375         final COPSPepOSReqStateMan manager = new COPSPepOSReqStateMan(_clientType,
376                 new COPSHandle(new COPSData(clientHandle)), process, clientSIs);
377         if (_managerMap.get(clientHandle) != null)
378             throw new COPSPepException("Duplicate Handle, rejecting " + clientHandle);
379         _managerMap.put(clientHandle, manager);
380         manager.initRequestState(_sock);
381         return manager;
382     }
383
384     /**
385      * Deletes a request state
386      * @param manager   Request state manager
387      * @throws COPSPepException
388      */
389     protected void deleteRequestState(COPSPepOSReqStateMan manager) throws COPSPepException {
390         manager.finalizeRequestState();
391     }
392
393     private void notifyCloseAllReqStateMan() throws COPSPepException {
394         for (final COPSPepOSReqStateMan man : _managerMap.values()) {
395                 man.processClosedConnection(_error);
396         }
397     }
398
399     private void notifyNoKAAllReqStateMan() throws COPSPepException {
400         for (final COPSPepOSReqStateMan man : _managerMap.values()) {
401             man.processNoKAConnection();
402         }
403     }
404
405     private void notifyAcctAllReqStateMan() throws COPSPepException {
406         for (final COPSPepOSReqStateMan man : _managerMap.values()) {
407             man.processAcctReport();
408         }
409     }
410
411 }