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