
stream.net.BufferedDataObjectStream Maven / Gradle / Ivy
/*
* streams library
*
* Copyright (C) 2011-2014 by Christian Bockermann, Hendrik Blom
*
* streams is a library, API and runtime environment for processing high
* volume data streams. It is composed of three submodules "stream-api",
* "stream-core" and "stream-runtime".
*
* The streams library (and its submodules) is free software: you can
* redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The stream.ai library (and its submodules) 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package stream.net;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ConnectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import stream.Data;
import stream.annotations.Parameter;
import stream.io.DataObjectStream;
import stream.io.SourceURL;
import stream.util.parser.TimeParser;
/**
* Adds a receive buffer to {@link DataObjectStream} for performance reasons and
* adds a reconnect functionality in case of an error.
*
* @author Hendrik Blom, Tobias Beckers
*
*/
public class BufferedDataObjectStream extends DataObjectStream {
private final Logger log = LoggerFactory
.getLogger(BufferedDataObjectStream.class);
// STREAMS-PARAMETERS
/**
* The buffer size (in bytes) used by this stream. If set to ≤ 0: The
* {@link BufferedInputStream}'s default buffer size is taken (usually 8192)
*/
protected int bufferSize;
/**
* the maximum amount of connection retries before the general connection
* attempt finally fails
*/
protected int connectionRetries;
/**
* the time between two connection attempts if the first connection attempt
* was unsuccessful
*/
protected long reconnectInterval;
// OTHER CLASS FIELDS
/** caches the return value of {@link #getConnectionString()} */
protected String urlCache;
protected boolean connected;
// CONSTRUCTORS
/** invokes {@link DataObjectStream#DataObjectStream(InputStream)} */
public BufferedDataObjectStream(InputStream in) {
super(in);
}
/** invokes {@link DataObjectStream#DataObjectStream(SourceURL)} */
public BufferedDataObjectStream(SourceURL url) {
super(url);
}
// GETTER AND SETTER FOR STREAMS-PARAMETERS
public int getBufferSize() {
return bufferSize;
}
@Parameter(required = false, defaultValue = "0", description = "The buffer size (in bytes) used by this stream. If set to <= 0: The BufferedInputStream's default buffer size is taken (usually 8192)")
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
public int getConnectionRetries() {
return connectionRetries;
}
@Parameter(required = false, defaultValue = "Integer.MAX_VALUE", description = "the maximum amount of connection retries before the general connection attempt finally fails")
public void setConnectionRetries(int connectionRetries) {
this.connectionRetries = connectionRetries;
}
public long getReconnectInterval() {
return reconnectInterval;
}
@Parameter(required = false, defaultValue = "5s", description = "the time between two connection attempts if the first connection attempt was unsuccessful")
public void setReconnectInterval(String reconnectIntervalString)
throws Exception {
this.reconnectInterval = TimeParser.parseTime(reconnectIntervalString);
}
/** {@inheritDoc} */
@Override
public void init() throws Exception {
input = null;
in = null;
bufferSize = 0;
connectionRetries = Integer.MAX_VALUE;
reconnectInterval = 5000;
urlCache = null;
connected = false;
}
/**
* Connects to the specified URL. Retries to connect up to
* {@link #connectionRetries} times if not successful.
*
* @throws ConnectException
* if it is not possible to connect to the target
*/
protected void connect() throws ConnectException {
// close old connection if there was one
close();
boolean connected = false;
int connectionRetryCounter = 0;
while (!connected && connectionRetryCounter <= connectionRetries) {
try {
log.info("Trying to open connection to {} ...",
getConnectionString());
if (connectionRetryCounter > 0)
log.info("Connection retry counter: {}",
connectionRetryCounter);
// get socket input stream
InputStream innerIn = getInputStream();
// add input buffer to input stream
innerIn = (bufferSize > 0) ? new BufferedInputStream(innerIn,
bufferSize) : new BufferedInputStream(innerIn);
// add deserialization stream
input = new ObjectInputStream(innerIn);
connected = true;
} catch (Exception e) {
log.warn("Unable to connect to {}: {}", getConnectionString(),
e.toString());
close();
try {
Thread.sleep(reconnectInterval);
} catch (InterruptedException ignore) {
}
connectionRetryCounter++;
}
}
if (connected)
log.info("Successfully connected to {}", getConnectionString());
else {
log.error(
"Giving up connection attempt after {} retries. Connection to {} unavailable.",
connectionRetryCounter - 1, getConnectionString());
close();
throw new ConnectException();
}
}
/**
*
* {@inheritDoc}
*
*
* See {@link BufferedDataObjectStream} for details.
*
*/
@Override
public Data readNext() throws ConnectException {
if (!connected) {
connect();
connected = true;
}
while (true) {
try {
return (Data) input.readObject();
} catch (Exception e) {
// EOFException and SocketException indicate a connection /
// socket error / network lost
// reconnect / reinitialize if any exception is thrown
log.error(
"Exception while reading data from socket stream {}: {}",
getConnectionString(), e.toString());
log.info("Trying to restart connection to {} ...",
getConnectionString());
connect();
}
}
}
/**
*
* Returns a string representing the connection url (works currently only
* with tcp connections).
*
*
* TODO Better: Implement and call SourceURL.toString() method
*
* @return a string representing the connection url
*/
protected String getConnectionString() {
if (urlCache == null) {
StringBuilder sb = new StringBuilder(url.getProtocol());
sb.append("://");
sb.append(url.getHost());
sb.append(":");
sb.append(url.getPort());
urlCache = sb.toString();
}
return urlCache;
}
/**
*
* Closes / Resets this connection such that all resources are freed and the
* next invocation of {@link #getInputStream()} will return a fresh new
* connection to the target.
*
*
*
* Does nothing if the connection is not open
*
*/
@Override
public void close() {
// stop if nothing exists to be closed
if (input == null && in == null)
return;
log.info("Closing connection to {} ...", getConnectionString());
try {
// free input stream resources
if (input != null)
input.close();
if (in != null)
in.close();
} catch (Exception e) {
log.warn("Exception while closing connection to {}: {}",
getConnectionString(), e.toString());
}
// reset input streams to null
input = null;
in = null;
log.info("Connection to {} closed.", getConnectionString());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy