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.util.ArrayList; 023import java.util.Collection; 024import java.util.Date; 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 query.append(" AND ").append(_contextColumn).append(" = ?"); 199 200 stmt = connection.prepareStatement(query.toString()); 201 stmt.setString(1, user.getLogin()); 202 stmt.setString(2, user.getPopulationId()); 203 stmt.setString(3, storageContext); 204 205 stmt.executeUpdate(); 206 } 207 finally 208 { 209 ConnectionHelper.cleanup(stmt); 210 } 211 212 } 213 214 /** 215 * Remove content from all user preferences 216 * @param contentId the content id to remove 217 * @throws UserPreferencesException if failed to remove user preferences 218 */ 219 public void removeContentFromUserPreferences(String contentId) throws UserPreferencesException 220 { 221 Connection connection = null; 222 PreparedStatement stmt = null; 223 224 try 225 { 226 connection = ConnectionHelper.getConnection(_dataSourceId); 227 228 StringBuilder query = new StringBuilder(); 229 query.append("DELETE FROM ").append(_prefIdToTable.get(ODFCartManager.CART_USER_PREF_CONTENT_IDS)).append(" WHERE ").append(_contentIdColumn).append(" like ?"); 230 231 stmt = connection.prepareStatement(query.toString()); 232 233 stmt.setString(1, contentId + "%"); 234 stmt.executeUpdate(); 235 236 ConnectionHelper.cleanup(stmt); 237 238 query = new StringBuilder(); 239 query.append("DELETE FROM ").append(_prefIdToTable.get(ODFCartManager.SUBSCRIPTION_USER_PREF_CONTENT_IDS)).append(" WHERE ").append(_contentIdColumn).append(" like ?"); 240 241 stmt = connection.prepareStatement(query.toString()); 242 243 stmt.setString(1, contentId + "%"); 244 stmt.executeUpdate(); 245 246 } 247 catch (SQLException e) 248 { 249 String message = "Database error trying to remove ODF content of id '" + contentId + "' from all user's preferences"; 250 getLogger().error(message, e); 251 throw new UserPreferencesException(message, e); 252 } 253 finally 254 { 255 ConnectionHelper.cleanup(stmt); 256 ConnectionHelper.cleanup(connection); 257 } 258 } 259 260 @Override 261 public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferences) throws UserPreferencesException 262 { 263 removeUserPreferences(preferences.keySet(), user, storageContext, contextVars); 264 265 Connection connection = null; 266 try 267 { 268 connection = ConnectionHelper.getConnection(_dataSourceId); 269 _insertPreferences(connection, preferences, user, storageContext); 270 } 271 catch (SQLException e) 272 { 273 String message = "Database error trying to set ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 274 getLogger().error(message, e); 275 throw new UserPreferencesException(message, e); 276 } 277 finally 278 { 279 ConnectionHelper.cleanup(connection); 280 } 281 } 282 283 private void _insertPreferences(Connection connection, Map<String, String> preferences, UserIdentity user, String storageContext) throws SQLException 284 { 285 for (String id : preferences.keySet()) 286 { 287 String table = _prefIdToTable.get(id); 288 289 String[] contentIdsTab = StringUtils.split(preferences.get(id), ","); 290 291 if (contentIdsTab.length > 0) 292 { 293 PreparedStatement stmt = null; 294 try 295 { 296 StringBuilder query = new StringBuilder(); 297 query.append("INSERT INTO ").append(table).append("(").append(_loginColumn).append(", ").append(_populationColumn); 298 query.append(", ").append(_contextColumn); 299 query.append(", ").append(_contentIdColumn); 300 301 StringBuilder values = new StringBuilder(); 302 for (int i = 0; i < contentIdsTab.length; i++) 303 { 304 if (i != 0) 305 { 306 values.append(","); 307 } 308 values.append("(?, ?, ?, ?)"); 309 } 310 311 query.append(") VALUES ").append(values); 312 313 int i = 1; 314 stmt = connection.prepareStatement(query.toString()); 315 316 for (String idContent : contentIdsTab) 317 { 318 stmt.setString(i++, user.getLogin()); 319 stmt.setString(i++, user.getPopulationId()); 320 stmt.setString(i++, storageContext); 321 stmt.setString(i++, idContent); 322 } 323 324 stmt.executeUpdate(); 325 } 326 finally 327 { 328 ConnectionHelper.cleanup(stmt); 329 } 330 } 331 } 332 333 } 334 335 @Override 336 public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 337 { 338 Connection connection = null; 339 PreparedStatement statement = null; 340 ResultSet rs = null; 341 String value = null; 342 String table = _prefIdToTable.get(id); 343 344 try 345 { 346 StringBuilder query = new StringBuilder(); 347 query.append("SELECT ").append(_contentIdColumn).append(" FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?"); 348 query.append(" AND ").append(_contextColumn).append(" = ?"); 349 350 connection = ConnectionHelper.getConnection(_dataSourceId); 351 352 statement = connection.prepareStatement(query.toString()); 353 statement.setString(1, user.getLogin()); 354 statement.setString(2, user.getPopulationId()); 355 statement.setString(3, storageContext); 356 357 rs = statement.executeQuery(); 358 359 List<String> results = new ArrayList<>(); 360 while (rs.next()) 361 { 362 results.add(rs.getString(1)); 363 } 364 365 value = StringUtils.join(results, ","); 366 } 367 catch (SQLException e) 368 { 369 String message = "Database error trying to get ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 370 getLogger().error(message, e); 371 throw new UserPreferencesException(message, e); 372 } 373 finally 374 { 375 ConnectionHelper.cleanup(rs); 376 ConnectionHelper.cleanup(statement); 377 ConnectionHelper.cleanup(connection); 378 } 379 380 return value; 381 } 382 383 @Override 384 public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 385 { 386 return null; 387 } 388 389 @Override 390 public Date getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 391 { 392 return null; 393 } 394 395 @Override 396 public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 397 { 398 return null; 399 } 400 401 @Override 402 public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 403 { 404 return null; 405 } 406 407}