Skip to content

Commit

Permalink
[remoteopenhab] New setting to restart the SSE connection after inact… (
Browse files Browse the repository at this point in the history
#10063)

* [remoteopenhab] New setting to restart the SSE connection after inactivity

Fix #9680

Signed-off-by: Laurent Garnier <[email protected]>

* Review comments: doc

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo authored Feb 6, 2021
1 parent 3e7ecbf commit 5141781
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 12 deletions.
3 changes: 2 additions & 1 deletion bundles/org.openhab.binding.remoteopenhab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ The `server` thing has the following configuration parameters:
| restPath | yes | The subpath of the REST API on the remote openHAB server. Default is /rest |
| token | no | The token to use when the remote openHAB server is setup to require authorization to run its REST API. |
| accessibilityInterval | no | Minutes between checking the remote server accessibility. 0 to disable the check. Default is 3. |
| aliveInterval | no | Number of last minutes to take into account to determine whether the remote server is alive. 0 to disable this feature. Default is 5. |
| aliveInterval | no | Number of last minutes to consider when monitoring the receipt of events from the remote server. If an event is received during this interval, the remote server is considered alive and its accessibility will not be verified. Use 0 to disable this feature. Default is 5. |
| restartIfNoActivity | no | Set it to true if you want to restart the connection (SSE) to the remote server when no events are received in the monitored interval. It is not necessary if the goal is to properly handle a short network outage (few seconds). This can be useful if you want to deal with a long network outage. Do not enable it if you remote server does not send events during the monitored interval under normal conditions, it will cause frequent restart of the connection and potential loss of events. Default is false. |

The `thing` thing has the following configuration parameters:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ public class RemoteopenhabServerConfiguration {
public String token = "";
public int accessibilityInterval = 3;
public int aliveInterval = 5;
public boolean restartIfNoActivity = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ public void initialize() {

updateStatus(ThingStatus.UNKNOWN);

scheduler.submit(this::checkConnection);
scheduler.submit(() -> checkConnection(false));
if (config.accessibilityInterval > 0) {
startCheckConnectionJob(config.accessibilityInterval, config.aliveInterval);
startCheckConnectionJob(config.accessibilityInterval, config.aliveInterval, config.restartIfNoActivity);
}
}

Expand Down Expand Up @@ -343,7 +343,7 @@ private void setStateOptions(List<RemoteopenhabItem> items) {
}
}

public void checkConnection() {
public void checkConnection(boolean restartSse) {
logger.debug("Try the root REST API...");
try {
restClient.tryApi();
Expand All @@ -367,6 +367,9 @@ public void checkConnection() {
"Dynamic creation of the channels for the remote server items failed");
stopStreamingUpdates();
}
} else if (restartSse) {
logger.debug("The SSE connection is restarted because there was no recent event received");
restartStreamingUpdates();
}
} catch (RemoteopenhabException e) {
logger.debug("{}", e.getMessage());
Expand All @@ -375,20 +378,20 @@ public void checkConnection() {
}
}

private void startCheckConnectionJob(int accessibilityInterval, int aliveInterval) {
private void startCheckConnectionJob(int accessibilityInterval, int aliveInterval, boolean restartIfNoActivity) {
ScheduledFuture<?> localCheckConnectionJob = checkConnectionJob;
if (localCheckConnectionJob == null || localCheckConnectionJob.isCancelled()) {
checkConnectionJob = scheduler.scheduleWithFixedDelay(() -> {
long millisSinceLastEvent = System.currentTimeMillis() - restClient.getLastEventTimestamp();
if (getThing().getStatus() != ThingStatus.ONLINE || aliveInterval == 0
|| restClient.getLastEventTimestamp() == 0) {
logger.debug("Time to check server accessibility");
checkConnection();
checkConnection(restartIfNoActivity && aliveInterval != 0);
} else if (millisSinceLastEvent > (aliveInterval * 60000)) {
logger.debug(
"Time to check server accessibility (maybe disconnected from streaming events, millisSinceLastEvent={})",
millisSinceLastEvent);
checkConnection();
checkConnection(restartIfNoActivity);
} else {
logger.debug(
"Bypass server accessibility check (receiving streaming events, millisSinceLastEvent={})",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.binding.remoteopenhab.internal.rest;

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
Expand All @@ -21,6 +22,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
Expand Down Expand Up @@ -182,7 +184,7 @@ public String getRemoteItemState(String itemName) throws RemoteopenhabException
public void sendCommandToRemoteItem(String itemName, Command command) throws RemoteopenhabException {
try {
String url = String.format("%s/%s", getRestApiUrl("items"), itemName);
executeUrl(HttpMethod.POST, url, "application/json", command.toFullString(), "text/plain", false);
executeUrl(HttpMethod.POST, url, "application/json", command.toFullString(), "text/plain", false, true);
} catch (RemoteopenhabException e) {
throw new RemoteopenhabException("Failed to send command to the remote item " + itemName
+ " using the items REST API: " + e.getMessage(), e);
Expand Down Expand Up @@ -470,11 +472,11 @@ private String extractThingUIDFromTopic(String topic, String eventType, String f
}

public String executeGetUrl(String url, String acceptHeader, boolean asyncReading) throws RemoteopenhabException {
return executeUrl(HttpMethod.GET, url, acceptHeader, null, null, asyncReading);
return executeUrl(HttpMethod.GET, url, acceptHeader, null, null, asyncReading, true);
}

public String executeUrl(HttpMethod httpMethod, String url, String acceptHeader, @Nullable String content,
@Nullable String contentType, boolean asyncReading) throws RemoteopenhabException {
@Nullable String contentType, boolean asyncReading, boolean retryIfEOF) throws RemoteopenhabException {
final Request request = httpClient.newRequest(url).method(httpMethod).timeout(REQUEST_TIMEOUT,
TimeUnit.MILLISECONDS);

Expand Down Expand Up @@ -517,6 +519,16 @@ public String executeUrl(HttpMethod httpMethod, String url, String acceptHeader,
}
} catch (RemoteopenhabException e) {
throw e;
} catch (ExecutionException e) {
// After a long network outage, the first HTTP request will fail with an EOFException exception.
// We retry the request a second time in this case.
Throwable cause = e.getCause();
if (retryIfEOF && cause instanceof EOFException) {
logger.debug("EOFException - retry the request");
return executeUrl(httpMethod, url, acceptHeader, content, contentType, asyncReading, false);
} else {
throw new RemoteopenhabException(e);
}
} catch (Exception e) {
throw new RemoteopenhabException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,24 @@

<parameter name="aliveInterval" type="integer" min="0" step="1" unit="min">
<label>Alive Interval</label>
<description>Number of last minutes to take into account to determine whether the remote server is alive. 0 to
disable this feature. Default is 5.</description>
<description>Number of last minutes to consider when monitoring the receipt of events from the remote server. If an
event is received during this interval, the remote server is considered alive and its accessibility will not be
verified. Use 0 to disable this feature. Default is 5.</description>
<default>5</default>
<advanced>true</advanced>
</parameter>

<parameter name="restartIfNoActivity" type="boolean">
<label>Restart if no Activity</label>
<description>Set it to true if you want to restart the connection (SSE) to the remote server when no events are
received in the monitored interval. It is not necessary if the goal is to properly handle a short network outage
(few seconds). This can be useful if you want to deal with a long network outage. Do not enable it if you remote
server does not send events during the monitored interval under normal conditions, it will cause frequent restart
of the connection and potential loss of events. Default is false.
</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>

Expand Down

0 comments on commit 5141781

Please sign in to comment.