View Javadoc
1   package fr.ifremer.reefdb.dao.system.rule;
2   
3   /*
4    * #%L
5    * Reef DB :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   *
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.google.common.collect.Lists;
27  import com.google.common.collect.Sets;
28  import fr.ifremer.quadrige3.core.dao.administration.program.ProgramImpl;
29  import fr.ifremer.quadrige3.core.dao.administration.user.DepartmentImpl;
30  import fr.ifremer.quadrige3.core.dao.administration.user.Quser;
31  import fr.ifremer.quadrige3.core.dao.administration.user.QuserImpl;
32  import fr.ifremer.quadrige3.core.dao.referential.StatusImpl;
33  import fr.ifremer.quadrige3.core.dao.system.rule.RuleList;
34  import fr.ifremer.quadrige3.core.dao.system.rule.RuleListDaoImpl;
35  import fr.ifremer.quadrige3.core.dao.technical.Assert;
36  import fr.ifremer.quadrige3.core.dao.technical.Beans;
37  import fr.ifremer.quadrige3.core.dao.technical.Dates;
38  import fr.ifremer.quadrige3.core.security.SecurityContextHelper;
39  import fr.ifremer.reefdb.config.ReefDbConfiguration;
40  import fr.ifremer.reefdb.dao.administration.user.ReefDbDepartmentDao;
41  import fr.ifremer.reefdb.dao.technical.Daos;
42  import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
43  import fr.ifremer.reefdb.dto.ReefDbBeans;
44  import fr.ifremer.reefdb.dto.configuration.control.PreconditionRuleDTO;
45  import fr.ifremer.reefdb.dto.configuration.control.RuleGroupDTO;
46  import fr.ifremer.reefdb.dto.configuration.control.RuleListDTO;
47  import fr.ifremer.reefdb.dto.referential.DepartmentDTO;
48  import fr.ifremer.reefdb.service.administration.program.ProgramStrategyService;
49  import fr.ifremer.reefdb.service.system.SystemService;
50  import org.apache.commons.collections4.CollectionUtils;
51  import org.apache.commons.lang3.mutable.MutableBoolean;
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  import org.hibernate.SessionFactory;
55  import org.hibernate.type.StringType;
56  import org.springframework.beans.factory.annotation.Autowired;
57  import org.springframework.dao.DataRetrievalFailureException;
58  import org.springframework.stereotype.Repository;
59  
60  import javax.annotation.Resource;
61  import java.time.LocalDate;
62  import java.util.*;
63  
64  import static org.nuiton.i18n.I18n.t;
65  
66  /**
67   * <p>ReefDbRuleListDaoImpl class.</p>
68   *
69   * @author Ludovic
70   */
71  @Repository("reefDbRuleListDao")
72  public class ReefDbRuleListDaoImpl extends RuleListDaoImpl implements ReefDbRuleListDao {
73  
74      private static final Log LOG = LogFactory.getLog(ReefDbRuleListDaoImpl.class);
75  
76      @Resource
77      protected ReefDbConfiguration config;
78      @Resource(name = "reefDbRuleDao")
79      protected ReefDbRuleDao ruleDao;
80      @Resource(name = "reefdbProgramStrategyService")
81      protected ProgramStrategyService programStrategyService;
82      @Resource(name = "reefDbDepartmentDao")
83      protected ReefDbDepartmentDao departmentDao;
84  
85      @Resource(name = "reefdbSystemService")
86      protected SystemService systemService;
87      /**
88       * <p>Constructor for ReefDbRuleListDaoImpl.</p>
89       *
90       * @param sessionFactory a {@link org.hibernate.SessionFactory} object.
91       */
92      @Autowired
93      public ReefDbRuleListDaoImpl(SessionFactory sessionFactory) {
94          super(sessionFactory);
95      }
96  
97      /**
98       * {@inheritDoc}
99       */
100     @Override
101     public List<RuleListDTO> getRuleLists() {
102 
103         Iterator<Object[]> it = queryIterator("allRuleList");
104 
105         return toRuleListDTOs(it);
106     }
107 
108     @Override
109     public List<RuleListDTO> getRuleListsForProgram(String programCode) {
110 
111         Iterator<Object[]> it = queryIterator("allRuleListWithProgramCode",
112                 "programCode", StringType.INSTANCE, programCode);
113 
114         return toRuleListDTOs(it);
115     }
116 
117     /**
118      * {@inheritDoc}
119      */
120     @Override
121     public RuleListDTO getRuleList(String ruleListCode) {
122         Assert.notBlank(ruleListCode);
123 
124         Object[] source = queryUnique("ruleListByCode", "ruleListCode", StringType.INSTANCE, ruleListCode);
125 
126         if (source == null) {
127             throw new DataRetrievalFailureException("can't load rule list with code = " + ruleListCode);
128         }
129 
130         RuleListDTO ruleList = toRuleListDTO(Arrays.asList(source).iterator(), config.getDbTimezone());
131 
132         if (fillAndValidRuleList(ruleList,
133                 programStrategyService.getManagedProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId()),
134                 programStrategyService.getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId())))
135             // return the valid rule list
136             return ruleList;
137 
138         return null;
139     }
140 
141     @Override
142     public boolean ruleListExists(String ruleListCode) {
143         Assert.notBlank(ruleListCode);
144 
145         return queryUnique("ruleListByCode", "ruleListCode", StringType.INSTANCE, ruleListCode) != null;
146     }
147 
148     /**
149      * {@inheritDoc}
150      */
151     @Override
152     public void saveRuleList(RuleListDTO source, Integer quserId) {
153         Assert.notNull(source);
154         Assert.notBlank(source.getCode());
155         Assert.notNull(source.getStatus());
156         Assert.notNull(quserId);
157 
158         RuleList target = get(source.getCode());
159         boolean isNew = false;
160         if (target == null) {
161             target = RuleList.Factory.newInstance();
162             target.setRuleListCd(source.getCode());
163             // Set status only if new entity
164             target.setStatus(load(StatusImpl.class, source.getStatus().getCode()));
165             isNew = true;
166         }
167 
168         // DTO -> Entity
169         beanToEntity(source, target, quserId, config.getDbTimezone());
170 
171         // Save it
172         if (isNew) {
173             getSession().save(target);
174         } else {
175             getSession().update(target);
176         }
177 
178         // Save rules
179         final List<String> rulesCdsToRemove = ReefDbBeans.collectProperties(target.getRules(), "ruleCd");
180 
181         // Save rules
182         if (CollectionUtils.isNotEmpty(source.getControlRules())) {
183             source.getControlRules().forEach(controlRule -> {
184                 ruleDao.save(controlRule, source.getCode());
185                 rulesCdsToRemove.remove(controlRule.getCode());
186                 if (!controlRule.isPreconditionsEmpty()) {
187                     for (PreconditionRuleDTO preconditionRule : controlRule.getPreconditions()) {
188                         rulesCdsToRemove.remove(preconditionRule.getBaseRule().getCode());
189                         rulesCdsToRemove.remove(preconditionRule.getUsedRule().getCode());
190                     }
191                 }
192                 if (!controlRule.isGroupsEmpty()) {
193                     for (RuleGroupDTO groupedRule : controlRule.getGroups()) {
194                         rulesCdsToRemove.remove(groupedRule.getRule().getCode());
195                     }
196                 }
197             });
198         }
199 
200         getSession().flush();
201         getSession().clear();
202 
203         // remove unused rules
204         if (CollectionUtils.isNotEmpty(rulesCdsToRemove)) {
205             ruleDao.removeByCds(Beans.asStringArray(rulesCdsToRemove));
206             // flush again because another get or load can read the removes rules
207             getSession().flush();
208             getSession().clear();
209         }
210     }
211 
212     // INTERNAL METHODS
213 
214     private boolean fillAndValidRuleList(RuleListDTO ruleList, Set<String> managedProgramCodes, Set<String> writableProgramCodes) {
215 
216         // get programs
217         List<String> programCodes = getProgramCodesByRuleListCode(ruleList.getCode());
218 
219         if (CollectionUtils.isEmpty(programCodes)) {
220             // skip this rule list
221             return false;
222         }
223 
224         // Check program write privilege
225         boolean canRead = programCodes.stream().anyMatch(writableProgramCodes::contains) || ReefDbBeans.isLocalStatus(ruleList.getStatus());
226         if (!canRead) {
227             if (LOG.isWarnEnabled()) {
228                 LOG.warn(t("reefdb.error.dao.ruleList.program.empty", ruleList.getCode()));
229             }
230             // skip this rule list
231             return false;
232         }
233 
234         // the current user must be manager of all programs to set this rule list as writable
235         boolean canWrite = managedProgramCodes.containsAll(programCodes) || ReefDbBeans.isLocalStatus(ruleList.getStatus());
236         ruleList.setReadOnly(!canWrite);
237 
238         // add programs
239         ruleList.setPrograms(programStrategyService.getProgramsByCodes(programCodes));
240 
241         // add services
242         ruleList.setDepartments(getControlledDepartmentsByRuleListCode(ruleList.getCode()));
243 
244         MutableBoolean incompatibleRule = new MutableBoolean(false);
245 
246         // add rules
247         ruleList.addAllControlRules(ruleDao.getControlRulesByRuleListCode(ruleList.getCode(), false /*active and non-active*/, incompatibleRule));
248         // add preconditioned rules
249         ruleList.addAllControlRules(ruleDao.getPreconditionedRulesByRuleListCode(ruleList.getCode(), false, incompatibleRule));
250         // add grouped rules
251         ruleList.addAllControlRules(ruleDao.getGroupedRulesByRuleListCode(ruleList.getCode(), false, incompatibleRule));
252 
253         // valid rules count (if loadRules is false, just count the rules in db)
254         if (ruleList.isControlRulesEmpty()) {
255             if (LOG.isWarnEnabled()) {
256                 LOG.warn(t("reefdb.error.dao.ruleList.rule.empty", ruleList.getCode()));
257             }
258             // skip this rule list
259             return false;
260         }
261         // set the rule list as read only if at least one rule is not compatible
262         if (incompatibleRule.booleanValue()) ruleList.setReadOnly(true);
263 
264         return true;
265     }
266 
267     private List<String> getProgramCodesByRuleListCode(String ruleListCode) {
268 
269         Assert.notBlank(ruleListCode);
270 
271         return queryListTyped("programCodesByRuleListCode",
272                 "ruleListCode", StringType.INSTANCE, ruleListCode);
273     }
274 
275     /**
276      * <p>getControlledDepartmentsByRuleListCode.</p>
277      *
278      * @param ruleListCode a {@link java.lang.String} object.
279      * @return a {@link java.util.List} object.
280      */
281     private List<DepartmentDTO> getControlledDepartmentsByRuleListCode(String ruleListCode) {
282         Assert.notBlank(ruleListCode);
283 
284         List<Integer> depIds = queryListTyped("departmentIdsByRuleListCode",
285                 "ruleListCode", StringType.INSTANCE, ruleListCode);
286 
287         List<DepartmentDTO> result = Lists.newArrayList();
288         if (CollectionUtils.isNotEmpty(depIds)) {
289             for (Integer depId : new HashSet<>(depIds)) {
290                 result.add(departmentDao.getDepartmentById(depId));
291             }
292         }
293         return result;
294     }
295 
296     private List<RuleListDTO> toRuleListDTOs(Iterator<Object[]> it) {
297 
298         Set<String> managedProgramCodes = programStrategyService.getManagedProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
299         Set<String> writableProgramCodes = programStrategyService.getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
300 
301         List<RuleListDTO> result = Lists.newArrayList();
302         TimeZone dbTimezone = config.getDbTimezone();
303 
304         while (it.hasNext()) {
305             Object[] source = it.next();
306             RuleListDTO ruleList = toRuleListDTO(Arrays.asList(source).iterator(), dbTimezone);
307 
308             if (fillAndValidRuleList(ruleList, managedProgramCodes, writableProgramCodes))
309                 // add the valid rule list to result
310                 result.add(ruleList);
311         }
312 
313         return result;
314     }
315 
316     private RuleListDTO toRuleListDTO(Iterator<Object> source, TimeZone dbTimezone) {
317         RuleListDTO result = ReefDbBeanFactory.newRuleListDTO();
318         result.setCode((String) source.next());
319         result.setActive(Daos.safeConvertToBoolean(source.next()));
320 
321         LocalDate startDate = Dates.convertToLocalDate(Daos.convertToDate(source.next()), dbTimezone);
322         result.setStartMonth(
323             Optional.ofNullable(startDate)
324                 .map(LocalDate::getMonthValue)
325                 .flatMap(month -> systemService.getMonths().stream().filter(monthDTO -> monthDTO.getId().equals(month)).findFirst())
326                 .orElse(null)
327         );
328 
329         LocalDate endDate = Dates.convertToLocalDate(Daos.convertToDate(source.next()), dbTimezone);
330         result.setEndMonth(
331             Optional.ofNullable(endDate)
332                 .map(LocalDate::getMonthValue)
333                 .flatMap(month -> systemService.getMonths().stream().filter(monthDTO -> monthDTO.getId().equals(month)).findFirst())
334                 .orElse(null)
335         );
336 
337         result.setDescription((String) source.next());
338         result.setStatus(Daos.getStatus((String) source.next()));
339         result.setCreationDate(Daos.convertToDate(source.next()));
340         result.setUpdateDate(Daos.convertToDate(source.next()));
341 
342         return result;
343     }
344 
345     private void beanToEntity(RuleListDTO source, RuleList target, int quserId, TimeZone dbTimezone) {
346 
347         target.setRuleListDc(source.getDescription());
348         target.setRuleListIsActive(Daos.convertToString(source.isActive()));
349 
350         // convert month to date
351         LocalDate startDate = Optional.ofNullable(source.getStartMonth())
352             .map(monthDTO -> LocalDate.of(LocalDate.now().getYear(), monthDTO.getId(), 1))
353             .orElse(null);
354         target.setRuleListFirstMonth(Dates.convertToDate(startDate, dbTimezone));
355 
356         LocalDate endDate = Optional.ofNullable(source.getEndMonth())
357             .map(monthDTO -> LocalDate.of(LocalDate.now().getYear(), monthDTO.getId(), 1).plusMonths(1).minusDays(1))
358             .orElse(null);
359         target.setRuleListLastMonth(Dates.convertToDate(endDate, dbTimezone));
360 
361         // update date (if remote = always null, as set by server)
362         if (ReefDbBeans.isLocalStatus(source.getStatus())) {
363             target.setUpdateDt(newUpdateTimestamp());
364         }
365 
366         // creation date
367         if (target.getRuleListCreationDt() == null) {
368             target.setRuleListCreationDt(newCreateDate());
369         }
370 
371         // manager user
372         if (CollectionUtils.isEmpty(target.getQusers())) {
373             // add current user as unique
374             Quser quser = load(QuserImpl.class, quserId);
375             target.setQusers(Sets.newHashSet(quser));
376         } else if (ReefDbBeans.findByProperty(target.getQusers(), "quserId", quserId) == null) {
377             // add current user to collection
378             Quser quser = load(QuserImpl.class, quserId);
379             target.getQusers().add(quser);
380         }
381 
382         // manager department
383         if (CollectionUtils.isEmpty(target.getRespDepartments())) {
384             // add current user department as unique
385             Quser quser = load(QuserImpl.class, quserId);
386             target.setRespDepartments(Sets.newHashSet(quser.getDepartment()));
387         } else {
388             // add current user's department to collection
389             Quser quser = load(QuserImpl.class, quserId);
390             if (ReefDbBeans.findByProperty(target.getRespDepartments(), "depId", quser.getDepartment().getDepId()) == null) {
391                 target.getRespDepartments().add(quser.getDepartment());
392             }
393         }
394 
395         // programs
396         if (source.getPrograms() == null) {
397             target.getPrograms().clear();
398         } else {
399             Daos.replaceEntities(target.getPrograms(),
400                     source.getPrograms(),
401                     vo -> load(ProgramImpl.class, Objects.requireNonNull(vo).getCode()));
402         }
403 
404         // do not remove remaining unused programs: links to programs will be automatically deleted when ruleList entity will be saved
405 
406         // departments
407         if (source.getDepartments() == null) {
408             target.getControledDepartments().clear();
409         } else {
410             Daos.replaceEntities(target.getControledDepartments(),
411                     source.getDepartments(),
412                     vo -> load(DepartmentImpl.class, Objects.requireNonNull(vo).getId()));
413         }
414 
415     }
416 
417 }