View Javadoc
1   package fr.ifremer.quadrige3.synchro.service.client;
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 com.google.common.base.Joiner;
27  import com.google.common.base.Splitter;
28  import com.google.common.io.Files;
29  import fr.ifremer.common.synchro.service.SynchroResult;
30  import fr.ifremer.quadrige3.core.config.QuadrigeConfiguration;
31  import fr.ifremer.quadrige3.core.dao.technical.Assert;
32  import fr.ifremer.quadrige3.core.dao.technical.Dates;
33  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
34  import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
35  import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientExportResult;
36  import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientExportToFileResult;
37  import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportFromFileResult;
38  import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportResult;
39  import fr.ifremer.quadrige3.synchro.service.data.DataSynchroContext;
40  import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroContext;
41  import fr.ifremer.quadrige3.synchro.vo.SynchroDateOperatorVO;
42  import fr.ifremer.quadrige3.synchro.vo.SynchroExportContextVO;
43  import org.apache.commons.collections4.CollectionUtils;
44  import org.apache.commons.io.FileUtils;
45  import org.apache.commons.lang3.StringUtils;
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  import org.nuiton.i18n.I18n;
49  import org.springframework.context.annotation.Lazy;
50  import org.springframework.stereotype.Service;
51  
52  import javax.annotation.Resource;
53  import java.io.File;
54  import java.io.FileWriter;
55  import java.io.IOException;
56  import java.nio.charset.Charset;
57  import java.text.DateFormat;
58  import java.text.SimpleDateFormat;
59  import java.util.Date;
60  import java.util.Set;
61  
62  /**
63   * Give access (read/write) to synchronization log file.
64   *
65   * Created by Benoit on 23/06/2015.
66   */
67  @Service("synchroHistoryService")
68  @Lazy
69  public class SynchroHistoryServiceImpl implements SynchroHistoryService {
70  	private static final Log log = LogFactory.getLog(SynchroHistoryServiceImpl.class);
71  
72  	private static final String HISTORY_LOG_FILE = "synchro.txt";
73  
74  	// Reserved some I18n string
75  	static {
76  		I18n.n("quadrige3.synchro.service.history.filter.date.EQUALS");
77  		I18n.n("quadrige3.synchro.service.history.filter.date.BETWEEN");
78  		I18n.n("quadrige3.synchro.service.history.filter.date.BEFORE");
79  		I18n.n("quadrige3.synchro.service.history.filter.date.BEFORE_OR_EQUALS");
80  		I18n.n("quadrige3.synchro.service.history.filter.date.AFTER");
81  		I18n.n("quadrige3.synchro.service.history.filter.date.AFTER_OR_EQUALS");
82  	}
83  
84  	@Resource
85  	private QuadrigeConfiguration configuration;
86  
87  	/** {@inheritDoc} */
88  	@Override
89  	public void save(int userId, SynchroClientImportResult result) {
90  		Assert.notNull(result);
91  		StringBuilder messageBuilder = new StringBuilder();
92  
93  		boolean withReferential = result.getReferentialResult() != null && result.getReferentialResult().getTotalTreated() > 0;
94  		boolean withData = result.getDataResult() != null && result.getDataResult().getTotalTreated() > 0;
95  
96  		// Nothing imported
97  		if (!withReferential && !withData) {
98  			return;
99  		}
100 
101 		Date referentialUpdateDate = result.getReferentialSynchronizationDate();
102 		Date dataUpdateDate = result.getDataSynchronizationDate();
103 
104 		DateFormat dateFormat = DateFormat.getDateTimeInstance(SimpleDateFormat.LONG, SimpleDateFormat.LONG);
105 
106 		// data + referential
107 		if (withData) {
108 			Set<String> programCodes = result.getDataContext().getProgramCodes();
109 			String programCodesStr = CollectionUtils.isEmpty(programCodes) ? "" : String.join(", ", programCodes);
110 
111 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.import", dateFormat.format(dataUpdateDate)));
112 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.import.importedPrograms", CollectionUtils.size(programCodes), programCodesStr));
113 		}
114 
115 		// referential only
116 		else {
117 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.import.referential",
118 					dateFormat.format(referentialUpdateDate)));
119 		}
120 
121 		// append to history file
122 		if (messageBuilder.length() > 0) {
123 			appendToHistoryFile(userId, messageBuilder.toString());
124 		}
125 	}
126 
127 	/** {@inheritDoc} */
128 	@Override
129 	public void save(int userId, SynchroClientImportFromFileResult result) {
130 		Assert.notNull(result);
131 		Assert.notNull(result.getFile());
132 		StringBuilder messageBuilder = new StringBuilder();
133 
134 		boolean withReferential = result.getReferentialResult() != null && result.getReferentialResult().getTotalTreated() > 0;
135 		boolean withData = result.getDataResult() != null && result.getDataResult().getTotalTreated() > 0;
136 
137 		Date nowDate = new Date();
138 		Date referentialUpdateDate = result.getReferentialSynchronizationDate();
139 		if (referentialUpdateDate == null) {
140 			referentialUpdateDate = nowDate;
141 		}
142 		Date dataUpdateDate = result.getDataSynchronizationDate();
143 		if (dataUpdateDate == null) {
144 			dataUpdateDate = nowDate;
145 		}
146 
147 		DateFormat dateFormat = DateFormat.getDateTimeInstance(SimpleDateFormat.LONG, SimpleDateFormat.LONG);
148 
149 		// data + referential
150 		if (withData && withReferential) {
151 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.importFromFile", dateFormat.format(dataUpdateDate), result.getFile()));
152 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.importFromFile.data"));
153 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.importFromFile.referential"));
154 		}
155 
156 		// data only
157 		else if (withData) {
158 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.importFromFile", dateFormat.format(dataUpdateDate), result.getFile()));
159 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.importFromFile.data"));
160 		}
161 
162 		// referential only
163 		else if (withReferential) {
164 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.importFromFile", dateFormat.format(referentialUpdateDate), result.getFile()));
165 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.importFromFile.referential"));
166 		}
167 
168 		// Nothing imported
169 		else {
170 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.importFromFile", dateFormat.format(nowDate), result.getFile()));
171 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.importFromFile.noData"));
172 		}
173 
174 		// append to history file
175 		if (messageBuilder.length() > 0) {
176 			appendToHistoryFile(userId, messageBuilder.toString());
177 		}
178 	}
179 
180 	/** {@inheritDoc} */
181 	@Override
182 	public void save(int userId, SynchroClientExportResult result) {
183 		Assert.notNull(result);
184 
185 		saveExportServerResult(userId, result.getServerResult(), result.getDataContext().getProgramCodes());
186 	}
187 
188 	/** {@inheritDoc} */
189 	@Override
190 	public void saveExportServerResult(int userId, SynchroResult serverResult, Set<String> programCodes) {
191 		StringBuilder messageBuilder = new StringBuilder();
192 
193 		boolean hasUpdates = serverResult != null && serverResult.getTotalTreated() > 0;
194 
195 		// Nothing exported
196 		if (!hasUpdates) {
197 			return;
198 		}
199 
200 		// Retrieve the system timestamp, from missing updates
201 		DateFormat dateFormat = DateFormat.getDateTimeInstance(SimpleDateFormat.LONG, SimpleDateFormat.LONG);
202 
203 		// data + referential
204 		// File tempDb = result.getFile();
205 		int surveyNbInserts = serverResult.getNbInserts(DataSynchroTables.SURVEY.name());
206 		int surveyNbUpdates = serverResult.getNbUpdates(DataSynchroTables.SURVEY.name());
207 		int surveyNbDeletes = serverResult.getNbDeletes(DataSynchroTables.SURVEY.name());
208 
209 		String rejectedRows = serverResult.getRejectedRows(DataSynchroTables.SURVEY.name());
210 		int surveyNbRejects = countErrorItems(rejectedRows);
211 
212 		String programCodesStr = CollectionUtils.isEmpty(programCodes)
213 				? ""
214 				: Joiner.on(", ").join(programCodes);
215 
216 		messageBuilder.append(I18n.t("quadrige3.synchro.service.history.export", dateFormat.format(new Date())));
217 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.exportedPrograms", CollectionUtils.size(programCodes), programCodesStr));
218 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.createdSurveys", surveyNbInserts));
219 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.updatedSurveys", surveyNbUpdates));
220 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.deletedSurveys", surveyNbDeletes));
221 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.rejectedSurveys", surveyNbRejects));
222 
223 		// append to history file
224 		if (messageBuilder.length() > 0) {
225 			appendToHistoryFile(userId, messageBuilder.toString());
226 		}
227 	}
228 
229 	/** {@inheritDoc} */
230 	@Override
231 	public void save(int userId, SynchroClientExportToFileResult result) {
232 		Assert.notNull(result);
233 		Assert.notNull(result.getFile());
234 		StringBuilder messageBuilder = new StringBuilder();
235 
236 		boolean withReferential = result.getReferentialResult() != null && result.getReferentialResult().getTotalTreated() > 0;
237 		boolean withData = result.getDataResult() != null && result.getDataResult().getTotalTreated() > 0;
238 
239 		// Nothing exported
240 		if (!withReferential && !withData) {
241 			return;
242 		}
243 
244 		// Retrieve the system timestamp, from missing updates
245 		DateFormat dateFormat = DateFormat.getDateTimeInstance(SimpleDateFormat.LONG, SimpleDateFormat.LONG);
246 
247 		// If some data has been exported
248 		if (withData) {
249 
250 			DataSynchroContext dataContext = result.getDataContext();
251 			Set<String> programCodes = dataContext.getProgramCodes();
252 			int surveyNbInserts = result.getDataResult().getNbInserts(DataSynchroTables.SURVEY.name());
253 
254 			StringBuilder filterMessageBuilder = new StringBuilder();
255 
256 			// Add filter isDirtyOnly
257 			if (dataContext.isDirtyOnly()) {
258 				filterMessageBuilder.append("\n\t")
259 						.append(I18n.t("quadrige3.synchro.service.history.filter.withDirtyOnly"));
260 			}
261 
262 			// Add filter on date
263 			if (dataContext.getDataStartDate() != null) {
264 				SynchroDateOperatorVO operator = dataContext.getDateOperator();
265 				String datePattern = I18n.t("quadrige3.synchro.service.history.filter.date.pattern");
266 				filterMessageBuilder.append("\n\t")
267 						.append(I18n.t("quadrige3.synchro.service.history.filter.date." + operator.name(),
268 								Dates.formatDate(dataContext.getDataStartDate(), datePattern),
269 								Dates.formatDate(dataContext.getDataEndDate(), datePattern)));
270 			}
271 
272 			// Add filter on programs
273 			if (CollectionUtils.isNotEmpty(programCodes)) {
274 				String programCodesStr = CollectionUtils.isEmpty(programCodes)
275 						? ""
276 						: Joiner.on(", ").join(programCodes);
277 				filterMessageBuilder
278 						.append("\n\t")
279 						.append(I18n.t("quadrige3.synchro.service.history.filter.program",
280 								CollectionUtils.size(programCodes),
281 								programCodesStr));
282 			}
283 
284 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.exportToFile",
285 					dateFormat.format(new Date()),
286 					result.getFile().getPath(),
287 					filterMessageBuilder.toString()));
288 			messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.exportToFile.exportedSurveys", surveyNbInserts));
289 		}
290 
291 		// If only local referential has been exported
292 		else if (withReferential) {
293 			ReferentialSynchroContext referentialContext = result.getReferentialContext();
294 			Set<String> programCodes = referentialContext.getProgramCodes();
295 
296 			StringBuilder filterMessageBuilder = new StringBuilder();
297 
298 			// Add filter program
299 			if (CollectionUtils.isNotEmpty(programCodes)) {
300 				String programCodesStr = CollectionUtils.isEmpty(programCodes)
301 						? ""
302 						: Joiner.on(", ").join(programCodes);
303 				filterMessageBuilder
304 						.append("\n\t")
305 						.append(I18n.t("quadrige3.synchro.service.history.filter.program",
306 								CollectionUtils.size(programCodes),
307 								programCodesStr));
308 			}
309 
310 			// Main message
311 			messageBuilder.append(I18n.t("quadrige3.synchro.service.history.exportToFile.referential",
312 					dateFormat.format(new Date()),
313 					result.getFile().getPath(),
314 					filterMessageBuilder.toString()));
315 		}
316 
317 		// Append to history file
318 		if (messageBuilder.length() > 0) {
319 			appendToHistoryFile(userId, messageBuilder.toString());
320 		}
321 	}
322 
323 	/** {@inheritDoc} */
324 	@Override
325 	public String getContent(int userId) {
326 		File file = getHistoryFileByUserId(userId);
327 		if (!file.exists()) {
328 			return null;
329 		}
330 
331 		try {
332 			return FileUtils.readFileToString(file, Charset.defaultCharset());
333 		} catch (IOException e) {
334 			throw new QuadrigeTechnicalException(I18n.t("quadrige3.service.synchroHistory.readContent.failed"), e);
335 		}
336 	}
337 
338 	/** {@inheritDoc} */
339 	@Override
340 	public File getHistoryFileByUserId(int userId) {
341 		return new File(
342 				configuration.getSynchronizationDirectory(),
343 				new StringBuilder()
344 						.append(userId)
345 						.append(File.separator)
346 						.append(HISTORY_LOG_FILE)
347 						.toString());
348 	}
349 
350 	@Override
351 	public void saveExportError(int userId, SynchroExportContextVO exportContext, String message) {
352 
353 		StringBuilder messageBuilder = new StringBuilder();
354 
355 		// Retrieve the system timestamp, from missing updates
356 		DateFormat dateFormat = DateFormat.getDateTimeInstance(SimpleDateFormat.LONG, SimpleDateFormat.LONG);
357 
358 		// Add filter program
359 		String programCodesStr = CollectionUtils.isEmpty(exportContext.getDataProgramCodes())
360 				? ""
361 				: Joiner.on(", ").join(exportContext.getDataProgramCodes());
362 		messageBuilder.append(I18n.t("quadrige3.synchro.service.history.export.failed", dateFormat.format(new Date())));
363 		messageBuilder.append("\n\t")
364 				.append(I18n.t("quadrige3.synchro.service.history.export.exportedPrograms", CollectionUtils.size(exportContext.getDataProgramCodes()), programCodesStr));
365 		messageBuilder.append("\n\t").append(I18n.t("quadrige3.synchro.service.history.export.error", message));
366 
367 		// Append to log file
368 		appendToHistoryFile(userId, messageBuilder.toString());
369 	}
370 
371 	/* -- protected mdethods -- */
372 
373 	/**
374 	 * <p>
375 	 * appendToHistoryFile.
376 	 * </p>
377 	 *
378 	 * @param userId
379 	 *            a int.
380 	 * @param messageToAppend
381 	 *            a {@link java.lang.String} object.
382 	 */
383 	protected void appendToHistoryFile(int userId, String messageToAppend) {
384 		File historyFile = getHistoryFileByUserId(userId);
385 
386 		try {
387 
388 			// Make sure file and dirs exists
389 			Files.createParentDirs(historyFile);
390 
391 			try (FileWriter fw = new FileWriter(historyFile, true/* append */)) {
392 				fw.write('\n');
393 				fw.write(messageToAppend);
394 				fw.write('\n');
395 			}
396 		} catch (IOException e) {
397 			throw new QuadrigeTechnicalException(I18n.t("quadrige3.synchro.service.history.error", historyFile.getPath()), e);
398 		}
399 	}
400 
401 	/**
402 	 * <p>
403 	 * countErrorItems.
404 	 * </p>
405 	 *
406 	 * @param rejectedRows
407 	 *            a {@link java.lang.String} object.
408 	 * @return a int.
409 	 */
410 	protected int countErrorItems(String rejectedRows) {
411 		if (StringUtils.isBlank(rejectedRows)) {
412 			return 0;
413 		}
414 		return Splitter.on('\n').trimResults().omitEmptyStrings().splitToList(rejectedRows).size();
415 	}
416 
417 }