package org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.utils.http;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.communication
.CommunicationHandlerException;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.communication.CommunicationUtils;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.communication.http
.HTTPCommunicationHandler;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.exception
.AgentCoreOperationException;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.internal.AgentConstants;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.internal.AgentCoreOperations;
import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.internal.AgentManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class HTTPCommunicationHandlerImpl extends HTTPCommunicationHandler {
private static final Log log = LogFactory.getLog(HTTPCommunicationHandlerImpl.class);
private static final AgentManager agentManager = AgentManager.getInstance();
private ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
private ScheduledFuture<?> dataPushServiceHandler;
private ScheduledFuture<?> connectorServiceHandler;
public HTTPCommunicationHandlerImpl() {
super();
}
public HTTPCommunicationHandlerImpl(int port) {
super(port);
}
public HTTPCommunicationHandlerImpl(int port, int reconnectionInterval) {
super(port, reconnectionInterval);
}
public ScheduledFuture<?> getDataPushServiceHandler() {
return dataPushServiceHandler;
}
public void connect() {
Runnable connect = new Runnable() {
public void run() {
if (!isConnected()) {
try {
processIncomingMessage();
server.start();
registerThisDevice();
publishDeviceData(agentManager.getAgentConfigs().getDataPushInterval());
log.info("HTTP Server started at port: " + port);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.warn("Unable to 'START' HTTP server. Will retry after " +
timeoutInterval / 1000 + " seconds.");
}
}
}
}
};
connectorServiceHandler = service.scheduleAtFixedRate(connect, 0, timeoutInterval,
TimeUnit.MILLISECONDS);
}
@Override
public void processIncomingMessage() {
server.setHandler(new AbstractHandler() {
public void handle(String s, Request request, HttpServletRequest
httpServletRequest,
HttpServletResponse httpServletResponse)
throws IOException, ServletException {
httpServletResponse.setContentType("text/html;charset=utf-8");
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
request.setHandled(true);
AgentManager agentManager = AgentManager.getInstance();
String pathContext = request.getPathInfo();
String separator = File.separator;
if (pathContext.toUpperCase().contains(
separator + AgentConstants.TEMPERATURE_CONTROL)) {
httpServletResponse.getWriter().println(
agentManager.getTemperature());
} else if (pathContext.toUpperCase().contains(
separator + AgentConstants.HUMIDITY_CONTROL)) {
httpServletResponse.getWriter().println(
agentManager.getHumidity());
} else if (pathContext.toUpperCase().contains(
separator + AgentConstants.BULB_CONTROL)) {
String[] pathVariables = pathContext.split(separator);
if (pathVariables.length != 3) {
httpServletResponse.getWriter().println(
"Invalid BULB-control received by the device. Need to be in " +
"'{host}:{port}/BULB/{ON|OFF}' format.");
return;
}
String switchState = pathVariables[2];
if (switchState == null) {
httpServletResponse.getWriter().println(
"Please specify switch-status of the BULB.");
} else {
boolean status = switchState.toUpperCase().equals(
AgentConstants.CONTROL_ON);
AgentCoreOperations.changeBulbStatus(status);
httpServletResponse.getWriter().println("Bulb is " + (status ?
AgentConstants.CONTROL_ON : AgentConstants.CONTROL_OFF));
}
} else {
httpServletResponse.getWriter().println(
"Invalid control command received by the device.");
}
}
});
}
@Override
public void publishDeviceData(int publishInterval) {
Runnable pushDataRunnable = new Runnable() {
@Override
public void run() {
int responseCode = -1;
String deviceOwner = agentManager.getAgentConfigs().getDeviceOwner();
String deviceID = agentManager.getAgentConfigs().getDeviceId();
String pushDataEndPointURL = agentManager.getPushDataAPIEP();
String pushDataPayload = null;
HttpURLConnection httpConnection = null;
try {
httpConnection = getHttpConnection(agentManager.getPushDataAPIEP());
httpConnection.setRequestMethod(AgentConstants.HTTP_POST);
httpConnection.setRequestProperty("Authorization", "Bearer " +
agentManager.getAgentConfigs().getAuthToken());
httpConnection.setRequestProperty("Content-Type",
AgentConstants.APPLICATION_JSON_TYPE);
double currentTemperature = agentManager.getTemperature();
pushDataPayload = String.format(AgentConstants.PUSH_DATA_PAYLOAD, deviceOwner,
deviceID,
(agentManager.getDeviceIP() + ":" + port),
currentTemperature);
if (log.isDebugEnabled()) {
log.debug(AgentConstants.LOG_APPENDER + "Push Data Payload is: " +
pushDataPayload);
}
httpConnection.setDoOutput(true);
DataOutputStream dataOutPutWriter = new DataOutputStream(
httpConnection.getOutputStream());
dataOutPutWriter.writeBytes(pushDataPayload);
dataOutPutWriter.flush();
dataOutPutWriter.close();
responseCode = httpConnection.getResponseCode();
httpConnection.disconnect();
log.info(AgentConstants.LOG_APPENDER + "Message - '" + pushDataPayload +
"' was published to server at: " + httpConnection.getURL());
} catch (ProtocolException exception) {
String errorMsg =
"Protocol specific error occurred when trying to set method to " +
AgentConstants.HTTP_POST + " for:" + pushDataEndPointURL;
log.error(AgentConstants.LOG_APPENDER + errorMsg);
} catch (IOException exception) {
String errorMsg =
"An IO error occurred whilst trying to get the response code from: " +
pushDataEndPointURL + " for a " + AgentConstants.HTTP_POST +
" " + "method.";
log.error(AgentConstants.LOG_APPENDER + errorMsg);
} catch (CommunicationHandlerException exception) {
log.error(AgentConstants.LOG_APPENDER +
"Error encountered whilst trying to create HTTP-Connection " +
"to IoT-Server EP at: " +
pushDataEndPointURL);
}
if (responseCode == HttpStatus.CONFLICT_409 ||
responseCode == HttpStatus.PRECONDITION_FAILED_412) {
log.warn(AgentConstants.LOG_APPENDER +
"DeviceIP is being Re-Registered due to Push-Data failure " +
"with response code: " +
responseCode);
registerThisDevice();
} else if (responseCode != HttpStatus.NO_CONTENT_204) {
if (log.isDebugEnabled()) {
log.error(AgentConstants.LOG_APPENDER + "Status Code: " + responseCode +
" encountered whilst trying to Push-Device-Data to IoT " +
"Server at: " +
agentManager.getPushDataAPIEP());
}
}
if (log.isDebugEnabled()) {
log.debug(AgentConstants.LOG_APPENDER + "Push-Data call with payload - " +
pushDataPayload + ", to IoT Server returned status " +
responseCode);
}
}
};
dataPushServiceHandler = service.scheduleAtFixedRate(pushDataRunnable, publishInterval,
publishInterval,
TimeUnit.SECONDS);
}
@Override
public void disconnect() {
Runnable stopConnection = new Runnable() {
public void run() {
while (isConnected()) {
try {
dataPushServiceHandler.cancel(true);
connectorServiceHandler.cancel(true);
closeConnection();
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.warn(AgentConstants.LOG_APPENDER +
"Unable to 'STOP' HTTP server at port: " + port);
}
try {
Thread.sleep(timeoutInterval);
} catch (InterruptedException e1) {
log.error(AgentConstants.LOG_APPENDER +
"HTTP-Termination: Thread Sleep Interrupt " +
"Exception");
}
}
}
}
};
Thread terminatorThread = new Thread(stopConnection);
terminatorThread.setDaemon(true);
terminatorThread.start();
}
@Override
public void processIncomingMessage(Object message) {
}
public void registerThisDevice() {
final Runnable ipRegistration = new Runnable() {
@Override
public void run() {
while (isConnected()) {
try {
int responseCode = registerDeviceIP(
agentManager.getAgentConfigs().getDeviceOwner(),
agentManager.getAgentConfigs().getDeviceId());
if (responseCode == HttpStatus.OK_200) {
break;
} else {
log.error(AgentConstants.LOG_APPENDER +
"Device Registration with IoT Server at:" + " " +
agentManager.getIpRegistrationEP() +
" failed with response - '" + responseCode + ":" +
HttpStatus.getMessage(responseCode) + "'");
}
} catch (AgentCoreOperationException exception) {
log.error(AgentConstants.LOG_APPENDER +
"Error encountered whilst trying to register the " +
"Device's IP at: " +
agentManager.getIpRegistrationEP() +
".\nCheck whether the network-interface provided is " +
"accurate");
}
try {
Thread.sleep(timeoutInterval);
} catch (InterruptedException e1) {
log.error(AgentConstants.LOG_APPENDER +
"Device Registration: Thread Sleep Interrupt Exception");
}
}
}
};
Thread ipRegisterThread = new Thread(ipRegistration);
ipRegisterThread.setDaemon(true);
ipRegisterThread.start();
}
/**
* This method calls the "Register-API" of the IoT Server in order to register the device's IP
* against its ID.
*
* @param deviceOwner the owner of the device by whose name the agent was downloaded.
* (Read from configuration file)
* @param deviceID the deviceId that is auto-generated whilst downloading the agent.
* (Read from configuration file)
* @return the status code of the HTTP-Post call to the Register-API of the IoT-Server
* @throws AgentCoreOperationException if any errors occur when an HTTPConnection session is
* created
*/
private int registerDeviceIP(String deviceOwner, String deviceID)
throws AgentCoreOperationException {
int responseCode = -1;
String networkInterface = "wlan0"; //agentManager.getNetworkInterface();
String deviceIPAddress = getDeviceIP(networkInterface);
if (deviceIPAddress == null) {
throw new AgentCoreOperationException(
"An IP address could not be retrieved for the selected network interface - '" +
networkInterface + ".");
}
agentManager.setDeviceIP(deviceIPAddress);
log.info(AgentConstants.LOG_APPENDER + "Device IP Address: " + deviceIPAddress);
String deviceIPRegistrationEP = agentManager.getIpRegistrationEP();
String registerEndpointURLString =
deviceIPRegistrationEP + File.separator + deviceOwner + File.separator + deviceID +
File.separator + deviceIPAddress + File.separator + port;
if (log.isDebugEnabled()) {
log.debug(AgentConstants.LOG_APPENDER + "DeviceIP Registration EndPoint: " +
registerEndpointURLString);
}
HttpURLConnection httpConnection;
try {
httpConnection = getHttpConnection(registerEndpointURLString);
} catch (CommunicationHandlerException e) {
String errorMsg =
"Protocol specific error occurred when trying to fetch an HTTPConnection to:" +
" " +
registerEndpointURLString;
log.error(AgentConstants.LOG_APPENDER + errorMsg);
throw new AgentCoreOperationException();
}
try {
httpConnection.setRequestMethod(AgentConstants.HTTP_POST);
httpConnection.setRequestProperty("Authorization", "Bearer " +
agentManager.getAgentConfigs().getAuthToken());
httpConnection.setDoOutput(true);
responseCode = httpConnection.getResponseCode();
} catch (ProtocolException exception) {
String errorMsg = "Protocol specific error occurred when trying to set method to " +
AgentConstants.HTTP_POST + " for:" + registerEndpointURLString;
log.error(AgentConstants.LOG_APPENDER + errorMsg);
throw new AgentCoreOperationException(errorMsg, exception);
} catch (IOException exception) {
String errorMsg = "An IO error occurred whilst trying to get the response code from:" +
" " +
registerEndpointURLString + " for a " + AgentConstants.HTTP_POST + " method.";
log.error(AgentConstants.LOG_APPENDER + errorMsg);
throw new AgentCoreOperationException(errorMsg, exception);
}
log.info(AgentConstants.LOG_APPENDER + "DeviceIP - " + deviceIPAddress +
", registration with IoT Server at : " +
agentManager.getAgentConfigs().getHTTPS_ServerEndpoint() +
" returned status " +
responseCode);
return responseCode;
}
/*------------------------------------------------------------------------------------------*/
/* Utility methods relevant to creating and sending HTTP requests to the Iot-Server */
/*------------------------------------------------------------------------------------------*/
/**
* This method is used to get the IP of the device in which the agent is run on.
*
* @return the IP Address of the device
* @throws AgentCoreOperationException if any errors occur whilst trying to get the IP address
*/
private String getDeviceIP() throws AgentCoreOperationException {
try {
return Inet4Address.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
String errorMsg = "Error encountered whilst trying to get the device IP address.";
log.error(AgentConstants.LOG_APPENDER + errorMsg);
throw new AgentCoreOperationException(errorMsg, e);
}
}
/**
* This is an overloaded method that fetches the public IPv4 address of the given network
* interface
*
* @param networkInterfaceName the network-interface of whose IPv4 address is to be retrieved
* @return the IP Address iof the device
* @throws AgentCoreOperationException if any errors occur whilst trying to get details of the
* given network interface
*/
private String getDeviceIP(String networkInterfaceName) throws
AgentCoreOperationException {
String ipAddress = null;
try {
Enumeration<InetAddress> interfaceIPAddresses = NetworkInterface.getByName(
networkInterfaceName).getInetAddresses();
for (; interfaceIPAddresses.hasMoreElements(); ) {
InetAddress ip = interfaceIPAddresses.nextElement();
ipAddress = ip.getHostAddress();
if (log.isDebugEnabled()) {
log.debug(AgentConstants.LOG_APPENDER + "IP Address: " + ipAddress);
}
if (CommunicationUtils.validateIPv4(ipAddress)) {
return ipAddress;
}
}
} catch (SocketException | NullPointerException exception) {
String errorMsg =
"Error encountered whilst trying to get IP Addresses of the network interface: " +
networkInterfaceName +
".\nPlease check whether the name of the network interface used is correct";
log.error(AgentConstants.LOG_APPENDER + errorMsg);
throw new AgentCoreOperationException(errorMsg, exception);
}
return ipAddress;
}
}