View Javadoc
1   package fr.ifremer.quadrige2.core.dao.administration.program;
2   
3   /*-
4    * #%L
5    * Quadrige2 Core :: Quadrige2 Server Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 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  import com.google.common.base.Function;
26  import com.google.common.base.Preconditions;
27  import fr.ifremer.quadrige2.core.config.Quadrige2Configuration;
28  import fr.ifremer.quadrige2.core.dao.administration.strategy.Strategy;
29  import fr.ifremer.quadrige2.core.dao.administration.strategy.StrategyDao;
30  import fr.ifremer.quadrige2.core.dao.referential.StatusImpl;
31  import fr.ifremer.quadrige2.core.dao.system.synchronization.DeletedItemHistoryExtendDao;
32  import fr.ifremer.quadrige2.core.dao.technical.*;
33  import fr.ifremer.quadrige2.core.exception.BadUpdateDtException;
34  import fr.ifremer.quadrige2.core.exception.DataLockedException;
35  import fr.ifremer.quadrige2.core.vo.administration.program.MonLocProgVO;
36  import fr.ifremer.quadrige2.core.vo.administration.program.ProgDepProgPrivVO;
37  import fr.ifremer.quadrige2.core.vo.administration.program.ProgQuserProgPrivVO;
38  import fr.ifremer.quadrige2.core.vo.administration.program.ProgramVO;
39  import fr.ifremer.quadrige2.core.vo.administration.strategy.StrategyVO;
40  import org.apache.commons.collections4.CollectionUtils;
41  import org.apache.commons.lang3.ArrayUtils;
42  import org.hibernate.LockMode;
43  import org.hibernate.LockOptions;
44  import org.hibernate.Session;
45  import org.hibernate.SessionFactory;
46  import org.hibernate.exception.LockTimeoutException;
47  import org.nuiton.i18n.I18n;
48  import org.springframework.beans.factory.annotation.Autowired;
49  import org.springframework.context.annotation.Lazy;
50  import org.springframework.stereotype.Repository;
51  
52  import javax.annotation.Nullable;
53  import javax.annotation.Resource;
54  import java.sql.Timestamp;
55  import java.util.Collection;
56  import java.util.List;
57  import java.util.Objects;
58  
59  /**
60   * <p>
61   * ProgramDaoImpl class.
62   * </p>
63   * 
64   * @see Program
65   */
66  @Repository("programDao")
67  @Lazy
68  public class ProgramDaoImpl
69  		extends ProgramDaoBase
70  {
71  
72  	@Resource
73  	private ProgDepProgPrivDao progDepProgPrivDao;
74  
75  	@Resource
76  	private ProgQuserProgPrivDao progQuserProgPrivDao;
77  
78  	@Resource
79  	private MonLocProgDao monLocProgDao;
80  
81  	@Resource(name = "strategyDao")
82  	private StrategyDao strategyDao;
83  
84  	@Resource
85  	private Quadrige2Configuration config;
86  
87  	@Resource(name = "deletedItemHistoryDao")
88  	private DeletedItemHistoryExtendDao deletedItemHistoryDao;
89  
90  	/**
91  	 * Constructor used by Spring
92  	 * 
93  	 * @param sessionFactory
94  	 *            a {@link org.hibernate.SessionFactory} object.
95  	 */
96  	@Autowired
97  	public ProgramDaoImpl(SessionFactory sessionFactory) {
98  		super();
99  		setSessionFactory(sessionFactory);
100 	}
101 
102 	/** {@inheritDoc} */
103 	@Override
104 	public void remove(Collection<Program> entities) {
105 		Preconditions.checkNotNull(entities);
106 		Preconditions.checkArgument(entities.size() > 0);
107 
108 		for (Program entity : entities) {
109 			remove(entity);
110 		}
111 	}
112 
113 	/** {@inheritDoc} */
114 	@Override
115 	public void remove(String progCd) {
116 		Program entity = get(progCd);
117 		if (entity != null) {
118 			remove(entity);
119 		}
120 	}
121 
122 	/** {@inheritDoc} */
123 	@Override
124 	public void remove(Program entity) {
125 
126 		// Remove strategies
127 		if (CollectionUtils.isNotEmpty(entity.getStrategies())) {
128 			strategyDao.remove(entity.getStrategies());
129 			entity.getStrategies().clear();
130 		}
131 
132 		// Remove privileges
133 		if (CollectionUtils.isNotEmpty(entity.getProgQuserProgPrivs())) {
134 			entity.getProgQuserProgPrivs().clear();
135 		}
136 		if (CollectionUtils.isNotEmpty(entity.getProgDepProgPrivs())) {
137 			entity.getProgDepProgPrivs().clear();
138 		}
139 
140 		// Insert into DeleteItemHistory
141 		deletedItemHistoryDao.insertDeletedItem(ProgramImpl.class, entity);
142 
143 		super.remove(entity);
144 	}
145 
146 	/** {@inheritDoc} */
147 	public void toProgramVO(
148 			Program source,
149 			ProgramVO target)
150 	{
151 		// copy base attributes
152 		super.toProgramVO(source, target);
153 
154 		// Status
155 		if (source.getStatus() == null) {
156 			target.setStatusCd(null);
157 		}
158 		else {
159 			target.setStatusCd(source.getStatus().getStatusCd());
160 		}
161 
162 		// privileges on department
163 		if (CollectionUtils.isEmpty(source.getProgDepProgPrivs())) {
164 			target.setProgDepProgPrivVOs(null);
165 		}
166 		else {
167 			target.setProgDepProgPrivVOs(progDepProgPrivDao.toProgDepProgPrivVOArray(source.getProgDepProgPrivs()));
168 		}
169 
170 		// privileges on user
171 		if (CollectionUtils.isEmpty(source.getProgQuserProgPrivs())) {
172 			target.setProgQuserProgPrivVOs(null);
173 		}
174 		else {
175 			target.setProgQuserProgPrivVOs(progQuserProgPrivDao.toProgQuserProgPrivVOArray(source.getProgQuserProgPrivs()));
176 		}
177 
178 		// Monitoring location
179 		if (CollectionUtils.isEmpty(source.getMonLocProgs())) {
180 			target.setMonLocProgVOs(null);
181 		}
182 		else {
183 			target.setMonLocProgVOs(monLocProgDao.toMonLocProgVOArray(source.getMonLocProgs()));
184 		}
185 
186 		// Strategies
187 		if (CollectionUtils.isEmpty(source.getStrategies())) {
188 			target.setStrategyVOs(null);
189 		}
190 		else {
191 			target.setStrategyVOs(strategyDao.toStrategyVOArray(source.getStrategies()));
192 		}
193 	}
194 
195 	/**
196 	 * Retrieves the entity object that is associated with the specified value object
197 	 * from the object store. If no such entity object exists in the object store,
198 	 * a new, blank entity is created
199 	 */
200 	private Program loadProgramFromProgramVO(ProgramVO programVO)
201 	{
202 		Program program = null;
203 		if (programVO.getProgCd() != null) {
204 			program = this.get(programVO.getProgCd());
205 		}
206 		if (program == null)
207 		{
208 			program = Program.Factory.newInstance();
209 		}
210 		return program;
211 	}
212 
213 	/** {@inheritDoc} */
214 	public Program programVOToEntity(ProgramVO programVO)
215 	{
216 		Program entity = this.loadProgramFromProgramVO(programVO);
217 		this.programVOToEntity(programVO, entity, true);
218 		return entity;
219 	}
220 
221 	/** {@inheritDoc} */
222 	@Override
223 	public void programVOToEntity(
224 			ProgramVO source,
225 			Program target,
226 			boolean copyIfNull)
227 	{
228 		programVOToEntity(source, target, copyIfNull, null, false);
229 	}
230 
231 	/** {@inheritDoc} */
232 	@Override
233 	public ProgramVO save(final ProgramVO program)
234 	{
235 		if (program == null)
236 		{
237 			throw new IllegalArgumentException(
238 					"fr.ifremer.quadrige2.core.dao.administration.program.ProgramDao.save(ProgramVO program) - 'program' can not be null");
239 		}
240 
241 		return this.handleSave(program);
242 	}
243 
244 	/**
245 	 * {@inheritDoc}
246 	 * 
247 	 * -- Internal methods --
248 	 */
249 	@Override
250 	protected ProgramVO handleSave(ProgramVO source) {
251 		Preconditions.checkNotNull(source.getProgCd());
252 
253 		// Load (or create) the entity
254 		Program entity = get(source.getProgCd());
255 
256 		boolean isNew = false;
257 		if (entity == null) {
258 			entity = Program.Factory.newInstance();
259 			isNew = true;
260 		}
261 
262 		if (!isNew) {
263 			// Check update_dt
264 			if (entity.getUpdateDt() != null) {
265 				Timestamp serverUpdateDtNoMillisecond = Dates.resetMillisecond(entity.getUpdateDt());
266 				Timestamp sourceUpdateDtNoMillisecond = Dates.resetMillisecond(source.getUpdateDt());
267 				if (!Objects.equals(sourceUpdateDtNoMillisecond, serverUpdateDtNoMillisecond)) {
268 					throw new BadUpdateDtException(I18n.t("quadrige2.dao.program.badUpdateDt", source.getProgCd(), serverUpdateDtNoMillisecond,
269 							sourceUpdateDtNoMillisecond));
270 				}
271 			}
272 
273 			// Lock
274 			try {
275 				Session.LockRequest lockRequest = getSession().buildLockRequest(LockOptions.UPGRADE);
276 				lockRequest.setLockMode(LockMode.UPGRADE_NOWAIT);
277 				lockRequest.setScope(true); // cascaded to owned collections and relationships.
278 				lockRequest.lock(entity);
279 			} catch (LockTimeoutException e) {
280 				throw new DataLockedException(I18n.t("quadrige2.dao.program.locked", source.getProgCd()), e);
281 			}
282 		}
283 
284 		// Update update_dt
285 		Timestamp newUpdateDt = getDatabaseCurrentTimestamp();
286 		// Add a delay (to make sure synchro will see it, event if this transaction is long)
287 		newUpdateDt = Dates.addSeconds(newUpdateDt, config.getExportDataUpdateDateShortDelayInSecond());
288 		source.setUpdateDt(newUpdateDt);
289 
290 		// VO -> Entity
291 		programVOToEntity(source, entity, true, newUpdateDt, false/* do not convert strategies */);
292 
293 		// Save entity
294 		if (isNew) {
295 			getSession().save(entity);
296 		} else {
297 			getSession().update(entity);
298 		}
299 
300 		List<Integer> strategiesIdsToRemove = Beans.collectProperties(entity.getStrategies(), "stratId");
301 
302 		// Save strategies
303 		if (ArrayUtils.isNotEmpty(source.getStrategyVOs())) {
304 			for (StrategyVO strategyVO : source.getStrategyVOs()) {
305 				strategyVO.setProgCd(entity.getProgCd());
306 				strategyVO = strategyDao.save(strategyVO, newUpdateDt);
307 
308 				strategiesIdsToRemove.remove(strategyVO.getStratId());
309 			}
310 		}
311 
312 		getSession().flush();
313 		getSession().clear();
314 
315 		// remove unused strategies
316 		if (CollectionUtils.isNotEmpty(strategiesIdsToRemove)) {
317 			strategyDao.removeByIds(strategiesIdsToRemove);
318 		}
319 
320 		return source;
321 	}
322 
323 	/**
324 	 * <p>
325 	 * programVOToEntity.
326 	 * </p>
327 	 * 
328 	 * @param source
329 	 *            a {@link fr.ifremer.quadrige2.core.vo.administration.program.ProgramVO} object.
330 	 * @param target
331 	 *            a {@link fr.ifremer.quadrige2.core.dao.administration.program.Program} object.
332 	 * @param copyIfNull
333 	 *            a boolean.
334 	 * @param updateDt
335 	 *            a {@link java.sql.Timestamp} object.
336 	 * @param withStrategies
337 	 *            a boolean.
338 	 */
339 	protected void programVOToEntity(
340 			final ProgramVO source,
341 			Program target,
342 			boolean copyIfNull,
343 			final Timestamp updateDt,
344 			boolean withStrategies)
345 	{
346 		// Copy simple attributes
347 		super.programVOToEntity(source, target, copyIfNull);
348 
349 		// Code
350 		if (copyIfNull || source.getProgCd() != null)
351 		{
352 			target.setProgCd(source.getProgCd());
353 		}
354 
355 		// Status
356 		if (copyIfNull || source.getStatusCd() != null)
357 		{
358 			if (source.getStatusCd() == null) {
359 				target.setStatus(null);
360 			}
361 			else {
362 				target.setStatus(load(StatusImpl.class, source.getStatusCd()));
363 			}
364 		}
365 
366 		// Privilege on department
367 		if (copyIfNull || ArrayUtils.isNotEmpty(source.getProgDepProgPrivVOs()))
368 		{
369 			if (ArrayUtils.isEmpty(source.getProgDepProgPrivVOs())) {
370 				target.getProgDepProgPrivs().clear();
371 			}
372 			else {
373 				Daos.replaceEntities(target.getProgDepProgPrivs(),
374 						source.getProgDepProgPrivVOs(),
375 						vo -> {
376 							ProgDepProgPriv entity = progDepProgPrivDao.progDepProgPrivVOToEntity(vo);
377 							if (updateDt != null) {
378 								entity.setUpdateDt(updateDt);
379 							}
380 							return entity;
381 						});
382 			}
383 		}
384 
385 		// Privilege on user
386 		if (copyIfNull || ArrayUtils.isNotEmpty(source.getProgQuserProgPrivVOs()))
387 		{
388 			if (ArrayUtils.isEmpty(source.getProgQuserProgPrivVOs())) {
389 				target.getProgQuserProgPrivs().clear();
390 			}
391 			else {
392 				Daos.replaceEntities(target.getProgQuserProgPrivs(),
393 						source.getProgQuserProgPrivVOs(),
394 						vo -> {
395 							ProgQuserProgPriv entity = progQuserProgPrivDao.progQuserProgPrivVOToEntity(vo);
396 							if (updateDt != null) {
397 								entity.setUpdateDt(updateDt);
398 							}
399 							return entity;
400 						});
401 			}
402 		}
403 
404 		// Monitoring location
405 		if (copyIfNull || ArrayUtils.isNotEmpty(source.getMonLocProgVOs()))
406 		{
407 			if (ArrayUtils.isEmpty(source.getMonLocProgVOs())) {
408 				target.getMonLocProgs().clear();
409 			} else {
410 				Daos.replaceEntities(target.getMonLocProgs(),
411 						source.getMonLocProgVOs(),
412 						vo -> {
413 							vo.setProgCd(source.getProgCd());
414 							MonLocProg entity = monLocProgDao.monLocProgVOToEntity(vo);
415 							if (updateDt != null) {
416 								entity.setUpdateDt(updateDt);
417 							}
418 							if (entity.getMonLocProgId() == null) {
419 								getSession().save(entity);
420 								vo.setMonLocProgId(entity.getMonLocProgId());
421 							}
422 							else {
423 								getSession().update(entity);
424 							}
425 							return entity;
426 						});
427 			}
428 		}
429 
430 		// Strategies
431 		if (withStrategies) {
432 			if (copyIfNull || source.getStrategyVOs() != null) {
433 				if (source.getStrategyVOs() == null) {
434 					target.getStrategies().clear();
435 				} else {
436 					Daos.replaceEntities(target.getStrategies(),
437 							source.getStrategyVOs(),
438 							vo -> {
439 								Strategy strategy = strategyDao.strategyVOToEntity(vo);
440 								strategy.setProgram(target);
441 								return strategy;
442 							});
443 				}
444 			}
445 		}
446 	}
447 
448 }