/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.devicemgt.raspberry.agent;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.log4j.Logger;
import org.wso2.siddhi.core.SiddhiManager;
import org.wso2.siddhi.core.event.Event;
import org.wso2.siddhi.core.stream.input.InputHandler;
import org.wso2.siddhi.core.stream.output.StreamCallback;
import org.wso2.siddhi.core.util.EventPrinter;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.concurrent.FutureCallback;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
/**
* This class reads the sonar reading and injects values
* to the siddhiEngine for processing on a routine basis
* also if the siddhiquery is updated the class takes
* care of re-initializing same.
*/
public class SidhdhiQuery implements Runnable {
private static final Logger log = Logger.getLogger(SidhdhiQuery.class);
final AgentConstants constants = new AgentConstants();
//Bam data push client
private static PushBamData pushBamData = new PushBamData();
private static SiddhiManager siddhiManager = new SiddhiManager();
public static PushBamData getPushBamData() {
return pushBamData;
}
public static SiddhiManager getSiddhiManager() {
return siddhiManager;
}
public static void setSiddhiManager(SiddhiManager siddhiManager) {
SidhdhiQuery.siddhiManager = siddhiManager;
}
//keeps track of bulb status. The start status is assumed as off
//TODO : pick up current bulb status from a API
boolean isBulbOn = false;
public void run() {
//Initialize Push data client
PushBamData pushdata = getPushBamData();
pushdata.initializeDataPublisher();
//Start the execution plan with pre-defined or previously persisted Siddhi query
StartExecutionPlan startExecutionPlan = new StartExecutionPlan().invoke();
while (true) {
//Check if there is new policy update available
if (AgentInitializer.isUpdated()) {
System.out.print("### Policy Update Detected!");
//Restart execution plan with new query
restartSiddhi();
startExecutionPlan = new StartExecutionPlan().invoke();
}
InputHandler inputHandler = startExecutionPlan.getInputHandler();
//Sending events to Siddhi
try {
//If sonar URL is present in the config file the program will read stats off the API
//If not it will look for a file which is also configurable via a property
String sonarUrl = constants.prop.getProperty("sonar.reading.url");
String sonarReading = null;
if (sonarUrl != null) {
sonarReading = readSonarData(sonarUrl);
}
if (sonarReading == null || sonarReading.equalsIgnoreCase("")) {
sonarReading = readFile(constants.prop.getProperty("sonar.reading.file.path"), StandardCharsets.UTF_8);
}
log.info("Pushing data to CEP - Sonar : " + sonarReading.trim());
inputHandler.send(new Object[]{"FIRE_1", Double.parseDouble(sonarReading)});
Thread.sleep(Integer.parseInt(constants.prop.getProperty("read.interval")));
// executionPlanRuntime.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Re-Initialize SiddhiManager
*/
private void restartSiddhi() {
siddhiManager.shutdown();
siddhiManager = new SiddhiManager();
}
/**
* Make http call to specified endpoint with events
* @param inEvents
* @param bulbEP
* @param logText
*/
private void performHTTPCall(Event[] inEvents, String bulbEP, String logText) {
if (inEvents != null && inEvents.length > 0) {
EventPrinter.print(inEvents);
String url = constants.prop.getProperty(bulbEP);
CloseableHttpAsyncClient httpclient = null;
httpclient = HttpAsyncClients.createDefault();
httpclient.start();
HttpGet request = new HttpGet(url);
log.info("Bulb Status : " + logText);
final CountDownLatch latch = new CountDownLatch(1);
Future<HttpResponse> future = httpclient.execute(
request, new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse httpResponse) {
latch.countDown();
}
@Override
public void failed(Exception e) {
latch.countDown();
}
@Override
public void cancelled() {
latch.countDown();
}
}
);
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Read content from a given file and return as a string
* @param path
* @param encoding
* @return
*/
static String readFile(String path, Charset encoding) {
byte[] encoded = new byte[0];
try {
encoded = Files.readAllBytes(Paths.get(path));
} catch (IOException e) {
log.error("Error reading Sidhdhi query from file.");
}
return new String(encoded, encoding);
}
/**
* Read sonar data from API URL
* @param sonarAPIUrl
* @return
*/
private String readSonarData(String sonarAPIUrl) {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(sonarAPIUrl);
String responseStr = null;
try {
HttpResponse response = client.execute(request);
log.debug("Response Code : " + response);
InputStream input = response.getEntity().getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(input, "UTF-8"));
responseStr = String.valueOf(br.readLine());
br.close();
} catch (IOException e) {
//log.error("Exception encountered while trying to make get request.");
log.error("Error while reading sonar reading from file!");
return responseStr;
}
return responseStr;
}
/**
* Initialize SiddhiExecution plan
*/
private class StartExecutionPlan {
private InputHandler inputHandler;
public InputHandler getInputHandler() {
return inputHandler;
}
public StartExecutionPlan invoke() {
String executionPlan;
executionPlan = readFile(constants.prop.getProperty("execution.plan.file.location"), StandardCharsets.UTF_8);
//Generating runtime
siddhiManager.addExecutionPlan(executionPlan);
siddhiManager.addCallback("bulbOnStream", new StreamCallback() {
@Override
public void receive(Event[] events) {
System.out.println("Bulb on Event Fired!");
if (events.length > 0) {
if (!isBulbOn) {
performHTTPCall(events, "bulb.on.api.endpoint", "Bulb Switched on!");
System.out.println("#### Performed HTTP call! ON.");
pushBamData.publishData(constants.prop.getProperty("device.id"),
constants.prop.getProperty("device.type"),
constants.prop.getProperty("device.user"),
"BULB SWITCHED ON");
isBulbOn = true;
}
}
}
});
siddhiManager.addCallback("bulbOffStream", new StreamCallback() {
@Override
public void receive(Event[] inEvents) {
System.out.println("Bulb off Event Fired");
if (isBulbOn) {
performHTTPCall(inEvents, "bulb.off.api.endpoint", "Bulb Switched off!");
System.out.println("#### Performed HTTP call! OFF.");
pushBamData.publishData(constants.prop.getProperty("device.id"),
constants.prop.getProperty("device.type"),
constants.prop.getProperty("device.user"),
"BULB SWITCHED OFF");
isBulbOn = false;
}
}
});
//Retrieving InputHandler to push events into Siddhi
inputHandler = siddhiManager.getInputHandler("fireAlarmEventStream");
//Starting event processing
System.out.println("Execution Plan Started!");
return this;
}
}
}