View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java.job;
20  
21  import java.time.OffsetDateTime;
22  import java.util.Objects;
23  import java.util.Optional;
24  import org.apache.syncope.common.lib.types.AuditElements;
25  import org.apache.syncope.common.lib.types.TaskType;
26  import org.apache.syncope.core.persistence.api.dao.TaskDAO;
27  import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
28  import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
29  import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
30  import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
31  import org.apache.syncope.core.provisioning.api.AuditManager;
32  import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
33  import org.apache.syncope.core.provisioning.api.event.JobStatusEvent;
34  import org.apache.syncope.core.provisioning.api.job.JobManager;
35  import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
36  import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
37  import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
38  import org.apache.syncope.core.spring.security.SecureRandomUtils;
39  import org.apache.syncope.core.spring.security.SecurityProperties;
40  import org.quartz.JobExecutionContext;
41  import org.quartz.JobExecutionException;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  import org.slf4j.MDC;
45  import org.springframework.beans.factory.annotation.Autowired;
46  import org.springframework.context.ApplicationEventPublisher;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  public abstract class AbstractSchedTaskJobDelegate<T extends SchedTask> implements SchedTaskJobDelegate {
50  
51      protected static final Logger LOG = LoggerFactory.getLogger(SchedTaskJobDelegate.class);
52  
53      @Autowired
54      protected SecurityProperties securityProperties;
55  
56      protected TaskType taskType;
57  
58      /**
59       * The actual task to be executed.
60       */
61      protected T task;
62  
63      /**
64       * Task execution DAO.
65       */
66      @Autowired
67      protected TaskExecDAO taskExecDAO;
68  
69      /**
70       * Task DAO.
71       */
72      @Autowired
73      protected TaskDAO taskDAO;
74  
75      @Autowired
76      protected TaskUtilsFactory taskUtilsFactory;
77  
78      @Autowired
79      protected TaskDataBinder taskDataBinder;
80  
81      /**
82       * Notification manager.
83       */
84      @Autowired
85      protected NotificationManager notificationManager;
86  
87      /**
88       * Audit manager.
89       */
90      @Autowired
91      protected AuditManager auditManager;
92  
93      @Autowired
94      protected ApplicationEventPublisher publisher;
95  
96      protected boolean interrupt;
97  
98      protected boolean interrupted;
99  
100     protected void setStatus(final String status) {
101         Objects.requireNonNull(task, "Task cannot be undefined");
102         publisher.publishEvent(new JobStatusEvent(this, taskDataBinder.buildRefDesc(task), status));
103     }
104 
105     @Override
106     public void interrupt() {
107         interrupt = true;
108     }
109 
110     @Override
111     public boolean isInterrupted() {
112         return interrupted;
113     }
114 
115     @SuppressWarnings("unchecked")
116     @Transactional
117     @Override
118     public void execute(
119             final TaskType taskType,
120             final String taskKey,
121             final boolean dryRun,
122             final JobExecutionContext context)
123             throws JobExecutionException {
124 
125         this.taskType = taskType;
126         task = (T) taskDAO.find(taskType, taskKey);
127         if (task == null) {
128             throw new JobExecutionException("Task " + taskKey + " not found");
129         }
130 
131         if (!task.isActive()) {
132             LOG.info("Task {} not active, aborting...", taskKey);
133             return;
134         }
135 
136         boolean manageOperationId = Optional.ofNullable(MDC.get(OPERATION_ID)).
137                 map(operationId -> false).
138                 orElseGet(() -> {
139                     MDC.put(OPERATION_ID, SecureRandomUtils.generateRandomUUID().toString());
140                     return true;
141                 });
142 
143         String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
144                 orElse(securityProperties.getAdminUser());
145         TaskExec<SchedTask> execution = taskUtilsFactory.getInstance(taskType).newTaskExec();
146         execution.setStart(OffsetDateTime.now());
147         execution.setTask(task);
148         execution.setExecutor(executor);
149 
150         setStatus("Initialization completed");
151 
152         AuditElements.Result result;
153 
154         try {
155             execution.setMessage(doExecute(dryRun, executor, context));
156             execution.setStatus(TaskJob.Status.SUCCESS.name());
157 
158             result = AuditElements.Result.SUCCESS;
159         } catch (JobExecutionException e) {
160             LOG.error("While executing task {}", taskKey, e);
161             result = AuditElements.Result.FAILURE;
162 
163             execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
164             execution.setStatus(TaskJob.Status.FAILURE.name());
165         }
166         execution.setEnd(OffsetDateTime.now());
167 
168         if (hasToBeRegistered(execution)) {
169             register(execution);
170         }
171         task = (T) taskDAO.save(task);
172 
173         setStatus(null);
174 
175         notificationManager.createTasks(
176                 executor,
177                 AuditElements.EventCategoryType.TASK,
178                 this.getClass().getSimpleName(),
179                 null,
180                 this.getClass().getSimpleName(), // searching for before object is too much expensive ...
181                 result,
182                 task,
183                 execution);
184 
185         auditManager.audit(
186                 executor,
187                 AuditElements.EventCategoryType.TASK,
188                 task.getClass().getSimpleName(),
189                 null,
190                 null, // searching for before object is too much expensive ...
191                 result,
192                 task,
193                 null);
194 
195         if (manageOperationId) {
196             MDC.remove(OPERATION_ID);
197         }
198     }
199 
200     /**
201      * The actual execution, delegated to child classes.
202      *
203      * @param dryRun whether to actually touch the data
204      * @param executor the user executing this task
205      * @param context Quartz' execution context, can be used to pass parameters to the job
206      * @return the task execution status to be set
207      * @throws JobExecutionException if anything goes wrong
208      */
209     protected abstract String doExecute(boolean dryRun, String executor, JobExecutionContext context)
210             throws JobExecutionException;
211 
212     /**
213      * Template method to determine whether this job's task execution has to be persisted or not.
214      *
215      * @param execution task execution
216      * @return whether to persist or not
217      */
218     protected boolean hasToBeRegistered(final TaskExec<?> execution) {
219         return false;
220     }
221 
222     protected void register(final TaskExec<?> execution) {
223         taskExecDAO.saveAndAdd(taskType, task.getKey(), execution);
224     }
225 }