001/** 002 * Copyright 2016 Tampere University of Technology, Pori Department 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package service.tut.pori.apilta.sensors; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023import java.util.UUID; 024 025import org.apache.log4j.Logger; 026import org.springframework.jdbc.core.JdbcTemplate; 027import org.springframework.transaction.TransactionStatus; 028import org.springframework.transaction.support.TransactionCallback; 029 030import core.tut.pori.dao.clause.SQLClause.SQLType; 031import core.tut.pori.http.parameters.DataGroups; 032import core.tut.pori.http.parameters.Limits; 033import service.tut.pori.apilta.sensors.datatypes.Condition; 034import service.tut.pori.apilta.sensors.datatypes.Output; 035import service.tut.pori.apilta.sensors.datatypes.SensorTask; 036import service.tut.pori.tasks.TaskDAO; 037import service.tut.pori.tasks.datatypes.Task; 038 039/** 040 * task dao for the sensors service 041 * 042 */ 043public class SensorTaskDAO extends TaskDAO { 044 private static final Logger LOGGER = Logger.getLogger(SensorTaskDAO.class); 045 /* tables */ 046 private static final String TABLE_MEASUREMENTS_TASKS_CONDITIONS = DATABASE+".measurements_tasks_conditions"; 047 private static final String TABLE_MEASUREMENTS_TASKS_OUTPUTS = DATABASE+".measurements_tasks_outputs"; 048 /* columns */ 049 private static final String COLUMN_CONDITION_ID = "condition_id"; 050 private static final String COLUMN_CONDITION_KEY = "condition_key"; 051 private static final String COLUMN_CONDITION_VALUE = "condition_value"; 052 private static final String COLUMN_FEATURE = "feature"; 053 /* sql */ 054 private static final String SQL_DELETE_CONDITIONS = "DELETE FROM "+TABLE_MEASUREMENTS_TASKS_CONDITIONS+" WHERE "+Definitions.COLUMN_TASK_ID+"=?"; 055 private static final String SQL_DELETE_OUTPUT = "DELETE FROM "+TABLE_MEASUREMENTS_TASKS_OUTPUTS+" WHERE "+Definitions.COLUMN_TASK_ID+"=?"; 056 private static final String SQL_INSERT_CONDITION = "INSERT INTO "+TABLE_MEASUREMENTS_TASKS_CONDITIONS+" ("+Definitions.COLUMN_TASK_ID+", "+COLUMN_CONDITION_ID+", "+COLUMN_CONDITION_KEY+", "+COLUMN_CONDITION_VALUE+", "+COLUMN_ROW_CREATED+") VALUES (?,?,?,?,NOW())"; 057 private static final int[] SQL_INSERT_CONDITION_SQL_TYPES = {SQLType.STRING.toInt(), SQLType.STRING.toInt(), SQLType.STRING.toInt(), SQLType.STRING.toInt()}; 058 private static final String SQL_INSERT_OUTPUT = "INSERT INTO "+TABLE_MEASUREMENTS_TASKS_OUTPUTS+" ("+Definitions.COLUMN_TASK_ID+", "+COLUMN_FEATURE+", "+COLUMN_ROW_CREATED+") VALUES (?,?,NOW())"; 059 private static final int[] SQL_INSERT_OUTPUT_SQL_TYPES = {SQLType.STRING.toInt(), SQLType.STRING.toInt()}; 060 private static final String SQL_GET_CONDITIONS = "SELECT "+COLUMN_CONDITION_ID+", "+COLUMN_CONDITION_KEY+", "+COLUMN_CONDITION_VALUE+" FROM "+TABLE_MEASUREMENTS_TASKS_CONDITIONS+" WHERE "+Definitions.COLUMN_TASK_ID+"=? ORDER BY "+COLUMN_CONDITION_ID; 061 private static final String SQL_GET_OUTPUT = "SELECT "+COLUMN_FEATURE+" FROM "+TABLE_MEASUREMENTS_TASKS_OUTPUTS+" WHERE "+Definitions.COLUMN_TASK_ID+"=?"; 062 private static final int[] SQL_TYPE_TASK_ID = {SQLType.STRING.toInt()}; 063 064 @Override 065 public String createTask(Task task) { 066 if(task instanceof SensorTask){ 067 return createTask((SensorTask) task); 068 }else{ 069 LOGGER.debug("Using super class for task insertion..."); 070 return super.createTask(task); 071 } 072 } 073 074 /** 075 * 076 * @param task 077 * @return identifier of the created task 078 */ 079 public String createTask(SensorTask task) { 080 return getTransactionTemplate().execute(new TransactionCallback<String>() { 081 @Override 082 public String doInTransaction(TransactionStatus arg0) { 083 String taskId = SensorTaskDAO.super.createTask(task); 084 if(taskId == null){ 085 LOGGER.warn("Failed to create base task."); 086 return null; 087 } 088 089 addConditions(task.getConditions(), taskId); 090 addOutput(task.getOutput(), taskId); 091 092 return taskId; 093 } 094 }); 095 } 096 097 /** 098 * 099 * @param output 100 * @param taskId 101 */ 102 private void addOutput(List<Output> output, String taskId) { 103 if(output == null || output.isEmpty()){ 104 LOGGER.warn("No output for task, id: "+taskId); 105 return; 106 } 107 108 JdbcTemplate t = getJdbcTemplate(); 109 Object[] ob = {taskId, null}; 110 for(Output o : output) { 111 ob[1] = o.getFeature(); 112 t.update(SQL_INSERT_OUTPUT, ob, SQL_INSERT_OUTPUT_SQL_TYPES); 113 } 114 } 115 116 /** 117 * 118 * @param conditions 119 * @param taskId 120 */ 121 private void addConditions(List<Condition> conditions, String taskId) { 122 if(conditions == null || conditions.isEmpty()){ 123 LOGGER.warn("No conditions for task, id: "+taskId); 124 return; 125 } 126 127 JdbcTemplate t = getJdbcTemplate(); 128 Object[] ob = {taskId, null, null, null}; 129 for(Condition condition : conditions){ 130 ob[1] = UUID.randomUUID().toString(); // generate random identifier for this condition 131 for(Entry<String, String> entry : condition.getConditions().entrySet()){ 132 ob[2] = entry.getKey(); 133 ob[3] = entry.getValue(); 134 t.update(SQL_INSERT_CONDITION, ob, SQL_INSERT_CONDITION_SQL_TYPES); 135 } 136 } 137 } 138 139 @Override 140 public boolean updateTask(Task task) throws IllegalArgumentException { 141 if(task instanceof SensorTask){ 142 return updateTask((SensorTask) task); 143 }else{ 144 LOGGER.debug("Using super class for task update..."); 145 return super.updateTask(task); 146 } 147 } 148 149 /** 150 * 151 * @param task 152 * @return true on success 153 */ 154 public boolean updateTask(SensorTask task) { 155 return getTransactionTemplate().execute(new TransactionCallback<Boolean>() { 156 @Override 157 public Boolean doInTransaction(TransactionStatus status) { 158 if(!SensorTaskDAO.super.updateTask(task)){ 159 LOGGER.warn("Base task update failed, aborting update..."); 160 return Boolean.FALSE; 161 } 162 163 for(String taskId : task.getTaskIds()){ 164 removeConditions(taskId); 165 addConditions(task.getConditions(), taskId); 166 removeOutput(taskId); 167 addOutput(task.getOutput(), taskId); 168 } 169 170 return Boolean.TRUE; 171 } 172 }); 173 } 174 175 /** 176 * 177 * @param taskId 178 */ 179 private void removeOutput(String taskId) { 180 getJdbcTemplate().update(SQL_DELETE_OUTPUT, new Object[]{taskId}, SQL_TYPE_TASK_ID); 181 } 182 183 /** 184 * 185 * @param taskId 186 */ 187 private void removeConditions(String taskId) { 188 getJdbcTemplate().update(SQL_DELETE_CONDITIONS, new Object[]{taskId}, SQL_TYPE_TASK_ID); 189 } 190 191 @Override 192 public SensorTask getTask(Long backendId, DataGroups dataGroups, Limits limits, String taskId) { 193 Task task = super.getTask(backendId, dataGroups, limits, taskId); 194 if(task == null) { 195 LOGGER.debug("Task not found, id: "+taskId); 196 return null; 197 } 198 199 SensorTask sTask = new SensorTask(task); 200 sTask.setConditions(getConditions(taskId)); 201 sTask.setOutput(getOuput(taskId)); 202 return sTask; 203 } 204 205 /** 206 * 207 * @param taskId 208 * @return list of output or null if none was found 209 */ 210 private List<Output> getOuput(String taskId) { 211 List<String> features = getJdbcTemplate().queryForList(SQL_GET_OUTPUT, new Object[]{taskId}, SQL_TYPE_TASK_ID, String.class); 212 if(features.isEmpty()){ 213 LOGGER.warn("No output for task, id: "+taskId); 214 return null; 215 } 216 217 ArrayList<Output> output = new ArrayList<>(features.size()); 218 for(String feature : features) { 219 Output o = new Output(); 220 o.setFeature(feature); 221 output.add(o); 222 } 223 224 return output; 225 } 226 227 /** 228 * 229 * @param taskId 230 * @return list of conditions or null if none was found 231 */ 232 private List<Condition> getConditions(String taskId) { 233 List<Map<String, Object>> rows = getJdbcTemplate().queryForList(SQL_GET_CONDITIONS, new Object[]{taskId}, SQL_TYPE_TASK_ID); 234 if(rows.isEmpty()){ 235 LOGGER.warn("No conditions for task, id: "+taskId); 236 return null; 237 } 238 239 ArrayList<Condition> conditions = new ArrayList<>(); 240 String conditionId = null; 241 Condition condition = null; 242 for(Map<String, Object> row : rows){ 243 Object tempId = row.get(COLUMN_CONDITION_ID); 244 if(!tempId.equals(conditionId)){ // the result list is sorted by condition id, so the condition collection changes when the id changes 245 condition = new Condition(); 246 condition.setConditions(new HashMap<>()); 247 conditions.add(condition); 248 conditionId = (String) tempId; 249 } 250 condition.getConditions().put((String) row.get(COLUMN_CONDITION_KEY), (String) row.get(COLUMN_CONDITION_VALUE)); 251 } // for rows 252 return conditions; 253 } 254 255 @Override 256 public void removeTask(String taskId) { 257 getTransactionTemplate().execute(new TransactionCallback<Void>() { 258 259 @Override 260 public Void doInTransaction(TransactionStatus status) { 261 removeConditions(taskId); 262 removeOutput(taskId); 263 SensorTaskDAO.super.removeTask(taskId); 264 return null; 265 } 266 }); 267 } 268} 269