package com.breakersoft.plow.service; import java.util.List; import java.util.Map; import java.util.UUID; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.breakersoft.plow.Defaults; import com.breakersoft.plow.FilterableJob; import com.breakersoft.plow.Folder; import com.breakersoft.plow.FrameRange; import com.breakersoft.plow.Job; import com.breakersoft.plow.JobId; import com.breakersoft.plow.Layer; import com.breakersoft.plow.MatcherFull; import com.breakersoft.plow.Output; import com.breakersoft.plow.Project; import com.breakersoft.plow.ServiceFull; import com.breakersoft.plow.Task; import com.breakersoft.plow.dao.FolderDao; import com.breakersoft.plow.dao.JobDao; import com.breakersoft.plow.dao.LayerDao; import com.breakersoft.plow.dao.OutputDao; import com.breakersoft.plow.dao.ProjectDao; import com.breakersoft.plow.dao.ServiceDao; import com.breakersoft.plow.dao.TaskDao; import com.breakersoft.plow.event.EventManager; import com.breakersoft.plow.event.JobLaunchEvent; import com.breakersoft.plow.exceptions.JobSpecException; import com.breakersoft.plow.monitor.PlowStats; import com.breakersoft.plow.thrift.DependSpecT; import com.breakersoft.plow.thrift.JobSpecT; import com.breakersoft.plow.thrift.JobState; import com.breakersoft.plow.thrift.LayerSpecT; import com.breakersoft.plow.thrift.TaskFilterT; import com.breakersoft.plow.thrift.TaskSpecT; import com.breakersoft.plow.thrift.TaskState; import com.breakersoft.plow.util.PlowUtils; @Service @Transactional public class JobServiceImpl implements JobService { private static final Logger logger = org.slf4j.LoggerFactory.getLogger(JobServiceImpl.class); @Autowired JobDao jobDao; @Autowired LayerDao layerDao; @Autowired TaskDao taskDao; @Autowired ProjectDao projectDao; @Autowired ServiceDao serviceDao; @Autowired FolderDao folderDao; @Autowired OutputDao outputDao; @Autowired EventManager eventManager; @Autowired DependService dependService; @Autowired FilterService filterService; @Override public JobLaunchEvent launch(JobSpecT jobspec) { logger.info("launching job spec: {} ", jobspec); try { final Project project = projectDao.get(jobspec.getProject()); final FilterableJob job = jobDao.create(project, jobspec, false); final Folder folder = filterJob(job, project); createJobTopology(job, project, jobspec, false); createDependencies(job, jobspec, false); jobDao.updateFrameStatesForLaunch(job); jobDao.updateFrameCountsForLaunch(job); jobDao.setJobState(job, JobState.RUNNING); createPostJob(job, jobspec); //TODO: do this someplace else. (tranny hook or aspect) // Don't want to add jobs to the dispatcher that fail to // commit to the DB. JobLaunchEvent event = new JobLaunchEvent(job, folder, jobspec); eventManager.post(event); logger.info("{} launch success", job.getName()); PlowStats.jobLaunchCount.incrementAndGet(); return event; } catch (Exception e) { PlowStats.jobLaunchFailCount.incrementAndGet(); throw new JobSpecException("Failed to launch job: " + e, e); } } private void createPostJob(Job parentJob, JobSpecT jobspec) { boolean hasPostLayers = false; // check if we have post layers! for (LayerSpecT blayer: jobspec.getLayers()) { if (blayer.isPost) { hasPostLayers = true; break; } } if (!hasPostLayers) { return; } final FilterableJob job = jobDao.create(parentJob, jobspec, true); filterJob(job, parentJob); createJobTopology(job, parentJob, jobspec, true); createDependencies(job, jobspec, true); jobDao.updateFrameStatesForLaunch(job); jobDao.updateFrameCountsForLaunch(job); jobDao.setJobState(job, JobState.POST); jobDao.tiePostJob(parentJob, job); } @Override public boolean shutdown(Job job) { if (jobDao.shutdown(job)) { jobDao.flipPostJob(job); return true; } return false; } private Folder filterJob(FilterableJob job, Project project) { // TODO: fully implement // First put it in the default folder. Folder folder = folderDao.getDefaultFolder(project); jobDao.updateFolder(job, folder); List<MatcherFull> matchers = filterService.getMatchers(job); filterService.filterJob(matchers, job); return folder; } private void createJobTopology( Job job, Project project, JobSpecT jobspec, boolean postJob) { int layerOrder = 0; for (LayerSpecT blayer: jobspec.getLayers()) { if (blayer.isPost != postJob) { continue; } if (blayer.isSetRange()) { logger.info("Creating layer {}, range: {}", blayer.name, blayer.range); final FrameRange frameRange = new FrameRange(blayer.range, blayer.chunk); prepLayer(blayer, frameRange); final Layer layer = layerDao.create(job, blayer, layerOrder); taskDao.batchCreate(layer, frameRange, layerOrder, blayer.minRam); } else if (blayer.isSetTasks()) { logger.info("Creating tasks in layer: {}", blayer.name); prepLayer(blayer, null); final Layer layer = layerDao.create(job, blayer, layerOrder); taskDao.batchCreate(layer, blayer.getTasks(), layerOrder, blayer.minRam); } else { throw new JobSpecException( "Layer {} cannot be launched, has no range or tasks."); } layerOrder++; } } /** * Apply defaults for unset layer values. * * @param layer */ private void prepLayer(LayerSpecT layer, FrameRange frameRange) { // Set a default service name, although it might not be setup // as a valid service. if (!PlowUtils.isValid(layer.getServ())) { logger.info("Setting server on {} to {}", layer.name, Defaults.DEFAULT_SERVICE); layer.setServ(Defaults.DEFAULT_SERVICE); } // Check if there is a service and apply values ServiceFull service = serviceDao.getServiceFull(layer.getServ()); if (service != null) { if (service.isSetMinCores() && !layer.isSetMinCores()) { layer.setMinCores(service.getMinCores()); } if (service.isSetMaxCores() && !layer.isSetMaxCores()) { layer.setMaxCores(service.getMaxCores()); } if (service.isSetMaxRam() && !layer.isSetMaxRam()) { layer.setMaxRam(service.getMaxRam()); } if (service.isSetMinRam() && !layer.isSetMinRam()) { layer.setMinRam(service.getMinRam()); } if (service.isSetMaxRetries() && !layer.isSetMaxRetries()) { layer.setMaxRetries(service.getMaxRetries()); } if (service.isSetTags() && !layer.isSetTags()) { layer.setTags(service.getTags()); } if (service.isSetThreadable() && !layer.isSetThreadable()) { layer.setThreadable(service.isThreadable()); } } if (!layer.isSetMaxCores()) { logger.info("Setting max cores default on {} to {}", layer.name, Defaults.DEFAULT_MAX_CORES); layer.setMaxCores(Defaults.DEFAULT_MAX_CORES); } if (!layer.isSetMinCores()) { logger.info("Setting min cores default on {} to {}", layer.name, Defaults.DEFAULT_MIN_CORES); layer.setMinCores(Defaults.DEFAULT_MIN_CORES); } if (!layer.isSetMinRam()) { logger.info("Setting min ram default on {} to {}", layer.name, Defaults.DEFAULT_MIN_RAM); layer.setMinRam(Defaults.DEFAULT_MIN_RAM); } if (!layer.isSetMaxRam()) { logger.info("Setting max ram default on {} to {}", layer.name, Defaults.DEFAULT_MAX_RAM); layer.setMaxRam(Defaults.DEFAULT_MAX_RAM); } if (!layer.isSetMaxRetries()) { logger.info("Setting max retries default on {} to {}", layer.name, Defaults.DEFAULT_MAX_RETRIES); layer.setMaxRetries(Defaults.DEFAULT_MAX_RETRIES); } if (!layer.isSetThreadable()) { logger.info("Setting threadable default on {} to {}", layer.name, Defaults.DEFAULT_THREADABLE); layer.setThreadable(Defaults.DEFAULT_THREADABLE); } if (!layer.isSetTags()) { logger.info("Setting tags default on {} to {}", layer.name, Defaults.DEFAULT_TAGS); layer.setTags(Defaults.DEFAULT_TAGS); } if (frameRange != null) { if (layer.chunk <= 0) { logger.info("Setting chunk size to %d", frameRange.chunkSize); layer.chunk = frameRange.chunkSize; } } } private void createDependencies(Job job, JobSpecT jspec, boolean postJob) { logger.info("Setting up dependencies in job {}", jspec.name); for (LayerSpecT layer: jspec.getLayers()) { if (layer.isPost != postJob) { continue; } if (layer.isSetDepends()) { for (DependSpecT depend: layer.getDepends()) { dependService.createDepend(job, depend); } } if (layer.isSetTasks()) { for (TaskSpecT task: layer.getTasks()) { if (task.isSetDepends()) { for (DependSpecT depend: task.getDepends()) { dependService.createDepend(job, depend); } } } } } if (jspec.isSetDepends()) { for (DependSpecT depend: jspec.getDepends()) { dependService.createDepend(job, depend); } } } @Override public void setJobMinCores(Job job, int value) { jobDao.setMinCores(job, value); } @Override public void setJobMaxCores(Job job, int value) { jobDao.setMaxCores(job, value); } @Override public Job getJob(UUID id) { return jobDao.get(id); } @Override public Job getActiveJob(UUID id) { return jobDao.getActive(id); } @Override public void setJobAttrs(Job job, Map<String,String> attrs) { jobDao.setAttrs(job, attrs); } @Override @Transactional(readOnly=true) public boolean hasWaitingFrames(Job job) { return jobDao.hasWaitingFrames(job); } @Override @Transactional(readOnly=true) public boolean isJobFinished(JobId job) { return jobDao.isFinished(job); } @Override @Transactional(readOnly=true) public boolean isDispatchable(JobId job) { return jobDao.isDispatchable(job); } @Override @Transactional(readOnly=true) public Task getTask(UUID id) { return taskDao.get(id); } @Override public boolean setTaskState(Task task, TaskState currentState, TaskState newState) { return taskDao.updateState(task, currentState, newState); } @Override public Task getTask(Layer layer, int number) { return taskDao.get(layer, number); } @Override public Layer getLayer(Job job, String layer) { return layerDao.get(job, layer); } @Override public Layer getLayer(Job job, int idx) { return layerDao.get(job, idx); } @Override public Layer getLayer(UUID id) { return layerDao.get(id); } @Override public Output addLayerOutput(Layer layer, String path, Map<String,String> attrs) { return outputDao.addOutput(layer, path, attrs); } @Override @Transactional(readOnly=true) public boolean isLayerComplete(Layer layer) { return layerDao.isFinished(layer); } @Override public boolean setJobState(Job job, JobState state) { return jobDao.setJobState(job, state); } @Override public void setJobPaused(Job job, boolean value) { jobDao.setPaused(job, value); } @Override public boolean isJobPaused(JobId job) { return jobDao.isPaused(job); } @Override @Transactional(readOnly=true) public List<Task> getTasks(TaskFilterT filter) { return taskDao.getTasks(filter); } @Override public boolean setTaskState(Task task, TaskState state) { return taskDao.setTaskState(task, state); } @Override public void setLayerMinCores(Layer layer, int cores) { layerDao.setMinCores(layer, cores); } @Override public void setLayerMaxCores(Layer layer, int cores) { layerDao.setMaxCores(layer, cores); } @Override public void setLayerMinRam(Layer layer, int ram) { layerDao.setMinRam(layer, ram); } @Override public void setLayerTags(Layer layer, List<String> tags) { layerDao.setTags(layer, tags); } @Override public void setLayerThreadable(Layer layer, boolean threadable) { layerDao.setThreadable(layer, threadable); } @Override public void updateOutputAttrs(UUID outputId, Map<String,String> attrs) { outputDao.updateAttrs(outputId, attrs); } @Override public void setOutputAttrs(UUID outputId, Map<String,String> attrs) { outputDao.setAttrs(outputId, attrs); } @Override @Transactional(readOnly=true) public Map<String,String> getOutputAttrs(UUID outputId) { return outputDao.getAttrs(outputId); } }