View Javadoc
1   package fr.ifremer.quadrige3.synchro.intercept.data.measurement;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: Quadrige3 Synchro 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  
26  import com.google.common.collect.ImmutableSet;
27  import com.google.common.collect.Lists;
28  import com.google.common.eventbus.Subscribe;
29  import fr.ifremer.common.synchro.dao.SynchroBaseDao;
30  import fr.ifremer.common.synchro.dao.SynchroTableDao;
31  import fr.ifremer.common.synchro.intercept.SynchroInterceptorBase;
32  import fr.ifremer.common.synchro.intercept.SynchroOperationRepository;
33  import fr.ifremer.common.synchro.meta.SynchroJoinMetadata;
34  import fr.ifremer.common.synchro.meta.SynchroTableMetadata;
35  import fr.ifremer.common.synchro.meta.event.LoadJoinEvent;
36  import fr.ifremer.common.synchro.meta.event.LoadTableEvent;
37  import fr.ifremer.quadrige3.core.dao.referential.ObjectTypeCode;
38  import fr.ifremer.quadrige3.core.dao.technical.Daos;
39  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
40  import fr.ifremer.quadrige3.synchro.intercept.data.AbstractDataInterceptor;
41  import fr.ifremer.quadrige3.synchro.meta.DatabaseColumns;
42  import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
43  import fr.ifremer.quadrige3.synchro.meta.referential.ReferentialSynchroTables;
44  import fr.ifremer.quadrige3.synchro.service.SynchroDirection;
45  import fr.ifremer.quadrige3.synchro.service.data.DataSynchroDatabaseConfiguration;
46  
47  import java.io.IOException;
48  import java.math.BigDecimal;
49  import java.sql.PreparedStatement;
50  import java.sql.ResultSet;
51  import java.sql.SQLException;
52  import java.util.List;
53  
54  /**
55   * <p>
56   * Abstract MeasurementAbstractInterceptor class.
57   * </p>
58   * 
59   */
60  public abstract class MeasurementAbstractInterceptor extends AbstractDataInterceptor {
61  
62  	private boolean enableIntegrityConstraints = true;
63  
64  	private String selectParentIdFromSamplingOperIdQuery;
65  	private String selectRemoteIdAndParentIdsFromSampleIdQuery;
66  	private String selectPmfmQuery;
67  	private String selectRemoteIdBySurveyIdQuery;
68  	private PreparedStatement selectParentIdFromSamplingOperIdStatement = null;
69  	private PreparedStatement selectRemoteIdAndParentIdsFromSampleIdStatement = null;
70  
71  	private ObjectTypeCode objectTypeCode;
72  	private int measIdIndex = -1;
73  	private int surveyIdIndex = -1;
74  	private int samplingOperIdIndex = -1;
75  	private int sampleIdIndex = -1;
76  	private int pmfmIdIndex = -1;
77  	private int objectIdIndex = -1;
78  	private int measDigitNumberIndex = -1;
79  	private int measNumerValueIndex = -1;
80  	private final String measIdColumnName;
81  	private final String measDigitNumberColumnName;
82  	private final String measNumerValueColumnName;
83  	private SynchroDirection direction;
84  
85  	public MeasurementAbstractInterceptor() {
86  		this(null, null, null, null);
87  	}
88  
89  	/**
90  	 * <p>
91  	 * Constructor for MeasurementAbstractInterceptor.
92  	 * </p>
93  	 *
94  	 * @param objectTypeCode
95  	 * @param measIdColumnName
96  	 * @param measDigitNumberColumnName column name for measDigitNumber
97  	 * @param measNumerValueColumnName  column name for measNumerValue
98  	 */
99  	public MeasurementAbstractInterceptor(ObjectTypeCode objectTypeCode, String measIdColumnName, String measDigitNumberColumnName, String measNumerValueColumnName) {
100 		super();
101 		this.objectTypeCode = objectTypeCode;
102 		this.measIdColumnName = measIdColumnName;
103 		this.selectParentIdFromSamplingOperIdQuery = initSelectRemoteIdAndParentIdFromSamplingOperIdQuery();
104 		this.selectRemoteIdAndParentIdsFromSampleIdQuery = initSelectRemoteIdAndParentIdFromSampleIdQuery();
105 		this.selectPmfmQuery = initSelectPmfm();
106 		this.selectRemoteIdBySurveyIdQuery = initSelectRemoteIdFromSurveyId();
107 		this.measDigitNumberColumnName = measDigitNumberColumnName;
108 		this.measNumerValueColumnName = measNumerValueColumnName;
109 		setEnableOnWrite(true);
110 	}
111 
112 	/** {@inheritDoc} */
113 	@Override
114 	public void init(DataSynchroDatabaseConfiguration config) {
115 		super.init(config);
116 		this.direction = config.getDirection();
117 		this.enableIntegrityConstraints = direction == SynchroDirection.EXPORT_TEMP2SERVER
118 				|| direction == SynchroDirection.IMPORT_TEMP2LOCAL;
119 	}
120 
121 	/** {@inheritDoc} */
122 	@Override
123 	public SynchroInterceptorBase clone() {
124 		MeasurementAbstractInterceptor newBean = (MeasurementAbstractInterceptor) super.clone();
125 		newBean.direction = this.direction;
126 		newBean.enableIntegrityConstraints = this.enableIntegrityConstraints;
127 		newBean.selectParentIdFromSamplingOperIdQuery = this.selectParentIdFromSamplingOperIdQuery;
128 		newBean.selectRemoteIdAndParentIdsFromSampleIdQuery = this.selectRemoteIdAndParentIdsFromSampleIdQuery;
129 		newBean.selectPmfmQuery = this.selectPmfmQuery;
130 		newBean.selectRemoteIdBySurveyIdQuery = this.selectRemoteIdBySurveyIdQuery;
131 		newBean.measIdIndex = this.measIdIndex;
132 		newBean.objectTypeCode = this.objectTypeCode;
133 		newBean.surveyIdIndex = this.surveyIdIndex;
134 		newBean.samplingOperIdIndex = this.samplingOperIdIndex;
135 		newBean.sampleIdIndex = this.sampleIdIndex;
136 		newBean.pmfmIdIndex = this.pmfmIdIndex;
137 		newBean.objectIdIndex = this.objectIdIndex;
138 		newBean.measDigitNumberIndex = this.measDigitNumberIndex;
139 		newBean.measNumerValueIndex = this.measNumerValueIndex;
140 		return newBean;
141 	}
142 
143 	/**
144 	 * <p>
145 	 * handleJoinLoad.
146 	 * </p>
147 	 * 
148 	 * @param e
149 	 *            a {@link fr.ifremer.common.synchro.meta.event.LoadJoinEvent} object.
150 	 */
151 	@Subscribe
152 	public void handleJoinLoad(LoadJoinEvent e) {
153 		SynchroJoinMetadata join = e.join;
154 		// Do not apply if mirror database
155 		if (!join.isValid()) {
156 			return;
157 		}
158 
159 		SynchroTableMetadata pkTable = join.getPkTable();
160 		String pkTableName = pkTable.getName();
161 		SynchroDirection direction = getConfig().getDirection();
162 
163 		// IMPORT: Server DB -> Temp DB
164 		if (direction == SynchroDirection.IMPORT_SERVER2TEMP) {
165 
166 			// Only keep link to SURVEY (survey_id)
167 			if (DataSynchroTables.SAMPLING_OPERATION.name().equalsIgnoreCase(pkTableName)
168 					|| DataSynchroTables.SAMPLE.name().equalsIgnoreCase(pkTableName)) {
169 
170 				join.setIsValid(false);
171 			}
172 		}
173 	}
174 
175 	/**
176 	 * <p>
177 	 * handleTableLoad.
178 	 * </p>
179 	 * 
180 	 * @param e
181 	 *            a {@link fr.ifremer.common.synchro.meta.event.LoadTableEvent} object.
182 	 */
183 	@Subscribe
184 	public void handleTableLoad(LoadTableEvent e) {
185 		SynchroTableMetadata table = e.table;
186 
187 		surveyIdIndex = table.getSelectColumnIndex(DatabaseColumns.SURVEY_ID.name());
188 		samplingOperIdIndex = table.getSelectColumnIndex(DatabaseColumns.SAMPLING_OPER_ID.name());
189 		sampleIdIndex = table.getSelectColumnIndex(DatabaseColumns.SAMPLE_ID.name());
190 		pmfmIdIndex = table.getSelectColumnIndex(DatabaseColumns.PMFM_ID.name());
191 		objectIdIndex = table.getSelectColumnIndex(DatabaseColumns.OBJECT_ID.name());
192 		if (measNumerValueColumnName != null) measNumerValueIndex = table.getSelectColumnIndex(measNumerValueColumnName);
193 		if (measDigitNumberColumnName != null) measDigitNumberIndex = table.getSelectColumnIndex(measDigitNumberColumnName);
194 		if (measIdColumnName != null) measIdIndex = table.getSelectColumnIndex(measIdColumnName);
195 
196 		// EXPORT: Temp DB -> Server DB
197 		if (direction == SynchroDirection.EXPORT_TEMP2SERVER) {
198 
199 			// When select by SURVEY_ID, some other ID should be NULL
200 			table.addSelectByFksWhereClause(DatabaseColumns.SURVEY_ID.name(),
201 					String.format("t.%s IS NULL AND t.%s IS NULL",
202 							DatabaseColumns.SAMPLING_OPER_ID.name(),
203 							DatabaseColumns.SAMPLE_ID.name()));
204 
205 			// When select by SAMPLING_OPER_ID, SAMPLE_ID should be NULL
206 			table.addSelectByFksWhereClause(DatabaseColumns.SAMPLING_OPER_ID.name(),
207 					String.format("t.%s IS NULL",
208 							DatabaseColumns.SAMPLE_ID.name()));
209 		}
210 	}
211 
212 	/** {@inheritDoc} */
213 	@Override
214 	protected void doOnWrite(Object[] data, List<Object> pk, SynchroTableDao sourceDao, SynchroTableDao targetDao, SynchroOperationRepository buffer,
215 			boolean insert)
216 			throws SQLException {
217 
218 		// IMPORT: Server DB -> Temp DB
219 		if (direction == SynchroDirection.IMPORT_SERVER2TEMP) {
220 			// delete surveyId if samplingOperId is set
221 			if (data[samplingOperIdIndex] != null) {
222 				data[surveyIdIndex] = null;
223 			}
224 
225 			// delete samplingOperId if sampleId is set
226 			if (data[sampleIdIndex] != null) {
227 				data[samplingOperIdIndex] = null;
228 			}
229 		}
230 
231 		// EXPORT: Local DB -> Temp DB
232 		else if (direction == SynchroDirection.EXPORT_LOCAL2TEMP) {
233 
234 			// Make sure precision is valid (workaround for mantis #37438)
235 			// A better solution can be to user BigDecimal in Hib mapping
236 			if (measNumerValueIndex != -1 && measDigitNumberIndex != -1) {
237 				BigDecimal measNumerValue = (BigDecimal) data[measNumerValueIndex];
238 				if (measNumerValue != null && data[measDigitNumberIndex] != null) {
239 					Double measDigitNumber = Double.parseDouble(data[measDigitNumberIndex].toString());
240 					data[measNumerValueIndex] = Daos.convertToBigDecimal(measNumerValue, measDigitNumber.intValue());
241 				}
242 			}
243 		}
244 
245 		// EXPORT: Temp DB -> Server DB
246 		else if (direction == SynchroDirection.EXPORT_TEMP2SERVER) {
247 			// Make sure the objectId is rewrite with remote identifier
248 			{
249 				Number objectId;
250 
251 				// If parent object is a sample
252 				if (data[sampleIdIndex] != null) {
253 					Long localSampleId = Long.parseLong(data[sampleIdIndex].toString());
254 					Number[] ids = getRemoteIdAndParentIdFromSampleId(sourceDao, localSampleId);
255 					objectId = ids[0];
256 					data[surveyIdIndex] = ids[1];
257 					data[samplingOperIdIndex] = ids[2];
258 				}
259 
260 				// If parent object is a sampling operation
261 				else if (data[samplingOperIdIndex] != null) {
262 					Long localSamplingOperId = Long.parseLong(data[samplingOperIdIndex].toString());
263 					Number[] ids = getRemoteIdAndParentIdFromSamplingOperId(sourceDao, localSamplingOperId);
264 					objectId = ids[0];
265 					data[surveyIdIndex] = ids[1];
266 				}
267 
268 				// If parent is survey
269 				else if (data[surveyIdIndex] != null) {
270 					objectId = getRemoteIdFromSurveyId(sourceDao, Long.parseLong(data[surveyIdIndex].toString()));
271 				}
272 
273 				// Else (should never append)
274 				else {
275 					throw new QuadrigeTechnicalException("Measurement found, without any parent filled. This should never append.");
276 				}
277 
278 				data[objectIdIndex] = objectId;
279 			}
280 		}
281 
282 		if (buffer != null && objectTypeCode != null && measIdIndex != -1) {
283 			Object measId = data[measIdIndex];
284 			if (measId != null) {
285 				buffer.addChildToUpdateFromManyColumns(DataSynchroTables.QUALIFICATION_HISTORY.name(),
286 					ImmutableSet.of(DatabaseColumns.OBJECT_TYPE_CD.name(), DatabaseColumns.QUAL_HIST_ELEMENT_ID.name()),
287 					Lists.newArrayList(objectTypeCode.value(), Long.parseLong(measId.toString())));
288 			}
289 		}
290 	}
291 
292 	/* -- Internal methods -- */
293 
294 	/**
295 	 * <p>
296 	 * initSelectRemoteIdAndParentIdFromSamplingOperIdQuery.
297 	 * </p>
298 	 * 
299 	 * @return a {@link java.lang.String} object.
300 	 */
301 	protected String initSelectRemoteIdAndParentIdFromSamplingOperIdQuery() {
302 		return String.format("select %s, %s from %s where %s = ?",
303 				DatabaseColumns.REMOTE_ID.name(),
304 				DatabaseColumns.SURVEY_ID.name(),
305 				DataSynchroTables.SAMPLING_OPERATION.name(),
306 				DatabaseColumns.SAMPLING_OPER_ID.name()
307 				);
308 	}
309 
310 	/**
311 	 * <p>
312 	 * initSelectRemoteIdAndParentIdFromSampleIdQuery.
313 	 * </p>
314 	 * 
315 	 * @return a {@link java.lang.String} object.
316 	 */
317 	protected String initSelectRemoteIdAndParentIdFromSampleIdQuery() {
318 		return String.format("select %s, %s, %s from %s where %s = ?",
319 				DatabaseColumns.REMOTE_ID.name(),
320 				DatabaseColumns.SURVEY_ID.name(),
321 				DatabaseColumns.SAMPLING_OPER_ID.name(),
322 				DataSynchroTables.SAMPLE.name(),
323 				DatabaseColumns.SAMPLE_ID.name()
324 				);
325 	}
326 
327 	/**
328 	 * <p>
329 	 * initSelectPmfm.
330 	 * </p>
331 	 * 
332 	 * @return a {@link java.lang.String} object.
333 	 */
334 	protected String initSelectPmfm() {
335 		return String.format("select %s || '%s' || %s || '%s' || %s || '%s' || %s from %s where %s = ?",
336 				DatabaseColumns.PAR_CD.name(),
337 				SynchroTableMetadata.PK_SEPARATOR,
338 				DatabaseColumns.MATRIX_ID.name(),
339 				SynchroTableMetadata.PK_SEPARATOR,
340 				DatabaseColumns.FRACTION_ID.name(),
341 				SynchroTableMetadata.PK_SEPARATOR,
342 				DatabaseColumns.METHOD_ID.name(),
343 				ReferentialSynchroTables.PMFM.name(),
344 				DatabaseColumns.PMFM_ID.name()
345 				);
346 	}
347 
348 	/**
349 	 * <p>
350 	 * initSelectRemoteIdFromSurveyId.
351 	 * </p>
352 	 * 
353 	 * @return a {@link java.lang.String} object.
354 	 */
355 	protected String initSelectRemoteIdFromSurveyId() {
356 		return String.format("select %s from %s where %s = ?",
357 				DatabaseColumns.REMOTE_ID.name(),
358 				DataSynchroTables.SURVEY.name(),
359 				DatabaseColumns.SURVEY_ID.name());
360 	}
361 
362 	/**
363 	 * <p>
364 	 * getRemoteIdAndParentIdFromSamplingOperId.
365 	 * </p>
366 	 * 
367 	 * @param dao
368 	 *            a {@link fr.ifremer.common.synchro.dao.SynchroBaseDao} object.
369 	 * @param samplingOperId
370 	 *            a long.
371 	 * @return an array of {@link java.lang.Number} objects.
372 	 * @throws java.sql.SQLException
373 	 *             if any.
374 	 */
375 	protected Number[] getRemoteIdAndParentIdFromSamplingOperId(SynchroBaseDao dao, long samplingOperId) throws SQLException {
376 		if (selectParentIdFromSamplingOperIdStatement == null || selectParentIdFromSamplingOperIdStatement.isClosed()) {
377 			selectParentIdFromSamplingOperIdStatement = dao.getPreparedStatement(selectParentIdFromSamplingOperIdQuery);
378 		}
379 		selectParentIdFromSamplingOperIdStatement.setLong(1, samplingOperId);
380 		ResultSet resultSet = null;
381 		try {
382 			resultSet = selectParentIdFromSamplingOperIdStatement.executeQuery();
383 			if (!resultSet.next()) {
384 				return null;
385 			}
386 			Number remoteId = null;
387 			if (resultSet.getObject(1) != null) {
388 				remoteId = (Number) resultSet.getObject(1);
389 			}
390 			Number surveyId = null;
391 			if (resultSet.getObject(2) != null) {
392 				surveyId = (Number) resultSet.getObject(2);
393 			}
394 			return new Number[] { remoteId, surveyId };
395 		} finally {
396 			Daos.closeSilently(resultSet);
397 		}
398 	}
399 
400 	/**
401 	 * <p>
402 	 * getRemoteIdAndParentIdFromSampleId.
403 	 * </p>
404 	 * 
405 	 * @param dao
406 	 *            a {@link fr.ifremer.common.synchro.dao.SynchroBaseDao} object.
407 	 * @param sampleId
408 	 *            a long.
409 	 * @return an array of {@link java.lang.Number} objects.
410 	 * @throws java.sql.SQLException
411 	 *             if any.
412 	 */
413 	protected Number[] getRemoteIdAndParentIdFromSampleId(SynchroBaseDao dao, long sampleId) throws SQLException {
414 		if (selectRemoteIdAndParentIdsFromSampleIdStatement == null || selectRemoteIdAndParentIdsFromSampleIdStatement.isClosed()) {
415 			selectRemoteIdAndParentIdsFromSampleIdStatement = dao.getPreparedStatement(selectRemoteIdAndParentIdsFromSampleIdQuery);
416 		}
417 
418 		selectRemoteIdAndParentIdsFromSampleIdStatement.setLong(1, sampleId);
419 		ResultSet resultSet = null;
420 		try {
421 			resultSet = selectRemoteIdAndParentIdsFromSampleIdStatement.executeQuery();
422 			if (!resultSet.next()) {
423 				return null;
424 			}
425 			Number remoteId = null;
426 			if (resultSet.getObject(1) != null) {
427 				remoteId = (Number) resultSet.getObject(1);
428 			}
429 			Number surveyId = null;
430 			if (resultSet.getObject(2) != null) {
431 				surveyId = (Number) resultSet.getObject(2);
432 			}
433 			Number samplingOperId = null;
434 			if (resultSet.getObject(3) != null) {
435 				samplingOperId = (Number) resultSet.getObject(3);
436 			}
437 
438 			return new Number[] { remoteId, surveyId, samplingOperId };
439 		} finally {
440 			Daos.closeSilently(resultSet);
441 		}
442 	}
443 
444 	/**
445 	 * <p>
446 	 * getPmfmParts.
447 	 * </p>
448 	 * 
449 	 * @param dao
450 	 *            a {@link fr.ifremer.common.synchro.dao.SynchroBaseDao} object.
451 	 * @param pmfmId
452 	 *            a long.
453 	 * @return a {@link java.util.List} object.
454 	 * @throws java.sql.SQLException
455 	 *             if any.
456 	 */
457 	protected List<Object> getPmfmParts(SynchroBaseDao dao, long pmfmId) throws SQLException {
458 		String resultStr = dao.getUniqueTyped(selectPmfmQuery, new Object[] { pmfmId });
459 		return SynchroTableMetadata.fromPkStr(resultStr);
460 	}
461 
462 	/**
463 	 * <p>
464 	 * getRemoteIdFromSurveyId.
465 	 * </p>
466 	 * 
467 	 * @param dao
468 	 *            a {@link fr.ifremer.common.synchro.dao.SynchroBaseDao} object.
469 	 * @param surveyId
470 	 *            a long.
471 	 * @return a {@link java.lang.Number} object.
472 	 * @throws java.sql.SQLException
473 	 *             if any.
474 	 */
475 	protected Number getRemoteIdFromSurveyId(SynchroBaseDao dao, long surveyId) throws SQLException {
476 		return dao.getUniqueTyped(selectRemoteIdBySurveyIdQuery, new Object[] { surveyId });
477 	}
478 
479 	/** {@inheritDoc} */
480 	@Override
481 	protected void doClose() throws IOException {
482 		super.doClose();
483 
484 		// Close first statement
485 		Daos.closeSilently(selectParentIdFromSamplingOperIdStatement);
486 		selectParentIdFromSamplingOperIdStatement = null;
487 
488 		// Close second statement
489 		Daos.closeSilently(selectRemoteIdAndParentIdsFromSampleIdStatement);
490 		selectRemoteIdAndParentIdsFromSampleIdStatement = null;
491 	}
492 }