View Javadoc
1   package fr.ifremer.quadrige3.synchro.intercept.data;
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.eventbus.Subscribe;
27  import fr.ifremer.common.synchro.dao.SynchroBaseDao;
28  import fr.ifremer.common.synchro.dao.SynchroTableDao;
29  import fr.ifremer.common.synchro.dao.SynchroTableDaoImpl;
30  import fr.ifremer.common.synchro.intercept.SynchroDeletePreventedException;
31  import fr.ifremer.common.synchro.intercept.SynchroInterceptorBase;
32  import fr.ifremer.common.synchro.intercept.SynchroOperationRepository;
33  import fr.ifremer.common.synchro.meta.SynchroTableMetadata;
34  import fr.ifremer.common.synchro.meta.event.CreateQueryEvent;
35  import fr.ifremer.common.synchro.meta.event.LoadTableEvent;
36  import fr.ifremer.common.synchro.query.SynchroQueryBuilder;
37  import fr.ifremer.common.synchro.query.SynchroQueryOperator;
38  import fr.ifremer.quadrige3.core.dao.referential.ObjectTypeCode;
39  import fr.ifremer.quadrige3.core.dao.technical.Daos;
40  import fr.ifremer.quadrige3.synchro.intercept.data.internal.ExportFkRemoteIdInterceptor;
41  import fr.ifremer.quadrige3.synchro.intercept.data.internal.ImportRemoteIdInterceptor;
42  import fr.ifremer.quadrige3.synchro.meta.DatabaseColumns;
43  import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
44  import fr.ifremer.quadrige3.synchro.service.SynchroDirection;
45  import org.apache.commons.collections4.CollectionUtils;
46  
47  import java.io.IOException;
48  import java.sql.PreparedStatement;
49  import java.sql.ResultSet;
50  import java.sql.SQLException;
51  import java.util.List;
52  
53  /**
54   * Manage columns rewriting ('id' AND 'remote_id') on table VALIDATION_HISTORY
55   *
56   * @author ludovic.pecquot@e-is.pro
57   * @since 1.0
58   */
59  public class ValidationHistoryInterceptor extends AbstractDataInterceptor {
60  
61  	private int pkColIndex = -1;
62  	private int elementIdColIndex = -1;
63  	private PreparedStatement selectElementIdStatement;
64  
65  	/**
66  	 * <p>
67  	 * Constructor for ValidationHistoryInterceptor.
68  	 * </p>
69  	 */
70  	public ValidationHistoryInterceptor() {
71  		super(
72  				DataSynchroTables.VALIDATION_HISTORY.name(),
73  				// IMPORT:
74  				SynchroDirection.IMPORT_TEMP2LOCAL,
75  				SynchroDirection.IMPORT_FILE2LOCAL,
76  				// EXPORT:
77  				SynchroDirection.EXPORT_TEMP2SERVER,
78  				SynchroDirection.EXPORT_LOCAL2TEMP,
79  				SynchroDirection.EXPORT_LOCAL2FILE
80  		);
81  		setEnableOnWrite(true);
82  		setEnableOnRead(true);
83  	}
84  
85  	@Override
86  	public SynchroInterceptorBase clone() {
87  		ValidationHistoryInterceptor clone = (ValidationHistoryInterceptor) super.clone();
88  		clone.pkColIndex = this.pkColIndex;
89  		clone.elementIdColIndex = this.elementIdColIndex;
90  		return clone;
91  	}
92  
93  	/**
94  	 * <p>
95  	 * handleQuery.
96  	 * </p>
97  	 *
98  	 * @param e
99  	 *            a {@link CreateQueryEvent} object.
100 	 */
101 	@Subscribe
102 	public void handleQuery(CreateQueryEvent e) {
103 
104 		switch (e.queryName) {
105 		// Select queries :
106 		case count:
107 		case countFromUpdateDate:
108 		case select:
109 		case selectFromUpdateDate:
110 		case selectMaxUpdateDate:
111 
112 			if (isInDirections(SynchroDirection.IMPORT_TEMP2LOCAL, SynchroDirection.IMPORT_FILE2LOCAL)) {
113 				e.sql = addRestrictionsOnImport(e.sql);
114 
115 			} else if (isInDirections(SynchroDirection.EXPORT_LOCAL2TEMP, SynchroDirection.EXPORT_LOCAL2FILE, SynchroDirection.EXPORT_TEMP2SERVER)) {
116 				e.sql = addRestrictionsOnExport(e.sql);
117 			}
118 			break;
119 		default:
120 			break;
121 		}
122 	}
123 
124 	/**
125 	 * <p>
126 	 * handleTableLoad.
127 	 * </p>
128 	 *
129 	 * @param e
130 	 *            a {@link LoadTableEvent} object.
131 	 */
132 	@Subscribe
133 	public void handleTableLoad(LoadTableEvent e) {
134 
135 		SynchroTableMetadata table = e.table;
136 		pkColIndex = table.getSelectColumnIndex(table.getPkNames().iterator().next());
137 		elementIdColIndex = table.getSelectColumnIndex(DatabaseColumns.VALID_HIST_ELEMENT_ID.name());
138 
139 		// IMPORT: Temp DB -> Local DB
140 		if (isInDirections(SynchroDirection.IMPORT_TEMP2LOCAL)) {
141 
142 			// Create and configure a interceptor, to rewrite survey remote ID into local ID
143 			ImportRemoteIdInterceptor remoteIdInterceptor = new ImportRemoteIdInterceptor(
144 					getConfig(),
145 					DataSynchroTables.SURVEY.name(),
146 					DatabaseColumns.VALID_HIST_ELEMENT_ID.name(),
147 				    elementIdColIndex,
148 					DatabaseColumns.SURVEY_ID.name(),
149 					false);
150 			table.addInterceptor(remoteIdInterceptor);
151 		}
152 
153 		// EXPORT: Temp DB -> Server DB
154 		else if (isInDirections(SynchroDirection.EXPORT_TEMP2SERVER)) {
155 
156 			// Create and configure a interceptor, to rewrite survey local ID into remote ID
157 			ExportFkRemoteIdInterceptor remoteIdInterceptor = new ExportFkRemoteIdInterceptor(
158 					getConfig(),
159 					DataSynchroTables.SURVEY.name(),
160 					DatabaseColumns.VALID_HIST_ELEMENT_ID.name(),
161 					elementIdColIndex,
162 					DatabaseColumns.SURVEY_ID.name(),
163 					false);
164 			table.addInterceptor(remoteIdInterceptor);
165 		}
166 	}
167 
168 	@Override
169 	protected void doOnRead(Object[] data, SynchroTableDao sourceDao, SynchroTableDao targetDao) throws SQLException {
170 		if (isInDirections(SynchroDirection.IMPORT_FILE2LOCAL)) {
171 			// Reset primary key to allow insertion
172 			data[pkColIndex] = null;
173 		}
174 	}
175 
176 	@Override
177 	protected void doOnWrite(Object[] data, List<Object> pk, SynchroTableDao sourceDao, SynchroTableDao targetDao, SynchroOperationRepository buffer, boolean insert) throws SQLException {
178 		if (isInDirections(SynchroDirection.IMPORT_FILE2LOCAL) && targetDao instanceof SynchroTableDaoImpl) {
179 
180 			// Affect new PK
181 			List<Object> pks = ((SynchroTableDaoImpl) targetDao).generateNewPk();
182 			data[pkColIndex] = pks.iterator().next();
183 
184 			// Try to remap survey id when importing from file
185 			if (data[elementIdColIndex] == null || !getConfig().getRemapPks().containsKey(DataSynchroTables.SURVEY.name().toLowerCase())) {
186 				return;
187 			}
188 
189 			// Get the source FK
190 			String fkStr = data[elementIdColIndex].toString();
191 
192 			// Try to get the corresponding local FK
193 			String localFkStr = getConfig().getRemapPks().get(DataSynchroTables.SURVEY.name().toLowerCase()).get(fkStr);
194 
195 			if (localFkStr != null && !localFkStr.equals(fkStr)) {
196 
197 				// Replace this FK
198 				data[elementIdColIndex] = Long.parseLong(localFkStr);
199 			}
200 
201 		}
202 	}
203 
204 	@Override
205 	protected void doOnDelete(List<Object> pk, SynchroTableDao sourceDao, SynchroTableDao targetDao, SynchroOperationRepository buffer) throws SQLException {
206 		if (isInDirections(SynchroDirection.IMPORT_FILE2LOCAL)) {
207 			// Don't delete if trying to remove remapped surveys
208 			if (getConfig().getRemapPks().containsKey(DataSynchroTables.SURVEY.name().toLowerCase()) && CollectionUtils.size(pk) == 1) {
209 				String elementId = getElementId(targetDao, pk.get(0));
210 				if (elementId != null && getConfig().getRemapPks().get(DataSynchroTables.SURVEY.name().toLowerCase()).containsKey(elementId)) {
211 					throw new SynchroDeletePreventedException();
212 				}
213 			}
214 		}
215 	}
216 
217 	@Override
218 	protected void doClose() throws IOException {
219 		super.doClose();
220 
221 		Daos.closeSilently(selectElementIdStatement);
222 		selectElementIdStatement = null;
223 	}
224 
225 	/* -- Internal methods -- */
226 
227 	/**
228 	 * <p>
229 	 * add restrictions on import
230 	 * </p>
231 	 *
232 	 * @param sql
233 	 *            a {@link String} object.
234 	 * @return a {@link String} object.
235 	 */
236 	protected String addRestrictionsOnImport(String sql) {
237 
238 		SynchroQueryBuilder query = SynchroQueryBuilder.newBuilder(sql);
239 
240 		// Limit to line on survey (object type code = PASS)
241 		query.addWhere(SynchroQueryOperator.AND, String.format("t.%s = '%s'",
242 				DatabaseColumns.OBJECT_TYPE_CD,
243 				ObjectTypeCode.SURVEY.value()));
244 
245 		return query.build();
246 	}
247 
248 	/**
249 	 * <p>
250 	 * add restrictions on export
251 	 * </p>
252 	 *
253 	 * @param sql
254 	 *            a {@link java.lang.String} object.
255 	 * @return a {@link java.lang.String} object.
256 	 */
257 	protected String addRestrictionsOnExport(String sql) {
258 
259 		SynchroQueryBuilder query = SynchroQueryBuilder.newBuilder(sql);
260 
261 		// Limit to line on survey (object type code = PASS)
262 		query.addWhere(SynchroQueryOperator.AND, String.format("t.%s = '%s'",
263 				DatabaseColumns.OBJECT_TYPE_CD,
264 				ObjectTypeCode.SURVEY.value()));
265 
266 		return query.build();
267 	}
268 
269 	private String getElementId(SynchroBaseDao dao, Object pk) throws SQLException {
270 		if (selectElementIdStatement == null || selectElementIdStatement.isClosed()) {
271 			selectElementIdStatement = dao.getPreparedStatement(
272 				String.format("SELECT %s FROM %s WHERE VALID_HIST_ID=?", DatabaseColumns.VALID_HIST_ELEMENT_ID.name(), DataSynchroTables.VALIDATION_HISTORY.name())
273 			);
274 		}
275 		selectElementIdStatement.setObject(1, pk);
276 		try (ResultSet rs = selectElementIdStatement.executeQuery()) {
277 			rs.next();
278 			return rs.getString(1);
279 		}
280 	}
281 
282 }