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.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.avalon.framework.component.Component;
029import org.apache.avalon.framework.configuration.Configurable;
030import org.apache.avalon.framework.configuration.Configuration;
031import org.apache.avalon.framework.configuration.ConfigurationException;
032import org.apache.commons.lang.StringUtils;
033
034import org.ametys.core.datasource.ConnectionHelper;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.core.userpref.UserPreferencesException;
037import org.ametys.core.userpref.UserPreferencesStorage;
038import org.ametys.runtime.config.Config;
039import org.ametys.runtime.plugin.component.AbstractLogEnabled;
040
041
042/**
043 * Specific storage for odf cart user preferences
044 */
045public class ODFCartUserPreferencesStorage extends AbstractLogEnabled implements UserPreferencesStorage, Configurable, Component
046{
047    /** The Avalon Role */
048    public static final String ROLE = ODFCartUserPreferencesStorage.class.getName();
049    
050    /** The id of the data source used. */
051    protected String _dataSourceId;
052    
053    /** The login column, cannot be null. */
054    protected String _loginColumn;
055    
056    /** The population id column, cannot be null. */
057    protected String _populationColumn;
058    
059    /** The context column, can be null if the database is not context-dependent. */
060    protected String _contextColumn;
061    
062    /** The content id column */
063    protected String _contentIdColumn;
064    
065    /** Mapping from preference id to table name. */
066    protected Map<String, String> _prefIdToTable;
067
068    @Override
069    public void configure(Configuration configuration) throws ConfigurationException
070    {
071        // Data source id
072        Configuration dataSourceConf = configuration.getChild("datasource", false);
073        if (dataSourceConf == null)
074        {
075            throw new ConfigurationException("The 'datasource' configuration node must be defined.", dataSourceConf);
076        }
077        
078        String dataSourceConfParam = dataSourceConf.getValue();
079        String dataSourceConfType = dataSourceConf.getAttribute("type", "config");
080        
081        if (StringUtils.equals(dataSourceConfType, "config"))
082        {
083            _dataSourceId = Config.getInstance().getValue(dataSourceConfParam);
084        }
085        else // expecting type="id"
086        {
087            _dataSourceId = dataSourceConfParam;
088        }
089        
090        // Default to "contentId".
091        _contentIdColumn = configuration.getChild("content-id").getValue("contentId");
092        // Default to "login".
093        _loginColumn = configuration.getChild("loginColumn").getValue("login").toLowerCase();
094        // Default to "population"
095        _populationColumn = configuration.getChild("populationColumn").getValue("population").toLowerCase();
096        // Default to 'context' (no context column).
097        _contextColumn = configuration.getChild("contextColumn").getValue("context");
098        
099        // Configure the preference-table mappings.
100        configureMappings(configuration.getChild("mappings"));
101    }
102    
103    /**
104     * Configure the mappings from preference ID to table name.
105     * @param configuration the mapping configuration root.
106     * @throws ConfigurationException if an error occurs.
107     */
108    public void configureMappings(Configuration configuration) throws ConfigurationException
109    {
110        _prefIdToTable = new HashMap<>();
111        
112        for (Configuration mappingConf : configuration.getChildren("mapping"))
113        {
114            String prefId = mappingConf.getAttribute("prefId");
115            String table = mappingConf.getAttribute("table");
116            
117            _prefIdToTable.put(prefId, table);
118        }
119    }
120    
121    @Override
122    public Map<String, String> getUnTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException
123    {
124        Map<String, String> prefs = new HashMap<>();
125        
126        for (String id : _prefIdToTable.keySet())
127        {
128            prefs.put(id, getUserPreferenceAsString(user, storageContext, contextVars, id));
129        }
130        
131        return prefs;
132    }
133    
134    @Override
135    public void removeUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException
136    {
137        Connection connection = null;
138        try
139        {
140            connection = ConnectionHelper.getConnection(_dataSourceId);
141            for (String id : _prefIdToTable.keySet())
142            {
143                _removeUserPreferencesFromTable(connection, user, storageContext, _prefIdToTable.get(id));
144            }
145        }
146        catch (SQLException e)
147        {
148            String message = "Database error trying to remove ODF cart preferences for user '" + user + "' in context '" + storageContext + "'.";
149            getLogger().error(message, e);
150            throw new UserPreferencesException(message, e);
151        }
152        finally
153        {
154            ConnectionHelper.cleanup(connection);
155        }
156    }
157
158    private void _removeUserPreferencesFromTable(Connection connection, UserIdentity user, String storageContext, String table) throws SQLException
159    {
160        PreparedStatement stmt = null;
161        
162        try
163        {
164            StringBuilder query = new StringBuilder();
165            query.append("DELETE FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?");
166            query.append(" AND ").append(_contextColumn).append(" = ?");
167            
168            stmt = connection.prepareStatement(query.toString());
169            stmt.setString(1, user.getLogin());
170            stmt.setString(2, user.getPopulationId());
171            stmt.setString(3, storageContext);
172            
173            stmt.executeUpdate();
174        }
175        finally
176        {
177            ConnectionHelper.cleanup(stmt);
178        }
179        
180    }
181
182    /**
183     * Remove content from all user preferences
184     * @param contentId the content id to remove
185     * @throws UserPreferencesException if failed to remove user preferences
186     */
187    public void removeContentFromUserPreferences(String contentId) throws UserPreferencesException
188    {
189        Connection connection = null;
190        PreparedStatement stmt = null;
191        
192        try
193        {
194            connection = ConnectionHelper.getConnection(_dataSourceId);
195            
196            StringBuilder query = new StringBuilder();
197            query.append("DELETE FROM ").append(_prefIdToTable.get(ODFCartManager.CART_USER_PREF_CONTENT_IDS)).append(" WHERE ").append(_contentIdColumn).append(" like ?");
198            
199            stmt = connection.prepareStatement(query.toString());
200            
201            stmt.setString(1, contentId + "%");
202            stmt.executeUpdate();
203        }
204        catch (SQLException e)
205        {
206            String message = "Database error trying to remove ODF content of id '" + contentId + "' from all user's preferences";
207            getLogger().error(message, e);
208            throw new UserPreferencesException(message, e);
209        }
210        finally
211        {
212            ConnectionHelper.cleanup(stmt);
213            ConnectionHelper.cleanup(connection);
214        }
215    }
216
217    @Override
218    public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferences) throws UserPreferencesException
219    {
220        removeUserPreferences(user, storageContext, contextVars);
221        
222        Connection connection = null;
223        try
224        {
225            connection = ConnectionHelper.getConnection(_dataSourceId);
226            _insertPreferences(connection, preferences, user, storageContext);
227        }
228        catch (SQLException e)
229        {
230            String message = "Database error trying to set ODF cart preferences of user '" + user + "' in context '" + storageContext + "'.";
231            getLogger().error(message, e);
232            throw new UserPreferencesException(message, e);
233        }
234        finally
235        {
236            ConnectionHelper.cleanup(connection);
237        }
238    }
239    
240    private void _insertPreferences(Connection connection, Map<String, String> preferences, UserIdentity user, String storageContext) throws SQLException
241    {
242        for (String id : preferences.keySet())
243        {
244            String table = _prefIdToTable.get(id);
245            
246            String[] contentIdsTab = StringUtils.split(preferences.get(id), ",");
247            
248            if (contentIdsTab.length > 0)
249            {
250                PreparedStatement stmt = null;
251                try
252                {
253                    StringBuilder query = new StringBuilder();
254                    query.append("INSERT INTO ").append(table).append("(").append(_loginColumn).append(", ").append(_populationColumn);
255                    query.append(", ").append(_contextColumn);
256                    query.append(", ").append(_contentIdColumn);
257    
258                    StringBuilder values = new StringBuilder();
259                    for (int i = 0; i < contentIdsTab.length; i++)
260                    {
261                        if (i != 0)
262                        {
263                            values.append(",");
264                        }
265                        values.append("(?, ?, ?, ?)");
266                    }
267                    
268                    query.append(") VALUES ").append(values);
269                    
270                    int i = 1;
271                    stmt = connection.prepareStatement(query.toString());
272                    
273                    for (String idContent : contentIdsTab)
274                    {
275                        stmt.setString(i++, user.getLogin());
276                        stmt.setString(i++, user.getPopulationId());
277                        stmt.setString(i++, storageContext);
278                        stmt.setString(i++, idContent);
279                    }
280                    
281                    stmt.executeUpdate();
282                }
283                finally
284                {
285                    ConnectionHelper.cleanup(stmt);
286                }
287            }
288        }
289        
290    }
291
292    @Override
293    public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
294    {
295        Connection connection = null;
296        PreparedStatement statement = null;
297        ResultSet rs = null;
298        String value = null;
299        String table = _prefIdToTable.get(id);
300        
301        try
302        {
303            StringBuilder query = new StringBuilder();
304            query.append("SELECT ").append(_contentIdColumn).append(" FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?");
305            query.append(" AND ").append(_contextColumn).append(" = ?");
306            
307            connection = ConnectionHelper.getConnection(_dataSourceId);
308            
309            statement = connection.prepareStatement(query.toString());
310            statement.setString(1, user.getLogin());
311            statement.setString(2, user.getPopulationId());
312            statement.setString(3, storageContext);
313            
314            rs = statement.executeQuery();
315            
316            List<String> results = new ArrayList<>();
317            while (rs.next())
318            {
319                results.add(rs.getString(1));
320            }
321            
322            value = StringUtils.join(results, ",");
323        }
324        catch (SQLException e)
325        {
326            String message = "Database error trying to get ODF cart preferences of user '" + user + "' in context '" + storageContext + "'.";
327            getLogger().error(message, e);
328            throw new UserPreferencesException(message, e);
329        }
330        finally
331        {
332            ConnectionHelper.cleanup(rs);
333            ConnectionHelper.cleanup(statement);
334            ConnectionHelper.cleanup(connection);
335        }
336        
337        return value;
338    }
339    
340    @Override
341    public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
342    {
343        return null;
344    }
345
346    @Override
347    public Date getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
348    {
349        return null;
350    }
351
352    @Override
353    public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
354    {
355        return null;
356    }
357
358    @Override
359    public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
360    {
361        return null;
362    }
363
364}