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