/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.util;
import org.ws4d.java.DPWSFramework;
import org.ws4d.java.concurrency.LockSupport;
import org.ws4d.java.structures.ArrayList;
import org.ws4d.java.structures.Iterator;
import org.ws4d.java.structures.LinkedList;
import org.ws4d.java.structures.List;
import org.ws4d.java.structures.ListIterator;
/**
* Class
*
* @author mspies
*/
public class WatchDog implements Runnable {
/** extra time buffer for removing callbacks */
final static int ADDITIONAL_TIME_BEFORE_CALLBACK_REMOVAL = 50; // milliseconds
/** list<TimedEntry> */
protected final LinkedList listEntries = new LinkedList();
/** <code>true</code> if class was started */
private boolean running = false;
/** lock protects list */
protected LockSupport lock = new LockSupport();
/** this */
private static WatchDog watchdog = new WatchDog();
/**
* Private constructor.
*/
private WatchDog() {}
public static WatchDog getInstance() {
return watchdog;
}
/**
* Registers timed object to observe.
*
* @param timedEntry
* @param timeUntilTimeout
*/
public void register(TimedEntry timedEntry, long timeUntilTimeout) {
lock.exclusiveLock();
try {
if (timedEntry.registered) {
update(timedEntry, timeUntilTimeout);
return;
}
timedEntry.setTimer(timeUntilTimeout);
if (listEntries.size() == 0) {
listEntries.add(timedEntry);
} else {
boolean added = false;
ListIterator it = listEntries.listIterator(listEntries.size());
while (it.hasPrevious()) {
TimedEntry temp = (TimedEntry) it.previous();
if (temp.compareTo(timedEntry) < 0) {
if (it.hasNext()) {
it.next();
}
it.add(timedEntry);
added = true;
break;
}
}
if (!added) {
it.add(timedEntry);
}
}
timedEntry.registered = true;
} finally {
lock.releaseExclusiveLock();
}
synchronized (this) {
this.notify();
}
}
/**
* Disables timed entry from managed objects of watchdog. Removing will
* occur later.
*
* @param timedEntry
*/
public void unregister(TimedEntry timedEntry) {
// lock.exclusiveLock();
lock.sharedLock();
try {
// XXX: Will be faster, if we can use entries of linked list.
// listEntries.remove(timedEntry);
// XXX: entry will be removed at timeout
timedEntry.disabled = true;
} finally {
lock.releaseSharedLock();
// lock.releaseExclusiveLock();
}
}
/**
* Updates timed entry with new time until timeout within managed objects of
* watchdog.
*
* @param timedEntry
* @param timeUntilTimeout
*/
public void update(TimedEntry timedEntry, long timeUntilTimeout) {
List timeoutObjects = new ArrayList(3);
lock.exclusiveLock();
try {
if (!timedEntry.registered) {
register(timedEntry, timeUntilTimeout);
return;
}
timedEntry.setTimer(timeUntilTimeout);
long currentTime = System.currentTimeMillis();
if (listEntries.size() > 1) {
boolean added = false;
ListIterator it = listEntries.listIterator();
while (it.hasNext()) {
TimedEntry entry = (TimedEntry) it.next();
if (entry.equals(timedEntry)) {
it.remove();
entry.registered = false;
} else if (entry.timeToRemove <= currentTime) {
it.remove();
if (!entry.disabled) {
// XXX previous unregistered entries won't receive
// timeout
timeoutObjects.add(entry);
}
entry.registered = false;
entry.disabled = false;
} else if (entry.compareTo(timedEntry) > 0 && !added) {
// if( it.hasPrevious() ){
// it.previous();
// }
it.previous();
it.add(timedEntry);
added = true;
if (!entry.registered) {
// we only break, if entry removed from old position
break;
}
} else if (!entry.registered) {
// we only break, if entry removed from old position
break;
}
}
if (!added) {
it.add(timedEntry);
}
}
timedEntry.registered = true;
timedEntry.disabled = false;
} finally {
lock.releaseExclusiveLock();
}
/*
* timeout for all removed objects
*/
callTimeouts(timeoutObjects);
synchronized (this) {
this.notify();
}
}
// ------------------------ RUNNABLE ------------------------------
/**
* Starts thread to remove timed out message requests.
*/
public void run() {
running = true;
while (running) {
try {
lock.sharedLock();
//(INGO) potentially DANGEROUS because the variable size in class LinkedList is not volatile
if (listEntries.size() == 0) {
synchronized (this) {
lock.releaseSharedLock();
wait();
}
} else {
TimedEntry e = (TimedEntry) listEntries.getFirst();
long millis2Sleep = e.timeToRemove - System.currentTimeMillis();
if (millis2Sleep > 0) {
// some extra millis;
millis2Sleep += ADDITIONAL_TIME_BEFORE_CALLBACK_REMOVAL;
synchronized (this) {
lock.releaseSharedLock();
this.wait(millis2Sleep);
}
} else {
lock.releaseSharedLock();
}
checkEntries();
}
} catch (InterruptedException e1) {
// e1.printStackTrace();
}
}
}
/**
* Stops thread, which handles timed entries.
*/
public void stop() {
running = false;
synchronized (this) {
notifyAll();
}
clearEntries();
}
// ---------------------------- PRIVATE ----------------------------
private void clearEntries() {
List timeoutObjects = new ArrayList(listEntries.size());
lock.exclusiveLock();
try {
for (Iterator it = listEntries.iterator(); it.hasNext();) {
final TimedEntry entry = (TimedEntry) it.next();
it.remove();
entry.registered = false;
entry.disabled = false;
timeoutObjects.add(entry);
}
} finally {
lock.releaseExclusiveLock();
}
callTimeouts(timeoutObjects);
}
/**
* Removes all entries with expired lifetime. Sends timeout to all removed
* callbacks.
*/
private void checkEntries() {
long currentTime = System.currentTimeMillis();
List timeoutObjects = new ArrayList(5);
lock.exclusiveLock();
try {
for (Iterator it = listEntries.iterator(); it.hasNext();) {
TimedEntry entry = (TimedEntry) it.next();
if (entry.timeToRemove <= currentTime) {
it.remove();
if (!entry.disabled) {
// XXX previous unregistered entries won't receive
// timeout
timeoutObjects.add(entry);
}
entry.registered = false;
entry.disabled = false;
} else {
break;
}
}
} finally {
lock.releaseExclusiveLock();
}
/*
* timeout for all removed objects
*/
callTimeouts(timeoutObjects);
}
/**
* Calls timeout callback methods of timed entries in separate threads.
*
* @param timeoutObjects
*/
private void callTimeouts(List timeoutObjects) {
for (Iterator it = timeoutObjects.iterator(); it.hasNext();) {
final TimedEntry entry = (TimedEntry) it.next();
DPWSFramework.getThreadPool().execute(new Runnable() {
public void run() {
entry.timedOut();
}
});
}
}
}