Commit 7cc9914e authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

1) Added events support. JM-1046

2) Made it clusterable.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@8142 b35dd754-fafc-0310-a699-88a17e54d16e
parent 5922ec45
...@@ -13,15 +13,21 @@ package org.jivesoftware.openfire.privacy; ...@@ -13,15 +13,21 @@ package org.jivesoftware.openfire.privacy;
import org.dom4j.DocumentFactory; import org.dom4j.DocumentFactory;
import org.dom4j.Element; import org.dom4j.Element;
import org.jivesoftware.util.cache.CacheSizes; import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.Log;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.roster.Roster; import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Packet; import org.xmpp.packet.Packet;
import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -38,7 +44,32 @@ import java.util.List; ...@@ -38,7 +44,32 @@ import java.util.List;
* *
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public class PrivacyList implements Cacheable { public class PrivacyList implements Cacheable, Externalizable {
/**
* Reuse the same factory for all the connections.
*/
private static XmlPullParserFactory factory = null;
private static ThreadLocal<XMPPPacketReader> localParser = null;
static {
try {
factory = XmlPullParserFactory.newInstance(MXParser.class.getName(), null);
factory.setNamespaceAware(true);
}
catch (XmlPullParserException e) {
Log.error("Error creating a parser factory", e);
}
// Create xmpp parser to keep in each thread
localParser = new ThreadLocal<XMPPPacketReader>() {
protected XMPPPacketReader initialValue() {
XMPPPacketReader parser = new XMPPPacketReader();
factory.setNamespaceAware(true);
parser.setXPPFactory(factory);
return parser;
}
};
}
private JID userJID; private JID userJID;
private String name; private String name;
...@@ -46,6 +77,12 @@ public class PrivacyList implements Cacheable { ...@@ -46,6 +77,12 @@ public class PrivacyList implements Cacheable {
private List<PrivacyItem> items = new ArrayList<PrivacyItem>(); private List<PrivacyItem> items = new ArrayList<PrivacyItem>();
private Roster roster; private Roster roster;
/**
* Constructor added for Externalizable. Do not use this constructor.
*/
public PrivacyList() {
}
public PrivacyList(String username, String name, boolean isDefault, Element listElement) { public PrivacyList(String username, String name, boolean isDefault, Element listElement) {
this.userJID = XMPPServer.getInstance().createJID(username, null); this.userJID = XMPPServer.getInstance().createJID(username, null);
this.name = name; this.name = name;
...@@ -54,6 +91,15 @@ public class PrivacyList implements Cacheable { ...@@ -54,6 +91,15 @@ public class PrivacyList implements Cacheable {
updateList(listElement); updateList(listElement);
} }
/**
* Returns the JID of the user that owns this privacy list.
*
* @return the JID of the user that owns this privacy list.
*/
public JID getUserJID() {
return userJID;
}
/** /**
* Returns the name that uniquely identifies this list among the users lists. * Returns the name that uniquely identifies this list among the users lists.
* *
...@@ -81,6 +127,8 @@ public class PrivacyList implements Cacheable { ...@@ -81,6 +127,8 @@ public class PrivacyList implements Cacheable {
*/ */
public void setDefaultList(boolean isDefault) { public void setDefaultList(boolean isDefault) {
this.isDefault = isDefault; this.isDefault = isDefault;
// Trigger event that this list has been modified
PrivacyListManager.getInstance().dispatchModifiedEvent(this);
} }
/** /**
...@@ -137,6 +185,17 @@ public class PrivacyList implements Cacheable { ...@@ -137,6 +185,17 @@ public class PrivacyList implements Cacheable {
* @param listElement the element containing a list of items. * @param listElement the element containing a list of items.
*/ */
public void updateList(Element listElement) { public void updateList(Element listElement) {
updateList(listElement, true);
}
/**
* Sets the new list items based on the specified Element. The Element must contain
* a list of item elements.
*
* @param listElement the element containing a list of items.
* @param notify true if a provicy list modified event will be triggered.
*/
private void updateList(Element listElement, boolean notify) {
// Reset the list of items of this list // Reset the list of items of this list
items = new ArrayList<PrivacyItem>(); items = new ArrayList<PrivacyItem>();
...@@ -159,6 +218,10 @@ public class PrivacyList implements Cacheable { ...@@ -159,6 +218,10 @@ public class PrivacyList implements Cacheable {
} }
// Sort items collections // Sort items collections
Collections.sort(items); Collections.sort(items);
if (notify) {
// Trigger event that this list has been modified
PrivacyListManager.getInstance().dispatchModifiedEvent(this);
}
} }
public int getCachedSize() { public int getCachedSize() {
...@@ -191,4 +254,22 @@ public class PrivacyList implements Cacheable { ...@@ -191,4 +254,22 @@ public class PrivacyList implements Cacheable {
return false; return false;
} }
} }
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSafeUTF(out, userJID.toString());
ExternalizableUtil.getInstance().writeBoolean(out, isDefault);
ExternalizableUtil.getInstance().writeSafeUTF(out, asElement().asXML());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
userJID = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
isDefault = ExternalizableUtil.getInstance().readBoolean(in);
String xml = ExternalizableUtil.getInstance().readSafeUTF(in);
try {
Element element = localParser.get().read(new StringReader(xml)).getRootElement();
updateList(element, false);
} catch (Exception e) {
Log.error("Error while parsing Privacy Property", e);
}
}
} }
...@@ -4,6 +4,9 @@ import org.dom4j.Element; ...@@ -4,6 +4,9 @@ import org.dom4j.Element;
import org.jivesoftware.util.cache.Cache; import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheManager; import org.jivesoftware.util.cache.CacheManager;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* A Privacy list manager creates, gets, updates and removes privacy lists. Loaded lists * A Privacy list manager creates, gets, updates and removes privacy lists. Loaded lists
* are kept in memory using a cache that will keep them at most for 6 hours. * are kept in memory using a cache that will keep them at most for 6 hours.
...@@ -13,9 +16,30 @@ import org.jivesoftware.util.cache.CacheManager; ...@@ -13,9 +16,30 @@ import org.jivesoftware.util.cache.CacheManager;
public class PrivacyListManager { public class PrivacyListManager {
private static final PrivacyListManager instance = new PrivacyListManager(); private static final PrivacyListManager instance = new PrivacyListManager();
private static Cache<String, PrivacyList> listsCache;
private PrivacyListProvider provider = new PrivacyListProvider(); private PrivacyListProvider provider = new PrivacyListProvider();
private Cache listsCache;
private List<PrivacyListEventListener> listeners = new CopyOnWriteArrayList<PrivacyListEventListener>();
static {
PrivacyListEventListener eventListener = new PrivacyListEventListener() {
public void privacyListCreated(PrivacyList list) {
// Do nothing
}
public void privacyListDeleting(String listName) {
// Do nothing
}
public void privacyListModified(PrivacyList list) {
// Set object again in cache. This is done so that other cluster nodes
// get refreshed with latest version of the object
listsCache.put(getCacheKey(list.getUserJID().getNode(), list.getName()), list);
}
};
instance.addListener(eventListener);
}
/** /**
* Returns the unique instance of this class. * Returns the unique instance of this class.
...@@ -48,6 +72,10 @@ public class PrivacyListManager { ...@@ -48,6 +72,10 @@ public class PrivacyListManager {
listsCache.put(getCacheKey(username, listName), list); listsCache.put(getCacheKey(username, listName), list);
// Save new list to database // Save new list to database
provider.createPrivacyList(username, list); provider.createPrivacyList(username, list);
// Trigger event that a new privacy list has been created
for (PrivacyListEventListener listener : listeners) {
listener.privacyListCreated(list);
}
return list; return list;
} }
...@@ -60,12 +88,16 @@ public class PrivacyListManager { ...@@ -60,12 +88,16 @@ public class PrivacyListManager {
* @param listName the name of the list being deleted. * @param listName the name of the list being deleted.
*/ */
public void deletePrivacyList(String username, String listName) { public void deletePrivacyList(String username, String listName) {
// Trigger event that a privacy list is being deleted
for (PrivacyListEventListener listener : listeners) {
listener.privacyListDeleting(listName);
}
// Remove the list from the cache // Remove the list from the cache
listsCache.remove(getCacheKey(username, listName)); listsCache.remove(getCacheKey(username, listName));
// Delete the privacy list from the DB // Delete the privacy list from the DB
provider.deletePrivacyList(username, listName); provider.deletePrivacyList(username, listName);
// Check if deleted list was the default list // Check if deleted list was the default list
PrivacyList defaultList = (PrivacyList) listsCache.get(getDefaultCacheKey(username)); PrivacyList defaultList = listsCache.get(getDefaultCacheKey(username));
if (defaultList != null && listName.equals(defaultList.getName())) { if (defaultList != null && listName.equals(defaultList.getName())) {
listsCache.remove(getDefaultCacheKey(username)); listsCache.remove(getDefaultCacheKey(username));
} }
...@@ -81,6 +113,10 @@ public class PrivacyListManager { ...@@ -81,6 +113,10 @@ public class PrivacyListManager {
for (String listName : provider.getPrivacyLists(username).keySet()) { for (String listName : provider.getPrivacyLists(username).keySet()) {
// Remove the list from the cache // Remove the list from the cache
listsCache.remove(getCacheKey(username, listName)); listsCache.remove(getCacheKey(username, listName));
// Trigger event that a privacy list is being deleted
for (PrivacyListEventListener listener : listeners) {
listener.privacyListDeleting(listName);
}
} }
// Delete user privacy lists from the DB // Delete user privacy lists from the DB
provider.deletePrivacyLists(username); provider.deletePrivacyLists(username);
...@@ -97,10 +133,10 @@ public class PrivacyListManager { ...@@ -97,10 +133,10 @@ public class PrivacyListManager {
public PrivacyList getDefaultPrivacyList(String username) { public PrivacyList getDefaultPrivacyList(String username) {
// Check if we have the default list in the cache // Check if we have the default list in the cache
String cacheKey = getDefaultCacheKey(username); String cacheKey = getDefaultCacheKey(username);
PrivacyList list = (PrivacyList) listsCache.get(cacheKey); PrivacyList list = listsCache.get(cacheKey);
if (list == null) { if (list == null) {
synchronized (username.intern()) { synchronized (username.intern()) {
list = (PrivacyList) listsCache.get(cacheKey); list = listsCache.get(cacheKey);
if (list == null) { if (list == null) {
// Load default list from the database // Load default list from the database
list = provider.loadDefaultPrivacyList(username); list = provider.loadDefaultPrivacyList(username);
...@@ -125,7 +161,7 @@ public class PrivacyListManager { ...@@ -125,7 +161,7 @@ public class PrivacyListManager {
public PrivacyList getPrivacyList(String username, String listName) { public PrivacyList getPrivacyList(String username, String listName) {
// Check if we have a list in the cache // Check if we have a list in the cache
String cacheKey = getCacheKey(username, listName); String cacheKey = getCacheKey(username, listName);
PrivacyList list = (PrivacyList) listsCache.get(cacheKey); PrivacyList list = listsCache.get(cacheKey);
if (list == null) { if (list == null) {
// Load the list from the database // Load the list from the database
list = provider.loadPrivacyList(username, listName); list = provider.loadPrivacyList(username, listName);
...@@ -158,17 +194,45 @@ public class PrivacyListManager { ...@@ -158,17 +194,45 @@ public class PrivacyListManager {
provider.updatePrivacyList(username, newDefault); provider.updatePrivacyList(username, newDefault);
} }
/**
* Registers a listener to receive events when a privacy list is created, updated or deleted.
*
* @param listener the listener.
*/
public void addListener(PrivacyListEventListener listener) {
if (listener == null) {
throw new NullPointerException();
}
listeners.add(listener);
}
/**
* Unregisters a listener to receive events.
*
* @param listener the listener.
*/
public void removeListener(PrivacyListEventListener listener) {
listeners.remove(listener);
}
/** /**
* Returns the key to use to locate a privacy list in the cache. * Returns the key to use to locate a privacy list in the cache.
*/ */
private String getCacheKey(String username, String listName) { private static String getCacheKey(String username, String listName) {
return username + listName; return username + listName;
} }
/** /**
* Returns the key to use to locate default privacy lists in the cache. * Returns the key to use to locate default privacy lists in the cache.
*/ */
private String getDefaultCacheKey(String username) { private static String getDefaultCacheKey(String username) {
return getCacheKey(username, "__d_e_f_a_u_l_t__"); return getCacheKey(username, "__d_e_f_a_u_l_t__");
} }
void dispatchModifiedEvent(PrivacyList privacyList) {
// Trigger event that a privacy list has been modified
for (PrivacyListEventListener listener : listeners) {
listener.privacyListModified(privacyList);
}
}
} }
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