/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.networkhealth.internal;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import org.openhab.binding.networkhealth.NetworkHealthBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.OnOffType;
import org.openhab.io.net.actions.Ping;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The RefreshService polls all configured hostnames with a configurable
* interval and post all values to the internal event bus. The interval is 1
* minute by default and can be changed via openhab.cfg.
*
* @author Thomas.Eichstaedt-Engelen
* @author Kai Kreuzer
* @since 0.6.0
*/
public class NetworkHealthBinding extends AbstractActiveBinding<NetworkHealthBindingProvider>implements ManagedService {
private static final Logger logger = LoggerFactory.getLogger(NetworkHealthBinding.class);
/** the port to use for connecting to a given host (defaults to 5000) */
private int timeout = 5000;
/** the refresh interval which is used to poll the vitality of the given hosts (defaults to 60000ms) */
private long refreshInterval = 60000;
/** The states will be cached for this period (in minutes, defaults to 0 = off). */
private int cachePeriod = 0;
/** If {@link #cachePeriod} is larger than 0, this field holds the time of the last cache purge. */
private long lastCachePurge = System.currentTimeMillis();
/** Cached state of all devices for which a binding exists. */
private final Map<String, Boolean> cachedStates = new HashMap<String, Boolean>();
@Override
protected String getName() {
return "NetworkHealth Refresh Service";
}
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
protected void addBindingProvider(NetworkHealthBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(NetworkHealthBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* @{inheritDoc}
*/
@Override
public void execute() {
for (NetworkHealthBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
String hostname = provider.getHostname(itemName);
int port = provider.getPort(itemName);
if (provider.getTimeout(itemName) > 0) {
timeout = provider.getTimeout(itemName);
}
boolean success = false;
try {
success = Ping.checkVitality(hostname, port, timeout);
if(success) {
logger.debug("established connection [host '{}' port '{}' timeout '{}']",
new Object[] { hostname, port, timeout });
} else {
logger.debug("couldn't establish connection [host '{}' port '{}' timeout '{}']",
new Object[] { hostname, port, timeout });
}
} catch (SocketTimeoutException se) {
logger.debug("timed out while connecting [host '{}' port '{}' timeout '{}']",
new Object[] { hostname, port, timeout });
} catch (IOException ioe) {
logger.debug("couldn't establish network connection [host '{}' port '{}' timeout '{}']",
new Object[] { hostname, port, timeout });
}
if (eventPublisher != null) {
// check cached state and update only if state differs
if (shouldPostUpdate(hostname, port, success)) {
eventPublisher.postUpdate(itemName, success ? OnOffType.ON : OnOffType.OFF);
}
}
}
}
}
/**
* Whether or not to post the new state to the event bus.
*
* @param hostname
* The hostname that was checked.
* @param port
* The port.
* @param newState
* The state whether the host is reachable or not.
* @return <code>true</code> if the event changed or it is not cached;
* <code>false</code> if the state is already cached and did not
* change.
*/
private boolean shouldPostUpdate(String hostname, int port, boolean newState) {
if (cachePeriod <= 0) {
return true; // caching disabled
}
long now = System.currentTimeMillis();
// clear cache after <cachePeriod> minutes
if (lastCachePurge + (cachePeriod * 60000) < now) {
cachedStates.clear();
lastCachePurge = now;
}
// post update only if state changed (and caching is enabled) to avoid
// spamming the bus
final Boolean cachedState = cachedStates.get(hostname + port);
if (cachedState == null || newState != cachedState.booleanValue()) {
cachedStates.put(hostname + port, newState);
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("rawtypes")
public void updated(Dictionary config) throws ConfigurationException {
if (config != null) {
String timeoutString = (String) config.get("timeout");
if (timeoutString != null && !timeoutString.isEmpty()) {
timeout = Integer.parseInt(timeoutString);
}
String refreshIntervalString = (String) config.get("refresh");
if (refreshIntervalString != null && !refreshIntervalString.isEmpty()) {
refreshInterval = Long.parseLong(refreshIntervalString);
}
// read cache period from configuration
String cachePeriodString = (String) config.get("cachePeriod");
if (cachePeriodString != null && !cachePeriodString.isEmpty()) {
cachePeriod = Integer.parseInt(cachePeriodString);
}
}
setProperlyConfigured(true);
}
}