001/* 002 * Copyright 2019 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.odfweb.cart; 017 018import java.sql.Connection; 019import java.sql.PreparedStatement; 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.time.ZonedDateTime; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.avalon.framework.component.Component; 030import org.apache.avalon.framework.configuration.Configurable; 031import org.apache.avalon.framework.configuration.Configuration; 032import org.apache.avalon.framework.configuration.ConfigurationException; 033import org.apache.commons.lang.StringUtils; 034 035import org.ametys.core.datasource.ConnectionHelper; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.userpref.UserPreferencesException; 038import org.ametys.core.userpref.UserPreferencesStorage; 039import org.ametys.runtime.config.Config; 040import org.ametys.runtime.plugin.component.AbstractLogEnabled; 041 042 043/** 044 * Specific storage for odf cart user preferences 045 */ 046public class ODFCartUserPreferencesStorage extends AbstractLogEnabled implements UserPreferencesStorage, Configurable, Component 047{ 048 /** The Avalon Role */ 049 public static final String ROLE = ODFCartUserPreferencesStorage.class.getName(); 050 051 /** The id of the data source used. */ 052 protected String _dataSourceId; 053 054 /** The login column, cannot be null. */ 055 protected String _loginColumn; 056 057 /** The population id column, cannot be null. */ 058 protected String _populationColumn; 059 060 /** The context column, can be null if the database is not context-dependent. */ 061 protected String _contextColumn; 062 063 /** The content id column */ 064 protected String _contentIdColumn; 065 066 /** Mapping from preference id to table name. */ 067 protected Map<String, String> _prefIdToTable; 068 069 @Override 070 public void configure(Configuration configuration) throws ConfigurationException 071 { 072 // Data source id 073 Configuration dataSourceConf = configuration.getChild("datasource", false); 074 if (dataSourceConf == null) 075 { 076 throw new ConfigurationException("The 'datasource' configuration node must be defined.", dataSourceConf); 077 } 078 079 String dataSourceConfParam = dataSourceConf.getValue(); 080 String dataSourceConfType = dataSourceConf.getAttribute("type", "config"); 081 082 if (StringUtils.equals(dataSourceConfType, "config")) 083 { 084 _dataSourceId = Config.getInstance().getValue(dataSourceConfParam); 085 } 086 else // expecting type="id" 087 { 088 _dataSourceId = dataSourceConfParam; 089 } 090 091 // Default to "contentId". 092 _contentIdColumn = configuration.getChild("content-id").getValue("contentId"); 093 // Default to "login". 094 _loginColumn = configuration.getChild("loginColumn").getValue("login").toLowerCase(); 095 // Default to "population" 096 _populationColumn = configuration.getChild("populationColumn").getValue("population").toLowerCase(); 097 // Default to 'context' (no context column). 098 _contextColumn = configuration.getChild("contextColumn").getValue("context"); 099 100 // Configure the preference-table mappings. 101 configureMappings(configuration.getChild("mappings")); 102 } 103 104 /** 105 * Configure the mappings from preference ID to table name. 106 * @param configuration the mapping configuration root. 107 * @throws ConfigurationException if an error occurs. 108 */ 109 public void configureMappings(Configuration configuration) throws ConfigurationException 110 { 111 _prefIdToTable = new HashMap<>(); 112 113 for (Configuration mappingConf : configuration.getChildren("mapping")) 114 { 115 String prefId = mappingConf.getAttribute("prefId"); 116 String table = mappingConf.getAttribute("table"); 117 118 _prefIdToTable.put(prefId, table); 119 } 120 } 121 122 @Override 123 public Map<String, String> getUnTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 124 { 125 Map<String, String> prefs = new HashMap<>(); 126 127 for (String id : _prefIdToTable.keySet()) 128 { 129 prefs.put(id, getUserPreferenceAsString(user, storageContext, contextVars, id)); 130 } 131 132 return prefs; 133 } 134 135 /** 136 * Remove the stored user preferences for a login in a given context. 137 * @param prefIds the id of user preferences to remove 138 * @param user the user. 139 * @param storageContext the preferences storage context. 140 * @param contextVars the context variables. 141 * @throws UserPreferencesException if an error occurred 142 */ 143 public void removeUserPreferences(Collection<String> prefIds, UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 144 { 145 Connection connection = null; 146 try 147 { 148 connection = ConnectionHelper.getConnection(_dataSourceId); 149 for (String id : prefIds) 150 { 151 _removeUserPreferencesFromTable(connection, user, storageContext, _prefIdToTable.get(id)); 152 } 153 } 154 catch (SQLException e) 155 { 156 String message = "Database error trying to remove ODF cart preferences for user '" + user + "' in context '" + storageContext + "'."; 157 getLogger().error(message, e); 158 throw new UserPreferencesException(message, e); 159 } 160 finally 161 { 162 ConnectionHelper.cleanup(connection); 163 } 164 } 165 166 @Override 167 public void removeUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 168 { 169 Connection connection = null; 170 try 171 { 172 connection = ConnectionHelper.getConnection(_dataSourceId); 173 for (String id : _prefIdToTable.keySet()) 174 { 175 _removeUserPreferencesFromTable(connection, user, storageContext, _prefIdToTable.get(id)); 176 } 177 } 178 catch (SQLException e) 179 { 180 String message = "Database error trying to remove ODF cart preferences for user '" + user + "' in context '" + storageContext + "'."; 181 getLogger().error(message, e); 182 throw new UserPreferencesException(message, e); 183 } 184 finally 185 { 186 ConnectionHelper.cleanup(connection); 187 } 188 } 189 190 private void _removeUserPreferencesFromTable(Connection connection, UserIdentity user, String storageContext, String table) throws SQLException 191 { 192 PreparedStatement stmt = null; 193 194 try 195 { 196 StringBuilder query = new StringBuilder(); 197 query.append("DELETE FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?"); 198 if (storageContext != null) 199 { 200 query.append(" AND ").append(_contextColumn).append(" = ?"); 201 } 202 203 stmt = connection.prepareStatement(query.toString()); 204 stmt.setString(1, user.getLogin()); 205 stmt.setString(2, user.getPopulationId()); 206 if (storageContext != null) 207 { 208 stmt.setString(3, storageContext); 209 } 210 211 stmt.executeUpdate(); 212 } 213 finally 214 { 215 ConnectionHelper.cleanup(stmt); 216 } 217 218 } 219 220 /** 221 * Remove content from all user preferences 222 * @param contentId the content id to remove 223 * @throws UserPreferencesException if failed to remove user preferences 224 */ 225 public void removeContentFromUserPreferences(String contentId) throws UserPreferencesException 226 { 227 Connection connection = null; 228 PreparedStatement stmt = null; 229 230 try 231 { 232 connection = ConnectionHelper.getConnection(_dataSourceId); 233 234 StringBuilder query = new StringBuilder(); 235 query.append("DELETE FROM ").append(_prefIdToTable.get(ODFCartManager.CART_USER_PREF_CONTENT_IDS)).append(" WHERE ").append(_contentIdColumn).append(" like ?"); 236 237 stmt = connection.prepareStatement(query.toString()); 238 239 stmt.setString(1, contentId + "%"); 240 stmt.executeUpdate(); 241 242 ConnectionHelper.cleanup(stmt); 243 244 query = new StringBuilder(); 245 query.append("DELETE FROM ").append(_prefIdToTable.get(ODFCartManager.SUBSCRIPTION_USER_PREF_CONTENT_IDS)).append(" WHERE ").append(_contentIdColumn).append(" like ?"); 246 247 stmt = connection.prepareStatement(query.toString()); 248 249 stmt.setString(1, contentId + "%"); 250 stmt.executeUpdate(); 251 252 } 253 catch (SQLException e) 254 { 255 String message = "Database error trying to remove ODF content of id '" + contentId + "' from all user's preferences"; 256 getLogger().error(message, e); 257 throw new UserPreferencesException(message, e); 258 } 259 finally 260 { 261 ConnectionHelper.cleanup(stmt); 262 ConnectionHelper.cleanup(connection); 263 } 264 } 265 266 @Override 267 public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferences) throws UserPreferencesException 268 { 269 removeUserPreferences(preferences.keySet(), user, storageContext, contextVars); 270 271 Connection connection = null; 272 try 273 { 274 connection = ConnectionHelper.getConnection(_dataSourceId); 275 _insertPreferences(connection, preferences, user, storageContext); 276 } 277 catch (SQLException e) 278 { 279 String message = "Database error trying to set ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 280 getLogger().error(message, e); 281 throw new UserPreferencesException(message, e); 282 } 283 finally 284 { 285 ConnectionHelper.cleanup(connection); 286 } 287 } 288 289 private void _insertPreferences(Connection connection, Map<String, String> preferences, UserIdentity user, String storageContext) throws SQLException 290 { 291 for (String id : preferences.keySet()) 292 { 293 String table = _prefIdToTable.get(id); 294 295 String[] contentIdsTab = StringUtils.split(preferences.get(id), ","); 296 297 if (contentIdsTab.length > 0) 298 { 299 PreparedStatement stmt = null; 300 try 301 { 302 StringBuilder query = new StringBuilder(); 303 query.append("INSERT INTO ").append(table).append("(").append(_loginColumn).append(", ").append(_populationColumn); 304 query.append(", ").append(_contextColumn); 305 query.append(", ").append(_contentIdColumn); 306 307 StringBuilder values = new StringBuilder(); 308 for (int i = 0; i < contentIdsTab.length; i++) 309 { 310 if (i != 0) 311 { 312 values.append(","); 313 } 314 values.append("(?, ?, ?, ?)"); 315 } 316 317 query.append(") VALUES ").append(values); 318 319 int i = 1; 320 stmt = connection.prepareStatement(query.toString()); 321 322 for (String idContent : contentIdsTab) 323 { 324 stmt.setString(i++, user.getLogin()); 325 stmt.setString(i++, user.getPopulationId()); 326 stmt.setString(i++, storageContext); 327 stmt.setString(i++, idContent); 328 } 329 330 stmt.executeUpdate(); 331 } 332 finally 333 { 334 ConnectionHelper.cleanup(stmt); 335 } 336 } 337 } 338 339 } 340 341 @Override 342 public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 343 { 344 Connection connection = null; 345 PreparedStatement statement = null; 346 ResultSet rs = null; 347 String value = null; 348 String table = _prefIdToTable.get(id); 349 350 try 351 { 352 StringBuilder query = new StringBuilder(); 353 query.append("SELECT ").append(_contentIdColumn).append(" FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?"); 354 query.append(" AND ").append(_contextColumn).append(" = ?"); 355 356 connection = ConnectionHelper.getConnection(_dataSourceId); 357 358 statement = connection.prepareStatement(query.toString()); 359 statement.setString(1, user.getLogin()); 360 statement.setString(2, user.getPopulationId()); 361 statement.setString(3, storageContext); 362 363 rs = statement.executeQuery(); 364 365 List<String> results = new ArrayList<>(); 366 while (rs.next()) 367 { 368 results.add(rs.getString(1)); 369 } 370 371 value = StringUtils.join(results, ","); 372 } 373 catch (SQLException e) 374 { 375 String message = "Database error trying to get ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 376 getLogger().error(message, e); 377 throw new UserPreferencesException(message, e); 378 } 379 finally 380 { 381 ConnectionHelper.cleanup(rs); 382 ConnectionHelper.cleanup(statement); 383 ConnectionHelper.cleanup(connection); 384 } 385 386 return value; 387 } 388 389 @Override 390 public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 391 { 392 return null; 393 } 394 395 @Override 396 public ZonedDateTime getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 397 { 398 return null; 399 } 400 401 @Override 402 public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 403 { 404 return null; 405 } 406 407 @Override 408 public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 409 { 410 return null; 411 } 412 413}