/*
* 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.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.openmuc.framework.config.ChannelConfig;
import org.openmuc.framework.config.ConfigService;
import org.openmuc.framework.config.ConfigWriteException;
import org.openmuc.framework.config.DeviceConfig;
import org.openmuc.framework.config.IdCollisionException;
import org.openmuc.framework.config.RootConfig;
import org.openmuc.framework.data.Flag;
import org.openmuc.framework.data.Record;
import org.openmuc.framework.data.Value;
import org.openmuc.framework.data.ValueType;
import org.openmuc.framework.dataaccess.Channel;
import org.openmuc.framework.dataaccess.DataAccessService;
import org.openmuc.framework.dataaccess.DataLoggerNotAvailableException;
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 ChannelResourceServlet extends GenericServlet {
private static final long serialVersionUID = -702876016040151438L;
private final static Logger logger = LoggerFactory.getLogger(ChannelResourceServlet.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 channelID, fromParameter, untilParameter, configField;
String pathInfo = pathAndQueryString[0];
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
response.setStatus(HttpServletResponse.SC_OK);
ToJson json = new ToJson();
if (pathInfo.equals("/")) {
doGetAllChannels(json);
}
else {
channelID = pathInfoArray[0].replace("/", "");
if (pathInfoArray.length == 1) {
doGetSpecificChannel(json, channelID, response);
}
else if (pathInfoArray.length == 2 && pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
doGetConfigs(json, channelID, response);
}
else if (pathInfoArray.length == 3 && pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
configField = pathInfoArray[2];
doGetConfigField(json, channelID, configField, response);
}
else if (pathInfoArray.length == 2 && pathInfoArray[1].startsWith(Const.HISTORY)) {
fromParameter = request.getParameter("from");
untilParameter = request.getParameter("until");
doGetHistory(json, channelID, fromParameter, untilParameter, response);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available.", " Rest Path = ", request.getPathInfo());
}
}
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 channelID = pathInfoArray[0].replace("/", "");
FromJson json = new FromJson(ServletLib.getJsonText(request));
if (pathInfoArray.length == 1) {
setAndWriteChannelConfig(channelID, 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 channelID = 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 {
ChannelConfig channelConfig = rootConfig.getChannel(channelID);
if (channelConfig != null) {
if (pathInfoArray.length == 2 && pathInfoArray[1].equalsIgnoreCase(Const.CONFIGS)) {
setAndWriteChannelConfig(channelID, response, json, true);
}
else if (pathInfoArray.length == 2 && pathInfoArray[1].equalsIgnoreCase(Const.LATESTRECORD)) {
doSetRecord(channelID, response, json);
}
else if (pathInfoArray.length == 1) {
doWriteChannel(channelID, response, json);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available.", " Rest Path = ", request.getPathInfo());
}
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested channel is not available.", " Channel = ", channelID);
}
}
}
}
@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 channelId;
String[] pathInfoArray = ServletLib.getPathInfoArray(pathInfo);
ChannelConfig channelConfig;
channelId = pathInfoArray[0].replace("/", "");
channelConfig = rootConfig.getChannel(channelId);
if (pathInfoArray.length != 1) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest path is not available", " Path Info = ", request.getPathInfo());
}
else if (channelConfig == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Channel \"" + channelId + "\" does not exist.");
}
else {
try {
channelConfig.delete();
configService.setConfig(rootConfig);
configService.writeConfigToFile();
if (rootConfig.getDriver(channelId) == null) {
response.setStatus(HttpServletResponse.SC_OK);
}
else {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
logger, "Not able to delete channel ", channelId);
}
} catch (ConfigWriteException e) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Not able to write into config.");
e.printStackTrace();
}
}
}
}
private void doGetConfigField(ToJson json, String channelID, String configField, HttpServletResponse response)
throws IOException {
ChannelConfig channelConfig = rootConfig.getChannel(channelID);
if (channelConfig != null) {
JsonObject jsoConfigAll = ToJson.getChannelConfigAsJsonObject(channelConfig);
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 = ", channelID);
}
}
private void doGetConfigs(ToJson json, String channelID, HttpServletResponse response) throws IOException {
ChannelConfig channelConfig = rootConfig.getChannel(channelID);
if (channelConfig != null) {
json.addChannelConfig(channelConfig);
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest channel is not available.", " ChannelID = ", channelID);
}
}
private void doGetHistory(ToJson json, String channelID, String fromParameter, String untilParameter,
HttpServletResponse response) {
long fromTimeStamp = 0, untilTimeStamp = 0;
List<String> channelIDs = dataAccess.getAllIds();
List<Record> records = null;
if (channelIDs.contains(channelID)) {
Channel channel = dataAccess.getChannel(channelID);
try {
fromTimeStamp = Long.parseLong(fromParameter);
untilTimeStamp = Long.parseLong(untilParameter);
} catch (NumberFormatException ex) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_BAD_REQUEST, logger,
"From/To value is not a long number.");
}
try {
records = channel.getLoggedRecords(fromTimeStamp, untilTimeStamp);
} catch (DataLoggerNotAvailableException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
e.getMessage());
} catch (IOException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger, e.getMessage());
}
json.addRecordList(records, channel.getValueType());
}
}
private boolean setAndWriteChannelConfig(String channelID, HttpServletResponse response, FromJson json,
boolean isHTTPPut) {
boolean ok = false;
try {
if (isHTTPPut) {
ok = setAndWriteHttpPutChannelConfig(channelID, response, json);
}
else {
ok = setAndWriteHttpPostChannelConfig(channelID, 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 channel \"", channelID, "\".");
e.printStackTrace();
} catch (RestConfigIsNotCorrectException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"Not correct formed channel 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());
} catch (IllegalStateException e) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_CONFLICT, logger, e.getMessage());
}
return ok;
}
private boolean setAndWriteHttpPutChannelConfig(String channelID, HttpServletResponse response, FromJson json)
throws JsonSyntaxException, ConfigWriteException, RestConfigIsNotCorrectException,
MissingJsonObjectException, IllegalStateException {
boolean ok = false;
ChannelConfig channelConfig = rootConfig.getChannel(channelID);
if (channelConfig != null) {
try {
json.setChannelConfig(channelConfig, channelID);
configService.setConfig(rootConfig);
configService.writeConfigToFile();
} catch (IdCollisionException e) {
}
response.setStatus(HttpServletResponse.SC_OK);
ok = true;
}
else {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, logger,
"Not able to access to channel ", channelID);
}
return ok;
}
private boolean setAndWriteHttpPostChannelConfig(String channelID, HttpServletResponse response, FromJson json)
throws JsonSyntaxException, ConfigWriteException, RestConfigIsNotCorrectException, Error,
MissingJsonObjectException, IllegalStateException {
boolean ok = false;
DeviceConfig deviceConfig;
ChannelConfig channelConfig = rootConfig.getChannel(channelID);
JsonObject jso = json.getJsonObject();
String deviceID = jso.get(Const.DEVICE).getAsString();
if (deviceID != null) {
deviceConfig = rootConfig.getDevice(deviceID);
}
else {
throw new Error("No device ID in JSON");
}
if (deviceConfig == null) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_CONFLICT, logger,
"Device does not exists: ", deviceID);
}
else if (channelConfig != null) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_CONFLICT, logger,
"Channel already exists: ", channelID);
}
else {
try {
channelConfig = deviceConfig.addChannel(channelID);
json.setChannelConfig(channelConfig, channelID);
if ((channelConfig.getValueType() == ValueType.STRING
|| channelConfig.getValueType() == ValueType.BYTE_ARRAY)
&& channelConfig.getValueTypeLength() == null) {
ServletLib.sendHTTPErrorAndLogErr(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"Channel ", channelID, " with value type ", channelConfig.getValueType().toString(),
", missing valueTypeLength.");
channelConfig.delete();
}
else {
configService.setConfig(rootConfig);
configService.writeConfigToFile();
}
} catch (IdCollisionException e) {
}
response.setStatus(HttpServletResponse.SC_OK);
ok = true;
}
return ok;
}
private void doGetSpecificChannel(ToJson json, String chId, HttpServletResponse response) throws IOException {
Channel channel = dataAccess.getChannel(chId);
if (channel != null) {
Record record = channel.getLatestRecord();
json.addRecord(record, channel.getValueType());
}
else {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_FOUND, logger,
"Requested rest channel is not available, ChannelID = " + chId);
}
}
private void doGetAllChannels(ToJson json) {
List<String> ids = dataAccess.getAllIds();
List<Channel> channels = new ArrayList<>(ids.size());
for (String id : ids) {
channels.add(dataAccess.getChannel(id));
}
json.addChannelRecordList(channels);
}
private void doSetRecord(String channelID, HttpServletResponse response, FromJson json) throws ClassCastException {
Channel channel = dataAccess.getChannel(channelID);
Record record = json.getRecord(channel.getValueType());
if (record.getFlag() == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"No flag setted.");
}
else if (record.getValue() == null) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_NOT_ACCEPTABLE, logger,
"No value setted.");
}
else {
Long timestamp = record.getTimestamp();
if (timestamp == null) {
timestamp = System.currentTimeMillis();
}
Record rec = new Record(record.getValue(), timestamp, record.getFlag());
channel.setLatestRecord(rec);
}
}
private void doWriteChannel(String channelID, HttpServletResponse response, FromJson json) {
Channel channel = dataAccess.getChannel(channelID);
Value value = json.getValue(channel.getValueType());
Flag flag = channel.write(value);
if (flag != Flag.VALID) {
ServletLib.sendHTTPErrorAndLogDebug(response, HttpServletResponse.SC_CONFLICT, logger,
"Problems by writing to channel. Flag = " + flag.toString());
}
}
private void setConfigAccess() {
this.dataAccess = handleDataAccessService(null);
this.configService = handleConfigService(null);
this.rootConfig = handleRootConfig(null);
}
}