37607: Enhance BlockingBuffer to allow for a timeout value
git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@348428 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5c22988774
commit
050c817ad5
|
@ -72,6 +72,7 @@ If this causes major headaches to anyone please contact commons-dev at jakarta.a
|
||||||
<li>ExtendedProperties - No longer uses an exception in normal processing [30497]</li>
|
<li>ExtendedProperties - No longer uses an exception in normal processing [30497]</li>
|
||||||
<li>BlockingBuffer - now includes stack trace if InterupttedException occurs [33700]</li>
|
<li>BlockingBuffer - now includes stack trace if InterupttedException occurs [33700]</li>
|
||||||
<li>BlockingBuffer - new methods that allow get and remove with a timeout [27691]</li>
|
<li>BlockingBuffer - new methods that allow get and remove with a timeout [27691]</li>
|
||||||
|
<li>BlockingBuffer - now allows you to specify a default timeout value for get/remove operations [37607]</li>
|
||||||
<li>Transformed*Map - new factory decorateTransform() that transforms any existing entries in the map [30959]</li>
|
<li>Transformed*Map - new factory decorateTransform() that transforms any existing entries in the map [30959]</li>
|
||||||
<li>ListOrderedMap - values can now be accessed as a List using valueList() [37015]</li>
|
<li>ListOrderedMap - values can now be accessed as a List using valueList() [37015]</li>
|
||||||
<li>ListOrderedMap - additional list-like method, setValue(int,Object)</li>
|
<li>ListOrderedMap - additional list-like method, setValue(int,Object)</li>
|
||||||
|
|
|
@ -83,6 +83,7 @@ component to ensure that it continues to meet a variety of needs.</p>
|
||||||
<li><a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a></li>
|
<li><a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a></li>
|
||||||
<li><a href="mailto:psteitz@apache.org">Phil Steitz</a></li>
|
<li><a href="mailto:psteitz@apache.org">Phil Steitz</a></li>
|
||||||
<li><a href="mailto:matth@apache.org">Matthew Hawthorne</a></li>
|
<li><a href="mailto:matth@apache.org">Matthew Hawthorne</a></li>
|
||||||
|
<li><a href="mailto:jcarman@apache.org">James Carman</a></li>
|
||||||
<li>Arun M. Thomas</li>
|
<li>Arun M. Thomas</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.apache.commons.collections.buffer.SynchronizedBuffer;
|
||||||
import org.apache.commons.collections.buffer.TransformedBuffer;
|
import org.apache.commons.collections.buffer.TransformedBuffer;
|
||||||
import org.apache.commons.collections.buffer.TypedBuffer;
|
import org.apache.commons.collections.buffer.TypedBuffer;
|
||||||
import org.apache.commons.collections.buffer.UnmodifiableBuffer;
|
import org.apache.commons.collections.buffer.UnmodifiableBuffer;
|
||||||
import org.apache.commons.collections.buffer.TimeoutBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides utility methods and decorators for {@link Buffer} instances.
|
* Provides utility methods and decorators for {@link Buffer} instances.
|
||||||
|
@ -99,8 +98,8 @@ public class BufferUtils {
|
||||||
* @throws IllegalArgumentException if the Buffer is null
|
* @throws IllegalArgumentException if the Buffer is null
|
||||||
* @since Commons Collections 3.2
|
* @since Commons Collections 3.2
|
||||||
*/
|
*/
|
||||||
public static Buffer timeoutBuffer(Buffer buffer, long timeout) {
|
public static Buffer blockingBuffer(Buffer buffer, long timeout) {
|
||||||
return TimeoutBuffer.decorate(buffer, timeout);
|
return BlockingBuffer.decorate(buffer, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,41 +15,42 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections.buffer;
|
package org.apache.commons.collections.buffer;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.Buffer;
|
||||||
|
import org.apache.commons.collections.BufferUnderflowException;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.apache.commons.collections.Buffer;
|
|
||||||
import org.apache.commons.collections.BufferUnderflowException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorates another <code>Buffer</code> to make {@link #get()} and
|
* Decorates another <code>Buffer</code> to make {@link #get()} and {@link #remove()} block when the <code>Buffer</code>
|
||||||
* {@link #remove()} block when the <code>Buffer</code> is empty.
|
* is empty.
|
||||||
* <p>
|
* <p/>
|
||||||
* If either <code>get</code> or <code>remove</code> is called on an empty
|
* If either <code>get</code> or <code>remove</code> is called on an empty <code>Buffer</code>, the calling thread waits
|
||||||
* <code>Buffer</code>, the calling thread waits for notification that
|
* for notification that an <code>add</code> or <code>addAll</code> operation has completed.
|
||||||
* an <code>add</code> or <code>addAll</code> operation has completed.
|
* <p/>
|
||||||
* <p>
|
* When one or more entries are added to an empty <code>Buffer</code>, all threads blocked in <code>get</code> or
|
||||||
* When one or more entries are added to an empty <code>Buffer</code>,
|
* <code>remove</code> are notified. There is no guarantee that concurrent blocked <code>get</code> or
|
||||||
* all threads blocked in <code>get</code> or <code>remove</code> are notified.
|
* <code>remove</code> requests will be "unblocked" and receive data in the order that they arrive.
|
||||||
* There is no guarantee that concurrent blocked <code>get</code> or
|
* <p/>
|
||||||
* <code>remove</code> requests will be "unblocked" and receive data in the
|
|
||||||
* order that they arrive.
|
|
||||||
* <p>
|
|
||||||
* This class is Serializable from Commons Collections 3.1.
|
* This class is Serializable from Commons Collections 3.1.
|
||||||
*
|
*
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
* @author Janek Bogucki
|
* @author Janek Bogucki
|
||||||
* @author Phil Steitz
|
* @author Phil Steitz
|
||||||
|
* @author James Carman
|
||||||
* @version $Revision$ $Date$
|
* @version $Revision$ $Date$
|
||||||
* @since Commons Collections 3.0
|
* @since Commons Collections 3.0
|
||||||
*/
|
*/
|
||||||
public class BlockingBuffer extends SynchronizedBuffer {
|
public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization version
|
* Serialization version
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = 1719328905017860541L;
|
private static final long serialVersionUID = 1719328905017860541L;
|
||||||
|
|
||||||
|
private long timeout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to create a blocking buffer.
|
* Factory method to create a blocking buffer.
|
||||||
*
|
*
|
||||||
|
@ -61,7 +62,20 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
return new BlockingBuffer( buffer );
|
return new BlockingBuffer( buffer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create a blocking buffer with a timeout value.
|
||||||
|
*
|
||||||
|
* @param buffer the buffer to decorate, must not be null
|
||||||
|
* @param timeout the maximum amount of time to block
|
||||||
|
* @return a new blocking buffer
|
||||||
|
* @throws IllegalArgumentException if the buffer is null
|
||||||
|
*/
|
||||||
|
public static Buffer decorate( Buffer buffer, long timeout ) {
|
||||||
|
return new BlockingBuffer( buffer, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor that wraps (not copies).
|
* Constructor that wraps (not copies).
|
||||||
*
|
*
|
||||||
|
@ -72,6 +86,18 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
super( buffer );
|
super( buffer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that wraps (not copies).
|
||||||
|
*
|
||||||
|
* @param buffer the buffer to decorate, must not be null
|
||||||
|
* @param timeout the maximum amount of time to block
|
||||||
|
* @throws IllegalArgumentException if the buffer is null
|
||||||
|
*/
|
||||||
|
protected BlockingBuffer( Buffer buffer, long timeout ) {
|
||||||
|
super( buffer );
|
||||||
|
this.timeout = timeout < 0 ? 0 : timeout;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
public boolean add( Object o ) {
|
public boolean add( Object o ) {
|
||||||
synchronized( lock ) {
|
synchronized( lock ) {
|
||||||
|
@ -90,8 +116,7 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the next value from the buffer, waiting until an object is
|
* Gets the next value from the buffer, waiting until an object is added if the buffer is empty.
|
||||||
* added if the buffer is empty.
|
|
||||||
*
|
*
|
||||||
* @throws BufferUnderflowException if an interrupt is received
|
* @throws BufferUnderflowException if an interrupt is received
|
||||||
*/
|
*/
|
||||||
|
@ -99,8 +124,14 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
synchronized( lock ) {
|
synchronized( lock ) {
|
||||||
while( collection.isEmpty() ) {
|
while( collection.isEmpty() ) {
|
||||||
try {
|
try {
|
||||||
|
if( timeout <= 0 ) {
|
||||||
lock.wait();
|
lock.wait();
|
||||||
} catch (InterruptedException e) {
|
}
|
||||||
|
else {
|
||||||
|
return get( timeout );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
PrintWriter out = new PrintWriter( new StringWriter() );
|
PrintWriter out = new PrintWriter( new StringWriter() );
|
||||||
e.printStackTrace( out );
|
e.printStackTrace( out );
|
||||||
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
||||||
|
@ -111,8 +142,8 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the next value from the buffer, waiting until an object is
|
* Gets the next value from the buffer, waiting until an object is added for up to the specified timeout value if
|
||||||
* added for up to the specified timeout value if the buffer is empty.
|
* the buffer is empty.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout value in milliseconds
|
* @param timeout the timeout value in milliseconds
|
||||||
* @throws BufferUnderflowException if an interrupt is received
|
* @throws BufferUnderflowException if an interrupt is received
|
||||||
|
@ -127,7 +158,8 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
try {
|
try {
|
||||||
lock.wait( timeLeft );
|
lock.wait( timeLeft );
|
||||||
timeLeft = expiration - System.currentTimeMillis();
|
timeLeft = expiration - System.currentTimeMillis();
|
||||||
} catch(InterruptedException e) {
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
PrintWriter out = new PrintWriter( new StringWriter() );
|
PrintWriter out = new PrintWriter( new StringWriter() );
|
||||||
e.printStackTrace( out );
|
e.printStackTrace( out );
|
||||||
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
||||||
|
@ -141,8 +173,7 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the next value from the buffer, waiting until an object is
|
* Removes the next value from the buffer, waiting until an object is added if the buffer is empty.
|
||||||
* added if the buffer is empty.
|
|
||||||
*
|
*
|
||||||
* @throws BufferUnderflowException if an interrupt is received
|
* @throws BufferUnderflowException if an interrupt is received
|
||||||
*/
|
*/
|
||||||
|
@ -150,8 +181,14 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
synchronized( lock ) {
|
synchronized( lock ) {
|
||||||
while( collection.isEmpty() ) {
|
while( collection.isEmpty() ) {
|
||||||
try {
|
try {
|
||||||
|
if( timeout <= 0 ) {
|
||||||
lock.wait();
|
lock.wait();
|
||||||
} catch (InterruptedException e) {
|
}
|
||||||
|
else {
|
||||||
|
return remove( timeout );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
PrintWriter out = new PrintWriter( new StringWriter() );
|
PrintWriter out = new PrintWriter( new StringWriter() );
|
||||||
e.printStackTrace( out );
|
e.printStackTrace( out );
|
||||||
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
||||||
|
@ -162,8 +199,8 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the next value from the buffer, waiting until an object is
|
* Removes the next value from the buffer, waiting until an object is added for up to the specified timeout value if
|
||||||
* added for up to the specified timeout value if the buffer is empty.
|
* the buffer is empty.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout value in milliseconds
|
* @param timeout the timeout value in milliseconds
|
||||||
* @throws BufferUnderflowException if an interrupt is received
|
* @throws BufferUnderflowException if an interrupt is received
|
||||||
|
@ -178,7 +215,8 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
try {
|
try {
|
||||||
lock.wait( timeLeft );
|
lock.wait( timeLeft );
|
||||||
timeLeft = expiration - System.currentTimeMillis();
|
timeLeft = expiration - System.currentTimeMillis();
|
||||||
} catch(InterruptedException e) {
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
PrintWriter out = new PrintWriter( new StringWriter() );
|
PrintWriter out = new PrintWriter( new StringWriter() );
|
||||||
e.printStackTrace( out );
|
e.printStackTrace( out );
|
||||||
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
throw new BufferUnderflowException( "Caused by InterruptedException: " + out.toString() );
|
||||||
|
@ -190,5 +228,4 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
return getBuffer().remove();
|
return getBuffer().remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,28 +15,25 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections.buffer;
|
package org.apache.commons.collections.buffer;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
import org.apache.commons.collections.AbstractTestObject;
|
||||||
|
import org.apache.commons.collections.Buffer;
|
||||||
|
import org.apache.commons.collections.BufferUnderflowException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import junit.framework.Test;
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
|
|
||||||
import org.apache.commons.collections.AbstractTestObject;
|
|
||||||
import org.apache.commons.collections.Buffer;
|
|
||||||
import org.apache.commons.collections.BufferUnderflowException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension of {@link TestObject} for exercising the {@link BlockingBuffer}
|
* Extension of {@link AbstractTestObject} for exercising the {@link BlockingBuffer} implementation.
|
||||||
* implementation.
|
|
||||||
*
|
|
||||||
* @since Commons Collections 3.0
|
|
||||||
* @version $Revision$
|
|
||||||
*
|
*
|
||||||
* @author Janek Bogucki
|
* @author Janek Bogucki
|
||||||
* @author Phil Steitz
|
* @author Phil Steitz
|
||||||
|
* @version $Revision$
|
||||||
|
* @since Commons Collections 3.0
|
||||||
*/
|
*/
|
||||||
public class TestBlockingBuffer extends AbstractTestObject {
|
public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
|
@ -62,74 +59,108 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add()}.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add(Object)}.
|
||||||
*/
|
*/
|
||||||
public void testGetWithAdd() {
|
public void testGetWithAdd() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
new DelayedAdd( blockingBuffer, obj ).start();
|
new DelayedAdd( blockingBuffer, obj ).start();
|
||||||
|
|
||||||
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
assertSame( obj, blockingBuffer.get() );
|
assertSame( obj, blockingBuffer.get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetWithAddTimeout() {
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer(), 500 );
|
||||||
|
Object obj = new Object();
|
||||||
|
new DelayedAdd( blockingBuffer, obj, 100 ).start();
|
||||||
|
|
||||||
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
|
assertSame( obj, blockingBuffer.get() );
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll()}.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}.
|
||||||
*/
|
*/
|
||||||
public void testGetWithAddAll() {
|
public void testGetWithAddAll() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
new DelayedAddAll( blockingBuffer, obj ).start();
|
new DelayedAddAll( blockingBuffer, obj ).start();
|
||||||
|
|
||||||
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
assertSame( obj, blockingBuffer.get() );
|
assertSame( obj, blockingBuffer.get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetWithAddAllTimeout() {
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer(), 500 );
|
||||||
|
Object obj = new Object();
|
||||||
|
new DelayedAddAll( blockingBuffer, obj, 100 ).start();
|
||||||
|
|
||||||
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
|
assertSame( obj, blockingBuffer.get() );
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add()}.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add(Object)}.
|
||||||
*/
|
*/
|
||||||
public void testRemoveWithAdd() {
|
public void testRemoveWithAdd() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
new DelayedAdd( blockingBuffer, obj ).start();
|
new DelayedAdd( blockingBuffer, obj ).start();
|
||||||
|
|
||||||
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
assertSame( obj, blockingBuffer.remove() );
|
assertSame( obj, blockingBuffer.remove() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRemoveWithAddTimeout() {
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer(), 100 );
|
||||||
|
Object obj = new Object();
|
||||||
|
new DelayedAdd( blockingBuffer, obj, 500 ).start();
|
||||||
|
try {
|
||||||
|
blockingBuffer.remove();
|
||||||
|
}
|
||||||
|
catch( BufferUnderflowException e ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()}.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}.
|
||||||
*/
|
*/
|
||||||
public void testRemoveWithAddAll() {
|
public void testRemoveWithAddAll() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
new DelayedAddAll( blockingBuffer, obj ).start();
|
new DelayedAddAll( blockingBuffer, obj ).start();
|
||||||
|
|
||||||
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
assertSame( obj, blockingBuffer.remove() );
|
assertSame( obj, blockingBuffer.remove() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRemoveWithAddAllTimeout() {
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer(), 100 );
|
||||||
|
Object obj = new Object();
|
||||||
|
new DelayedAddAll( blockingBuffer, obj, 500 ).start();
|
||||||
|
try {
|
||||||
|
blockingBuffer.remove();
|
||||||
|
}
|
||||||
|
catch( BufferUnderflowException e ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add()} using multiple read threads.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add(Object)} using multiple read
|
||||||
*
|
* threads.
|
||||||
* Two read threads should block on an empty buffer until one object
|
* <p/>
|
||||||
* is added then both threads should complete.
|
* Two read threads should block on an empty buffer until one object is added then both threads should complete.
|
||||||
*/
|
*/
|
||||||
public void testBlockedGetWithAdd() {
|
public void testBlockedGetWithAdd() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -149,19 +180,20 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
// There should not be any threads waiting.
|
// There should not be any threads waiting.
|
||||||
if (thread1.isAlive() || thread2.isAlive())
|
if( thread1.isAlive() || thread2.isAlive() ) {
|
||||||
fail( "Live thread(s) when both should be dead." );
|
fail( "Live thread(s) when both should be dead." );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)} using
|
||||||
*
|
* multiple read threads.
|
||||||
* Two read threads should block on an empty buffer until a
|
* <p/>
|
||||||
* singleton is added then both threads should complete.
|
* Two read threads should block on an empty buffer until a singleton is added then both threads should complete.
|
||||||
*/
|
*/
|
||||||
public void testBlockedGetWithAddAll() {
|
public void testBlockedGetWithAddAll() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -181,16 +213,17 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
// There should not be any threads waiting.
|
// There should not be any threads waiting.
|
||||||
if (thread1.isAlive() || thread2.isAlive())
|
if( thread1.isAlive() || thread2.isAlive() ) {
|
||||||
fail( "Live thread(s) when both should be dead." );
|
fail( "Live thread(s) when both should be dead." );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests interrupted {@link BlockingBuffer#get()}.
|
* Tests interrupted {@link BlockingBuffer#get()}.
|
||||||
*/
|
*/
|
||||||
public void testInterruptedGet() {
|
public void testInterruptedGet() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -204,10 +237,8 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// Chill, so thread can throw and add message to exceptionList
|
// Chill, so thread can throw and add message to exceptionList
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
assertTrue( "Thread interrupt should have led to underflow",
|
assertTrue( "Thread interrupt should have led to underflow",
|
||||||
exceptionList.contains( "BufferUnderFlow" ) );
|
exceptionList.contains( "BufferUnderFlow" ) );
|
||||||
|
|
||||||
if( thread.isAlive() ) {
|
if( thread.isAlive() ) {
|
||||||
fail( "Read thread has hung." );
|
fail( "Read thread has hung." );
|
||||||
}
|
}
|
||||||
|
@ -215,15 +246,15 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add()} using multiple read threads.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add(Object)} using multiple read
|
||||||
*
|
* threads.
|
||||||
* Two read threads should block on an empty buffer until one
|
* <p/>
|
||||||
* object is added then one thread should complete. The remaining
|
* Two read threads should block on an empty buffer until one object is added then one thread should complete. The
|
||||||
* thread should complete after the addition of a second object.
|
* remaining thread should complete after the addition of a second object.
|
||||||
*/
|
*/
|
||||||
public void testBlockedRemoveWithAdd() {
|
public void testBlockedRemoveWithAdd() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -235,7 +266,6 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// give hungry read threads ample time to hang
|
// give hungry read threads ample time to hang
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
blockingBuffer.add( obj );
|
blockingBuffer.add( obj );
|
||||||
|
|
||||||
// allow notified threads to complete
|
// allow notified threads to complete
|
||||||
|
@ -243,28 +273,27 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// There should be one thread waiting.
|
// There should be one thread waiting.
|
||||||
assertTrue( "There is one thread waiting", thread1.isAlive() ^ thread2.isAlive() );
|
assertTrue( "There is one thread waiting", thread1.isAlive() ^ thread2.isAlive() );
|
||||||
|
|
||||||
blockingBuffer.add( obj );
|
blockingBuffer.add( obj );
|
||||||
|
|
||||||
// allow notified thread to complete
|
// allow notified thread to complete
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
// There should not be any threads waiting.
|
// There should not be any threads waiting.
|
||||||
if(thread1.isAlive() || thread2.isAlive())
|
if( thread1.isAlive() || thread2.isAlive() ) {
|
||||||
fail( "Live thread(s) when both should be dead." );
|
fail( "Live thread(s) when both should be dead." );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}
|
||||||
*
|
* using multiple read threads.
|
||||||
* Two read threads should block on an empty buffer until a
|
* <p/>
|
||||||
* singleton collection is added then one thread should
|
* Two read threads should block on an empty buffer until a singleton collection is added then one thread should
|
||||||
* complete. The remaining thread should complete after the
|
* complete. The remaining thread should complete after the addition of a second singleton.
|
||||||
* addition of a second singleton.
|
|
||||||
*/
|
*/
|
||||||
public void testBlockedRemoveWithAddAll1() {
|
public void testBlockedRemoveWithAddAll1() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -276,7 +305,6 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// give hungry read threads ample time to hang
|
// give hungry read threads ample time to hang
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
blockingBuffer.addAll( Collections.singleton( obj ) );
|
blockingBuffer.addAll( Collections.singleton( obj ) );
|
||||||
|
|
||||||
// allow notified threads to complete
|
// allow notified threads to complete
|
||||||
|
@ -284,33 +312,30 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// There should be one thread waiting.
|
// There should be one thread waiting.
|
||||||
assertTrue( "There is one thread waiting", thread1.isAlive() ^ thread2.isAlive() );
|
assertTrue( "There is one thread waiting", thread1.isAlive() ^ thread2.isAlive() );
|
||||||
|
|
||||||
blockingBuffer.addAll( Collections.singleton( obj ) );
|
blockingBuffer.addAll( Collections.singleton( obj ) );
|
||||||
|
|
||||||
// allow notified thread to complete
|
// allow notified thread to complete
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
// There should not be any threads waiting.
|
// There should not be any threads waiting.
|
||||||
if(thread1.isAlive() || thread2.isAlive())
|
if( thread1.isAlive() || thread2.isAlive() ) {
|
||||||
fail( "Live thread(s) when both should be dead." );
|
fail( "Live thread(s) when both should be dead." );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}
|
||||||
*
|
* using multiple read threads.
|
||||||
* Two read threads should block on an empty buffer until a
|
* <p/>
|
||||||
* collection with two distinct objects is added then both
|
* Two read threads should block on an empty buffer until a collection with two distinct objects is added then both
|
||||||
* threads should complete. Each thread should have read a
|
* threads should complete. Each thread should have read a different object.
|
||||||
* different object.
|
|
||||||
*/
|
*/
|
||||||
public void testBlockedRemoveWithAddAll2() {
|
public void testBlockedRemoveWithAddAll2() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj1 = new Object();
|
Object obj1 = new Object();
|
||||||
Object obj2 = new Object();
|
Object obj2 = new Object();
|
||||||
|
|
||||||
Set objs = Collections.synchronizedSet( new HashSet() );
|
Set objs = Collections.synchronizedSet( new HashSet() );
|
||||||
objs.add( obj1 );
|
objs.add( obj1 );
|
||||||
objs.add( obj2 );
|
objs.add( obj2 );
|
||||||
|
@ -323,25 +348,24 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// give hungry read threads ample time to hang
|
// give hungry read threads ample time to hang
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
blockingBuffer.addAll( objs );
|
blockingBuffer.addAll( objs );
|
||||||
|
|
||||||
// allow notified threads to complete
|
// allow notified threads to complete
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
assertEquals( "Both objects were removed", 0, objs.size() );
|
assertEquals( "Both objects were removed", 0, objs.size() );
|
||||||
|
|
||||||
// There should not be any threads waiting.
|
// There should not be any threads waiting.
|
||||||
if(thread1.isAlive() || thread2.isAlive())
|
if( thread1.isAlive() || thread2.isAlive() ) {
|
||||||
fail( "Live thread(s) when both should be dead." );
|
fail( "Live thread(s) when both should be dead." );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests interrupted remove.
|
* Tests interrupted remove.
|
||||||
*/
|
*/
|
||||||
public void testInterruptedRemove() {
|
public void testInterruptedRemove() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
Buffer blockingBuffer = BlockingBuffer.decorate( new MyBuffer() );
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
|
@ -355,10 +379,8 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// Chill, so thread can throw and add message to exceptionList
|
// Chill, so thread can throw and add message to exceptionList
|
||||||
delay();
|
delay();
|
||||||
|
|
||||||
assertTrue( "Thread interrupt should have led to underflow",
|
assertTrue( "Thread interrupt should have led to underflow",
|
||||||
exceptionList.contains( "BufferUnderFlow" ) );
|
exceptionList.contains( "BufferUnderFlow" ) );
|
||||||
|
|
||||||
if( thread.isAlive() ) {
|
if( thread.isAlive() ) {
|
||||||
fail( "Read thread has hung." );
|
fail( "Read thread has hung." );
|
||||||
}
|
}
|
||||||
|
@ -384,11 +406,21 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
catch( BufferUnderflowException e ) {
|
catch( BufferUnderflowException e ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class DelayedAdd extends Thread {
|
protected static class DelayedAdd extends Thread {
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
|
long delay = 1000;
|
||||||
|
|
||||||
|
public DelayedAdd( Buffer buffer, Object obj, long delay ) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.obj = obj;
|
||||||
|
this.delay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
DelayedAdd( Buffer buffer, Object obj ) {
|
DelayedAdd( Buffer buffer, Object obj ) {
|
||||||
super();
|
super();
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
|
@ -396,13 +428,12 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// wait for other thread to block on get() or remove()
|
// wait for other thread to block on get() or remove()
|
||||||
Thread.sleep(100);
|
Thread.sleep( delay );
|
||||||
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
buffer.add( obj );
|
buffer.add( obj );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,8 +441,17 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
protected static class DelayedAddAll extends Thread {
|
protected static class DelayedAddAll extends Thread {
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
|
long delay = 100;
|
||||||
|
|
||||||
|
public DelayedAddAll( Buffer buffer, Object obj, long delay ) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.obj = obj;
|
||||||
|
this.delay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
DelayedAddAll( Buffer buffer, Object obj ) {
|
DelayedAddAll( Buffer buffer, Object obj ) {
|
||||||
super();
|
super();
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
|
@ -419,13 +459,12 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// wait for other thread to block on get() or remove()
|
// wait for other thread to block on get() or remove()
|
||||||
Thread.sleep(100);
|
Thread.sleep( delay );
|
||||||
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
buffer.addAll( Collections.singleton( obj ) );
|
buffer.addAll( Collections.singleton( obj ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,9 +472,13 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
protected static class ReadThread extends Thread {
|
protected static class ReadThread extends Thread {
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
ArrayList exceptionList = null;
|
ArrayList exceptionList = null;
|
||||||
|
|
||||||
String action = "get";
|
String action = "get";
|
||||||
|
|
||||||
Set objs;
|
Set objs;
|
||||||
|
|
||||||
ReadThread( Buffer buffer, Object obj ) {
|
ReadThread( Buffer buffer, Object obj ) {
|
||||||
|
@ -470,30 +513,35 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
try {
|
try {
|
||||||
if( action == "get" ) {
|
if( action == "get" ) {
|
||||||
assertSame( obj, buffer.get() );
|
assertSame( obj, buffer.get() );
|
||||||
} else {
|
}
|
||||||
if (null != obj)
|
else {
|
||||||
|
if( null != obj ) {
|
||||||
assertSame( obj, buffer.remove() );
|
assertSame( obj, buffer.remove() );
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
assertTrue( objs.remove( buffer.remove() ) );
|
assertTrue( objs.remove( buffer.remove() ) );
|
||||||
}
|
}
|
||||||
} catch (BufferUnderflowException ex) {
|
}
|
||||||
|
}
|
||||||
|
catch( BufferUnderflowException ex ) {
|
||||||
exceptionList.add( "BufferUnderFlow" );
|
exceptionList.add( "BufferUnderFlow" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class MyBuffer extends LinkedList implements Buffer {
|
protected static class MyBuffer extends LinkedList implements Buffer {
|
||||||
|
|
||||||
public Object get() {
|
public Object get() {
|
||||||
if(isEmpty())
|
if( isEmpty() ) {
|
||||||
throw new BufferUnderflowException();
|
throw new BufferUnderflowException();
|
||||||
|
}
|
||||||
return get( 0 );
|
return get( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object remove() {
|
public Object remove() {
|
||||||
if(isEmpty())
|
if( isEmpty() ) {
|
||||||
throw new BufferUnderflowException();
|
throw new BufferUnderflowException();
|
||||||
|
}
|
||||||
return remove( 0 );
|
return remove( 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,7 +549,9 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
private void delay() {
|
private void delay() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep( 100 );
|
Thread.sleep( 100 );
|
||||||
} catch (InterruptedException e) {}
|
}
|
||||||
|
catch( InterruptedException e ) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCompatibilityVersion() {
|
public String getCompatibilityVersion() {
|
||||||
|
@ -510,12 +560,13 @@ public class TestBlockingBuffer extends AbstractTestObject {
|
||||||
|
|
||||||
// public void testCreate() throws Exception {
|
// public void testCreate() throws Exception {
|
||||||
// Buffer buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
|
// Buffer buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
|
||||||
// writeExternalFormToDisk((java.io.Serializable) buffer, "D:/dev/collections/data/test/BlockingBuffer.emptyCollection.version3.1.obj");
|
// writeExternalFormToDisk((java.io.Serializable) buffer,
|
||||||
|
// "D:/dev/collections/data/test/BlockingBuffer.emptyCollection.version3.1.obj");
|
||||||
// buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
|
// buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
|
||||||
// buffer.add("A");
|
// buffer.add("A");
|
||||||
// buffer.add("B");
|
// buffer.add("B");
|
||||||
// buffer.add("C");
|
// buffer.add("C");
|
||||||
// writeExternalFormToDisk((java.io.Serializable) buffer, "D:/dev/collections/data/test/BlockingBuffer.fullCollection.version3.1.obj");
|
// writeExternalFormToDisk((java.io.Serializable) buffer,
|
||||||
|
// "D:/dev/collections/data/test/BlockingBuffer.fullCollection.version3.1.obj");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue