PrivacyListProvider.java 14 KB
Newer Older
Gaston Dombiak's avatar
Gaston Dombiak committed
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision: $
 * $Date: $
 *
6
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
Gaston Dombiak's avatar
Gaston Dombiak committed
7
 *
8 9 10 11 12 13 14 15 16 17 18
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
Gaston Dombiak's avatar
Gaston Dombiak committed
19 20
 */

21
package org.jivesoftware.openfire.privacy;
Gaston Dombiak's avatar
Gaston Dombiak committed
22 23 24 25 26

import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
27
import java.util.Collections;
Gaston Dombiak's avatar
Gaston Dombiak committed
28 29 30 31
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
32
import java.util.concurrent.atomic.AtomicInteger;
Gaston Dombiak's avatar
Gaston Dombiak committed
33

34 35 36 37 38 39
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Gaston Dombiak's avatar
Gaston Dombiak committed
40 41
/**
 * Provider for the privacy lists system. Privacy lists are read and written
42
 * from the <tt>ofPrivacyList</tt> database table.
Gaston Dombiak's avatar
Gaston Dombiak committed
43 44 45 46 47
 *
 * @author Gaston Dombiak
 */
public class PrivacyListProvider {

48 49
	private static final Logger Log = LoggerFactory.getLogger(PrivacyListProvider.class);

50
    private static final String PRIVACY_LIST_COUNT =
51
            "SELECT count(*) from ofPrivacyList";
Gaston Dombiak's avatar
Gaston Dombiak committed
52
    private static final String LOAD_LIST_NAMES =
53
            "SELECT name, isDefault FROM ofPrivacyList WHERE username=?";
Gaston Dombiak's avatar
Gaston Dombiak committed
54
    private static final String LOAD_PRIVACY_LIST =
55
            "SELECT isDefault, list FROM ofPrivacyList WHERE username=? AND name=?";
Gaston Dombiak's avatar
Gaston Dombiak committed
56
    private static final String LOAD_DEFAULT_PRIVACY_LIST =
57
            "SELECT name, list FROM ofPrivacyList WHERE username=? AND isDefault=1";
Gaston Dombiak's avatar
Gaston Dombiak committed
58
    private static final String DELETE_PRIVACY_LIST =
59
            "DELETE FROM ofPrivacyList WHERE username=? AND name=?";
Gaston Dombiak's avatar
Gaston Dombiak committed
60
    private static final String DELETE_PRIVACY_LISTS =
61
            "DELETE FROM ofPrivacyList WHERE username=?";
Gaston Dombiak's avatar
Gaston Dombiak committed
62
    private static final String UPDATE_PRIVACY_LIST =
63
            "UPDATE ofPrivacyList SET isDefault=?, list=? WHERE username=? AND name=?";
Gaston Dombiak's avatar
Gaston Dombiak committed
64
    private static final String INSERT_PRIVACY_LIST =
65
            "INSERT INTO ofPrivacyList (username, name, isDefault, list) VALUES (?, ?, ?, ?)";
Gaston Dombiak's avatar
Gaston Dombiak committed
66

guus's avatar
guus committed
67
    private static final int POOL_SIZE = 50;
Gaston Dombiak's avatar
Gaston Dombiak committed
68 69 70
    /**
     * Pool of SAX Readers. SAXReader is not thread safe so we need to have a pool of readers.
     */
guus's avatar
guus committed
71
    private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>(POOL_SIZE);
Gaston Dombiak's avatar
Gaston Dombiak committed
72

73 74 75 76 77
    /**
     * Stores the total number of privacy lists.
     */
    private AtomicInteger privacyListCount;

Gaston Dombiak's avatar
Gaston Dombiak committed
78 79 80
    public PrivacyListProvider() {
        super();
        // Initialize the pool of sax readers
guus's avatar
guus committed
81
        for (int i=0; i<POOL_SIZE; i++) {
82 83 84
            SAXReader xmlReader = new SAXReader();
            xmlReader.setEncoding("UTF-8");
            xmlReaders.add(xmlReader);
Gaston Dombiak's avatar
Gaston Dombiak committed
85
        }
86 87 88 89 90 91 92 93

        // Load the total number of privacy lists in the database. We're looking
        // for the (very common) special case that there are no privacy lists stored.
        // In that case, we can optimize away many database calls. In the future, a
        // better general-case solution may be to cache all privacy lists defined
        // if there are less than, say, 500.
        privacyListCount = new AtomicInteger(0);
        loadPrivacyListCount();
Gaston Dombiak's avatar
Gaston Dombiak committed
94 95 96 97 98 99 100 101 102 103
    }

