/*
* Copyright 2011-16 Fraunhofer ISE
*
* This file is part of OpenMUC.
* For more information visit http://www.openmuc.org
*
* OpenMUC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenMUC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenMUC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openmuc.framework.server.restws.servlets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.openmuc.framework.config.ArgumentSyntaxException;
import org.openmuc.framework.config.ChannelConfig;
import org.openmuc.framework.config.ChannelScanInfo;
import org.openmuc.framework.config.ConfigService;
import org.openmuc.framework.config.ConfigWriteException;
import org.openmuc.framework.config.DeviceConfig;
import org.openmuc.framework.config.DriverConfig;
import org.openmuc.framework.config.DriverNotAvailableException;
import org.openmuc.framework.config.IdCollisionException;
import org.openmuc.framework.config.RootConfig;
import org.openmuc.framework.config.ScanException;
import org.openmuc.framework.dataaccess.Channel;
import org.openmuc.framework.dataaccess.DataAccessService;
import org.openmuc.framework.dataaccess.DeviceState;
import org.openmuc.framework.lib.json.Const;
import org.openmuc.framework.lib.json.FromJson;
import org.openmuc.framework.lib.json.ToJson;
import org.openmuc.framework.lib.json.exceptions.MissingJsonObjectException;
import org.openmuc.framework.lib.json.exceptions.RestConfigIsNotCorrectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
public class DeviceResourceServlet extends GenericServlet {
private static final long serialVersionUID = 4619892734239871891L;
private final static Logger logger = LoggerFactory.getLogger(DeviceResourceServlet.class);
private DataAccessService dataAccess;
private ConfigService configService;
private RootConfig rootConfig;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
String[] pathAndQueryString = checkIfItIsACorrectRest(request, response, logger);
if (pathAndQueryString != null) {
setConfigAccess();
String deviceID, configField;
String pathInfo = pathAndQueryString[0];
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
List<String> deviceList = doGetDeviceList();
response.setStatus(HttpServletResponse.SC_OK);
ToJson json = new ToJson();
if (pathInfo.equals("/")) {
json.addStringList(Const.DEVICES, deviceList);
}
else {
deviceID = pathInfoArray[0].replace("/", "");
if (deviceList.contains(deviceID)) {
List<Channel> deviceChannelList = doGetDeviceChannelList(deviceID);
DeviceState deviceState = configService.getDeviceState(deviceID);
if (pathInfoArray.length == 1) {
json.addChannelRecordList(deviceChannelList);
json.addDeviceState(deviceState);
}
else if (pathInfoArray[1].equalsIgnoreCase(Const.STATE)) {
json.addDeviceState(deviceState);
}
else if (pathInfoArray.length > 1 && pathInfoArray[1].equals(Const.CHANNELS)) {
json.addChannelList(deviceChannelList);
json.addDeviceState(deviceState);
}
else if (pathInfoArray.length == 2 && pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
doGetConfigs(json, deviceID, response);
}
else if (pathInfoArray.length == 3 && pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
configField = pathInfoArray[2];
doGetConfigField(json, deviceID, configField, response);
}
else if (pathInfoArray[1].equalsIgnoreCase(Const.SCAN)) {
String settings = request.getParameter(Const.SETTINGS);
List<ChannelScanInfo> channelScanInfoList = scanForAllChannels(deviceID, settings, response);
json.addChannelScanInfoList(channelScanInfoList);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest device is not available, DeviceID = " + deviceID);
}
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest device is not available, DeviceID = " + deviceID);
}
}
sendJson(json, response);
}
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
String[] pathAndQueryString = checkIfItIsACorrectRest(request, response, logger);
if (pathAndQueryString != null) {
setConfigAccess();
String pathInfo = pathAndQueryString[ServletLib.PATH_ARRAY_NR];
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
String deviceID = pathInfoArray[0].replace("/", "");
FromJson json = new FromJson(ServletLib.getJsonText(request));
if (pathInfoArray.length == 1) {
setAndWriteDeviceConfig(deviceID, response, json, false);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available.", " Rest Path = ", request.getPathInfo());
}
}
}
@Override
public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
String[] pathAndQueryString = checkIfItIsACorrectRest(request, response, logger);
if (pathAndQueryString != null) {
setConfigAccess();
String pathInfo = pathAndQueryString[ServletLib.PATH_ARRAY_NR];
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
String deviceID = pathInfoArray[0].replace("/", "");
FromJson json = new FromJson(ServletLib.getJsonText(request));
if (pathInfoArray.length < 1) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available.", " Rest Path = ", request.getPathInfo());
}
else {
DeviceConfig deviceConfig = rootConfig.getDevice(deviceID);
if (deviceConfig != null && pathInfoArray.length == 2
&& pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
setAndWriteDeviceConfig(deviceID, response, json, true);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available.", " Rest Path = ", request.getPathInfo());
}
}
}
}
@Override
public void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/json");
String[] pathAndQueryString = checkIfItIsACorrectRest(request, response, logger);
if (pathAndQueryString != null) {
setConfigAccess();
String pathInfo = pathAndQueryString[0];
String deviceID = null;
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
deviceID = pathInfoArray[0].replace("/", "");
DeviceConfig deviceConfig = rootConfig.getDevice(deviceID);
if (pathInfoArray.length != 1) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available", " Path Info = ", request.getPathInfo());
}
else if (deviceConfig == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Device \"" + deviceID + "\" does not exist.");
}
else {
try {
deviceConfig.delete();
configService.setConfig(rootConfig);
configService.writeConfigToFile();
if (rootConfig.getDriver(deviceID) == null) {
response.setStatus(HttpServletResponse.SC_OK);
}
else {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
logger, "Not able to delete driver ", deviceID);
}
} catch (ConfigWriteException e) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Not able to write into config.");
e.printStackTrace();
}
}
}
}
private List<ChannelScanInfo> scanForAllChannels(String deviceID, String settings, HttpServletResponse response) {
List<ChannelScanInfo> channelList = new ArrayList<>();
List<ChannelScanInfo> scannedDevicesList;
try {
scannedDevicesList = configService.scanForChannels(deviceID, settings);
for (ChannelScanInfo scannedDevice : scannedDevicesList) {
channelList.add(scannedDevice);
}
} catch (UnsupportedOperationException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Device does not support scanning.", " deviceId = ", deviceID);
} catch (DriverNotAvailableException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest device is not available.", " deviceId = ", deviceID);
} catch (ArgumentSyntaxException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"Argument syntax was wrong.", " deviceId = ", deviceID, " Settings = ", settings);
} catch (ScanException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Error while scan device channels", " deviceId = ", deviceID, " Settings = ", settings);
}
return channelList;
}
private List<Channel> doGetDeviceChannelList(String deviceID) {
List<Channel> deviceChannelList = new ArrayList<>();
Collection<ChannelConfig> channelConfig;
channelConfig = rootConfig.getDevice(deviceID).getChannels();
for (ChannelConfig chCf : channelConfig) {
deviceChannelList.add(dataAccess.getChannel(chCf.getId()));
}
return deviceChannelList;
}
private List<String> doGetDeviceList() {
List<String> deviceList = new ArrayList<>();
Collection<DriverConfig> driverConfig;
driverConfig = rootConfig.getDrivers();
Collection<DeviceConfig> deviceConfig = new ArrayList<>();
for (DriverConfig drvCfg : driverConfig) {
String driverId = drvCfg.getId();
deviceConfig.addAll(rootConfig.getDriver(driverId).getDevices());
}
for (DeviceConfig devCfg : deviceConfig) {
deviceList.add(devCfg.getId());
}
return deviceList;
}
private void doGetConfigField(ToJson json, String deviceID, String configField, HttpServletResponse response)
throws IOException {
DeviceConfig deviceConfig = rootConfig.getDevice(deviceID);
if (deviceConfig != null) {
JsonObject jsoConfigAll = ToJson.getDeviceConfigAsJsonObject(deviceConfig);
if (jsoConfigAll == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Could not find JSON object \"configs\"");
}
else {
JsonElement jseConfigField = jsoConfigAll.get(configField);
if (jseConfigField == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest config field is not available.", " configField = ", configField);
}
else {
JsonObject jso = new JsonObject();
jso.add(configField, jseConfigField);
json.addJsonObject(Const.CONFIGS, jso);
}
}
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest channel is not available.", " ChannelID = ", deviceID);
}
}
private void doGetConfigs(ToJson json, String deviceID, HttpServletResponse response) throws IOException {
DeviceConfig deviceConfig;
deviceConfig = rootConfig.getDevice(deviceID);
if (deviceConfig != null) {
json.addDeviceConfig(deviceConfig);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest device is not available.", " DeviceID = ", deviceID);
}
}
private boolean setAndWriteDeviceConfig(String deviceID, HttpServletResponse response, FromJson json,
boolean isHTTPPut) {
boolean ok = false;
try {
if (isHTTPPut) {
ok = setAndWriteHttpPutDeviceConfig(deviceID, response, json);
}
else {
ok = setAndWriteHttpPostDeviceConfig(deviceID, response, json);
}
} catch (JsonSyntaxException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_CONFLICT, logger,
"JSON syntax is wrong.");
} catch (ConfigWriteException e) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_CONFLICT, logger,
"Could not write device \"", deviceID, "\".");
e.printStackTrace();
} catch (RestConfigIsNotCorrectException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"Not correct formed device config json.", " JSON = ", json.getJsonObject().toString());
} catch (Error e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
e.getMessage());
} catch (MissingJsonObjectException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger, e.getMessage());
e.printStackTrace();
} catch (IllegalStateException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_CONFLICT, logger, e.getMessage());
}
return ok;
}
private boolean setAndWriteHttpPutDeviceConfig(String deviceID, HttpServletResponse response, FromJson json)
throws JsonSyntaxException, ConfigWriteException, RestConfigIsNotCorrectException,
MissingJsonObjectException, IllegalStateException {
boolean ok = false;
DeviceConfig deviceConfig = rootConfig.getDevice(deviceID);
if (deviceConfig != null) {
try {
json.setDeviceConfig(deviceConfig, deviceID);
} catch (IdCollisionException e) {
}
configService.setConfig(rootConfig);
configService.writeConfigToFile();
response.setStatus(HttpServletResponse.SC_OK);
ok = true;
}
else {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Not able to access to device ", deviceID);
}
return ok;
}
private boolean setAndWriteHttpPostDeviceConfig(String deviceID, HttpServletResponse response, FromJson json)
throws JsonSyntaxException, ConfigWriteException, RestConfigIsNotCorrectException, Error,
MissingJsonObjectException, IllegalStateException {
boolean ok = false;
DriverConfig driverConfig;
DeviceConfig deviceConfig = rootConfig.getDevice(deviceID);
JsonObject jso = json.getJsonObject();
String driverID = jso.get(Const.DRIVER).getAsString();
if (driverID != null) {
driverConfig = rootConfig.getDriver(driverID);
}
else {
throw new Error("No driver ID in JSON");
}
if (driverConfig == null) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_CONFLICT, logger,
"Driver does not exists: ", driverID);
}
else if (deviceConfig != null) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_CONFLICT, logger,
"Device already exists: ", deviceID);
}
else {
try {
deviceConfig = driverConfig.addDevice(deviceID);
json.setDeviceConfig(deviceConfig, deviceID);
} catch (IdCollisionException e) {
}
configService.setConfig(rootConfig);
configService.writeConfigToFile();
response.setStatus(HttpServletResponse.SC_OK);
ok = true;
}
return ok;
}
private void setConfigAccess() {
this.dataAccess = handleDataAccessService(null);
this.configService = handleConfigService(null);
this.rootConfig = handleRootConfig(null);
}
}