View Javadoc
1   package fr.ifremer.quadrige3.synchro.action;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: Quadrige3 Client 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 fr.ifremer.common.synchro.config.SynchroConfiguration;
27  import fr.ifremer.common.synchro.service.RejectedRow;
28  import fr.ifremer.quadrige3.core.ProgressionCoreModel;
29  import fr.ifremer.quadrige3.core.action.CommandLines;
30  import fr.ifremer.quadrige3.core.config.QuadrigeConfiguration;
31  import fr.ifremer.quadrige3.core.dao.administration.user.QuserJdbcDao;
32  import fr.ifremer.quadrige3.core.dao.administration.user.QuserJdbcDaoImpl;
33  import fr.ifremer.quadrige3.core.dao.technical.Daos;
34  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
35  import fr.ifremer.quadrige3.core.service.ServiceLocator;
36  import fr.ifremer.quadrige3.synchro.service.client.SynchroClientService;
37  import fr.ifremer.quadrige3.synchro.service.client.SynchroRejectedRowResolver;
38  import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportResult;
39  import fr.ifremer.quadrige3.synchro.vo.SynchroImportContextVO;
40  import liquibase.util.StringUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.nuiton.i18n.I18n;
44  import org.springframework.dao.DataRetrievalFailureException;
45  
46  import java.sql.SQLException;
47  import java.util.Map;
48  import java.util.Properties;
49  
50  /**
51   * Command-line action, to import data and referential from the central database.
52   * Created by blavenie on 20/08/15.
53   */
54  public class ImportAction {
55  	/* Logger */
56  	private static final Log log = LogFactory.getLog(ImportAction.class);
57  
58  	/**
59  	 * Import referential only
60  	 */
61  	public void importReferential() {
62  
63  		// Get connections properties :
64  		Properties sourceConnectionProperties = SynchroConfiguration.getInstance().getImportConnectionProperties();
65  		Properties targetConnectionProperties = QuadrigeConfiguration.getInstance().getConnectionProperties();
66  
67  		// Log starting
68  		if (log.isInfoEnabled()) {
69  			log.info(I18n.t("quadrige3.action.synchro.import.referential"));
70  		}
71  
72  		// Check connections
73  		if (!checkConnection(sourceConnectionProperties, targetConnectionProperties)) {
74  			return;
75  		}
76  
77  		// Create the progression model
78  		ProgressionCoreModel progressionModel = createConsoleProgressionModel(101);
79  
80  		// Create the service and context
81  		SynchroClientService service = ServiceLocator.instance().getService("synchroClientService", SynchroClientService.class);
82  		SynchroImportContextVO importContext = new SynchroImportContextVO();
83  		importContext.setWithReferential(true);
84  
85  		progressionModel.setMessage(I18n.t("quadrige3.service.synchro.import.referential.message", I18n.t("quadrige3.action.synchro.import.init")));
86  		progressionModel.increments(1);
87  
88  		try {
89  			SynchroClientImportResult result = service.importFromServerDatabase(-1,
90  					importContext,
91  					createSynchroRejectedRowResolver(),
92  					progressionModel,
93  					100
94  					);
95  
96  			onSuccess(result, progressionModel);
97  		} catch (QuadrigeTechnicalException e) {
98  			onError(e, progressionModel);
99  		}
100 
101 		// Shutdown database at end
102 		try {
103 			Daos.shutdownDatabase(targetConnectionProperties);
104 		} catch (SQLException e) {
105 			throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.synchro.import.shutdown"),
106 					e);
107 		}
108 	}
109 
110 	/**
111 	 * Import data only
112 	 */
113 	public void importData() {
114 		// Get connections properties :
115 		Properties sourceConnectionProperties = SynchroConfiguration.getInstance().getImportConnectionProperties();
116 		Properties targetConnectionProperties = QuadrigeConfiguration.getInstance().getConnectionProperties();
117 
118 		// Log target connection
119 		if (log.isInfoEnabled()) {
120 			log.info(I18n.t("quadrige3.action.synchro.import.data",
121 					QuadrigeConfiguration.getInstance().getImportNbYearDataHistory()
122 					));
123 		}
124 
125 		// Check connections
126 		if (!checkConnection(sourceConnectionProperties, targetConnectionProperties)) {
127 			return;
128 		}
129 
130 		// Create the context
131 		Integer userId = getUserId(sourceConnectionProperties);
132 
133 		// Create progression model
134 		ProgressionCoreModel progressionModel = createConsoleProgressionModel(101);
135 
136 		// Create the service and context
137 		SynchroClientService service = ServiceLocator.instance().getService("synchroClientService", SynchroClientService.class);
138 		SynchroImportContextVO importContext = new SynchroImportContextVO();
139 		importContext.setWithData(true);
140 
141 		progressionModel.setMessage(I18n.t("quadrige3.service.synchro.import.data.message", I18n.t("quadrige3.action.synchro.import.init")));
142 		progressionModel.increments(1);
143 
144 		try {
145 			SynchroClientImportResult result = service.importFromServerDatabase(userId,
146 					importContext,
147 					createSynchroRejectedRowResolver(),
148 					progressionModel,
149 					100
150 					);
151 
152 			onSuccess(result, progressionModel);
153 		} catch (QuadrigeTechnicalException e) {
154 			onError(e, progressionModel);
155 		}
156 
157 		// Shutdown database at end
158 		try {
159 			Daos.shutdownDatabase(targetConnectionProperties);
160 		} catch (SQLException e) {
161 			throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.synchro.import.shutdown"),
162 					e);
163 		}
164 	}
165 
166 	/**
167 	 * Import referential AND data only
168 	 */
169 	public void importReferentialAndData() {
170 		// Get connections properties :
171 		Properties sourceConnectionProperties = SynchroConfiguration.getInstance().getImportConnectionProperties();
172 		Properties targetConnectionProperties = QuadrigeConfiguration.getInstance().getConnectionProperties();
173 
174 		// Log target connection
175 		if (log.isInfoEnabled()) {
176 			log.info(I18n.t("quadrige3.action.synchro.import.all",
177 					QuadrigeConfiguration.getInstance().getImportNbYearDataHistory()
178 					));
179 		}
180 
181 		// Check connections
182 		if (!checkConnection(sourceConnectionProperties, targetConnectionProperties)) {
183 			return;
184 		}
185 
186 		// Create the context
187 		Integer userId = getUserId(sourceConnectionProperties);
188 
189 		// Create progression model
190 		ProgressionCoreModel progressionModel = createConsoleProgressionModel(101);
191 
192 		// Create the service and context
193 		SynchroClientService service = ServiceLocator.instance().getService("synchroClientService", SynchroClientService.class);
194 		SynchroImportContextVO importContext = new SynchroImportContextVO();
195 		importContext.setWithData(true);
196 		importContext.setWithReferential(true);
197 
198 		progressionModel.setMessage(I18n.t("quadrige3.service.synchro.import.data.message", I18n.t("quadrige3.action.synchro.import.init")));
199 		progressionModel.increments(1);
200 
201 		try {
202 			SynchroClientImportResult result = service.importFromServerDatabase(userId,
203 					importContext,
204 					createSynchroRejectedRowResolver(),
205 					progressionModel,
206 					100
207 					);
208 
209 			onSuccess(result, progressionModel);
210 		} catch (QuadrigeTechnicalException e) {
211 			onError(e, progressionModel);
212 		}
213 
214 		// Shutdown database at end
215 		try {
216 			Daos.shutdownDatabase(targetConnectionProperties);
217 
218 		} catch (SQLException e) {
219 			throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.synchro.import.shutdown"),
220 					e);
221 		}
222 	}
223 
224 	/* -- Internal methods -- */
225 
226 	/**
227 	 * <p>
228 	 * getUserId.
229 	 * </p>
230 	 * 
231 	 * @param connectionProperties
232 	 *            a {@link java.util.Properties} object.
233 	 * @return a {@link java.lang.Integer} object.
234 	 */
235 	protected Integer getUserId(Properties connectionProperties) {
236 		Integer userId = null;
237 
238 		while (userId == null) {
239 			// Get the username
240 			String username = CommandLines.readNotBlankInput(I18n.t("quadrige3.error.synchro.getUser.username"));
241 
242 			// Find the user, by username
243 			QuserJdbcDao quserJdbcDao = new QuserJdbcDaoImpl();
244 			try {
245 				userId = quserJdbcDao.getUserIdByUsername(connectionProperties, username);
246 			} catch (DataRetrievalFailureException e) {
247 				throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.synchro.getUser"),
248 						e);
249 			}
250 
251 			// Display a warning message before new loop iteration
252 			if (userId == null) {
253 				log.error(I18n.t("quadrige3.error.synchro.getUser.notFound", username));
254 			}
255 		}
256 		return userId;
257 	}
258 
259 	/**
260 	 * <p>
261 	 * checkConnection.
262 	 * </p>
263 	 * 
264 	 * @param sourceConnectionProperties
265 	 *            a {@link java.util.Properties} object.
266 	 * @param targetConnectionProperties
267 	 *            a {@link java.util.Properties} object.
268 	 * @return a boolean.
269 	 */
270 	protected boolean checkConnection(
271 			Properties sourceConnectionProperties,
272 			Properties targetConnectionProperties) {
273 
274 		// Log target connection
275 		if (log.isInfoEnabled()) {
276 			log.info("Connecting to target database...\n" + Daos.getLogString(targetConnectionProperties));
277 		}
278 
279 		// Check target connection
280 		boolean isValidConnection = Daos.isValidConnectionProperties(targetConnectionProperties);
281 		if (!isValidConnection) {
282 			log.error("Connection error: could not connect to target database.");
283 			return false;
284 		}
285 
286 		// Log source connection
287 		if (log.isInfoEnabled()) {
288 			log.info("Connecting to source database...\n" + Daos.getLogString(sourceConnectionProperties));
289 		}
290 
291 		// Check source connection
292 		String sourceJdbcUrl = Daos.getUrl(sourceConnectionProperties);
293 		if (!Daos.isOracleDatabase(sourceJdbcUrl) && !Daos.isPostgresqlDatabase(sourceJdbcUrl)) {
294 			// Source database = an oracle or pgsql connection (see file synchro-test-write.properties)
295 			log.warn("Source database is not a valid database. Make sure your configuration file is correct.");
296 		}
297 		isValidConnection = Daos.isValidConnectionProperties(sourceConnectionProperties);
298 		if (!isValidConnection) {
299 			log.warn("Connection error: could not connect to source database.");
300 			return false;
301 		}
302 
303 		return true;
304 	}
305 
306 	/**
307 	 * <p>
308 	 * createSynchroRejectedRowResolver.
309 	 * </p>
310 	 * 
311 	 * @return a {@link fr.ifremer.quadrige3.synchro.service.client.SynchroRejectedRowResolver} object.
312 	 */
313 	protected SynchroRejectedRowResolver createSynchroRejectedRowResolver() {
314 		return new SynchroRejectedRowResolver() {
315 			@Override
316 			public RejectedRow.ResolveStrategy resolveReject(RejectedRow.Cause rejectStatus, String rejectInfos, String rejectMessage) {
317 				return RejectedRow.ResolveStrategy.DO_NOTHING;
318 			}
319 
320 			@Override
321 			public void showRejectMessage(Map<RejectedRow.Cause, String> rejectedRows, RejectedRow.Cause status, boolean failMessage) {
322 				// Do nothing
323 			}
324 		};
325 	}
326 
327 	/**
328 	 * Create a new progress model, that display message in the console
329 	 * 
330 	 * @param total
331 	 *            a int.
332 	 * @return a {@link ProgressionCoreModel} object.
333 	 */
334 	protected ProgressionCoreModel createConsoleProgressionModel(int total) {
335 
336 		final ProgressionCoreModel progressionModel = new ProgressionCoreModel();
337 		progressionModel.setCurrent(0);
338 		progressionModel.setTotal(total);
339 
340 		// Listen 'current' attribute changes
341 		progressionModel.addPropertyChangeListener(fr.ifremer.common.synchro.type.ProgressionModel.PROPERTY_CURRENT,
342 				evt -> displayProgressionToConsole(progressionModel));
343 
344 		// Listen message changes
345 		progressionModel.addPropertyChangeListener(fr.ifremer.common.synchro.type.ProgressionModel.PROPERTY_MESSAGE,
346 				evt -> displayProgressionToConsole(progressionModel));
347 
348 		return progressionModel;
349 	}
350 
351 	/**
352 	 * <p>
353 	 * displayProgressionToConsole.
354 	 * </p>
355 	 * 
356 	 * @param progressionModel
357 	 *            a {@link ProgressionCoreModel} object.
358 	 */
359 	protected void displayProgressionToConsole(final ProgressionCoreModel progressionModel) {
360 		int current = progressionModel.getCurrent();
361 		StringBuilder sb = new StringBuilder();
362 		sb.append("[");
363 		if (current > 0) {
364 			sb.append(StringUtils.repeat("-", current));
365 		}
366 		sb.append('>')
367 				.append(StringUtils.repeat(" ", progressionModel.getTotal() - progressionModel.getCurrent()))
368 				.append("] ");
369 
370 		String message = progressionModel.getMessage();
371 		if (org.apache.commons.lang3.StringUtils.isNotBlank(message)) {
372 			sb.append(message);
373 		}
374 		sb.append('\r');
375 
376 		// display result on console
377 		System.out.print(sb.toString());
378 	}
379 
380 	/**
381 	 * <p>
382 	 * onError.
383 	 * </p>
384 	 * 
385 	 * @param e
386 	 *            a {@link java.lang.Exception} object.
387 	 * @param progressionModel
388 	 *            a {@link ProgressionCoreModel} object.
389 	 */
390 	protected void onError(Exception e, ProgressionCoreModel progressionModel) {
391 		System.out.println("[ERROR] " + e.getMessage() + StringUtils.repeat(" ", progressionModel.getTotal()));
392 		log.error(e.getMessage(), e);
393 	}
394 
395 	/**
396 	 * <p>
397 	 * onSuccess.
398 	 * </p>
399 	 * 
400 	 * @param result
401 	 *            a {@link fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportResult} object.
402 	 * @param progressionModel
403 	 *            a {@link ProgressionCoreModel} object.
404 	 */
405 	protected void onSuccess(SynchroClientImportResult result, ProgressionCoreModel progressionModel) {
406 		System.out.println("[Done]" + StringUtils.repeat(" ", progressionModel.getTotal())); // end of display console
407 
408 		int nbRowReferential = result.getReferentialResult() != null ? result.getReferentialResult().getTotalTreated() : 0;
409 		int nbRowData = result.getDataResult() != null ? result.getDataResult().getTotalTreated() : 0;
410 
411 		if (nbRowData != 0 && nbRowReferential != 0) {
412 			log.info(I18n.t("quadrige3.action.synchro.import.success.all", nbRowReferential, nbRowData));
413 		}
414 		else if (nbRowData != 0) {
415 			log.info(I18n.t("quadrige3.action.synchro.import.success.data", nbRowData));
416 		}
417 		else if (nbRowReferential != 0) {
418 			log.info(I18n.t("quadrige3.action.synchro.import.success.referential", nbRowReferential));
419 		}
420 		else {
421 			log.info(I18n.t("quadrige3.action.synchro.import.noRow"));
422 		}
423 	}
424 }