    /**
     * Returns the names of the existing privacy lists indicating which one is the
     * default privacy list associated to a user.
     *
     * @param username the username of the user to get his privacy lists names.
     * @return the names of the existing privacy lists with a default flag.
     */
    public Map<String, Boolean> getPrivacyLists(String username) {
104 105 106 107 108
        // If there are no privacy lists stored, this method is a no-op.
        if (privacyListCount.get() == 0) {
            return Collections.emptyMap();
        }

Gaston Dombiak's avatar
Gaston Dombiak committed
109 110 111
        Map<String, Boolean> names = new HashMap<String, Boolean>();
        Connection con = null;
        PreparedStatement pstmt = null;
Matt Tucker's avatar
Matt Tucker committed
112
        ResultSet rs = null;
Gaston Dombiak's avatar
Gaston Dombiak committed
113 114 115 116
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOAD_LIST_NAMES);
            pstmt.setString(1, username);
Matt Tucker's avatar
Matt Tucker committed
117
            rs = pstmt.executeQuery();
Gaston Dombiak's avatar
Gaston Dombiak committed
118 119 120 121 122 123 124 125
            while (rs.next()) {
                names.put(rs.getString(1), rs.getInt(2) == 1);
            }
        }
        catch (Exception e) {
            Log.error("Error loading names of privacy lists for username: " + username, e);
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
126
            DbConnectionManager.closeConnection(rs, pstmt, con);
Gaston Dombiak's avatar
Gaston Dombiak committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140
        }
        return names;
    }

    /**
     * Loads the requested privacy list from the database. Returns <tt>null</tt> if a list
     * with the specified name does not exist.
     *
     * @param username the username of the user to get his privacy list.
     * @param listName name of the list to load.
     * @return the privacy list with the specified name or <tt>null</tt> if a list
     *         with the specified name does not exist.
     */
    public PrivacyList loadPrivacyList(String username, String listName) {
141 142 143 144 145 146 147 148 149
        // If there are no privacy lists stored, this method is a no-op.
        if (privacyListCount.get() == 0) {
            return null;
        }

        boolean isDefault = false;
        String listValue = null;

        Connection con = null;
Gaston Dombiak's avatar
Gaston Dombiak committed
150
        PreparedStatement pstmt = null;
Matt Tucker's avatar
Matt Tucker committed
151
        ResultSet rs = null;
152

Gaston Dombiak's avatar
Gaston Dombiak committed
153 154 155 156 157
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOAD_PRIVACY_LIST);
            pstmt.setString(1, username);
            pstmt.setString(2, listName);
Matt Tucker's avatar
Matt Tucker committed
158
            rs = pstmt.executeQuery();
159 160 161 162 163 164
            if (rs.next()) {
                isDefault = rs.getInt(1) == 1;
                listValue = rs.getString(2);
            }
            else {
                return null;
Gaston Dombiak's avatar
Gaston Dombiak committed
165 166 167 168
            }
        }
        catch (Exception e) {
            Log.error("Error loading privacy list: " + listName + " of username: " + username, e);
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
            return null;
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }

        PrivacyList privacyList = null;
        SAXReader xmlReader = null;
        try {
            // Get a sax reader from the pool
            xmlReader = xmlReaders.take();
            Element listElement = xmlReader.read(new StringReader(listValue)).getRootElement();
            privacyList = new PrivacyList(username, listName, isDefault, listElement);
        }
        catch (Exception e) {
184
            Log.error(e.getMessage(), e);
Gaston Dombiak's avatar
Gaston Dombiak committed
185 186 187 188 189 190 191
        }
        finally {
            // Return the sax reader to the pool
            if (xmlReader != null) {
                xmlReaders.add(xmlReader);
            }
        }
192 193


Gaston Dombiak's avatar
Gaston Dombiak committed
194 195 196 197 198 199 200 201 202 203 204
        return privacyList;
    }

    /**
     * Loads the default privacy list of a given user from the database. Returns <tt>null</tt>
     * if no list was found.
     *
     * @param username the username of the user to get his default privacy list.
     * @return the default privacy list or <tt>null</tt> if no list was found.
     */
    public PrivacyList loadDefaultPrivacyList(String username) {
205 206 207 208 209 210 211 212 213
        // If there are no privacy lists stored, this method is a no-op.
        if (privacyListCount.get() == 0) {
            return null;
        }

        String listName = null;
        String listValue = null;

        Connection con = null;
Gaston Dombiak's avatar
Gaston Dombiak committed
214
        PreparedStatement pstmt = null;
Matt Tucker's avatar
Matt Tucker committed
215
        ResultSet rs = null;
Gaston Dombiak's avatar
Gaston Dombiak committed
216 217 218 219
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOAD_DEFAULT_PRIVACY_LIST);
            pstmt.setString(1, username);
Matt Tucker's avatar
Matt Tucker committed
220
            rs = pstmt.executeQuery();
221 222 223 224 225 226
            if (rs.next()) {
                listName = rs.getString(1);
                listValue = rs.getString(2);
            }
            else {
                return null;
Gaston Dombiak's avatar
Gaston Dombiak committed
227 228 229 230
            }
        }
        catch (Exception e) {
            Log.error("Error loading default privacy list of username: " + username, e);
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
            return null;
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }

        PrivacyList privacyList = null;
        SAXReader xmlReader = null;
        try {
            // Get a sax reader from the pool
            xmlReader = xmlReaders.take();
            Element listElement = xmlReader.read(new StringReader(listValue)).getRootElement();
            privacyList = new PrivacyList(username, listName, true, listElement);
        }
        catch (Exception e) {
246
            Log.error(e.getMessage(), e);
Gaston Dombiak's avatar
Gaston Dombiak committed
247 248 249 250 251 252 253
        }
        finally {
            // Return the sax reader to the pool
            if (xmlReader != null) {
                xmlReaders.add(xmlReader);
            }
        }
