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 @@ ...@@ -2,7 +2,7 @@
* $Revision: 1217 $ * $Revision: 1217 $
* $Date: 2005-04-11 14:11:06 -0700 (Mon, 11 Apr 2005) $ * $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), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
...@@ -17,6 +17,7 @@ import org.dom4j.Node; ...@@ -17,6 +17,7 @@ import org.dom4j.Node;
import org.jivesoftware.util.*; import org.jivesoftware.util.*;
import org.jivesoftware.openfire.vcard.VCardManager; import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.openfire.vcard.VCardProvider; import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.openfire.XMPPServer;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import javax.naming.directory.Attributes; import javax.naming.directory.Attributes;
...@@ -99,11 +100,17 @@ import java.util.*; ...@@ -99,11 +100,17 @@ import java.util.*;
*/ */
public class LdapVCardProvider implements VCardProvider, PropertyEventListener { public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
private LdapManager manager; private final LdapManager manager;
private final VCardManager vCardManager;
private VCardTemplate template; private VCardTemplate template;
public LdapVCardProvider() { public LdapVCardProvider() {
manager = LdapManager.getInstance(); this(LdapManager.getInstance(), XMPPServer.getInstance().getVCardManager());
}
public LdapVCardProvider(LdapManager ldapManager, VCardManager vCardManager) {
manager = ldapManager;
this.vCardManager = vCardManager;
initTemplate(); initTemplate();
// Listen to property events so that the template is always up to date // Listen to property events so that the template is always up to date
PropertyEventDispatcher.addListener(this); PropertyEventDispatcher.addListener(this);
...@@ -188,6 +195,11 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener { ...@@ -188,6 +195,11 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public void setVCard(String username, Element vCardElement) {
throw new UnsupportedOperationException();
}
public void deleteVCard(String username) { public void deleteVCard(String username) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
...@@ -209,7 +221,7 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener { ...@@ -209,7 +221,7 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
if ("ldap.vcard-mapping".equals(property)) { if ("ldap.vcard-mapping".equals(property)) {
initTemplate(); initTemplate();
// Reset cache of vCards // Reset cache of vCards
VCardManager.getInstance().reset(); vCardManager.reset();
} }
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* $Revision: 3062 $ * $Revision: 3062 $
* $Date: 2005-11-11 13:26:30 -0300 (Fri, 11 Nov 2005) $ * $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), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
...@@ -15,8 +15,8 @@ import org.dom4j.Element; ...@@ -15,8 +15,8 @@ import org.dom4j.Element;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.AlreadyExistsException; import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.NotFoundException; import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.InternalServerErrorException;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Connection; import java.sql.Connection;
...@@ -27,32 +27,30 @@ import java.util.concurrent.BlockingQueue; ...@@ -27,32 +27,30 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
/** /**
* Default implementation of the VCardProvider interface, which reads and writes data * Default implementation of the VCardProvider interface, which reads and writes data from the
* from the <tt>jiveVCard</tt> database table. * <tt>jiveVCard</tt> database table.
* *
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public class DefaultVCardProvider implements VCardProvider { public class DefaultVCardProvider implements VCardProvider {
private static final String LOAD_PROPERTIES = private static final String LOAD_PROPERTIES =
"SELECT value FROM jiveVCard WHERE username=?"; "SELECT value FROM jiveVCard WHERE username=?";
private static final String DELETE_PROPERTIES = private static final String DELETE_PROPERTIES =
"DELETE FROM jiveVCard WHERE username=?"; "DELETE FROM jiveVCard WHERE username=?";
private static final String UPDATE_PROPERTIES = private static final String UPDATE_PROPERTIES =
"UPDATE jiveVCard SET value=? WHERE username=?"; "UPDATE jiveVCard SET value=? WHERE username=?";
private static final String INSERT_PROPERTY = 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>(); private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>();
public DefaultVCardProvider() { public DefaultVCardProvider() {
super(); super();
// Initialize the pool of sax readers // Initialize the pool of sax readers
for (int i=0; i<10; i++) { for (int i = 0; i < 10; i++) {
SAXReader xmlReader = new SAXReader(); SAXReader xmlReader = new SAXReader();
xmlReader.setEncoding("UTF-8"); xmlReader.setEncoding("UTF-8");
xmlReaders.add(xmlReader); xmlReaders.add(xmlReader);
...@@ -60,44 +58,40 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -60,44 +58,40 @@ public class DefaultVCardProvider implements VCardProvider {
} }
public Element loadVCard(String username) { public Element loadVCard(String username) {
synchronized (username.intern()) { Element vCardElement = null;
Element vCardElement = null; java.sql.Connection con = null;
java.sql.Connection con = null; PreparedStatement pstmt = null;
PreparedStatement pstmt = null; SAXReader xmlReader = null;
SAXReader xmlReader = null; ResultSet rs = null;
try { try {
// Get a sax reader from the pool // Get a sax reader from the pool
xmlReader = xmlReaders.take(); xmlReader = xmlReaders.take();
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PROPERTIES); pstmt = con.prepareStatement(LOAD_PROPERTIES);
pstmt.setString(1, username); pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
while (rs.next()) { while (rs.next()) {
vCardElement = vCardElement =
xmlReader.read(new StringReader(rs.getString(1))).getRootElement(); xmlReader.read(new StringReader(rs.getString(1))).getRootElement();
}
}
catch (Exception e) {
Log.error("Error loading vCard of username: " + username, e);
} }
finally { }
// Return the sax reader to the pool catch (Exception e) {
if (xmlReader != null) { throw new InternalServerErrorException("Error loading vCard of username: "
xmlReaders.add(xmlReader); + username, e);
} }
try { if (pstmt != null) { pstmt.close(); } } finally {
catch (Exception e) { Log.error(e); } // Return the sax reader to the pool
try { if (con != null) { con.close(); } } if (xmlReader != null) {
catch (Exception e) { Log.error(e); } xmlReaders.add(xmlReader);
} }
return vCardElement; DbConnectionManager.closeConnection(rs, pstmt, con);
} }
return vCardElement;
} }
public void createVCard(String username, Element vCardElement) throws AlreadyExistsException { public void createVCard(String username, Element vCardElement) throws AlreadyExistsException {
if (loadVCard(username) != null) { if (username == null || vCardElement == null) {
// The user already has a vCard throw new NullPointerException("Parameters cannot be null.");
throw new AlreadyExistsException("Username " + username + " already has a vCard");
} }
Connection con = null; Connection con = null;
...@@ -110,20 +104,18 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -110,20 +104,18 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate(); pstmt.executeUpdate();
} }
catch (SQLException e) { catch (SQLException e) {
Log.error("Error creating vCard for username: " + username, e); throw new InternalServerErrorException("Error creating vCard for username: "
+ username, e);
} }
finally { finally {
try { if (pstmt != null) { pstmt.close(); } } DbConnectionManager.closeConnection(pstmt, con);
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
} }
} }
public void updateVCard(String username, Element vCardElement) throws NotFoundException { public void updateVCard(String username, Element vCardElement) throws NotFoundException {
if (loadVCard(username) == null) { if (username == null || vCardElement == null) {
// The user already has a vCard throw new NullPointerException("Parameters cannot be null.");
throw new NotFoundException("Username " + username + " does not have a vCard");
} }
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
...@@ -135,17 +127,43 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -135,17 +127,43 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate(); pstmt.executeUpdate();
} }
catch (SQLException e) { catch (SQLException e) {
Log.error("Error updating vCard of username: " + username, e); throw new InternalServerErrorException("Error updating vCard of username: "
+ username, e);
} }
finally { finally {
try { if (pstmt != null) { pstmt.close(); } } DbConnectionManager.closeConnection(pstmt, con);
catch (Exception e) { Log.error(e); } }
try { if (con != null) { con.close(); } } }
catch (Exception e) { Log.error(e); }
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) { public void deleteVCard(String username) {
if (username == null) {
throw new NullPointerException("Username cannot be null.");
}
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
try { try {
...@@ -155,13 +173,11 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -155,13 +173,11 @@ public class DefaultVCardProvider implements VCardProvider {
pstmt.executeUpdate(); pstmt.executeUpdate();
} }
catch (SQLException e) { catch (SQLException e) {
Log.error("Error deleting vCard of username: " + username, e); throw new InternalServerErrorException("Error deleting vCard of username: "
+ username, e);
} }
finally { finally {
try { if (pstmt != null) { pstmt.close(); } } DbConnectionManager.closeConnection(pstmt, con);
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
} }
} }
......
...@@ -23,6 +23,7 @@ public interface VCardListener { ...@@ -23,6 +23,7 @@ public interface VCardListener {
* A vCard was created. * A vCard was created.
* *
* @param user the user for which the vCard was created. * @param user the user for which the vCard was created.
* @deprecated use {@link #vCardSet(String)}
*/ */
public void vCardCreated(String user); public void vCardCreated(String user);
...@@ -30,9 +31,17 @@ public interface VCardListener { ...@@ -30,9 +31,17 @@ public interface VCardListener {
* A vCard was updated. * A vCard was updated.
* *
* @param user the user for which the vCard was updated. * @param user the user for which the vCard was updated.
* @deprecated use {@link #vCardSet(String)}
*/ */
public void vCardUpdated(String user); 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. * A vCard was deleted.
* *
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* $Revision: 1651 $ * $Revision: 1651 $
* $Date: 2005-07-20 00:20:39 -0300 (Wed, 20 Jul 2005) $ * $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), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
...@@ -36,7 +36,7 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider ...@@ -36,7 +36,7 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
private static VCardManager instance; private static VCardManager instance;
private Cache<String, Element> vcardCache; 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. * 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 ...@@ -62,7 +62,6 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
super("VCard Manager"); super("VCard Manager");
String cacheName = "VCard"; String cacheName = "VCard";
vcardCache = CacheFactory.createCache(cacheName); vcardCache = CacheFactory.createCache(cacheName);
this.eventHandler = new EventHandler();
} }
/** /**
...@@ -114,51 +113,16 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider ...@@ -114,51 +113,16 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
* *
* @param username The username of the user to set his new vCard. * @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. * @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 { public void setVCard(String username, Element vCardElement) {
boolean created = false;
boolean updated = false;
if (provider.isReadOnly()) { if (provider.isReadOnly()) {
throw new UnsupportedOperationException("VCard provider is read-only."); throw new UnsupportedOperationException("VCard provider is read-only.");
} }
Element oldVCard = getOrLoadVCard(username); synchronized (getLock(username)) {
// See if we need to update the vCard or insert a new one. provider.setVCard(username, vCardElement);
if (oldVCard != null) { vcardCache.put(username, vCardElement);
// 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);
} }
dispatchVCardSet(username);
} }
/** /**
...@@ -172,11 +136,17 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider ...@@ -172,11 +136,17 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
if (provider.isReadOnly()) { if (provider.isReadOnly()) {
throw new UnsupportedOperationException("VCard provider is read-only."); throw new UnsupportedOperationException("VCard provider is read-only.");
} }
Element oldVCard = getOrLoadVCard(username); boolean deleted = false;
if (oldVCard != null) { synchronized (getLock(username)) {
vcardCache.remove(username); Element oldVCard = getOrLoadVCard(username);
// Delete the property from the DB if it was present in memory if (oldVCard != null) {
provider.deleteVCard(username); // 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 // Alert listeners that a vCard has been deleted
dispatchVCardDeleted(username); dispatchVCardDeleted(username);
} }
...@@ -196,14 +166,20 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider ...@@ -196,14 +166,20 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
} }
private Element getOrLoadVCard(String username) { private Element getOrLoadVCard(String username) {
Element vCardElement = vcardCache.get(username); synchronized (getLock(username)) {
if (vCardElement == null) { Element vCardElement = vcardCache.get(username);
vCardElement = provider.loadVCard(username); if (vCardElement == null) {
if (vCardElement != null) { vCardElement = provider.loadVCard(username);
vcardCache.put(username, vCardElement); 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 ...@@ -242,20 +218,9 @@ public class VCardManager extends BasicModule implements ServerFeaturesProvider
* *
* @param user the user for which the vCard was set. * @param user the user for which the vCard was set.
*/ */
private void dispatchVCardUpdated(String user) { private void dispatchVCardSet(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) {
for (VCardListener listener : listeners) { for (VCardListener listener : listeners) {
listener.vCardCreated(user); listener.vCardSet(user);
} }
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* $Revision: 1651 $ * $Revision: 1651 $
* $Date: 2005-07-20 00:20:39 -0300 (Wed, 20 Jul 2005) $ * $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), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
...@@ -41,6 +41,7 @@ public interface VCardProvider { ...@@ -41,6 +41,7 @@ public interface VCardProvider {
* @throws AlreadyExistsException if the user already has a vCard. * @throws AlreadyExistsException if the user already has a vCard.
* @throws UnsupportedOperationException if the provider does not support the * @throws UnsupportedOperationException if the provider does not support the
* operation. * operation.
* @deprecated use {@link #setVCard(String, org.dom4j.Element)}
*/ */
void createVCard(String username, Element vCardElement) throws AlreadyExistsException; void createVCard(String username, Element vCardElement) throws AlreadyExistsException;
...@@ -54,9 +55,18 @@ public interface VCardProvider { ...@@ -54,9 +55,18 @@ public interface VCardProvider {
* @throws NotFoundException if the vCard to update does not exist. * @throws NotFoundException if the vCard to update does not exist.
* @throws UnsupportedOperationException if the provider does not support the * @throws UnsupportedOperationException if the provider does not support the
* operation. * operation.
* @deprecated use {@link #setVCard(String, org.dom4j.Element)}
*/ */
void updateVCard(String username, Element vCardElement) throws NotFoundException; 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 * Delets a user vcard. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend vcard store. * 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