blob: b715fb999d6d203054e4dfc90455a06a0e957ed4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.cluster.util;
/**
* A smart queue, used for async replication <BR>
* the "smart" part of this queue is that if the session is already queued for
* replication, and it is updated again, the session will simply be replaced,
* hence we don't replicate stuff that is obsolete. Put this into util, since it
* is quite generic.
*
* @author Filip Hanik
* @version 1.0
*/
import java.util.LinkedList;
import java.util.HashMap;
public class SmartQueue {
public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
.getLog(SmartQueue.class);
/**
* This is the actual queue
*/
private LinkedList queue = new LinkedList();
/**
* And this is only for performance, fast lookups
*/
private HashMap queueMap = new HashMap();
private Object mutex = new Object();
public SmartQueue() {
}
/**
* Add an object to the queue
*
* @param entry -
* the smart entry
*/
public void add(SmartEntry entry) {
/*
* make sure we are within a synchronized block since we are dealing
* with two unsync collections
*/
synchronized (mutex) {
/* check to see if this object has already been queued */
SmartEntry current = (SmartEntry) queueMap.get(entry.getKey());
if (current == null) {
/* the object has not been queued, at it to the end of the queue */
if (log.isDebugEnabled())
log.debug("[" + Thread.currentThread().getName()
+ "][SmartQueue] Adding new object=" + entry);
queue.addLast(entry);
queueMap.put(entry.getKey(), entry);
} else {
/* the object has been queued, replace the value */
if (log.isDebugEnabled())
log.debug("[" + Thread.currentThread().getName()
+ "][SmartQueue] Replacing old object=" + current);
current.setValue(entry.getValue());
if (log.isDebugEnabled())
log.debug("with new object=" + current);
}
/*
* wake up all the threads that are waiting for the lock to be
* released
*/
mutex.notifyAll();
}
}
public int size() {
synchronized (mutex) {
return queue.size();
}
}
/**
* Blocks forever until an element has been added to the queue
*/
public SmartEntry remove() {
return remove(0);
}
public SmartEntry remove(long timeout) {
SmartEntry result = null;
long startEntry = System.currentTimeMillis();
synchronized (mutex) {
while (size() == 0) {
try {
if (log.isDebugEnabled())
log
.debug("["
+ Thread.currentThread().getName()
+ "][SmartQueue] Queue sleeping until object added size="
+ size() + ".");
if ((timeout != 0)
&& ((System.currentTimeMillis() - startEntry) > timeout)) {
return null;
}
mutex.wait(timeout);
if (log.isDebugEnabled())
log
.debug("["
+ Thread.currentThread().getName()
+ "][SmartQueue] Queue woke up or interrupted size="
+ size() + ".");
} catch (IllegalMonitorStateException ex) {
throw ex;
} catch (InterruptedException ex) {
}//catch
}//while
/* guaranteed that we are not empty by now */
result = (SmartEntry) queue.removeFirst();
queueMap.remove(result.getKey());
if (log.isDebugEnabled())
log.debug("[" + Thread.currentThread().getName()
+ "][SmartQueue] Returning=" + result);
}
return result;
}
public static class SmartEntry {
protected Object key;
protected Object value;
public SmartEntry(Object key, Object value) {
if (key == null)
throw new IllegalArgumentException(
"SmartEntry key can not be null.");
if (value == null)
throw new IllegalArgumentException(
"SmartEntry value can not be null.");
this.key = key;
this.value = value;
}
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
if (value == null)
throw new IllegalArgumentException(
"SmartEntry value can not be null.");
this.value = value;
}
public int hashCode() {
return key.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof SmartEntry))
return false;
SmartEntry other = (SmartEntry) o;
return other.getKey().equals(getKey());
}
public String toString() {
return "[SmartyEntry key=" + key + " value=" + value + "]";
}
}
}