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;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.Log;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.roster.Roster;
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.Packet;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
......@@ -38,7 +44,32 @@ import java.util.List;
*
* @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 String name;
......@@ -46,6 +77,12 @@ public class PrivacyList implements Cacheable {
private List<PrivacyItem> items = new ArrayList<PrivacyItem>();
private Roster roster;
/**
* Constructor added for Externalizable. Do not use this constructor.
*/
public PrivacyList() {
}
public PrivacyList(String username, String name, boolean isDefault, Element listElement) {
this.userJID = XMPPServer.getInstance().createJID(username, null);
this.name = name;
......@@ -54,6 +91,15 @@ public class PrivacyList implements Cacheable {
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.
*
......@@ -81,6 +127,8 @@ public class PrivacyList implements Cacheable {
*/
public void setDefaultList(boolean 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 {
* @param listElement the element containing a list of items.
*/
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
items = new ArrayList<PrivacyItem>();
......@@ -159,6 +218,10 @@ public class PrivacyList implements Cacheable {
}
// Sort items collections
Collections.sort(items);
if (notify) {
// Trigger event that this list has been modified
PrivacyListManager.getInstance().dispatchModifiedEvent(this);
}
}
public int getCachedSize() {
......@@ -191,4 +254,22 @@ public class PrivacyList implements Cacheable {
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;
import org.jivesoftware.util.cache.Cache;
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
* 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;
public class PrivacyListManager {
private static final PrivacyListManager instance = new PrivacyListManager();
private static Cache<String, PrivacyList> listsCache;
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.
......@@ -48,6 +72,10 @@ public class PrivacyListManager {
listsCache.put(getCacheKey(username, listName), list);
// Save new list to database
provider.createPrivacyList(username, list);
// Trigger event that a new privacy list has been created
for (PrivacyListEventListener listener : listeners) {
listener.privacyListCreated(list);
}
return list;
}
......@@ -60,12 +88,16 @@ public class PrivacyListManager {
* @param listName the name of the list being deleted.
*/
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
listsCache.remove(getCacheKey(username, listName));
// Delete the privacy list from the DB
provider.deletePrivacyList(username, listName);
// 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())) {
listsCache.remove(getDefaultCacheKey(username));
}
......@@ -81,6 +113,10 @@ public class PrivacyListManager {
for (String listName : provider.getPrivacyLists(username).keySet()) {
// Remove the list from the cache
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
provider.deletePrivacyLists(username);
......@@ -97,10 +133,10 @@ public class PrivacyListManager {
public PrivacyList getDefaultPrivacyList(String username) {
// Check if we have the default list in the cache
String cacheKey = getDefaultCacheKey(username);
PrivacyList list = (PrivacyList) listsCache.get(cacheKey);
PrivacyList list = listsCache.get(cacheKey);
if (list == null) {
synchronized (username.intern()) {
list = (PrivacyList) listsCache.get(cacheKey);
list = listsCache.get(cacheKey);
if (list == null) {
// Load default list from the database
list = provider.loadDefaultPrivacyList(username);
......@@ -125,7 +161,7 @@ public class PrivacyListManager {
public PrivacyList getPrivacyList(String username, String listName) {
// Check if we have a list in the cache
String cacheKey = getCacheKey(username, listName);
PrivacyList list = (PrivacyList) listsCache.get(cacheKey);
PrivacyList list = listsCache.get(cacheKey);
if (list == null) {
// Load the list from the database
list = provider.loadPrivacyList(username, listName);
......@@ -158,17 +194,45 @@ public class PrivacyListManager {
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.
*/
private String getCacheKey(String username, String listName) {
private static String getCacheKey(String username, String listName) {
return username + listName;
}
/**
* 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__");
}
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