1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.logic;
20
21 import java.lang.reflect.Method;
22 import java.time.OffsetDateTime;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30 import javax.ws.rs.core.Response;
31 import org.apache.commons.lang3.ArrayUtils;
32 import org.apache.commons.lang3.tuple.Pair;
33 import org.apache.commons.lang3.tuple.Triple;
34 import org.apache.syncope.common.lib.SyncopeClientException;
35 import org.apache.syncope.common.lib.form.SyncopeForm;
36 import org.apache.syncope.common.lib.to.ExecTO;
37 import org.apache.syncope.common.lib.to.JobTO;
38 import org.apache.syncope.common.lib.to.MacroTaskTO;
39 import org.apache.syncope.common.lib.to.PropagationTaskTO;
40 import org.apache.syncope.common.lib.to.SchedTaskTO;
41 import org.apache.syncope.common.lib.to.TaskTO;
42 import org.apache.syncope.common.lib.types.AnyTypeKind;
43 import org.apache.syncope.common.lib.types.ClientExceptionType;
44 import org.apache.syncope.common.lib.types.ExecStatus;
45 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
46 import org.apache.syncope.common.lib.types.JobAction;
47 import org.apache.syncope.common.lib.types.JobType;
48 import org.apache.syncope.common.lib.types.TaskType;
49 import org.apache.syncope.common.rest.api.RESTHeaders;
50 import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
51 import org.apache.syncope.common.rest.api.beans.ExecSpecs;
52 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
53 import org.apache.syncope.core.persistence.api.dao.JobStatusDAO;
54 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
55 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
56 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
57 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
58 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
59 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
60 import org.apache.syncope.core.persistence.api.entity.Notification;
61 import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
62 import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
63 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
64 import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
65 import org.apache.syncope.core.persistence.api.entity.task.Task;
66 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
67 import org.apache.syncope.core.persistence.api.entity.task.TaskUtils;
68 import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
69 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
70 import org.apache.syncope.core.provisioning.api.job.JobManager;
71 import org.apache.syncope.core.provisioning.api.job.JobNamer;
72 import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
73 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
74 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
75 import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
76 import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate;
77 import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
78 import org.apache.syncope.core.spring.security.AuthContextUtils;
79 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
80 import org.identityconnectors.framework.common.objects.ObjectClass;
81 import org.quartz.JobDataMap;
82 import org.quartz.JobKey;
83 import org.quartz.SchedulerException;
84 import org.springframework.dao.InvalidDataAccessApiUsageException;
85 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
86 import org.springframework.security.access.prepost.PreAuthorize;
87 import org.springframework.transaction.annotation.Transactional;
88
89 public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
90
91 protected final TaskDAO taskDAO;
92
93 protected final TaskExecDAO taskExecDAO;
94
95 protected final ExternalResourceDAO resourceDAO;
96
97 protected final NotificationDAO notificationDAO;
98
99 protected final TaskDataBinder binder;
100
101 protected final PropagationTaskExecutor taskExecutor;
102
103 protected final NotificationJobDelegate notificationJobDelegate;
104
105 protected final TaskUtilsFactory taskUtilsFactory;
106
107 public TaskLogic(
108 final JobManager jobManager,
109 final SchedulerFactoryBean scheduler,
110 final JobStatusDAO jobStatusDAO,
111 final TaskDAO taskDAO,
112 final TaskExecDAO taskExecDAO,
113 final ExternalResourceDAO resourceDAO,
114 final NotificationDAO notificationDAO,
115 final TaskDataBinder binder,
116 final PropagationTaskExecutor taskExecutor,
117 final NotificationJobDelegate notificationJobDelegate,
118 final TaskUtilsFactory taskUtilsFactory) {
119
120 super(jobManager, scheduler, jobStatusDAO);
121
122 this.taskDAO = taskDAO;
123 this.taskExecDAO = taskExecDAO;
124 this.resourceDAO = resourceDAO;
125 this.notificationDAO = notificationDAO;
126 this.binder = binder;
127 this.taskExecutor = taskExecutor;
128 this.notificationJobDelegate = notificationJobDelegate;
129 this.taskUtilsFactory = taskUtilsFactory;
130 }
131
132 protected void securityChecks(final String entitlement, final String realm) {
133 Set<String> authRealms = AuthContextUtils.getAuthorizations().get(entitlement);
134 if (authRealms.stream().noneMatch(r -> realm.startsWith(r))) {
135 throw new DelegatedAdministrationException(realm, MacroTask.class.getSimpleName(), null);
136 }
137 }
138
139 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_CREATE + "')")
140 public <T extends SchedTaskTO> T createSchedTask(final TaskType type, final T taskTO) {
141 TaskUtils taskUtils = taskUtilsFactory.getInstance(taskTO);
142 if (taskUtils.getType() != type) {
143 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
144 sce.getElements().add("Found " + type + ", expected " + taskUtils.getType());
145 throw sce;
146 }
147
148 if (taskUtils.getType() == TaskType.MACRO) {
149 securityChecks(IdRepoEntitlement.TASK_CREATE, ((MacroTaskTO) taskTO).getRealm());
150 }
151
152 SchedTask task = binder.createSchedTask(taskTO, taskUtils);
153 task = taskDAO.save(task);
154
155 try {
156 jobManager.register(
157 task,
158 task.getStartAt(),
159 AuthContextUtils.getUsername());
160 } catch (Exception e) {
161 LOG.error("While registering quartz job for task " + task.getKey(), e);
162
163 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
164 sce.getElements().add(e.getMessage());
165 throw sce;
166 }
167
168 return binder.getTaskTO(task, taskUtils, false);
169 }
170
171 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_UPDATE + "')")
172 public <T extends SchedTaskTO> T updateSchedTask(final TaskType type, final SchedTaskTO taskTO) {
173 SchedTask task = taskDAO.find(type, taskTO.getKey());
174 if (task == null) {
175 throw new NotFoundException("Task " + taskTO.getKey());
176 }
177
178 TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
179 if (taskUtils.getType() != type) {
180 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
181 sce.getElements().add("Found " + type + ", expected " + taskUtils.getType());
182 throw sce;
183 }
184
185 if (taskUtils.getType() == TaskType.MACRO) {
186 securityChecks(IdRepoEntitlement.TASK_UPDATE, ((MacroTask) task).getRealm().getFullPath());
187 securityChecks(IdRepoEntitlement.TASK_UPDATE, ((MacroTaskTO) taskTO).getRealm());
188 }
189
190 binder.updateSchedTask(task, taskTO, taskUtils);
191 task = taskDAO.save(task);
192 try {
193 jobManager.register(
194 task,
195 task.getStartAt(),
196 AuthContextUtils.getUsername());
197 } catch (Exception e) {
198 LOG.error("While registering quartz job for task " + task.getKey(), e);
199
200 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
201 sce.getElements().add(e.getMessage());
202 throw sce;
203 }
204
205 return binder.getTaskTO(task, taskUtils, false);
206 }
207
208 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_LIST + "')")
209 @Transactional(readOnly = true)
210 public <T extends TaskTO> Pair<Integer, List<T>> search(
211 final TaskType type,
212 final String resource,
213 final String notification,
214 final AnyTypeKind anyTypeKind,
215 final String entityKey,
216 final int page,
217 final int size,
218 final List<OrderByClause> orderByClauses,
219 final boolean details) {
220
221 try {
222 if (type == null) {
223 throw new IllegalArgumentException("type is required");
224 }
225
226 ExternalResource resourceObj = resourceDAO.find(resource);
227 if (resource != null && resourceObj == null) {
228 throw new IllegalArgumentException("Missing External Resource: " + resource);
229 }
230
231 Notification notificationObj = notificationDAO.find(notification);
232 if (notification != null && notificationObj == null) {
233 throw new IllegalArgumentException("Missing Notification: " + notification);
234 }
235
236 int count = taskDAO.count(
237 type,
238 resourceObj,
239 notificationObj,
240 anyTypeKind,
241 entityKey);
242
243 List<T> result = taskDAO.findAll(
244 type,
245 resourceObj,
246 notificationObj,
247 anyTypeKind,
248 entityKey,
249 page,
250 size,
251 orderByClauses).stream().
252 <T>map(task -> binder.getTaskTO(task, taskUtilsFactory.getInstance(type), details)).
253 collect(Collectors.toList());
254
255 return Pair.of(count, result);
256 } catch (IllegalArgumentException | InvalidDataAccessApiUsageException e) {
257 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
258 sce.getElements().add(e.getMessage());
259 throw sce;
260 }
261 }
262
263 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
264 @Transactional(readOnly = true)
265 public <T extends TaskTO> T read(final TaskType type, final String key, final boolean details) {
266 Task<?> task = taskDAO.find(type, key);
267 if (task == null) {
268 throw new NotFoundException("Task " + key);
269 }
270
271 TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
272 if (type != null && taskUtils.getType() != type) {
273 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
274 sce.getElements().add("Found " + type + ", expected " + taskUtils.getType());
275 throw sce;
276 }
277
278 if (taskUtils.getType() == TaskType.MACRO) {
279 securityChecks(IdRepoEntitlement.TASK_READ, ((MacroTask) task).getRealm().getFullPath());
280 }
281
282 return binder.getTaskTO(task, taskUtilsFactory.getInstance(task), details);
283 }
284
285 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
286 @Transactional(readOnly = true)
287 public SyncopeForm getMacroTaskForm(final String key) {
288 MacroTask task = taskDAO.find(key).
289 filter(MacroTask.class::isInstance).map(MacroTask.class::cast).
290 orElseThrow(() -> new NotFoundException("MacroTask " + key));
291
292 securityChecks(IdRepoEntitlement.TASK_READ, task.getRealm().getFullPath());
293
294 return binder.getMacroTaskForm(task);
295 }
296
297 protected ExecTO doExecute(
298 final Task<?> task,
299 final OffsetDateTime startAt,
300 final Map<String, Object> additionalDataMap) {
301
302 if (startAt != null && startAt.isBefore(OffsetDateTime.now())) {
303 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
304 sce.getElements().add("Cannot schedule in the past");
305 throw sce;
306 }
307
308 TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
309 String executor = AuthContextUtils.getUsername();
310
311 ExecTO result = null;
312 switch (taskUtils.getType()) {
313 case PROPAGATION:
314 PropagationTask propagationTask = (PropagationTask) task;
315 PropagationTaskInfo taskInfo = new PropagationTaskInfo(
316 propagationTask.getResource(),
317 propagationTask.getOperation(),
318 new ObjectClass(propagationTask.getObjectClassName()),
319 propagationTask.getAnyTypeKind(),
320 propagationTask.getAnyType(),
321 propagationTask.getEntityKey(),
322 propagationTask.getConnObjectKey(),
323 propagationTask.getPropagationData());
324 taskInfo.setKey(propagationTask.getKey());
325 taskInfo.setOldConnObjectKey(propagationTask.getOldConnObjectKey());
326
327 TaskExec<PropagationTask> propExec = taskExecutor.execute(
328 taskInfo, new DefaultPropagationReporter(), executor);
329 result = binder.getExecTO(propExec);
330 break;
331
332 case NOTIFICATION:
333 TaskExec<NotificationTask> notExec = notificationJobDelegate.executeSingle(
334 (NotificationTask) task, executor);
335 result = binder.getExecTO(notExec);
336 break;
337
338 case SCHEDULED:
339 case PULL:
340 case PUSH:
341 case MACRO:
342 if (!((SchedTask) task).isActive()) {
343 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
344 sce.getElements().add("Task " + task.getKey() + " is not active");
345 throw sce;
346 }
347
348 if (taskUtils.getType() == TaskType.MACRO) {
349 securityChecks(IdRepoEntitlement.TASK_EXECUTE, ((MacroTask) task).getRealm().getFullPath());
350 }
351
352 try {
353 Map<String, Object> jobDataMap = jobManager.register(
354 (SchedTask) task,
355 startAt,
356 executor);
357 jobDataMap.putAll(additionalDataMap);
358
359 if (startAt == null) {
360 scheduler.getScheduler().triggerJob(JobNamer.getJobKey(task), new JobDataMap(jobDataMap));
361 }
362 } catch (Exception e) {
363 LOG.error("While executing task {}", task, e);
364
365 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
366 sce.getElements().add(e.getMessage());
367 throw sce;
368 }
369
370 result = new ExecTO();
371 result.setJobType(JobType.TASK);
372 result.setRefKey(task.getKey());
373 result.setRefDesc(binder.buildRefDesc(task));
374 result.setStart(OffsetDateTime.now());
375 result.setExecutor(executor);
376 result.setStatus("JOB_FIRED");
377 result.setMessage("Job fired; waiting for results...");
378 break;
379
380 default:
381 }
382
383 return result;
384 }
385
386 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
387 @Override
388 public ExecTO execute(final ExecSpecs specs) {
389 Task<?> task = taskDAO.find(specs.getKey()).
390 orElseThrow(() -> new NotFoundException("Task " + specs.getKey()));
391
392 return doExecute(
393 task,
394 specs.getStartAt(),
395 Map.of(JobManager.DRY_RUN_JOBDETAIL_KEY, specs.getDryRun()));
396 }
397
398 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
399 public ExecTO execute(final ExecSpecs specs, final SyncopeForm macroTaskForm) {
400 MacroTask task = taskDAO.find(specs.getKey()).
401 filter(MacroTask.class::isInstance).map(MacroTask.class::cast).
402 orElseThrow(() -> new NotFoundException("MacroTask " + specs.getKey()));
403
404 return doExecute(
405 task,
406 specs.getStartAt(),
407 Map.of(JobManager.DRY_RUN_JOBDETAIL_KEY, specs.getDryRun(),
408 MacroJobDelegate.MACRO_TASK_FORM_JOBDETAIL_KEY, macroTaskForm));
409 }
410
411 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
412 public <T extends TaskTO> T delete(final TaskType type, final String key) {
413 Task<?> task = taskDAO.find(type, key);
414 if (task == null) {
415 throw new NotFoundException("Task " + key);
416 }
417
418 TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
419 if (type != null && taskUtils.getType() != type) {
420 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
421 sce.getElements().add("Found " + type + ", expected " + taskUtils.getType());
422 throw sce;
423 }
424
425 if (taskUtils.getType() == TaskType.MACRO) {
426 securityChecks(IdRepoEntitlement.TASK_DELETE, ((MacroTask) task).getRealm().getFullPath());
427 }
428
429 T taskToDelete = binder.getTaskTO(task, taskUtils, true);
430
431 if (TaskType.SCHEDULED == taskUtils.getType()
432 || TaskType.PULL == taskUtils.getType()
433 || TaskType.PUSH == taskUtils.getType()
434 || TaskType.MACRO == taskUtils.getType()) {
435
436 jobManager.unregister(task);
437 }
438
439 taskDAO.delete(task);
440 return taskToDelete;
441 }
442
443 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
444 @Override
445 public Pair<Integer, List<ExecTO>> listExecutions(
446 final String key,
447 final OffsetDateTime before,
448 final OffsetDateTime after,
449 final int page,
450 final int size,
451 final List<OrderByClause> orderByClauses) {
452
453 Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
454
455 if (task instanceof MacroTask) {
456 securityChecks(IdRepoEntitlement.TASK_READ, ((MacroTask) task).getRealm().getFullPath());
457 }
458
459 Integer count = taskExecDAO.count(task, before, after);
460
461 List<ExecTO> result = taskExecDAO.findAll(task, before, after, page, size, orderByClauses).stream().
462 map(exec -> binder.getExecTO(exec)).collect(Collectors.toList());
463
464 return Pair.of(count, result);
465 }
466
467 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_LIST + "')")
468 @Override
469 public List<ExecTO> listRecentExecutions(final int max) {
470 return taskExecDAO.findRecent(max).stream().
471 map(exec -> {
472 try {
473 if (exec.getTask() instanceof MacroTask) {
474 securityChecks(IdRepoEntitlement.TASK_DELETE,
475 ((MacroTask) exec.getTask()).getRealm().getFullPath());
476 }
477
478 return binder.getExecTO(exec);
479 } catch (DelegatedAdministrationException e) {
480 LOG.error("Skip executions for command task", e);
481 return null;
482 }
483 }).
484 filter(Objects::nonNull).
485 collect(Collectors.toList());
486 }
487
488 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
489 @Override
490 public ExecTO deleteExecution(final String execKey) {
491 TaskExec<?> exec = taskExecDAO.find(execKey).
492 orElseThrow(() -> new NotFoundException("Task execution " + execKey));
493
494 if (exec.getTask() instanceof MacroTask) {
495 securityChecks(IdRepoEntitlement.TASK_DELETE, ((MacroTask) exec.getTask()).getRealm().getFullPath());
496 }
497
498 ExecTO executionToDelete = binder.getExecTO(exec);
499 taskExecDAO.delete(exec);
500 return executionToDelete;
501 }
502
503 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
504 @Override
505 public List<BatchResponseItem> deleteExecutions(
506 final String key,
507 final OffsetDateTime before,
508 final OffsetDateTime after) {
509
510 Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
511
512 List<BatchResponseItem> batchResponseItems = new ArrayList<>();
513
514 taskExecDAO.findAll(task, before, after, -1, -1, List.of()).forEach(exec -> {
515 BatchResponseItem item = new BatchResponseItem();
516 item.getHeaders().put(RESTHeaders.RESOURCE_KEY, List.of(exec.getKey()));
517 batchResponseItems.add(item);
518
519 try {
520 if (exec.getTask() instanceof MacroTask) {
521 securityChecks(IdRepoEntitlement.TASK_DELETE,
522 ((MacroTask) exec.getTask()).getRealm().getFullPath());
523 }
524
525 taskExecDAO.delete(exec);
526 item.setStatus(Response.Status.OK.getStatusCode());
527 } catch (Exception e) {
528 LOG.error("Error deleting execution {} of task {}", exec.getKey(), key, e);
529 item.setStatus(Response.Status.BAD_REQUEST.getStatusCode());
530 item.setContent(ExceptionUtils2.getFullStackTrace(e));
531 }
532 });
533
534 return batchResponseItems;
535 }
536
537 @Override
538 protected Triple<JobType, String, String> getReference(final JobKey jobKey) {
539 String key = JobNamer.getTaskKeyFromJobName(jobKey.getName());
540
541 Task<?> task = taskDAO.find(key).orElse(null);
542 return task == null || !(task instanceof SchedTask)
543 ? null
544 : Triple.of(JobType.TASK, key, binder.buildRefDesc(task));
545 }
546
547 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_LIST + "')")
548 @Override
549 public List<JobTO> listJobs() {
550 return super.doListJobs(true);
551 }
552
553 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
554 @Override
555 public JobTO getJob(final String key) {
556 Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
557
558 if (task instanceof MacroTask) {
559 securityChecks(IdRepoEntitlement.TASK_READ, ((MacroTask) task).getRealm().getFullPath());
560 }
561
562 JobTO jobTO = null;
563 try {
564 jobTO = getJobTO(JobNamer.getJobKey(task), false);
565 } catch (SchedulerException e) {
566 LOG.error("Problems while retrieving scheduled job {}", JobNamer.getJobKey(task), e);
567
568 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
569 sce.getElements().add(e.getMessage());
570 throw sce;
571 }
572 if (jobTO == null) {
573 throw new NotFoundException("Job for task " + key);
574 }
575 return jobTO;
576 }
577
578 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
579 @Override
580 public void actionJob(final String key, final JobAction action) {
581 Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
582
583 if (task instanceof MacroTask) {
584 securityChecks(IdRepoEntitlement.TASK_EXECUTE, ((MacroTask) task).getRealm().getFullPath());
585 }
586
587 doActionJob(JobNamer.getJobKey(task), action);
588 }
589
590 @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
591 public List<PropagationTaskTO> purgePropagations(
592 final OffsetDateTime since,
593 final List<ExecStatus> statuses,
594 final List<String> resources) {
595
596 return taskDAO.purgePropagations(since, statuses, Optional.ofNullable(resources).
597 map(r -> r.stream().map(resourceDAO::find).
598 filter(Objects::nonNull).collect(Collectors.toList())).
599 orElse(null));
600 }
601
602 @Override
603 protected TaskTO resolveReference(final Method method, final Object... args)
604 throws UnresolvedReferenceException {
605
606 String key = null;
607
608 if (ArrayUtils.isNotEmpty(args)
609 && !"deleteExecution".equals(method.getName()) && !"readExecution".equals(method.getName())) {
610
611 for (int i = 0; key == null && i < args.length; i++) {
612 if (args[i] instanceof String) {
613 key = (String) args[i];
614 } else if (args[i] instanceof TaskTO) {
615 key = ((TaskTO) args[i]).getKey();
616 }
617 }
618 }
619
620 if (key != null) {
621 String taskKey = key;
622 try {
623 Task<?> task = taskDAO.find(taskKey).orElseThrow(() -> new NotFoundException("Task " + taskKey));
624 return binder.getTaskTO(task, taskUtilsFactory.getInstance(task), false);
625 } catch (Throwable ignore) {
626 LOG.debug("Unresolved reference", ignore);
627 throw new UnresolvedReferenceException(ignore);
628 }
629 }
630
631 throw new UnresolvedReferenceException();
632 }
633 }