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.reference;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.List;
023import java.util.Set;
024import java.util.UUID;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.log4j.Logger;
028
029import service.tut.pori.apilta.sensors.datatypes.Measurement;
030import service.tut.pori.apilta.sensors.datatypes.MeasurementList;
031import service.tut.pori.apilta.sensors.datatypes.SensorTask;
032import service.tut.pori.tasks.Definitions;
033import service.tut.pori.tasks.datatypes.TaskBackend;
034import service.tut.pori.tasks.datatypes.TaskBackend.Status;
035import core.tut.pori.http.parameters.DataGroups;
036import core.tut.pori.http.parameters.DateIntervalParameter.Interval;
037import core.tut.pori.http.parameters.Limits;
038import core.tut.pori.users.UserIdentity;
039
040/**
041 * The reference implementations for Content Analysis Service.
042 *
043 */
044public final class SensorsReferenceCore {
045  private static final int BUFFER_SIZE = 256;
046  private static final SensorsXMLObjectCreator CREATOR = new SensorsXMLObjectCreator(null);
047  private static final DataGroups DATA_GROUPS_TRAFFIC_DATA = new DataGroups(DataGroups.DATA_GROUP_ALL);
048  private static final Logger LOGGER = Logger.getLogger(SensorsReferenceCore.class);
049  
050  /**
051   * 
052   */
053  private SensorsReferenceCore(){
054    // nothing needed
055  }
056
057  /**
058   * 
059   * @param limits
060   * @return measurements
061   */
062  public static MeasurementList generateTrafficData(Limits limits) {
063    MeasurementList datalist = new MeasurementList();
064    datalist.setMeasurements(CREATOR.generateMeasurementList(null, null, DATA_GROUPS_TRAFFIC_DATA, limits, null));
065    return datalist;
066  }
067
068  /**
069   * 
070   * @param authenticatedUser 
071   * @param backendId if null, value is automatically generated
072   * @param taskIds optional ids, one random id will be generated if none is given
073   * @param taskType
074   * @return task details for a submitted task
075   */
076  public static SensorTask generateTaskDetails(UserIdentity authenticatedUser, Long backendId, Collection<String> taskIds, String taskType) {
077    if(UserIdentity.isValid(authenticatedUser)){
078      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
079    }
080    return CREATOR.generateTaskDetails((backendId == null ? Math.abs(CREATOR.getRandom().nextLong()) : backendId), taskIds, taskType);
081  }
082  
083  /**
084   * 
085   * @param authenticatedUser 
086   * @param dataGroups
087   * @param limits
088   * @param taskId 
089   * @param taskType
090   * @return task details for a finished task
091   */
092  public static SensorTask generateTaskResults(UserIdentity authenticatedUser, DataGroups dataGroups, Limits limits, String taskId, String taskType) {
093    if(UserIdentity.isValid(authenticatedUser)){
094      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
095    }
096    return CREATOR.generateTaskResults(null, dataGroups, limits, (StringUtils.isBlank(taskId) ? null : Arrays.asList(taskId)), taskType);
097  }
098
099  /**
100   * reference implementation of the method for adding a task to a back end
101   * 
102   * Note: this will not call the callback URI with task finished
103   *  
104   * @param authenticatedUser 
105   * @param task
106   * @throws IllegalArgumentException on invalid task
107   */
108  public static void addTask(UserIdentity authenticatedUser, SensorTask task) throws IllegalArgumentException {
109    if(UserIdentity.isValid(authenticatedUser)){
110      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
111    }
112    
113    if(createTask(authenticatedUser, task) == null){
114      throw new IllegalArgumentException("Invalid task.");
115    }
116    
117    List<String> taskId = task.getTaskIds();
118    if(taskId == null || taskId.size() != 1){
119      throw new IllegalArgumentException("Task must have exactly one identifier.");
120    }
121    
122    if(task.getTaskTypes().contains(Definitions.TASK_TYPE_VIRTUAL)){
123      throw new IllegalArgumentException("This back end cannot process tasks of type: "+Definitions.TASK_TYPE_VIRTUAL);
124    }
125    
126    List<TaskBackend> backends = task.getBackends();
127    if(backends.size() != 1){
128      throw new IllegalArgumentException("Only a single back end can be given for add task."); // add task always targeted to a single back end
129    }
130  }
131
132  /**
133   * 
134   * @param authenticatedUser 
135   * @param task
136   * @throws IllegalArgumentException on invalid task
137   */
138  public static void taskFinished(UserIdentity authenticatedUser, SensorTask task) throws IllegalArgumentException {
139    if(UserIdentity.isValid(authenticatedUser)){
140      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
141    }
142    
143    List<String> taskIds = task.getTaskIds();
144    if(taskIds == null || taskIds.isEmpty()){
145      throw new IllegalArgumentException("Invalid task: task identifier missing.");
146    }
147    
148    if(!SensorTask.isValid(task) || task.getConditions() != null){ // it is enough to check that either conditions or output is not present as the validity is checked through isValid()
149      throw new IllegalArgumentException("Invalid task.");
150    }
151  }
152
153  /**
154   * Note: this will not schedule back end calls even if the task contains back ends (is not virtual)
155   * 
156   * @param authenticatedUser 
157   * @param task
158   * @return id for the created task
159   * @throws IllegalArgumentException on invalid task
160   */
161  public static String createTask(UserIdentity authenticatedUser, SensorTask task) throws IllegalArgumentException {
162    Set<String> taskTypes = task.getTaskTypes();
163    if(taskTypes == null || taskTypes.isEmpty()){
164      throw new IllegalArgumentException("Invalid task: no task type.");
165    }
166    
167    List<TaskBackend> backends = task.getBackends();
168    if(backends == null){
169      throw new IllegalArgumentException("Invalid task: no back ends.");
170    }
171    for(TaskBackend backend : backends){ // reset all status information to not started before task validation
172      backend.setStatus(Status.NOT_STARTED);
173    }
174    
175    if(UserIdentity.isValid(authenticatedUser)){
176      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
177    }
178    
179    if(!SensorTask.isValid(task) || task.getConditions() == null || !UserIdentity.isValid(task.getUserId())){ // it is enough to check that either conditions or output exists as the validity is checked through isValid()
180      throw new IllegalArgumentException("Invalid task.");
181    }
182    
183    return UUID.randomUUID().toString();
184  }
185  
186  /**
187   * Note: this will not schedule back end calls even if the task contains back ends (is not virtual)
188   * 
189   * @param authenticatedUser 
190   * @param task
191   * @return id for the created task
192   * @throws IllegalArgumentException on invalid task
193   */
194  public static String modifyTask(UserIdentity authenticatedUser, SensorTask task) throws IllegalArgumentException {
195    List<String> taskIds = task.getTaskIds();
196    if(taskIds == null || taskIds.size() != 1){
197      throw new IllegalArgumentException("The modified task must have exactly one task identifier.");
198    }
199    createTask(authenticatedUser, task); // use create task to validate
200    return taskIds.iterator().next();
201  }
202
203  /**
204   * 
205   * @param authenticatedUser 
206   * @param backendId 
207   * @param file the contents of the file are simply iterated as raw byte data (all content is discarded)
208   * @return randomly generated guid for the file
209   * @throws IllegalArgumentException on bad data
210   */
211  @SuppressWarnings("unused")
212  public static String createFile(UserIdentity authenticatedUser, Long backendId, InputStream file) throws IllegalArgumentException {
213    try {
214      byte[] buffer = new byte[BUFFER_SIZE];
215      while(file.read(buffer) > 0){
216        // simply discard all data
217      }
218    } catch (IOException ex) {
219      LOGGER.error(ex, ex);
220      throw new IllegalArgumentException("Failed to read file.");
221    }
222    if(UserIdentity.isValid(authenticatedUser)){
223      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
224    }
225    return CREATOR.createGUID(); // accept any file, return random GUID
226  }
227
228  /**
229   * 
230   * @param authenticatedUser
231   * @param backendIdFilter
232   * @param createdFilter
233   * @param dataGroups 
234   * @param limits
235   * @param measurementIdFilter
236   * @param taskIds 
237   * @return randomly generated measurement id list
238   */
239  public static MeasurementList getMeasurements(UserIdentity authenticatedUser, long[] backendIdFilter, Set<Interval> createdFilter, DataGroups dataGroups, Limits limits, List<String> measurementIdFilter, Collection<String> taskIds) {
240    if(UserIdentity.isValid(authenticatedUser)){
241      LOGGER.debug("Authenticated user, id: "+authenticatedUser.getUserId()); // simply log the user id for debug
242    }
243    
244    if(taskIds != null && !taskIds.isEmpty()) {
245      LOGGER.debug("Ignored task identifiers.");
246    }
247    
248    List<Measurement> measurements = CREATOR.generateMeasurementList(backendIdFilter, createdFilter, dataGroups, limits, measurementIdFilter);
249    if(measurements == null){
250      LOGGER.debug("Empty measurement list generated based on the given parameters.");
251      return null;
252    }
253    MeasurementList list = new MeasurementList();
254    list.setMeasurements(measurements);
255    return list;
256  }
257
258  /**
259   * 
260   * @param authenticatedUser
261   * @param backendId
262   * @param dataGroups
263   * @param limits
264   * @param taskIds
265   * @param taskType
266   * @return pseudo-randomly generated task
267   */
268  @SuppressWarnings("unused")
269  public static SensorTask queryTaskDetails(UserIdentity authenticatedUser, Long backendId, DataGroups dataGroups, Limits limits, List<String> taskIds, String taskType) {
270    if(!DataGroups.isEmpty(dataGroups)) {
271      LOGGER.debug("Ignored data groups.");
272    }
273    return generateTaskDetails(authenticatedUser, backendId, taskIds, taskType);
274  }
275}