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