For a element of code we have to write, a Buffer of Strings needs to be created. There is basically one Thread reading files & parsing them (Dom-elements as String). On another side there are Threads reading from the "buffer". The current implementation seems fine : Please comment in case you find issues. I tested the code this afternoon & it doesn't deadlock neither doesn't it seem to be slow ! Starving or queuing under some regular condition (Poper too fasts/slow or Pushers ...) but I would like to prove it with unit tests...
Here comes the problem : How can I test the close() more than once ?
I currently introduced in the code some methods that I don't like to see in there... Namely :
void reOpen ()
{
log.info ("!!! !!! !!! void reOpen () !!! !!! !!!");
isClosed = false;
}
void flush ()
throws IOException
{
if (isClosed)
{
String msg = "Holder was closed";
log.warn (msg);
//throw new IOException (msg);
}
in = null;
out = null;
}
I definitively don't see how I could do that differently than introduce those methods that allow me to manipulate the internals of the instance while not opening those too much to the outside world.
Tx for your Help !
\T,
___________________________________
/*
* Created on 14-Aug-2003
*
*/
package com.tls.util.common;
import java.io.IOException;
import org.apache.log4j.Logger;
import com.tls.util.Helper;
/**
*
*
* @author Thomas SMETS
*
*/
publicclass StringBufferer
{
privatestaticfinal Logger log = Logger.getLogger (StringBufferer.class);
publicstaticfinal String ELEMENTS_STACK_SIZE = "ELEMENTS_STACK_SIZE";
publicstaticfinalint DEFAULT_STACK_SIZE = 4096;
publicstaticfinalint START_IN = -1;
publicstaticfinalint START_OUT = -1;
/**
* The fact that we create the <code>instance</code> reference makes it
* Thread-safe inherently.
*/
privatestaticfinal StringBufferer instance = new StringBufferer ();
private String [] in = null,
out = null;
private Object inLock = new Object (),
outLock = new Object ();
privateint stackSize = DEFAULT_STACK_SIZE;
privateint stackSizeLimit = DEFAULT_STACK_SIZE - 1;
privateint inPos = START_IN,
outPos = stackSizeLimit;
privateboolean isClosed = false;
/**
* Hidden constructor
*/
StringBufferer ()
{
log.info ("Created");
String property = System.getProperty(ELEMENTS_STACK_SIZE);
try
{
stackSize = Integer.parseInt( property );
} catch (NumberFormatException nfe)
{
log.warn ("Number format Exception. Property value was : " + property, nfe);
stackSize = DEFAULT_STACK_SIZE;
}
stackSizeLimit = stackSize - 1 ;
outPos = stackSizeLimit;
}
/**
* Singelton thread safe.
*
* @return
*/
publicstatic StringBufferer getInstance ()
{
return instance;
}
/**
*
*
* @param aDomeElementAsString
*/
publicvoid put (String aDomeElementAsString)
throws IOException
{
if (aDomeElementAsString==null)
return;
if (isClosed)
{
log.error ("Stack closed...");
thrownew IOException ("Stack closed");
}
log.debug ("Putting : " + aDomeElementAsString);
while (inPos==stackSizeLimit)
{
checkSwapNeeded();
synchronized (inLock)
{
try
{
log.debug ("Waiting that some free space is made available");
inLock.wait ();
log.debug ("Woken up ! There might be some free space");
} catch (InterruptedException ie)
{
log.info ( "Thread has been interrupted. Message is " + ie.getMessage(),
ie );
}
}
}
if (in == null)
in = new String[stackSize];
in[++inPos] = aDomeElementAsString;
checkSwapNeeded ();
}
/**
*
*
* @return the String
*/
public String get ()
{
log.debug ("Retrieving the next element");
while ( out==null | outPos == stackSizeLimit)
{
try
{
synchronized (inLock)
{
log.debug( "About to starve... I'll wake up a few waiting on the inLock" );
inLock.notifyAll ();
}
synchronized (outLock)
{
log.debug( "Starving" );
outLock.wait (100);
}
} catch (InterruptedException ie)
{
log.warn ( "Somebody interrupted during my sleep. Message : " + ie.getMessage(),
ie);
}
}
return (out[++outPos]);
}
/**
* This method estimates if a <code>swap</code> between the <code>in</code>
* & the <code>out</code> may be needed
*
*
*/
privatevoid checkSwapNeeded ()
{
if (inPos==stackSizeLimit & outPos==stackSizeLimit)
swap ();
}
privatevoid swap ()
{
synchronized (outLock)
{
log.debug ("Swapping");
out = in;
inPos = START_IN;
outPos = START_OUT;
log.debug ("outLock.notifyAll()");
outLock.notifyAll();
}
synchronized (inLock)
{
log.debug ("inLock.notifyAll()");
inLock.notifyAll();
}
}
/**
* This is to be invoked whenever the Reader has finished to <code>put ()</code>
* element on the stack.
*/
publicvoid close ()
{
log.info ("Closing the DOMHolder");
isClosed = true;
}
void reOpen ()
{
log.info ("!!! !!! !!! void reOpen () !!! !!! !!!");
isClosed = false;
}
/**
*
*
*/
void flush ()
throws IOException
{
if (isClosed)
{
String msg = "Holder was closed";
log.warn (msg);
//throw new IOException (msg);
}
in = null;
out = null;
}
/**
* @see Object#toString
*/
public String toString ()
{
StringBuffer buf = new StringBuffer ( 2 * stackSize * 50);
String[] inCopy = new String[stackSize],
outCopy = new String[stackSize];
int inSize = 0,
outSize = 0;
Helper hlpr = Helper.getInstance();
synchronized (inLock)
{
System.arraycopy(in, 0, inCopy, 0, inSize = inPos);
}
synchronized (outLock)
{
System.arraycopy(out, 0, outCopy, 0, outSize = outPos);
}
int i = 1;
buf.append("in = ")
.append (hlpr.showArray(inCopy))
.append (Helper.LINE_SEPARATOR)
.append ("out = ")
.append (hlpr.showArray(outCopy))
.append (Helper.LINE_SEPARATOR);
return buf.toString ();
}
/**
* @return
*/
publicint getStackSize ()
{
return stackSize;
}
}