2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.velux.internal.bridge.slip.io;
15 import java.io.Closeable;
16 import java.io.IOException;
17 import java.net.ConnectException;
18 import java.net.SocketTimeoutException;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.velux.internal.VeluxBindingConstants;
22 import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
23 import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
24 import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * 2nd Level I/O interface towards the <B>Velux</B> bridge.
30 * It provides methods for a pure client/server communication.
32 * The following class access methods exist:
34 * <LI>{@link Connection#io} for a complete pair of request and response messages,</LI>
35 * <LI>{@link Connection#isAlive} to check the presence of a connection,</LI>
36 * <LI>{@link Connection#isMessageAvailable} to check the presence of an incoming message,</LI>
37 * <LI>{@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,</LI>
38 * <LI>{@link Connection#lastCommunication} returns the timestamp of the last communication,</LI>
39 * <LI>{@link Connection#resetConnection} for resetting the current connection.</LI>
42 * @author Guenther Schreiner - Initial contribution.
45 public class Connection implements Closeable {
46 private final Logger logger = LoggerFactory.getLogger(Connection.class);
49 * ***************************
50 * ***** Private Objects *****
54 * Timestamp of last successful communication in milliseconds.
56 private long lastSuccessfulCommunicationInMSecs = 0;
58 * Timestamp of last communication in milliseconds.
60 private long lastCommunicationInMSecs = 0;
62 * SSL socket for communication.
64 private SSLconnection connectivity = SSLconnection.UNKNOWN;
66 private String host = VeluxBindingConstants.UNKNOWN_IP_ADDRESS;
69 * **************************
70 * ***** Public Methods *****
74 * Base level communication with the <B>SlipVeluxBridg</B>.
76 * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port.
77 * @param request as Array of bytes representing the structure of the message to be send.
78 * @return <b>response</b> of type Array of byte containing all received informations.
79 * @throws java.net.ConnectException in case of unrecoverable communication failures.
80 * @throws java.io.IOException in case of continuous communication I/O failures.
82 public synchronized byte[] io(VeluxBridgeHandler bridgeInstance, byte[] request)
83 throws ConnectException, IOException {
84 VeluxBridgeConfiguration cfg = bridgeInstance.veluxBridgeConfiguration();
86 logger.trace("io() on {}: called.", host);
88 lastCommunicationInMSecs = System.currentTimeMillis();
92 IOException lastIOE = new IOException("Unexpected I/O exception.");
96 if (!connectivity.isReady()) {
97 // dispose old connectivity class instances (if any)
100 logger.trace("io() on {}: connecting to port {}", cfg.ipAddress, cfg.tcpPort);
101 connectivity = new SSLconnection(bridgeInstance);
102 } catch (ConnectException ce) {
103 throw new ConnectException(String
104 .format("raised a non-recoverable error during connection setup: %s", ce.getMessage()));
105 } catch (IOException e) {
106 logger.warn("io() on {}: raised an error during connection setup: {}.", host, e.getMessage());
107 lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage()));
111 boolean sending = request.length > 0;
114 if (logger.isTraceEnabled()) {
115 logger.trace("io() on {}: sending packet with {} bytes: {}", host, request.length,
116 new Packet(request));
118 logger.debug("io() on {}: sending packet of size {}.", host, request.length);
120 if (connectivity.isReady()) {
121 connectivity.send(request);
123 } catch (IOException e) {
124 logger.info("io() on {}: raised an error during sending: {}.", host, e.getMessage());
128 byte[] packet = new byte[0];
129 logger.trace("io() on {}: receiving bytes.", host);
130 if (connectivity.isReady()) {
131 packet = connectivity.receive();
132 // in receive-only mode, a zero length response packet is NOT a timeout
133 if (sending && (packet.length == 0)) {
134 throw new SocketTimeoutException("read time out after send");
137 if (logger.isTraceEnabled()) {
138 logger.trace("io() on {}: received packet with {} bytes: {}", host, packet.length,
141 logger.debug("io() on {}: received packet with {} bytes.", host, packet.length);
143 lastSuccessfulCommunicationInMSecs = System.currentTimeMillis();
144 lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs;
145 logger.trace("io() on {}: finished.", host);
147 } catch (IOException ioe) {
148 logger.info("io() on {}: Exception occurred during I/O: {}.", host, ioe.getMessage());
150 // Error Retries with Exponential Backoff
151 long waitTime = ((long) Math.pow(2, retryCount)
152 * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
153 logger.trace("io() on {}: wait time {} msecs.", host, waitTime);
155 Thread.sleep(waitTime);
156 } catch (InterruptedException ie) {
157 logger.trace("io() on {}: wait interrupted.", host);
160 } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries);
161 if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) {
162 logger.info("io() on {}: socket I/O failed {} times.", host,
163 bridgeInstance.veluxBridgeConfiguration().retries);
165 logger.trace("io() on {}: shutting down connection.", host);
167 logger.trace("io() on {}: finishes with failure by throwing exception.", host);
172 * Returns the status of the current connection.
174 * @return state as boolean.
176 public boolean isAlive() {
177 logger.trace("isAlive() on {}: called.", host);
178 return connectivity.isReady();
182 * Returns the availability of an incoming message.
184 * @return state as boolean.
186 public synchronized boolean isMessageAvailable() {
187 logger.trace("isMessageAvailable() on {}: called.", host);
188 if (!connectivity.isReady()) {
189 logger.trace("isMessageAvailable() on {}: lost connection, there may be messages", host);
192 boolean result = connectivity.available();
193 logger.trace("isMessageAvailable() on {}: there are {}messages waiting.", host, result ? "" : "no ");
198 * Returns the timestamp in milliseconds since Unix epoch
199 * of last successful communication.
201 * @return timestamp in milliseconds.
203 public long lastSuccessfulCommunication() {
204 return lastSuccessfulCommunicationInMSecs;
208 * Returns the timestamp in milliseconds since Unix epoch
209 * of last communication.
211 * @return timestamp in milliseconds.
213 public long lastCommunication() {
214 return lastCommunicationInMSecs;
218 * Resets an open connectivity by closing the socket and resetting the authentication information.
220 public synchronized void resetConnection() {
221 logger.trace("resetConnection() on {}: called.", host);
223 connectivity.close();
224 } catch (IOException e) {
225 logger.info("resetConnection() on {}: raised an error during connection close: {}.", host, e.getMessage());
227 logger.trace("resetConnection() on {}: done.", host);
231 public void close() throws IOException {