Commit 8da314d2 authored by Alex Wenckus's avatar Alex Wenckus Committed by alex

Made VCardManager thread safe. JM-1083

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@8572 b35dd754-fafc-0310-a699-88a17e54d16e
parent 37072d8d
......@@ -2,7 +2,7 @@
* $Revision: 1217 $
* $Date: 2005-04-11 14:11:06 -0700 (Mon, 11 Apr 2005) $
*
* Copyright (C) 2005 Jive Software. All rights reserved.
* Copyright (C) 2007 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -17,6 +17,7 @@ import org.dom4j.Node;
import org.jivesoftware.util.*;
import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.openfire.XMPPServer;
import org.xmpp.packet.JID;
import javax.naming.directory.Attributes;
......@@ -99,11 +100,17 @@ import java.util.*;
*/
public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
private LdapManager manager;
private final LdapManager manager;
private final VCardManager vCardManager;
private VCardTemplate template;
public LdapVCardProvider() {
manager = LdapManager.getInstance();
this(LdapManager.getInstance(), XMPPServer.getInstance().getVCardManager());
}
public LdapVCardProvider(LdapManager ldapManager, VCardManager vCardManager) {
manager = ldapManager;
this.vCardManager = vCardManager;
initTemplate();
// Listen to property events so that the template is always up to date
PropertyEventDispatcher.addListener(this);
......@@ -188,6 +195,11 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
throw new UnsupportedOperationException();
}
public void setVCard(String username, Element vCardElement) {
throw new UnsupportedOperationException();
}
public void deleteVCard(String username) {
throw new UnsupportedOperationException();
}
......@@ -209,7 +221,7 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
if ("ldap.vcard-mapping".equals(property)) {
initTemplate();
// Reset cache of vCards
VCardManager.getInstance().reset();
vCardManager.reset();
}
}
......
......@@ -3,7 +3,7 @@
* $Revision: 3062 $
* $Date: 2005-11-11 13:26:30 -0300 (Fri, 11 Nov 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
* Copyright (C) 2007 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -15,8 +15,8 @@ import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.InternalServerErrorException;
import java.io.StringReader;
import java.sql.Connection;
......@@ -27,32 +27,30 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Default implementation of the VCardProvider interface, which reads and writes data
* from the <tt>jiveVCard</tt> database table.
* Default implementation of the VCardProvider interface, which reads and writes data from the
* <tt>jiveVCard</tt> database table.
*
* @author Gaston Dombiak
*/
public class DefaultVCardProvider implements VCardProvider {
private static final String LOAD_PROPERTIES =
"SELECT value FROM jiveVCard WHERE username=?";
"SELECT value FROM jiveVCard WHERE username=?";
private static final String DELETE_PROPERTIES =
"DELETE FROM jiveVCard WHERE username=?";
"DELETE FROM jiveVCard WHERE username=?";
private static final String UPDATE_PROPERTIES =
"UPDATE jiveVCard SET value=? WHERE username=?";
"UPDATE jiveVCard SET value=? WHERE username=?";
private static final String INSERT_PROPERTY =
"INSERT INTO jiveVCard (username, value) VALUES (?, ?)";
"INSERT INTO jiveVCard (username, value) VALUES (?, ?)";
/**
* Pool of SAX Readers. SAXReader is not thread safe so we need to have a pool of readers.
*/
/** Pool of SAX Readers. SAXReader is not thread safe so we need to have a pool of readers. */
private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>();
public DefaultVCardProvider() {
super();
// Initialize the pool of sax readers
for (int i=0; i<10; i++) {
for (int i = 0; i < 10; i++) {
SAXReader xmlReader = new SAXReader();
xmlReader.setEncoding("UTF-8");
xmlReaders.add(xmlReader);
......@@ -60,44 +58,40 @@ public class DefaultVCardProvider implements VCardProvider {
}
public Element loadVCard(String username) {
synchronized (username.intern()) {
Element vCardElement = null;
java.sql.Connection con = null;
PreparedStatement pstmt = null;
SAXReader xmlReader = null;
try {
// Get a sax reader from the pool
xmlReader = xmlReaders.take();
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PROPERTIES);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
vCardElement =
xmlReader.read(new StringReader(rs.getString(1))).getRootElement();
}
}
catch (Exception e) {
Log.error("Error loading vCard of username: " + username, e);
Element vCardElement = null;
java.sql.Connection con = null;
PreparedStatement pstmt = null;
SAXReader xmlReader = null;
ResultSet rs = null;
try {
// Get a sax reader from the pool
xmlReader = xmlReaders.take();
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PROPERTIES);
pstmt.setString(1, username);
rs = pstmt.executeQuery();
while (rs.next()) {
vCardElement =
xmlReader.read(new StringReader(rs.getString(1))).getRootElement();
}
finally {
// Return the sax reader to the pool
if (xmlReader != null) {
xmlReaders.add(xmlReader);
}
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
throw new InternalServerErrorException("Error loading vCard of username: "
+ username, e);
}
finally {
// Return the sax reader to the pool
if (xmlReader != null) {
xmlReaders.add(xmlReader);
}
return vCardElement;
DbConnectionManager.closeConnection(rs, pstmt, con);
}
return vCardElement;
}
public void createVCard(String username, Element vCardElement) throws AlreadyExistsException {
if (loadVCard(username) != null) {
// The user already has a vCard
throw new AlreadyExistsException("Username " + username + " already has a vCard");
if (username == null || vCardElement == null) {
throw new NullPointerException("Parameters cannot be null.");
}
Connection con = null;
......@@ -110,20 +104,18 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate();
}
catch (SQLException e) {
Log.error("Error creating vCard for username: " + username, e);
throw new InternalServerErrorException("Error creating vCard for username: "
+ username, e);
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
DbConnectionManager.closeConnection(pstmt, con);
}
}
public void updateVCard(String username, Element vCardElement) throws NotFoundException {
if (loadVCard(username) == null) {
// The user already has a vCard
throw new NotFoundException("Username " + username + " does not have a vCard");
if (username == null || vCardElement == null) {
throw new NullPointerException("Parameters cannot be null.");
}
Connection con = null;
PreparedStatement pstmt = null;
......@@ -135,17 +127,43 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate();
}
catch (SQLException e) {
Log.error("Error updating vCard of username: " + username, e);
throw new InternalServerErrorException("Error updating vCard of username: "
+ username, e);
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
DbConnectionManager.closeConnection(pstmt, con);
}
}
public void setVCard(String username, Element vCardElement) {
if (username == null || vCardElement == null) {
throw new NullPointerException("Parameters cannot be null.");
}
Element oldVcard = loadVCard(username);
try {
if (oldVcard == null) {
createVCard(username, vCardElement);
}
else {
updateVCard(username, vCardElement);
}
}
catch (AlreadyExistsException e) {
throw new InternalServerErrorException("Unable to set vCard for user:"
+ username, e);
}
catch (NotFoundException e) {
throw new InternalServerErrorException("Unable to set vCard for user:"
+ username, e);
}
}
public void deleteVCard(String username) {
if (username == null) {
throw new NullPointerException("Username cannot be null.");
}
Connection con = null;
PreparedStatement pstmt = null;
try {
......@@ -155,13 +173,11 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate();
}
catch (SQLException e) {
Log.error("Error deleting vCard of username: " + username, e);
throw new InternalServerErrorException("Error deleting vCard of username: "
+ username, e);
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
DbConnectionManager.closeConnection(pstmt, con);
}
}
......
......@@ -23,6 +23,7 @@ public interface VCardListener {
* A vCard was created.
*
* @param user the user for which the vCard was created.
* @deprecated use {@link #vCardSet(String)}
*/
public void vCardCreated(String user);
......@@ -30,9 +31,17 @@ public interface VCardListener {
* A vCard was updated.
*
* @param user the user for which the vCard was updated.
* @deprecated use {@link #vCardSet(String)}
*/
public void vCardUpdated(String user);
/**
* A vCard was set.
*
* @param username the user for which the vCard was set
*/
public void vCardSet(String username);
/**
* A vCard was deleted.
*
......
......@@ -3,7 +3,7 @@
* $Revision: 1651 $
* $Date: 2005-07-20 00:20:39 -0300 (Wed, 20 Jul 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
* Copyright (C) 2007 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -36,7 +36,7 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
private static VCardManager instance;
private Cache<String, Element> vcardCache;
private EventHandler eventHandler;
private final EventHandler eventHandler = new EventHandler();
/**
* List of listeners that will be notified when vCards are created, updated or deleted.
*/
......@@ -62,7 +62,6 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
super("VCard Manager");
String cacheName = "VCard";
vcardCache = CacheFactory.createCache(cacheName);
this.eventHandler = new EventHandler();
}
/**
......@@ -114,51 +113,16 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
*
* @param username The username of the user to set his new vCard.
* @param vCardElement The DOM element sent by the user as his new vcard.
* @throws Exception if an error occured while storing the new vCard.
*/
public void setVCard(String username, Element vCardElement) throws Exception {
boolean created = false;
boolean updated = false;
public void setVCard(String username, Element vCardElement) {
if (provider.isReadOnly()) {
throw new UnsupportedOperationException("VCard provider is read-only.");
}
Element oldVCard = getOrLoadVCard(username);
// See if we need to update the vCard or insert a new one.
if (oldVCard != null) {
// Only update the vCard in the database if the vCard has changed.
if (!oldVCard.equals(vCardElement)) {
try {
provider.updateVCard(username, vCardElement);
updated = true;
}
catch (NotFoundException e) {
Log.warn("Tried to update a vCard that does not exist", e);
provider.createVCard(username, vCardElement);
created = true;
}
}
}
else {
try {
provider.createVCard(username, vCardElement);
created = true;
}
catch (AlreadyExistsException e) {
Log.warn("Tried to create a vCard when one already exist", e);
provider.updateVCard(username, vCardElement);
updated = true;
}
}
vcardCache.put(username, vCardElement);
// Dispatch vCard events
if (created) {
// Alert listeners that a new vCard has been created
dispatchVCardCreated(username);
} else if (updated) {
// Alert listeners that a vCard has been updated
dispatchVCardUpdated(username);
synchronized (getLock(username)) {
provider.setVCard(username, vCardElement);
vcardCache.put(username, vCardElement);
}
dispatchVCardSet(username);
}
/**
......@@ -172,11 +136,17 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
if (provider.isReadOnly()) {
throw new UnsupportedOperationException("VCard provider is read-only.");
}
Element oldVCard = getOrLoadVCard(username);
if (oldVCard != null) {
vcardCache.remove(username);
// Delete the property from the DB if it was present in memory
provider.deleteVCard(username);
boolean deleted = false;
synchronized (getLock(username)) {
Element oldVCard = getOrLoadVCard(username);
if (oldVCard != null) {
// Delete the property from the DB if it was present in memory
provider.deleteVCard(username);
vcardCache.remove(username);
deleted = true;
}
}
if (deleted) {
// Alert listeners that a vCard has been deleted
dispatchVCardDeleted(username);
}
......@@ -196,14 +166,20 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
}
private Element getOrLoadVCard(String username) {
Element vCardElement = vcardCache.get(username);
if (vCardElement == null) {
vCardElement = provider.loadVCard(username);
if (vCardElement != null) {
vcardCache.put(username, vCardElement);
synchronized (getLock(username)) {
Element vCardElement = vcardCache.get(username);
if (vCardElement == null) {
vCardElement = provider.loadVCard(username);
if (vCardElement != null) {
vcardCache.put(username, vCardElement);
}
}
return vCardElement;
}
return vCardElement;
}
private Object getLock(String username) {
return (username + VCardManager.class.toString()).intern();
}
/**
......@@ -242,20 +218,9 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
*
* @param user the user for which the vCard was set.
*/
private void dispatchVCardUpdated(String user) {
for (VCardListener listener : listeners) {
listener.vCardUpdated(user);
}
}
/**
* Dispatches that a vCard was created to all listeners.
*
* @param user the user for which the vCard was created.
*/
private void dispatchVCardCreated(String user) {
private void dispatchVCardSet(String user) {
for (VCardListener listener : listeners) {
listener.vCardCreated(user);
listener.vCardSet(user);
}
}
......
......@@ -3,7 +3,7 @@
* $Revision: 1651 $
* $Date: 2005-07-20 00:20:39 -0300 (Wed, 20 Jul 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
* Copyright (C) 2007 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -41,6 +41,7 @@ public interface VCardProvider {
* @throws AlreadyExistsException if the user already has a vCard.
* @throws UnsupportedOperationException if the provider does not support the
* operation.
* @deprecated use {@link #setVCard(String, org.dom4j.Element)}
*/
void createVCard(String username, Element vCardElement) throws AlreadyExistsException;
......@@ -54,9 +55,18 @@ public interface VCardProvider {
* @throws NotFoundException if the vCard to update does not exist.
* @throws UnsupportedOperationException if the provider does not support the
* operation.
* @deprecated use {@link #setVCard(String, org.dom4j.Element)}
*/
void updateVCard(String username, Element vCardElement) throws NotFoundException;
/**
* Sets the vCard of the user to the passed in element.
*
* @param username the username of the user whose vCard is being set
* @param vCardElement the vCard being set.
*/
void setVCard(String username, Element vCardElement);
/**
* Delets a user vcard. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend vcard store.
......
/**
* $Revision:$
* $Date:$
*
* Copyright (C) 2007 Jive Software. All rights reserved.
* This software is the proprietary information of Jive Software. Use is subject to license terms.
*/
package org.jivesoftware.util;
/**
* A generic exception for when errors occur in the system.
*/
public class InternalServerErrorException extends RuntimeException {
public InternalServerErrorException(String s, Exception e) {
super(s, e);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment