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