/**
* Copyright (c) 2014-2017 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.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.sceneEvent;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.config.Config;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.SceneManager;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.serverConnection.constants.JSONApiResponseKeysEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.serverConnection.constants.JSONRequestConstants;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.serverConnection.impl.JSONResponseHandler;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.eclipse.smarthome.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* If someone call or undo a scene, the {@link SceneManager} will get a notification
* to update the state of the internal saved {@link InternalScene} or directly the {@link Device}, if it was a
* device scene.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class EventListener {
private Logger logger = LoggerFactory.getLogger(EventListener.class);
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
private ScheduledFuture<?> pollingScheduler = null;
private final String EVENT_NAME_CALL = "callScene";
private final String EVENT_NAME_UNDO = "undoScene";
private final int ID = 11;
private final String INVALID_SESSION = "Invalid session!";
private final String UNKNOWN_TOKEN = "Token " + ID + " not found!";
private final ConnectionManager connManager;
private SceneManager sceneManager;
private Config config;
/**
* Creates a new {@link EventListener}. To get notified by call and undo scene events you have to call
* {@link #start()}.
*
* @param connectionManager must not be null
* @param sceneManager must not be null
*/
public EventListener(ConnectionManager connectionManager, SceneManager sceneManager) {
this.connManager = connectionManager;
this.config = connectionManager.getConfig();
this.sceneManager = sceneManager;
}
/**
* Stops this {@link EventListener}.
*/
public synchronized void stop() {
if (pollingScheduler != null && !pollingScheduler.isCancelled()) {
pollingScheduler.cancel(true);
pollingScheduler = null;
unsubscribe();
logger.debug("Stop EventListener");
}
}
/**
* Starts this {@link EventListener}.
*/
public synchronized void start() {
if (subscribe() && (pollingScheduler == null || pollingScheduler.isCancelled())) {
pollingScheduler = scheduler.scheduleAtFixedRate(runableListener, 0,
config.getEventListenerRefreshinterval(), TimeUnit.MICROSECONDS);
logger.debug("Start EventListener");
}
}
private boolean subscribe() {
if (connManager.checkConnection()) {
boolean transmitted = connManager.getDigitalSTROMAPI().subscribeEvent(this.connManager.getSessionToken(),
EVENT_NAME_CALL, this.ID, config.getConnectionTimeout(), config.getReadTimeout());
transmitted = connManager.getDigitalSTROMAPI().subscribeEvent(this.connManager.getSessionToken(),
EVENT_NAME_UNDO, this.ID, config.getConnectionTimeout(), config.getReadTimeout());
if (!transmitted) {
logger.error("Couldn't subscribe EventListener ... maybe timeout because system is to busy ...");
} else {
logger.debug("subscribe successfull");
return true;
}
} else {
logger.error("Couldn't subscribe eventListener, because there is no token (no connection)");
}
return false;
}
private Runnable runableListener = new Runnable() {
@Override
public void run() {
String request = getEventAsRequest(ID, 500);
if (request != null) {
String response = connManager.getHttpTransport().execute(request);
JsonObject responseObj = JSONResponseHandler.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responseObj)) {
JsonObject obj = JSONResponseHandler.getResultJsonObject(responseObj);
if (obj != null && obj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT.getKey()) instanceof JsonArray) {
JsonArray array = (JsonArray) obj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT.getKey());
try {
handleEvent(array);
} catch (Exception e) {
logger.error("An Exception occurred", e);
}
}
} else {
String errorStr = null;
if (responseObj != null
&& responseObj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT_ERROR.getKey()) != null) {
errorStr = responseObj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT_ERROR.getKey())
.getAsString();
}
if (errorStr != null && (errorStr.equals(INVALID_SESSION) || errorStr.contains(UNKNOWN_TOKEN))) {
unsubscribe();
subscribe();
} else if (errorStr != null) {
pollingScheduler.cancel(true);
logger.error("Unknown error message at event response: " + errorStr);
}
}
}
}
};
private String getEventAsRequest(int subscriptionID, int timeout) {
if (connManager.checkConnection()) {
return JSONRequestConstants.JSON_EVENT_GET + JSONRequestConstants.PARAMETER_TOKEN
+ connManager.getSessionToken() + JSONRequestConstants.INFIX_PARAMETER_SUBSCRIPTION_ID
+ subscriptionID + JSONRequestConstants.INFIX_PARAMETER_TIMEOUT + timeout;
}
return null;
}
private boolean unsubscribeEvent(String name, int subscriptionID) {
if (connManager.checkConnection()) {
return connManager.getDigitalSTROMAPI().unsubscribeEvent(connManager.getSessionToken(), EVENT_NAME_CALL,
this.ID, Config.DEFAULT_CONNECTION_TIMEOUT, Config.DEFAULT_READ_TIMEOUT);
}
return false;
}
private boolean unsubscribe() {
return this.unsubscribeEvent(this.EVENT_NAME_CALL, this.ID);
}
private void handleEvent(JsonArray array) {
if (array.size() > 0) {
Event event = new JSONEventImpl(array);
for (EventItem item : event.getEventItems()) {
logger.info(item.getProperties().toString());
this.sceneManager.handleEvent(item);
}
}
}
}