254

Gaston Dombiak's avatar
Gaston Dombiak committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
        return privacyList;
    }

    /**
     * Creates and saves the new privacy list to the database.
     *
     * @param username the username of the user that created a new privacy list.
     * @param list the PrivacyList to save.
     */
    public void createPrivacyList(String username, PrivacyList list) {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(INSERT_PRIVACY_LIST);
            pstmt.setString(1, username);
            pstmt.setString(2, list.getName());
            pstmt.setInt(3, (list.isDefault() ? 1 : 0));
            pstmt.setString(4, list.asElement().asXML());
            pstmt.executeUpdate();
        }
        catch (Exception e) {
            Log.error("Error adding privacy list: " + list.getName() + " of username: " + username,
                    e);
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
281
            DbConnectionManager.closeConnection(pstmt, con);
Gaston Dombiak's avatar
Gaston Dombiak committed
282
        }
283 284 285
        // Set the privacy list count to -1. We don't know how many privacy lists there
        // are, but it's not "0", which is the case we care about.
        privacyListCount.set(-1);
Gaston Dombiak's avatar
Gaston Dombiak committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    }

    /**
     * Updated the existing privacy list in the database.
     *
     * @param username the username of the user that updated a privacy list.
     * @param list the PrivacyList to update in the database.
     */
    public void updatePrivacyList(String username, PrivacyList list) {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(UPDATE_PRIVACY_LIST);
            pstmt.setInt(1, (list.isDefault() ? 1 : 0));
            pstmt.setString(2, list.asElement().asXML());
            pstmt.setString(3, username);
            pstmt.setString(4, list.getName());
            pstmt.executeUpdate();
        }
        catch (Exception e) {
307 308
            Log.error("Error updating privacy list: " + list.getName() + " of username: " +
                    username, e);
Gaston Dombiak's avatar
Gaston Dombiak committed
309 310
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
311
            DbConnectionManager.closeConnection(pstmt, con);
Gaston Dombiak's avatar
Gaston Dombiak committed
312 313 314 315 316 317 318 319 320 321
        }
    }

    /**
     * Deletes an existing privacy list from the database.
     *
     * @param username the username of the user that deleted a privacy list.
     * @param listName the name of the PrivacyList to delete.
     */
    public void deletePrivacyList(String username, String listName) {
322 323 324 325
        // If there are no privacy lists stored, this method is a no-op.
        if (privacyListCount.get() == 0) {
            return;
        }
Gaston Dombiak's avatar
Gaston Dombiak committed
326 327 328 329 330 331 332 333 334 335 336 337 338
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(DELETE_PRIVACY_LIST);
            pstmt.setString(1, username);
            pstmt.setString(2, listName);
            pstmt.executeUpdate();
        }
        catch (Exception e) {
            Log.error("Error deleting privacy list: " + listName + " of username: " + username, e);
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
339
            DbConnectionManager.closeConnection(pstmt, con);
Gaston Dombiak's avatar
Gaston Dombiak committed
340
        }
341 342 343
        // Set the privacy list count to -1. We don't know how many privacy lists there
        // are, but it's probably not "0", which is the case we care about.
        privacyListCount.set(-1);
Gaston Dombiak's avatar
Gaston Dombiak committed
344 345 346 347 348 349 350 351
    }

    /**
     * Deletes all existing privacy list from the database for the given user.
     *
     * @param username the username of the user whose privacy lists are going to be deleted.
     */
    public void deletePrivacyLists(String username) {
352 353 354 355
        // If there are no privacy lists stored, this method is a no-op.
        if (privacyListCount.get() == 0) {
            return;
        }
Gaston Dombiak's avatar
Gaston Dombiak committed
356 357 358 359 360 361 362 363 364 365 366 367
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(DELETE_PRIVACY_LISTS);
            pstmt.setString(1, username);
            pstmt.executeUpdate();
        }
        catch (Exception e) {
            Log.error("Error deleting privacy lists of username: " + username, e);
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
368
            DbConnectionManager.closeConnection(pstmt, con);
Gaston Dombiak's avatar
Gaston Dombiak committed
369
        }
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
        // Set the privacy list count to -1. We don't know how many privacy lists there
        // are, but it's probably not "0", which is the case we care about.
        privacyListCount.set(-1);
    }

    /**
     * Loads the total number of privacy lists stored in the database.
     */
    private void loadPrivacyListCount() {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(PRIVACY_LIST_COUNT);
            rs = pstmt.executeQuery();
            rs.next();
            privacyListCount.set(rs.getInt(1));
        }
        catch (Exception e) {
390
            Log.error(e.getMessage(), e);
391 392 393 394
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
Gaston Dombiak's avatar
Gaston Dombiak committed
395 396
    }
}