Commit dd960eeb authored by Tom Evans's avatar Tom Evans Committed by tevans

OF-668: Ensure Pubsub write cache is persisted during Openfire shutdown

- Improved exception handling during shutdown
- Refactored LinkedList / LinkedListNode to use generics
- Refactored PublishedItem to remove persistence-specific retry attribute
- Added RetryWrapper to improve pubsub item persistence

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13651 b35dd754-fafc-0310-a699-88a17e54d16e
parent 33b5e8c0
...@@ -42,6 +42,7 @@ import java.util.concurrent.CopyOnWriteArrayList; ...@@ -42,6 +42,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.eclipse.jetty.util.log.Log;
import org.jivesoftware.database.DbConnectionManager; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.admin.AdminManager; import org.jivesoftware.openfire.admin.AdminManager;
import org.jivesoftware.openfire.audit.AuditManager; import org.jivesoftware.openfire.audit.AuditManager;
...@@ -361,6 +362,7 @@ public class XMPPServer { ...@@ -361,6 +362,7 @@ public class XMPPServer {
} }
if (isStandAlone()) { if (isStandAlone()) {
Log.info("Registering shutdown hook (standalone mode)");
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread()); Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());
} }
...@@ -682,6 +684,7 @@ public class XMPPServer { ...@@ -682,6 +684,7 @@ public class XMPPServer {
* inside of another server. * inside of another server.
*/ */
public void stop() { public void stop() {
Log.info("Initiating shutdown ...");
// Only do a system exit if we're running standalone // Only do a system exit if we're running standalone
if (isStandAlone()) { if (isStandAlone()) {
// if we're in a wrapper, we have to tell the wrapper to shut us down // if we're in a wrapper, we have to tell the wrapper to shut us down
...@@ -934,24 +937,42 @@ public class XMPPServer { ...@@ -934,24 +937,42 @@ public class XMPPServer {
ClusterManager.shutdown(); ClusterManager.shutdown();
// Notify server listeners that the server is about to be stopped // Notify server listeners that the server is about to be stopped
for (XMPPServerListener listener : listeners) { for (XMPPServerListener listener : listeners) {
listener.serverStopping(); try {
listener.serverStopping();
} catch (Exception ex) {
Log.error("Exception during listener shutdown", ex);
}
} }
// If we don't have modules then the server has already been shutdown // If we don't have modules then the server has already been shutdown
if (modules.isEmpty()) { if (modules.isEmpty()) {
return; return;
} }
Log.info("Shutting down " + modules.size() + " modules ...");
// Get all modules and stop and destroy them // Get all modules and stop and destroy them
for (Module module : modules.values()) { for (Module module : modules.values()) {
module.stop(); try {
module.destroy(); module.stop();
module.destroy();
} catch (Exception ex) {
Log.error("Exception during module shutdown", ex);
}
} }
// Stop all plugins // Stop all plugins
Log.info("Shutting down plugins ...");
if (pluginManager != null) { if (pluginManager != null) {
pluginManager.shutdown(); try {
pluginManager.shutdown();
} catch (Exception ex) {
Log.error("Exception during plugin shutdown", ex);
}
} }
modules.clear(); modules.clear();
// Stop the Db connection manager. // Stop the Db connection manager.
DbConnectionManager.destroyConnectionProvider(); try {
DbConnectionManager.destroyConnectionProvider();
} catch (Exception ex) {
Log.error("Exception during DB shutdown", ex);
}
// Shutdown the task engine. // Shutdown the task engine.
TaskEngine.getInstance().shutdown(); TaskEngine.getInstance().shutdown();
......
...@@ -101,10 +101,6 @@ public class PublishedItem implements Serializable { ...@@ -101,10 +101,6 @@ public class PublishedItem implements Serializable {
* XML representation of the payload (for serialization) * XML representation of the payload (for serialization)
*/ */
private String payloadXML; private String payloadXML;
/**
* Persistence retry counter
*/
private volatile transient int retryCount = 0;
/** /**
* Creates a published item * Creates a published item
...@@ -292,14 +288,6 @@ public class PublishedItem implements Serializable { ...@@ -292,14 +288,6 @@ public class PublishedItem implements Serializable {
return getItemKey(nodeId,id); return getItemKey(nodeId,id);
} }
/**
* Returns (and increments) the item persistence retry counter
* @return Number of attempts made to persist this item to the DB
*/
public int getRetryCount() {
return retryCount++;
}
/** /**
* Returns a string that uniquely identifies this published item * Returns a string that uniquely identifies this published item
* in the following format: <i>nodeId:itemId</i> * in the following format: <i>nodeId:itemId</i>
......
...@@ -27,20 +27,21 @@ package org.jivesoftware.util; ...@@ -27,20 +27,21 @@ package org.jivesoftware.util;
* reference to the node that is to be deleted.<p> * reference to the node that is to be deleted.<p>
* *
* @author Jive Software * @author Jive Software
* @param <E>
*/ */
public class LinkedList { public class LinkedList<E> {
/** /**
* The root of the list keeps a reference to both the first and last * The root of the list keeps a reference to both the first and last
* elements of the list. * elements of the list.
*/ */
private LinkedListNode head; private LinkedListNode<E> head;
/** /**
* Creates a new linked list. * Creates a new linked list.
*/ */
public LinkedList() { public LinkedList() {
head = new LinkedListNode("head"); head = new LinkedListNode<E>();
} }
/** /**
...@@ -48,8 +49,8 @@ public class LinkedList { ...@@ -48,8 +49,8 @@ public class LinkedList {
* *
* @return the first element of the list. * @return the first element of the list.
*/ */
public LinkedListNode getFirst() { public LinkedListNode<E> getFirst() {
LinkedListNode node = head.next; LinkedListNode<E> node = head.next;
if (node == head) { if (node == head) {
return null; return null;
} }
...@@ -61,8 +62,8 @@ public class LinkedList { ...@@ -61,8 +62,8 @@ public class LinkedList {
* *
* @return the last element of the list. * @return the last element of the list.
*/ */
public LinkedListNode getLast() { public LinkedListNode<E> getLast() {
LinkedListNode node = head.previous; LinkedListNode<E> node = head.previous;
if (node == head) { if (node == head) {
return null; return null;
} }
...@@ -74,7 +75,7 @@ public class LinkedList { ...@@ -74,7 +75,7 @@ public class LinkedList {
* *
* @param node the node to add to the beginning of the list. * @param node the node to add to the beginning of the list.
*/ */
public LinkedListNode addFirst(LinkedListNode node) { public LinkedListNode<E> addFirst(LinkedListNode<E> node) {
return node.insert(head.next, head); return node.insert(head.next, head);
} }
...@@ -85,8 +86,8 @@ public class LinkedList { ...@@ -85,8 +86,8 @@ public class LinkedList {
* @param object the object to add to the beginning of the list. * @param object the object to add to the beginning of the list.
* @return the node created to wrap the object. * @return the node created to wrap the object.
*/ */
public LinkedListNode addFirst(Object object) { public LinkedListNode<E> addFirst(E object) {
return new LinkedListNode(object, head.next, head); return new LinkedListNode<E>(object, head.next, head);
} }
/** /**
...@@ -94,7 +95,7 @@ public class LinkedList { ...@@ -94,7 +95,7 @@ public class LinkedList {
* *
* @param node the node to add to the beginning of the list. * @param node the node to add to the beginning of the list.
*/ */
public LinkedListNode addLast(LinkedListNode node) { public LinkedListNode<E> addLast(LinkedListNode<E> node) {
return node.insert(head, head.previous); return node.insert(head, head.previous);
} }
...@@ -105,8 +106,8 @@ public class LinkedList { ...@@ -105,8 +106,8 @@ public class LinkedList {
* @param object the object to add to the end of the list. * @param object the object to add to the end of the list.
* @return the node created to wrap the object. * @return the node created to wrap the object.
*/ */
public LinkedListNode addLast(Object object) { public LinkedListNode<E> addLast(E object) {
return new LinkedListNode(object, head, head.previous); return new LinkedListNode<E>(object, head, head.previous);
} }
/** /**
...@@ -114,7 +115,7 @@ public class LinkedList { ...@@ -114,7 +115,7 @@ public class LinkedList {
*/ */
public void clear() { public void clear() {
//Remove all references in the list. //Remove all references in the list.
LinkedListNode node = getLast(); LinkedListNode<E> node = getLast();
while (node != null) { while (node != null) {
node.remove(); node.remove();
node = getLast(); node = getLast();
...@@ -132,7 +133,7 @@ public class LinkedList { ...@@ -132,7 +133,7 @@ public class LinkedList {
*/ */
@Override @Override
public String toString() { public String toString() {
LinkedListNode node = head.next; LinkedListNode<E> node = head.next;
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
while (node != head) { while (node != head) {
buf.append(node.toString()).append(", "); buf.append(node.toString()).append(", ");
......
...@@ -38,11 +38,11 @@ package org.jivesoftware.util; ...@@ -38,11 +38,11 @@ package org.jivesoftware.util;
* @author Jive Software * @author Jive Software
* @see org.jivesoftware.util.LinkedList * @see org.jivesoftware.util.LinkedList
*/ */
public class LinkedListNode { public class LinkedListNode<E> {
public LinkedListNode previous; public LinkedListNode<E> previous;
public LinkedListNode next; public LinkedListNode<E> next;
public Object object; public E object;
/** /**
* This class is further customized for the CoolServlets cache system. It * This class is further customized for the CoolServlets cache system. It
...@@ -61,9 +61,8 @@ public class LinkedListNode { ...@@ -61,9 +61,8 @@ public class LinkedListNode {
* Constructs an self-referencing node. This node acts as a start/end * Constructs an self-referencing node. This node acts as a start/end
* sentinel when traversing nodes in a LinkedList. * sentinel when traversing nodes in a LinkedList.
*/ */
public LinkedListNode(Object object) { public LinkedListNode() {
previous = next = this; previous = next = this;
this.object = object;
} }
/** /**
...@@ -73,7 +72,7 @@ public class LinkedListNode { ...@@ -73,7 +72,7 @@ public class LinkedListNode {
* @param next a reference to the next LinkedListNode in the list. * @param next a reference to the next LinkedListNode in the list.
* @param previous a reference to the previous LinkedListNode in the list. * @param previous a reference to the previous LinkedListNode in the list.
*/ */
public LinkedListNode(Object object, LinkedListNode next, LinkedListNode previous) { public LinkedListNode(E object, LinkedListNode<E> next, LinkedListNode<E> previous) {
if (next != null && previous != null) { if (next != null && previous != null) {
this.insert(next, previous); this.insert(next, previous);
} }
...@@ -84,7 +83,7 @@ public class LinkedListNode { ...@@ -84,7 +83,7 @@ public class LinkedListNode {
* Removes this node from the linked list that it was a part of. * Removes this node from the linked list that it was a part of.
* @return This node; next and previous references dropped * @return This node; next and previous references dropped
*/ */
public LinkedListNode remove() { public LinkedListNode<E> remove() {
previous.next = next; previous.next = next;
next.previous = previous; next.previous = previous;
previous = next = null; previous = next = null;
...@@ -95,7 +94,7 @@ public class LinkedListNode { ...@@ -95,7 +94,7 @@ public class LinkedListNode {
* Inserts this node into the linked list that it will be a part of. * Inserts this node into the linked list that it will be a part of.
* @return This node, updated to reflect previous/next changes * @return This node, updated to reflect previous/next changes
*/ */
public LinkedListNode insert(LinkedListNode next, LinkedListNode previous) { public LinkedListNode<E> insert(LinkedListNode<E> next, LinkedListNode<E> previous) {
this.next = next; this.next = next;
this.previous = previous; this.previous = previous;
this.previous.next = this.next.previous = this; this.previous.next = this.next.previous = this;
......
...@@ -70,13 +70,13 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -70,13 +70,13 @@ public class DefaultCache<K, V> implements Cache<K, V> {
* Linked list to maintain order that cache objects are accessed * Linked list to maintain order that cache objects are accessed
* in, most used to least used. * in, most used to least used.
*/ */
protected org.jivesoftware.util.LinkedList lastAccessedList; protected org.jivesoftware.util.LinkedList<K> lastAccessedList;
/** /**
* Linked list to maintain time that cache objects were initially added * Linked list to maintain time that cache objects were initially added
* to the cache, most recently added to oldest added. * to the cache, most recently added to oldest added.
*/ */
protected org.jivesoftware.util.LinkedList ageList; protected org.jivesoftware.util.LinkedList<K> ageList;
/** /**
* Maximum size in bytes that the cache can grow to. * Maximum size in bytes that the cache can grow to.
...@@ -127,8 +127,8 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -127,8 +127,8 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// is too small in almost all cases, so we set it bigger. // is too small in almost all cases, so we set it bigger.
map = new HashMap<K, CacheObject<V>>(103); map = new HashMap<K, CacheObject<V>>(103);
lastAccessedList = new org.jivesoftware.util.LinkedList(); lastAccessedList = new org.jivesoftware.util.LinkedList<K>();
ageList = new org.jivesoftware.util.LinkedList(); ageList = new org.jivesoftware.util.LinkedList<K>();
} }
public synchronized V put(K key, V value) { public synchronized V put(K key, V value) {
...@@ -153,12 +153,12 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -153,12 +153,12 @@ public class DefaultCache<K, V> implements Cache<K, V> {
DefaultCache.CacheObject<V> cacheObject = new DefaultCache.CacheObject<V>(value, objectSize); DefaultCache.CacheObject<V> cacheObject = new DefaultCache.CacheObject<V>(value, objectSize);
map.put(key, cacheObject); map.put(key, cacheObject);
// Make an entry into the cache order list. // Make an entry into the cache order list.
LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key); LinkedListNode<K> lastAccessedNode = lastAccessedList.addFirst(key);
// Store the cache order list entry so that we can get back to it // Store the cache order list entry so that we can get back to it
// during later lookups. // during later lookups.
cacheObject.lastAccessedListNode = lastAccessedNode; cacheObject.lastAccessedListNode = lastAccessedNode;
// Add the object to the age list // Add the object to the age list
LinkedListNode ageNode = ageList.addFirst(key); LinkedListNode<K> ageNode = ageList.addFirst(key);
// We make an explicit call to currentTimeMillis() so that total accuracy // We make an explicit call to currentTimeMillis() so that total accuracy
// of lifetime calculations is better than one second. // of lifetime calculations is better than one second.
ageNode.timestamp = System.currentTimeMillis(); ageNode.timestamp = System.currentTimeMillis();
...@@ -191,7 +191,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -191,7 +191,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// Remove the object from it's current place in the cache order list, // Remove the object from it's current place in the cache order list,
// and re-insert it at the front of the list. // and re-insert it at the front of the list.
cacheObject.lastAccessedListNode.remove(); cacheObject.lastAccessedListNode.remove();
lastAccessedList.addFirst(cacheObject.lastAccessedListNode); lastAccessedList.addFirst((LinkedListNode<K>) cacheObject.lastAccessedListNode);
return cacheObject.object; return cacheObject.object;
} }
...@@ -224,9 +224,9 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -224,9 +224,9 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// Now, reset all containers. // Now, reset all containers.
map.clear(); map.clear();
lastAccessedList.clear(); lastAccessedList.clear();
lastAccessedList = new org.jivesoftware.util.LinkedList(); lastAccessedList = new org.jivesoftware.util.LinkedList<K>();
ageList.clear(); ageList.clear();
ageList = new org.jivesoftware.util.LinkedList(); ageList = new org.jivesoftware.util.LinkedList<K>();
cacheSize = 0; cacheSize = 0;
cacheHits = 0; cacheHits = 0;
...@@ -558,7 +558,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -558,7 +558,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// of the linked list until they are no longer too old. We get to avoid // of the linked list until they are no longer too old. We get to avoid
// any hash lookups or looking at any more objects than is strictly // any hash lookups or looking at any more objects than is strictly
// neccessary. // neccessary.
LinkedListNode node = ageList.getLast(); LinkedListNode<K> node = ageList.getLast();
// If there are no entries in the age list, return. // If there are no entries in the age list, return.
if (node == null) { if (node == null) {
return; return;
...@@ -639,14 +639,14 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -639,14 +639,14 @@ public class DefaultCache<K, V> implements Cache<K, V> {
* accessed, the node is removed from its current spot in the list and * accessed, the node is removed from its current spot in the list and
* moved to the front. * moved to the front.
*/ */
public LinkedListNode lastAccessedListNode; public LinkedListNode<?> lastAccessedListNode;
/** /**
* A reference to the node in the age order list. We keep the reference * A reference to the node in the age order list. We keep the reference
* here to avoid linear scans of the list. The reference is used if the * here to avoid linear scans of the list. The reference is used if the
* object has to be deleted from the list. * object has to be deleted from the list.
*/ */
public LinkedListNode ageListNode; public LinkedListNode<?> ageListNode;
/** /**
* A count of the number of times the object has been read from cache. * A count of the number of times the object has been read from cache.
......
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