001/** 002 * Copyright 2015 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.contentanalysis.video; 017 018import java.util.EnumSet; 019import java.util.Iterator; 020import java.util.List; 021 022import org.apache.commons.lang3.StringUtils; 023import org.apache.log4j.Logger; 024import org.quartz.JobExecutionContext; 025import org.quartz.JobExecutionException; 026 027import service.tut.pori.contentanalysis.AnalysisBackend.Capability; 028import service.tut.pori.contentanalysis.AsyncTask; 029import service.tut.pori.contentanalysis.BackendStatus; 030import service.tut.pori.contentanalysis.BackendStatusList; 031import service.tut.pori.contentanalysis.MediaObject; 032import service.tut.pori.contentanalysis.MediaObjectDAO; 033import service.tut.pori.contentanalysis.MediaObjectList; 034import core.tut.pori.context.ServiceInitializer; 035import core.tut.pori.users.UserIdentity; 036 037/** 038 * Analysis task for video content. 039 */ 040public class VideoAnalysisTask extends AsyncTask { 041 private static final Logger LOGGER = Logger.getLogger(VideoAnalysisTask.class); 042 043 @Override 044 public void execute(JobExecutionContext context) throws JobExecutionException { 045 executeAddTask(EnumSet.of(Capability.VIDEO_ANALYSIS), ServiceInitializer.getDAOHandler().getSQLDAO(VideoTaskDAO.class), getTaskId(context.getMergedJobDataMap())); 046 } 047 048 /** 049 * Checks that the given response is for a valid task, that the back-end has permissions for the task, and that the given result data is valid. 050 * On valid data, media objects for the videos will be updated (if there are changes). The given response will be modified to be valid, if possible. 051 * 052 * @param response 053 * @throws IllegalArgumentException 054 */ 055 public static void taskFinished(VideoTaskResponse response) throws IllegalArgumentException { 056 Integer backendId = response.getBackendId(); 057 Long taskId = response.getTaskId(); 058 059 VideoTaskDAO taskDAO = ServiceInitializer.getDAOHandler().getSQLDAO(VideoTaskDAO.class); 060 BackendStatus backendStatus = taskDAO.getBackendStatus(backendId, taskId); 061 if(backendStatus == null){ 062 LOGGER.warn("Backend, id: "+backendId+" returned results for task, not given to the backend. TaskId: "+taskId); 063 throw new IllegalArgumentException("This task is not given for backend, id: "+backendId); 064 } 065 066 TaskStatus status = response.getStatus(); 067 if(status == null){ 068 LOGGER.warn("Task status not available."); 069 status = TaskStatus.UNKNOWN; 070 } 071 backendStatus.setStatus(status); 072 073 try{ 074 VideoList results = response.getVideoList(); 075 if(VideoList.isEmpty(results)){ 076 LOGGER.warn("No results returned by the backendId: "+backendId); 077 return; 078 } 079 080 if(!VideoList.isValid(results)){ 081 LOGGER.warn("Invalid "+Definitions.ELEMENT_VIDEOLIST+"."); 082 } 083 084 VideoDAO videoDAO = ServiceInitializer.getDAOHandler().getSolrDAO(VideoDAO.class); 085 if(!videoDAO.setOwners(results)){ 086 LOGGER.warn("Could not get owner information for all videos."); 087 } 088 089 VideoList associations = new VideoList(); 090 MediaObjectList insert = new MediaObjectList(); 091 MediaObjectList update = new MediaObjectList(); 092 MediaObjectDAO vdao = ServiceInitializer.getDAOHandler().getSolrDAO(MediaObjectDAO.class); 093 for(Iterator<Video> videoIter = results.getVideos().iterator(); videoIter.hasNext();){ 094 Video video = videoIter.next(); 095 String guid = video.getGUID(); 096 UserIdentity userId = video.getOwnerUserId(); 097 if(!UserIdentity.isValid(userId)){ // if this photo does not exist, there won't be userId 098 LOGGER.warn("Ignoring non-existing video, GUID: "+guid+" from backend, id: "+backendId); 099 videoIter.remove(); 100 continue; 101 } 102 BackendStatusList c = video.getBackendStatus(); 103 if(BackendStatusList.isEmpty(c)){ 104 LOGGER.debug("Backend status not available for video, GUID: "+guid); 105 }else if(c.getCombinedStatus() == TaskStatus.ERROR){ 106 LOGGER.warn("Error condition detected for video, GUID: "+guid); 107 }else{ 108 List<BackendStatus> sList = c.getBackendStatuses(); 109 if(sList.size() > 1){ 110 backendStatus.setStatus(TaskStatus.ERROR); 111 throw new IllegalArgumentException("Multiple back-end statuses."); 112 } 113 if(!backendId.equals(sList.get(0).getBackendId())){ 114 backendStatus.setStatus(TaskStatus.ERROR); 115 throw new IllegalArgumentException("Invalid back-end status."); 116 } 117 } 118 MediaObjectList vObjects = video.getMediaObjects(); 119 if(!MediaObjectList.isEmpty(vObjects)){ // make sure all objects have proper user 120 for(MediaObject mediaObject : vObjects.getMediaObjects()){ // check that the objects are valid 121 if(!backendId.equals(mediaObject.getBackendId())){ 122 LOGGER.warn("Task backend id: "+backendId+" does not match the back-end id: "+mediaObject.getBackendId()+" given for media object, objectId: "+mediaObject.getObjectId()); 123 mediaObject.setBackendId(backendId); 124 } 125 mediaObject.setOwnerUserId(userId); 126 } 127 vdao.resolveObjectIds(vObjects); // resolve ids for update/insert sort 128 Video iVideo = null; 129 for(MediaObject vo : vObjects.getMediaObjects()){ // re-sort to to updated and new 130 if(StringUtils.isBlank(vo.getMediaObjectId())){ // no media object id, this is a new one 131 if(iVideo == null){ 132 associations.getVideo(guid); // get target photo for insertion 133 if(iVideo == null){ 134 iVideo = new Video(guid); 135 associations.addVideo(iVideo); 136 } 137 } 138 iVideo.addMediaObject(vo); 139 insert.addMediaObject(vo); 140 }else{ 141 update.addMediaObject(vo); 142 } 143 } // for 144 }else{ 145 LOGGER.warn("Ignored video without objects, GUID: "+guid); 146 videoIter.remove(); 147 } 148 } 149 150 if(MediaObjectList.isEmpty(insert)){ 151 LOGGER.debug("Nothing to insert."); 152 }else if(!MediaObjectList.isValid(insert)){ 153 backendStatus.setStatus(TaskStatus.ERROR); 154 throw new IllegalArgumentException("Invalid media object list."); 155 }else if(!videoDAO.insert(insert)){ 156 LOGGER.warn("Failed to insert new objects."); 157 }else{ 158 videoDAO.associate(associations); 159 } 160 161 if(MediaObjectList.isEmpty(update)){ 162 LOGGER.debug("Nothing to update"); 163 }else if(!MediaObjectList.isValid(update)){ 164 backendStatus.setStatus(TaskStatus.ERROR); 165 throw new IllegalArgumentException("Invalid media object list."); 166 }else if(!videoDAO.update(update)){ 167 LOGGER.warn("Failed to update objects."); 168 } 169 170 taskDAO.updateMediaStatus(results.getVideos(), taskId); 171 VideoContentCore.scheduleBackendFeedback(backendId, results, taskId); 172 } finally { 173 taskDAO.updateTaskStatus(backendStatus, taskId); 174 ServiceInitializer.getEventHandler().publishEvent(new AsyncTaskEvent(backendId, VideoAnalysisTask.class, status, taskId, TaskType.ANALYSIS)); 175 } 176 } 177}