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;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.eclipse.jetty.util.log.Log;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.admin.AdminManager;
import org.jivesoftware.openfire.audit.AuditManager;
......@@ -361,6 +362,7 @@ public class XMPPServer {
}
if (isStandAlone()) {
Log.info("Registering shutdown hook (standalone mode)");
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());
}
......@@ -682,6 +684,7 @@ public class XMPPServer {
* inside of another server.
*/
public void stop() {
Log.info("Initiating shutdown ...");
// Only do a system exit if we're running standalone
if (isStandAlone()) {
// if we're in a wrapper, we have to tell the wrapper to shut us down
......@@ -934,24 +937,42 @@ public class XMPPServer {
ClusterManager.shutdown();
// Notify server listeners that the server is about to be stopped
for (XMPPServerListener listener : listeners) {
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 (modules.isEmpty()) {
return;
}
Log.info("Shutting down " + modules.size() + " modules ...");
// Get all modules and stop and destroy them
for (Module module : modules.values()) {
try {
module.stop();
module.destroy();
} catch (Exception ex) {
Log.error("Exception during module shutdown", ex);
}
}
// Stop all plugins
Log.info("Shutting down plugins ...");
if (pluginManager != null) {
try {
pluginManager.shutdown();
} catch (Exception ex) {
Log.error("Exception during plugin shutdown", ex);
}
}
modules.clear();
// Stop the Db connection manager.
try {
DbConnectionManager.destroyConnectionProvider();
} catch (Exception ex) {
Log.error("Exception during DB shutdown", ex);
}
// Shutdown the task engine.
TaskEngine.getInstance().shutdown();
......
......@@ -101,10 +101,6 @@ public class PublishedItem implements Serializable {
* XML representation of the payload (for serialization)
*/
private String payloadXML;
/**
* Persistence retry counter
*/
private volatile transient int retryCount = 0;
/**
* Creates a published item
......@@ -292,14 +288,6 @@ public class PublishedItem implements Serializable {
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
* in the following format: <i>nodeId:itemId</i>
......
......@@ -27,20 +27,21 @@ package org.jivesoftware.util;
* reference to the node that is to be deleted.<p>
*
* @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
* elements of the list.
*/
private LinkedListNode head;
private LinkedListNode<E> head;
/**
* Creates a new linked list.
*/
public LinkedList() {
head = new LinkedListNode("head");
head = new LinkedListNode<E>();
}
/**
......@@ -48,8 +49,8 @@ public class LinkedList {
*
* @return the first element of the list.
*/
public LinkedListNode getFirst() {
LinkedListNode node = head.next;
public LinkedListNode<E> getFirst() {
LinkedListNode<E> node = head.next;
if (node == head) {
return null;
}
......@@ -61,8 +62,8 @@ public class LinkedList {
*
* @return the last element of the list.
*/
public LinkedListNode getLast() {
LinkedListNode node = head.previous;
public LinkedListNode<E> getLast() {
LinkedListNode<E> node = head.previous;
if (node == head) {
return null;
}
......@@ -74,7 +75,7 @@ public class LinkedList {
*
* @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);
}
......@@ -85,8 +86,8 @@ public class LinkedList {
* @param object the object to add to the beginning of the list.
* @return the node created to wrap the object.
*/
public LinkedListNode addFirst(Object object) {
return new LinkedListNode(object, head.next, head);
public LinkedListNode<E> addFirst(E object) {
return new LinkedListNode<E>(object, head.next, head);
}
/**
......@@ -94,7 +95,7 @@ public class LinkedList {
*
* @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);
}
......@@ -105,8 +106,8 @@ public class LinkedList {
* @param object the object to add to the end of the list.
* @return the node created to wrap the object.
*/
public LinkedListNode addLast(Object object) {
return new LinkedListNode(object, head, head.previous);
public LinkedListNode<E> addLast(E object) {
return new LinkedListNode<E>(object, head, head.previous);
}
/**
......@@ -114,7 +115,7 @@ public class LinkedList {
*/
public void clear() {
//Remove all references in the list.
LinkedListNode node = getLast();
LinkedListNode<E> node = getLast();
while (node != null) {
node.remove();
node = getLast();
......@@ -132,7 +133,7 @@ public class LinkedList {
*/
@Override
public String toString() {
LinkedListNode node = head.next;
LinkedListNode<E> node = head.next;
StringBuilder buf = new StringBuilder();
while (node != head) {
buf.append(node.toString()).append(", ");
......
......@@ -38,11 +38,11 @@ package org.jivesoftware.util;
* @author Jive Software
* @see org.jivesoftware.util.LinkedList
*/
public class LinkedListNode {
public class LinkedListNode<E> {
public LinkedListNode previous;
public LinkedListNode next;
public Object object;
public LinkedListNode<E> previous;
public LinkedListNode<E> next;
public E object;
/**
* This class is further customized for the CoolServlets cache system. It
......@@ -61,9 +61,8 @@ public class LinkedListNode {
* Constructs an self-referencing node. This node acts as a start/end
* sentinel when traversing nodes in a LinkedList.
*/
public LinkedListNode(Object object) {
public LinkedListNode() {
previous = next = this;
this.object = object;
}
/**
......@@ -73,7 +72,7 @@ public class LinkedListNode {
* @param next a reference to the next 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) {
this.insert(next, previous);
}
......@@ -84,7 +83,7 @@ public class LinkedListNode {
* Removes this node from the linked list that it was a part of.
* @return This node; next and previous references dropped
*/
public LinkedListNode remove() {
public LinkedListNode<E> remove() {
previous.next = next;
next.previous = previous;
previous = next = null;
......@@ -95,7 +94,7 @@ public class LinkedListNode {
* Inserts this node into the linked list that it will be a part of.
* @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.previous = previous;
this.previous.next = this.next.previous = this;
......
......@@ -70,13 +70,13 @@ public class DefaultCache<K, V> implements Cache<K, V> {
* Linked list to maintain order that cache objects are accessed
* 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
* 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.
......@@ -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.
map = new HashMap<K, CacheObject<V>>(103);
lastAccessedList = new org.jivesoftware.util.LinkedList();
ageList = new org.jivesoftware.util.LinkedList();
lastAccessedList = new org.jivesoftware.util.LinkedList<K>();
ageList = new org.jivesoftware.util.LinkedList<K>();
}
public synchronized V put(K key, V value) {
......@@ -153,12 +153,12 @@ public class DefaultCache<K, V> implements Cache<K, V> {
DefaultCache.CacheObject<V> cacheObject = new DefaultCache.CacheObject<V>(value, objectSize);
map.put(key, cacheObject);
// 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
// during later lookups.
cacheObject.lastAccessedListNode = lastAccessedNode;
// 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
// of lifetime calculations is better than one second.
ageNode.timestamp = System.currentTimeMillis();
......@@ -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,
// and re-insert it at the front of the list.
cacheObject.lastAccessedListNode.remove();
lastAccessedList.addFirst(cacheObject.lastAccessedListNode);
lastAccessedList.addFirst((LinkedListNode<K>) cacheObject.lastAccessedListNode);
return cacheObject.object;
}
......@@ -224,9 +224,9 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// Now, reset all containers.
map.clear();
lastAccessedList.clear();
lastAccessedList = new org.jivesoftware.util.LinkedList();
lastAccessedList = new org.jivesoftware.util.LinkedList<K>();
ageList.clear();
ageList = new org.jivesoftware.util.LinkedList();
ageList = new org.jivesoftware.util.LinkedList<K>();
cacheSize = 0;
cacheHits = 0;
......@@ -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
// any hash lookups or looking at any more objects than is strictly
// neccessary.
LinkedListNode node = ageList.getLast();
LinkedListNode<K> node = ageList.getLast();
// If there are no entries in the age list, return.
if (node == null) {
return;
......@@ -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
* moved to the front.
*/
public LinkedListNode lastAccessedListNode;
public LinkedListNode<?> lastAccessedListNode;
/**
* 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
* 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.
......
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