]> git.basschouten.com Git - openhab-addons.git/blob
d6e32c297cb415a3e165f35040c6f8a0f12ea624
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.velux.internal.bridge.slip.io;
14
15 import java.io.IOException;
16 import java.net.ConnectException;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.openhab.binding.velux.internal.VeluxBindingConstants;
20 import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance;
21 import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * 2nd Level I/O interface towards the <B>Velux</B> bridge.
27  * It provides methods for a pure client/server communication.
28  * <P>
29  * The following class access methods exist:
30  * <UL>
31  * <LI>{@link Connection#io} for a complete pair of request and response messages,</LI>
32  * <LI>{@link Connection#isAlive} to check the presence of a connection,</LI>
33  * <LI>{@link Connection#isMessageAvailable} to check the presence of an incoming message,</LI>
34  * <LI>{@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,</LI>
35  * <LI>{@link Connection#lastCommunication} returns the timestamp of the last communication,</LI>
36  * <LI>{@link Connection#resetConnection} for resetting the current connection.</LI>
37  * </UL>
38  *
39  * @author Guenther Schreiner - Initial contribution.
40  */
41 @NonNullByDefault
42 public class Connection {
43     private final Logger logger = LoggerFactory.getLogger(Connection.class);
44
45     /*
46      * ***************************
47      * ***** Private Objects *****
48      */
49
50     /**
51      * Timestamp of last successful communication in milliseconds.
52      */
53     private long lastSuccessfulCommunicationInMSecs = 0;
54     /**
55      * Timestamp of last communication in milliseconds.
56      */
57     private long lastCommunicationInMSecs = 0;
58     /**
59      * SSL socket for communication.
60      */
61     private SSLconnection connectivity = SSLconnection.UNKNOWN;
62
63     private String host = VeluxBindingConstants.UNKNOWN_IP_ADDRESS;
64
65     /*
66      * **************************
67      * ***** Public Methods *****
68      */
69
70     /**
71      * Base level communication with the <B>SlipVeluxBridg</B>.
72      *
73      * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port.
74      * @param request as Array of bytes representing the structure of the message to be send.
75      * @return <b>response</b> of type Array of byte containing all received informations.
76      * @throws java.net.ConnectException in case of unrecoverable communication failures.
77      * @throws java.io.IOException in case of continuous communication I/O failures.
78      */
79     public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request)
80             throws ConnectException, IOException {
81         logger.trace("io() on {}: called.", host);
82
83         lastCommunicationInMSecs = System.currentTimeMillis();
84
85         /** Local handles */
86         int retryCount = 0;
87         IOException lastIOE = new IOException("Unexpected I/O exception.");
88
89         do {
90             try {
91                 if (!connectivity.isReady()) {
92                     try {
93                         // From configuration
94                         host = bridgeInstance.veluxBridgeConfiguration().ipAddress;
95                         int port = bridgeInstance.veluxBridgeConfiguration().tcpPort;
96                         int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs;
97
98                         logger.trace("io() on {}: connecting to port {}", host, port);
99                         connectivity = new SSLconnection(host, port);
100                         connectivity.setTimeout(timeoutMsecs);
101                     } catch (ConnectException ce) {
102                         throw new ConnectException(String
103                                 .format("raised a non-recoverable error during connection setup: %s", ce.getMessage()));
104                     } catch (IOException e) {
105                         logger.warn("io() on {}: raised an error during connection setup: {}.", host, e.getMessage());
106                         lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage()));
107                         continue;
108                     }
109                 }
110                 if (request.length > 0) {
111                     try {
112                         if (logger.isTraceEnabled()) {
113                             logger.trace("io() on {}: sending packet with {} bytes: {}", host, request.length,
114                                     new Packet(request));
115                         } else {
116                             logger.debug("io() on {}: sending packet of size {}.", host, request.length);
117                         }
118                         if (connectivity.isReady()) {
119                             connectivity.send(request);
120                         }
121                     } catch (IOException e) {
122                         logger.info("io() on {}: raised an error during sending: {}.", host, e.getMessage());
123                         break;
124                     }
125
126                     // Give the bridge some time to breathe
127                     if (bridgeInstance.veluxBridgeConfiguration().timeoutMsecs > 0) {
128                         logger.trace("io() on {}: wait time {} msecs.", host,
129                                 bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
130                         try {
131                             Thread.sleep(bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
132                         } catch (InterruptedException ie) {
133                             logger.trace("io() on {}: wait interrupted.", host);
134                         }
135                     }
136                 }
137                 byte[] packet = new byte[0];
138                 logger.trace("io() on {}: receiving bytes.", host);
139                 if (connectivity.isReady()) {
140                     packet = connectivity.receive();
141                 }
142                 if (logger.isTraceEnabled()) {
143                     logger.trace("io() on {}: received packet with {} bytes: {}", host, packet.length,
144                             new Packet(packet));
145                 } else {
146                     logger.debug("io() on {}: received packet with {} bytes.", host, packet.length);
147                 }
148                 lastSuccessfulCommunicationInMSecs = System.currentTimeMillis();
149                 lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs;
150                 logger.trace("io() on {}: finished.", host);
151                 return packet;
152             } catch (IOException ioe) {
153                 logger.info("io() on {}: Exception occurred during I/O: {}.", host, ioe.getMessage());
154                 lastIOE = ioe;
155                 // Error Retries with Exponential Backoff
156                 long waitTime = ((long) Math.pow(2, retryCount)
157                         * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
158                 logger.trace("io() on {}: wait time {} msecs.", host, waitTime);
159                 try {
160                     Thread.sleep(waitTime);
161                 } catch (InterruptedException ie) {
162                     logger.trace("io() on {}: wait interrupted.", host);
163                 }
164             }
165         } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries);
166         if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) {
167             logger.info("io() on {}: socket I/O failed {} times.", host,
168                     bridgeInstance.veluxBridgeConfiguration().retries);
169         }
170         logger.trace("io() on {}: shutting down connection.", host);
171         if (connectivity.isReady()) {
172             connectivity.close();
173         }
174         logger.trace("io() on {}: finishes with failure by throwing exception.", host);
175         throw lastIOE;
176     }
177
178     /**
179      * Returns the status of the current connection.
180      *
181      * @return state as boolean.
182      */
183     public boolean isAlive() {
184         logger.trace("isAlive() on {}: called.", host);
185         return connectivity.isReady();
186     }
187
188     /**
189      * Returns the availability of an incoming message.
190      *
191      * @return state as boolean.
192      */
193     public synchronized boolean isMessageAvailable() {
194         logger.trace("isMessageAvailable() on {}: called.", host);
195         try {
196             if ((connectivity.isReady()) && (connectivity.available())) {
197                 logger.trace("isMessageAvailable() on {}: there is a message waiting.", host);
198                 return true;
199             }
200         } catch (IOException e) {
201             logger.trace("isMessageAvailable() on {}: lost connection due to {}.", host, e.getMessage());
202             resetConnection();
203         }
204         logger.trace("isMessageAvailable() on {}: no message waiting.", host);
205         return false;
206     }
207
208     /**
209      * Returns the timestamp in milliseconds since Unix epoch
210      * of last successful communication.
211      *
212      * @return timestamp in milliseconds.
213      */
214     public long lastSuccessfulCommunication() {
215         return lastSuccessfulCommunicationInMSecs;
216     }
217
218     /**
219      * Returns the timestamp in milliseconds since Unix epoch
220      * of last communication.
221      *
222      * @return timestamp in milliseconds.
223      */
224     public long lastCommunication() {
225         return lastCommunicationInMSecs;
226     }
227
228     /**
229      * Resets an open connectivity by closing the socket and resetting the authentication information.
230      */
231     public synchronized void resetConnection() {
232         logger.trace("resetConnection() on {}: called.", host);
233         try {
234             connectivity.close();
235         } catch (IOException e) {
236             logger.info("resetConnection() on {}: raised an error during connection close: {}.", host, e.getMessage());
237         }
238         logger.trace("resetConnection() on {}: done.", host);
239     }
240 }