/*
Copyright (c) 2007 Health Market Science, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
You can contact Health Market Science at info@healthmarketscience.com
or at the following address:
Health Market Science
2700 Horizon Drive
Suite 200
King of Prussia, PA 19406
*/
package com.healthmarketscience.rmiio;
import java.io.IOException;
import java.io.OutputStream;
import com.healthmarketscience.rmiio.util.EncodingInputStream;
/**
* Base class for implementing the server side of a RemoteIterator where the
* objects need to be encoded on the fly. This object sends objects to the
* RemoteIteratorClient through the internal RemoteInputStreamServer as
* needed. Implementations of this class must implement the writeNextObject()
* method, which should write the next object in the iteration to the local
* output stream (which in turn will forward to data to the
* RemoteIteratorClient). An Implementation should override closeIterator()
* to do cleanup (like flushing and closing the output stream) after the last
* object has been sent. The local output stream must <b>only</b> be written
* during a call to writeNextObject() or closeIterator(). Note, users of this
* class should ensure that the close() method is called one way or another,
* or shutdown of the process may be delayed.
* <p>
* Note, a real-time-ish iterator is available if compression is disabled and
* noDelay is enabled (noDelay is ignored if compression is enabled). This
* will attempt to send objects across the wire ASAP, at the expense of more
* wire traffic.
*
* @author James Ahlborn
*/
public abstract class EncodingRemoteIteratorServer<DataType>
extends RemoteIteratorServer<DataType>
{
/** the OutputStream which subclasses should use to write out an object
during a call to writeNextObject() (and possibly closeIterator()). */
protected OutputStream _localOStream;
public EncodingRemoteIteratorServer()
throws IOException
{
this(true, RemoteInputStreamServer.DUMMY_MONITOR);
}
public EncodingRemoteIteratorServer(boolean useCompression)
throws IOException
{
this(useCompression, false, RemoteInputStreamServer.DUMMY_MONITOR);
}
public EncodingRemoteIteratorServer(boolean useCompression,
boolean noDelay)
throws IOException
{
this(useCompression, noDelay, RemoteInputStreamServer.DUMMY_MONITOR);
}
public EncodingRemoteIteratorServer(
boolean useCompression,
RemoteStreamMonitor<RemoteInputStreamServer> monitor)
throws IOException
{
this(useCompression, false, monitor);
}
public EncodingRemoteIteratorServer(
boolean useCompression,
boolean noDelay,
RemoteStreamMonitor<RemoteInputStreamServer> monitor)
throws IOException
{
this(useCompression, noDelay, monitor,
RemoteInputStreamServer.DEFAULT_CHUNK_SIZE);
}
public EncodingRemoteIteratorServer(
boolean useCompression,
boolean noDelay,
RemoteStreamMonitor<RemoteInputStreamServer> monitor,
int chunkSize)
throws IOException
{
// note, noDelay makes no sense if we are using compression, so just
// disable in that case (this is not techically incorrect, compression
// just overrides noDelay)
super(new EncodingInputStreamImpl(chunkSize, (noDelay && !useCompression)),
useCompression, monitor, chunkSize);
((EncodingInputStreamImpl)_localIStream).setOuter(this);
_localOStream = ((EncodingInputStreamImpl)_localIStream).getOutputStream();
}
/**
* Closes any resources held by this iterator. Subclasses should
* flush/close OutputStream during this call.
*/
protected void closeIterator()
throws IOException
{
_localOStream.close();
}
/**
* If there are more objects in the iteration, writes an object to the
* _localOStream and returns <code>true</code>, otherwise returns
* <code>false</code>.
*/
protected abstract boolean writeNextObject()
throws IOException;
/**
* InputStream which lies under the RemoteInputStream and "reads" objects as
* necessary when data is requested by the client. the _localOStream used
* by the implementation of the EncodingRemoteIteratorServer actually
* forwards data to this class, which fills a buffer passed in during a
* read() call (and possibly an overflow buffer if necessary).
*/
private static class EncodingInputStreamImpl extends EncodingInputStream
{
/** because of the way things are created, this class cannot be an inner
class. instead, we get a reference to our outer class after initial
construction. */
private EncodingRemoteIteratorServer _outer;
private EncodingInputStreamImpl(int chunkSize, boolean noDelay) {
super(chunkSize, noDelay);
}
private void setOuter(EncodingRemoteIteratorServer outer) {
_outer = outer;
}
private OutputStream getOutputStream() {
return createOutputStream();
}
@Override
public void encode(int suggestedLength)
throws IOException
{
if(!_outer.writeNextObject()) {
// this call should close the OutputStream
_outer.closeIterator();
}
}
}
}