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.base.Joiner;
27  import com.google.common.collect.ImmutableList;
28  import com.google.common.collect.ImmutableSet;
29  import com.google.common.collect.Sets;
30  import com.google.common.eventbus.Subscribe;
31  import fr.ifremer.common.synchro.intercept.SynchroInterceptorBase;
32  import fr.ifremer.common.synchro.meta.SynchroTableMetadata;
33  import fr.ifremer.common.synchro.meta.event.CreateQueryEvent;
34  import fr.ifremer.common.synchro.meta.event.LoadTableEvent;
35  import fr.ifremer.common.synchro.query.SynchroQueryBuilder;
36  import fr.ifremer.common.synchro.query.SynchroQueryOperator;
37  import fr.ifremer.quadrige3.core.dao.ObjectTypes;
38  import fr.ifremer.quadrige3.synchro.meta.DatabaseColumns;
39  import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
40  import fr.ifremer.quadrige3.synchro.service.SynchroDirection;
41  import org.apache.commons.collections4.CollectionUtils;
42  
43  import java.util.Collection;
44  import java.util.HashSet;
45  import java.util.List;
46  import java.util.Set;
47  
48  /**
49   * Import DELETED_ITEM_HISTORY rows, limited to data tables
50   * 
51   * @author Benoit Lavenier <benoit.lavenier@e-is.pro>
52   * @since 1.0
53   */
54  public class DeletedItemHistoryInterceptor extends AbstractDataInterceptor {
55  
56  	/** Constant <code>NATURAL_ID_COLUMNS</code> */
57  	private static final List<String> NATURAL_ID_COLUMNS = ImmutableList.of(
58  			DatabaseColumns.OBJECT_TYPE_CD.name(),
59  			DatabaseColumns.OBJECT_ID.name(),
60  			DatabaseColumns.OBJECT_CD.name());
61  
62  	private String whereClauseOnTableIncludes = null;
63  	private String whereClauseOnTableIncludesExceptSurvey = null;
64  
65  	/**
66  	 * <p>
67  	 * Constructor for DeletedItemHistoryInterceptor.
68  	 * </p>
69  	 */
70  	public DeletedItemHistoryInterceptor() {
71  
72  		super(DataSynchroTables.DELETED_ITEM_HISTORY.name(),
73  				// Exclude Temp -> Local (process in a special code)
74  				SynchroDirection.IMPORT_SERVER2TEMP,
75  				SynchroDirection.EXPORT_LOCAL2TEMP,
76  				SynchroDirection.EXPORT_TEMP2SERVER);
77  	}
78  
79  	/** {@inheritDoc} */
80  	@Override
81  	public SynchroInterceptorBase clone() {
82  		DeletedItemHistoryInterceptor newBean = (DeletedItemHistoryInterceptor) super.clone();
83  		newBean.whereClauseOnTableIncludes = this.whereClauseOnTableIncludes;
84  		newBean.whereClauseOnTableIncludesExceptSurvey = this.whereClauseOnTableIncludesExceptSurvey;
85  		return newBean;
86  	}
87  
88  	/**
89  	 * <p>
90  	 * handleCreateQuery.
91  	 * </p>
92  	 * 
93  	 * @param e
94  	 *            a {@link fr.ifremer.common.synchro.meta.event.CreateQueryEvent} object.
95  	 */
96  	@Subscribe
97  	public void handleCreateQuery(CreateQueryEvent e) {
98  		SynchroDirection direction = getConfig().getDirection();
99  
100 		switch (e.queryName) {
101 		case count:
102 		case countFromUpdateDate:
103 		case select:
104 		case selectFromUpdateDate:
105 		case selectMaxUpdateDate:
106 			if (direction == SynchroDirection.EXPORT_LOCAL2TEMP) {
107 				// add a specific restriction on export survey deletion
108 				e.sql = addRestrictionsOnExportToTemp(e.sql);
109 			} else {
110 				// default restriction
111 				e.sql = addRestrictionsOnIncludedTables(e.sql);
112 			}
113 			break;
114 
115 		default:
116 			break;
117 		}
118 	}
119 
120 	/**
121 	 * <p>
122 	 * handleTableLoad.
123 	 * </p>
124 	 * 
125 	 * @param e
126 	 *            a {@link fr.ifremer.common.synchro.meta.event.LoadTableEvent} object.
127 	 */
128 	@Subscribe
129 	public void handleTableLoad(LoadTableEvent e) {
130 		SynchroTableMetadata table = e.table;
131 		SynchroDirection direction = getConfig().getDirection();
132 
133 		// Do not import this table to local
134 		boolean isRoot = isInDirections(SynchroDirection.IMPORT_SERVER2TEMP,
135 				SynchroDirection.EXPORT_LOCAL2TEMP,
136 				SynchroDirection.EXPORT_TEMP2SERVER);
137 
138 		e.table.setRoot(isRoot);
139 
140 		// Export: Temp DB -> Server DB
141 		if (direction == SynchroDirection.EXPORT_TEMP2SERVER) {
142 			// Define natural id, to avoid 2 rows inserted (because trigger will insert row also)
143 			table.addUniqueConstraint("NATURAL_ID_UNIQUE_C", NATURAL_ID_COLUMNS, SynchroTableMetadata.DuplicateKeyStrategy.WARN);
144 		}
145 	}
146 
147 	/* -- Internal methods -- */
148 
149 	private String addRestrictionsOnIncludedTables(String sql) {
150 
151 		SynchroQueryBuilder query = SynchroQueryBuilder.newBuilder(sql);
152 
153 		// Add limit on included tables
154 		if (whereClauseOnTableIncludes == null) {
155 			whereClauseOnTableIncludes = createWhereClauseOnTableIncludes(null); // all tables
156 		}
157 		query.addWhere(SynchroQueryOperator.AND, whereClauseOnTableIncludes);
158 
159 		return query.build();
160 	}
161 
162 	/**
163 	 * Mantis #42706 : Filter deletion on survey on selected programs
164 	 */
165 	private String addRestrictionsOnExportToTemp(String sql) {
166 
167 		Set<String> programCodes = getConfig().getProgramCodes();
168 
169 		// fallback to standard restriction if no program set
170 		if (CollectionUtils.isEmpty(programCodes))
171 			return addRestrictionsOnIncludedTables(sql);
172 
173 		// first sub query
174 		SynchroQueryBuilder query = SynchroQueryBuilder.newBuilder(sql);
175 
176 		// default where clause on non already synchronized lines
177 		query.addWhere(SynchroQueryOperator.AND, DatabaseColumns.REMOTE_ID.name() + " is null");
178 
179 		// Add limit on included tables except SURVEY
180 		if (whereClauseOnTableIncludesExceptSurvey == null) {
181 			whereClauseOnTableIncludesExceptSurvey = createWhereClauseOnTableIncludes(ImmutableSet.of(DataSynchroTables.SURVEY.name())); // all tables except SURVEY
182 		}
183 
184 		// sub query to determinate surveys to delete (join on SURVEY and SURVEY_PROG to filter on program)
185 		Set<String> objectTypeCds = ObjectTypes.getObjectTypeFromTableName(DataSynchroTables.SURVEY.name());
186 		String whereClauseOnSurveyToDelete = String.format("%s IN ('%s') AND %s IN " +
187 						"(SELECT s.%s FROM %s s INNER JOIN %s sp ON sp.%s = s.%s " +
188 						"WHERE sp.%s IN ('%s'))",
189 				DatabaseColumns.OBJECT_TYPE_CD, Joiner.on("','").join(objectTypeCds), DatabaseColumns.OBJECT_ID,
190 				DatabaseColumns.REMOTE_ID, DataSynchroTables.SURVEY, DataSynchroTables.SURVEY_PROG, DatabaseColumns.SURVEY_ID, DatabaseColumns.SURVEY_ID,
191 				DatabaseColumns.PROG_CD, Joiner.on("','").join(programCodes));
192 
193 		query.addWhere(SynchroQueryOperator.AND, String.format("((%s) OR (%s))", whereClauseOnTableIncludesExceptSurvey, whereClauseOnSurveyToDelete));
194 
195 		// union both sub queries
196 		return query.build();
197 	}
198 
199 	private String createWhereClauseOnTableIncludes(Collection<String> tablesToIgnore) {
200 		Set<String> tableToIncludes = new HashSet<>(DataSynchroTables.getImportTablesIncludes());
201 		if (CollectionUtils.isNotEmpty(tablesToIgnore)) {
202 			tableToIncludes.removeAll(tablesToIgnore);
203 		}
204 
205 		// If no tables configured : should return no lines
206 		if (CollectionUtils.isEmpty(tableToIncludes)) {
207 			return "1=2";
208 		}
209 
210 		Set<String> allObjectTypeCds = Sets.newHashSet();
211 		for (String tableName : tableToIncludes) {
212 			Set<String> objectTypeCds = ObjectTypes.getObjectTypeFromTableName(tableName);
213 			if (CollectionUtils.isNotEmpty(objectTypeCds)) {
214 				allObjectTypeCds.addAll(objectTypeCds);
215 			}
216 			else {
217 				// By default, add the table name itself (i.e. PERSON_SESSION_VESSEL)
218 				allObjectTypeCds.add(tableName);
219 			}
220 		}
221 
222 		// Remove itself
223 		allObjectTypeCds.remove(DataSynchroTables.DELETED_ITEM_HISTORY.name());
224 
225 		return String.format("%s IN ('%s')", DatabaseColumns.OBJECT_TYPE_CD, Joiner.on("','").join(allObjectTypeCds));
226 	}
227 
228 }