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.persistence.jpa.dao;
20  
21  import java.time.OffsetDateTime;
22  import java.util.ArrayList;
23  import java.util.Comparator;
24  import java.util.List;
25  import java.util.Optional;
26  import java.util.stream.Collectors;
27  import javax.persistence.Query;
28  import org.apache.syncope.common.lib.types.TaskType;
29  import org.apache.syncope.core.persistence.api.dao.TaskDAO;
30  import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
31  import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
32  import org.apache.syncope.core.persistence.api.entity.task.Task;
33  import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
34  import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
35  import org.apache.syncope.core.persistence.jpa.entity.task.AbstractTaskExec;
36  import org.springframework.transaction.annotation.Transactional;
37  import org.springframework.util.ReflectionUtils;
38  
39  public class JPATaskExecDAO extends AbstractDAO<TaskExec<?>> implements TaskExecDAO {
40  
41      protected final TaskDAO taskDAO;
42  
43      protected final TaskUtilsFactory taskUtilsFactory;
44  
45      public JPATaskExecDAO(final TaskDAO taskDAO, final TaskUtilsFactory taskUtilsFactory) {
46          this.taskDAO = taskDAO;
47          this.taskUtilsFactory = taskUtilsFactory;
48      }
49  
50      @SuppressWarnings("unchecked")
51      @Override
52      public <T extends Task<T>> TaskExec<T> find(final TaskType type, final String key) {
53          return (TaskExec<T>) entityManager().find(taskUtilsFactory.getInstance(type).getTaskExecEntity(), key);
54      }
55  
56      @Override
57      public Optional<TaskExec<?>> find(final String key) {
58          TaskExec<?> task = find(TaskType.SCHEDULED, key);
59          if (task == null) {
60              task = find(TaskType.PULL, key);
61          }
62          if (task == null) {
63              task = find(TaskType.PUSH, key);
64          }
65          if (task == null) {
66              task = find(TaskType.MACRO, key);
67          }
68          if (task == null) {
69              task = find(TaskType.PROPAGATION, key);
70          }
71          if (task == null) {
72              task = find(TaskType.NOTIFICATION, key);
73          }
74  
75          return Optional.ofNullable(task);
76      }
77  
78      @SuppressWarnings("unchecked")
79      protected <T extends Task<T>> List<TaskExec<T>> findRecent(final TaskType type, final int max) {
80          Query query = entityManager().createQuery(
81                  "SELECT e FROM " + taskUtilsFactory.getInstance(type).getTaskExecEntity().getSimpleName() + " e "
82                  + "WHERE e.end IS NOT NULL ORDER BY e.end DESC");
83          query.setMaxResults(max);
84  
85          List<Object> result = query.getResultList();
86          return result.stream().map(e -> (TaskExec<T>) e).collect(Collectors.toList());
87      }
88  
89      @Override
90      public List<TaskExec<?>> findRecent(final int max) {
91          List<TaskExec<?>> recent = new ArrayList<>();
92  
93          for (TaskType taskType : TaskType.values()) {
94              recent.addAll(findRecent(taskType, max));
95          }
96  
97          return recent.stream().
98                  sorted(Comparator.comparing(TaskExec<?>::getEnd).reversed()).
99                  limit(max).
100                 collect(Collectors.toList());
101     }
102 
103     @SuppressWarnings("unchecked")
104     protected TaskExec<?> findLatest(final TaskType type, final Task<?> task, final String field) {
105         Query query = entityManager().createQuery(
106                 "SELECT e FROM " + taskUtilsFactory.getInstance(type).getTaskExecEntity().getSimpleName() + " e "
107                 + "WHERE e.task=:task ORDER BY e." + field + " DESC");
108         query.setParameter("task", task);
109         query.setMaxResults(1);
110 
111         List<Object> result = query.getResultList();
112         return result == null || result.isEmpty()
113                 ? null
114                 : (TaskExec<?>) result.get(0);
115     }
116 
117     @Override
118     public TaskExec<?> findLatestStarted(final TaskType type, final Task<?> task) {
119         return findLatest(type, task, "start");
120     }
121 
122     @Override
123     public TaskExec<?> findLatestEnded(final TaskType type, final Task<?> task) {
124         return findLatest(type, task, "end");
125     }
126 
127     protected StringBuilder query(
128             final StringBuilder select,
129             final Task<?> task,
130             final OffsetDateTime before,
131             final OffsetDateTime after) {
132 
133         StringBuilder query = select.
134                 append(taskUtilsFactory.getInstance(task).getTaskExecEntity().getSimpleName()).
135                 append(" e WHERE e.task=:task ");
136         if (before != null) {
137             query.append("AND e.start <= :before ");
138         }
139         if (after != null) {
140             query.append("AND e.start >= :after ");
141         }
142         return query;
143     }
144 
145     @Override
146     public int count(
147             final Task<?> task,
148             final OffsetDateTime before,
149             final OffsetDateTime after) {
150 
151         StringBuilder queryString = query(new StringBuilder("SELECT COUNT(e) FROM "), task, before, after);
152 
153         Query query = entityManager().createQuery(queryString.toString());
154         query.setParameter("task", task);
155         if (before != null) {
156             query.setParameter("before", before);
157         }
158         if (after != null) {
159             query.setParameter("after", after);
160         }
161 
162         return ((Number) query.getSingleResult()).intValue();
163     }
164 
165     protected String toOrderByStatement(final List<OrderByClause> orderByClauses) {
166         StringBuilder statement = new StringBuilder();
167 
168         orderByClauses.forEach(clause -> {
169             String field = clause.getField().trim();
170             if (ReflectionUtils.findField(AbstractTaskExec.class, field) != null) {
171                 statement.append("e.").append(field).append(' ').append(clause.getDirection().name());
172             }
173         });
174 
175         if (statement.length() == 0) {
176             statement.append(" ORDER BY e.id DESC");
177         } else {
178             statement.insert(0, " ORDER BY ");
179         }
180         return statement.toString();
181     }
182 
183     @SuppressWarnings("unchecked")
184     @Override
185     public List<TaskExec<?>> findAll(
186             final Task<?> task,
187             final OffsetDateTime before,
188             final OffsetDateTime after,
189             final int page,
190             final int itemsPerPage,
191             final List<OrderByClause> orderByClauses) {
192 
193         StringBuilder queryString = query(new StringBuilder("SELECT e FROM "), task, before, after).
194                 append(toOrderByStatement(orderByClauses));
195 
196         Query query = entityManager().createQuery(queryString.toString());
197         query.setParameter("task", task);
198         if (before != null) {
199             query.setParameter("before", before);
200         }
201         if (after != null) {
202             query.setParameter("after", after);
203         }
204 
205         // page starts from 1, while setFirtResult() starts from 0
206         query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
207 
208         if (itemsPerPage >= 0) {
209             query.setMaxResults(itemsPerPage);
210         }
211 
212         List<Object> result = query.getResultList();
213         return result.stream().map(e -> (TaskExec<?>) e).collect(Collectors.toList());
214     }
215 
216     @Transactional(rollbackFor = { Throwable.class })
217     @Override
218     public <T extends Task<T>> TaskExec<T> save(final TaskExec<T> execution) {
219         return entityManager().merge(execution);
220     }
221 
222     @Transactional(rollbackFor = { Throwable.class })
223     @Override
224     public <T extends Task<T>> void saveAndAdd(
225             final TaskType taskType, final String taskKey, final TaskExec<T> execution) {
226 
227         T task = taskDAO.find(taskType, taskKey);
228         task.add(execution);
229         taskDAO.save(task);
230     }
231 
232     @Override
233     public <T extends Task<T>> void delete(final TaskType taskType, final String key) {
234         TaskExec<T> execution = find(taskType, key);
235         if (execution == null) {
236             return;
237         }
238 
239         delete(execution);
240     }
241 
242     @Override
243     public <T extends Task<T>> void delete(final TaskExec<T> execution) {
244         if (execution.getTask() != null) {
245             execution.getTask().getExecs().remove(execution);
246         }
247 
248         entityManager().remove(execution);
249     }
250 }