diff --git a/src/main/java/ch/eitchnet/utils/io/FileProgressListener.java b/src/main/java/ch/eitchnet/utils/io/FileProgressListener.java
new file mode 100644
index 000000000..b22649b00
--- /dev/null
+++ b/src/main/java/ch/eitchnet/utils/io/FileProgressListener.java
@@ -0,0 +1,41 @@
+package ch.eitchnet.utils.io;
+
+/**
+ *
+ * This interface defines an API for use in situations where long running jobs notify observers of the jobs status. The
+ * jobs has a size which is a primitive long value e.g. the number of bytes parsed/ to be parsed in a file
+ *
+ *
+ * @author Robert von Burg
+ */
+public interface FileProgressListener {
+
+ /**
+ * Notify the listener that the progress has begun
+ *
+ * @param size
+ * the size of the job which is to be accomplished
+ */
+ public void begin(long size);
+
+ /**
+ * Notifies the listener of incremental progress
+ *
+ * @param percent
+ * percent completed
+ * @param position
+ * the position relative to the job size
+ */
+ public void progress(int percent, long position);
+
+ /**
+ * Notifies the listener that the progress is completed
+ *
+ * @param percent
+ * the percent completed. Ideally the value would be 100, but in cases of errors it can be less
+ * @param position
+ * the position where the job finished. Ideally the value would be the same as the size given at
+ * {@link #begin(long)} but in case of errors it can be different
+ */
+ public void end(int percent, long position);
+}
diff --git a/src/main/java/ch/eitchnet/utils/io/FileStreamProgressWatcher.java b/src/main/java/ch/eitchnet/utils/io/FileStreamProgressWatcher.java
new file mode 100644
index 000000000..9ffc13a46
--- /dev/null
+++ b/src/main/java/ch/eitchnet/utils/io/FileStreamProgressWatcher.java
@@ -0,0 +1,81 @@
+package ch.eitchnet.utils.io;
+
+import java.text.MessageFormat;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * File stream progress monitoring thread
+ *
+ * @author Robert von Burg
+ */
+public class FileStreamProgressWatcher implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(FileStreamProgressWatcher.class);
+ private ProgressableFileInputStream inputStream;
+ private boolean run = false;
+ private FileProgressListener progressListener;
+ private long millis;
+
+ /**
+ * @param millis
+ * @param progressListener
+ * @param inputStream
+ */
+ public FileStreamProgressWatcher(long millis, FileProgressListener progressListener,
+ ProgressableFileInputStream inputStream) {
+ this.millis = millis;
+ this.progressListener = progressListener;
+ this.inputStream = inputStream;
+ }
+
+ @Override
+ public void run() {
+ this.run = true;
+
+ this.progressListener.begin(this.inputStream.getFileSize());
+
+ while (this.run) {
+ try {
+
+ int percentComplete = this.inputStream.getPercentComplete();
+
+ if (this.inputStream.isClosed()) {
+ logger.info(MessageFormat.format("Input Stream is closed at: {0}%", percentComplete)); //$NON-NLS-1$
+
+ this.run = false;
+ this.progressListener.end(percentComplete, this.inputStream.getBytesRead());
+
+ } else if (percentComplete < 100) {
+
+ this.progressListener.progress(percentComplete, this.inputStream.getBytesRead());
+
+ } else if (percentComplete >= 100) {
+
+ this.run = false;
+ this.progressListener.end(percentComplete, this.inputStream.getBytesRead());
+
+ }
+
+ if (this.run) {
+ Thread.sleep(this.millis);
+ }
+
+ } catch (InterruptedException e) {
+
+ logger.info(MessageFormat.format("Work stopped: {0}", e.getLocalizedMessage())); //$NON-NLS-1$
+ this.run = false;
+ int percentComplete = this.inputStream.getPercentComplete();
+ this.progressListener.end(percentComplete, this.inputStream.getBytesRead());
+
+ } catch (Exception e) {
+
+ logger.error(e.getMessage(), e);
+ this.run = false;
+ int percentComplete = this.inputStream.getPercentComplete();
+ this.progressListener.end(percentComplete, Long.MAX_VALUE);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ch/eitchnet/utils/io/LoggingFileProgressListener.java b/src/main/java/ch/eitchnet/utils/io/LoggingFileProgressListener.java
new file mode 100644
index 000000000..4cafa25fd
--- /dev/null
+++ b/src/main/java/ch/eitchnet/utils/io/LoggingFileProgressListener.java
@@ -0,0 +1,43 @@
+package ch.eitchnet.utils.io;
+
+import java.text.MessageFormat;
+
+import org.slf4j.Logger;
+
+import ch.eitchnet.utils.helper.FileHelper;
+
+/**
+ * @author Robert von Burg
+ */
+public class LoggingFileProgressListener implements FileProgressListener {
+
+ private final Logger logger;
+ private final String name;
+
+ /**
+ * @param logger
+ * @param name
+ */
+ public LoggingFileProgressListener(Logger logger, String name) {
+ this.logger = logger;
+ this.name = name;
+ }
+
+ @Override
+ public void begin(long size) {
+ String msg = "Starting to read {0} with a size of {1}"; //$NON-NLS-1$
+ this.logger.info(MessageFormat.format(msg, this.name, FileHelper.humanizeFileSize(size)));
+ }
+
+ @Override
+ public void progress(int percent, long position) {
+ String msg = "Read {0}% of {1} at position {2}"; //$NON-NLS-1$
+ this.logger.info(MessageFormat.format(msg, percent, this.name, FileHelper.humanizeFileSize(position)));
+ }
+
+ @Override
+ public void end(int percent, long position) {
+ String msg = "Finished reading {0} at position {1} ({2}%)"; //$NON-NLS-1$
+ this.logger.info(MessageFormat.format(msg, this.name, percent, FileHelper.humanizeFileSize(position)));
+ }
+}
diff --git a/src/main/java/ch/eitchnet/utils/io/ProgressableFileInputStream.java b/src/main/java/ch/eitchnet/utils/io/ProgressableFileInputStream.java
new file mode 100644
index 000000000..94890cc72
--- /dev/null
+++ b/src/main/java/ch/eitchnet/utils/io/ProgressableFileInputStream.java
@@ -0,0 +1,146 @@
+package ch.eitchnet.utils.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ *
+ * This sub class of {@link FileInputStream} allows to follow the currently read bytes of a {@link File}. In conjunction
+ * with a {@link Thread} and a {@link FileProgressListener} it is possible to track the progress of a long running job on
+ * bigger files
+ *
+ *
+ * @author Robert von Burg
+ */
+public class ProgressableFileInputStream extends FileInputStream {
+
+ private long fileSize;
+ private long bytesRead;
+ private boolean closed;
+
+ /**
+ * Constructs a normal {@link FileInputStream} with the given {@link File}
+ *
+ * @param file
+ * the file to read
+ * @throws FileNotFoundException
+ * thrown if the {@link File} does not exist
+ */
+ public ProgressableFileInputStream(File file) throws FileNotFoundException {
+ super(file);
+ this.fileSize = file.length();
+ }
+
+ /**
+ * @see java.io.FileInputStream#read()
+ */
+ @Override
+ public int read() throws IOException {
+ synchronized (this) {
+ this.bytesRead++;
+ }
+ return super.read();
+ }
+
+ /**
+ * @see java.io.FileInputStream#read(byte[], int, int)
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int read = super.read(b, off, len);
+ if (read != -1) {
+ synchronized (this) {
+ this.bytesRead += read;
+ }
+ }
+ return read;
+ }
+
+ /**
+ * @see java.io.FileInputStream#read(byte[])
+ */
+ @Override
+ public int read(byte[] b) throws IOException {
+ int read = super.read(b);
+ if (read != -1) {
+ synchronized (this) {
+ this.bytesRead += read;
+ }
+ }
+ return read;
+ }
+
+ /**
+ * @see java.io.FileInputStream#skip(long)
+ */
+ @Override
+ public long skip(long n) throws IOException {
+ long skip = super.skip(n);
+ if (skip != -1) {
+ synchronized (this) {
+ this.bytesRead += skip;
+ }
+ }
+ return skip;
+ }
+
+ /**
+ * @see java.io.FileInputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ this.closed = true;
+ super.close();
+ }
+
+ /**
+ * Returns the size of the file being read
+ *
+ * @return the size of the file being read
+ */
+ public long getFileSize() {
+ return this.fileSize;
+ }
+
+ /**
+ * Returns the number of bytes already read
+ *
+ * @return the number of bytes already read
+ */
+ public long getBytesRead() {
+ synchronized (this) {
+ if (this.bytesRead > this.fileSize)
+ this.bytesRead = this.fileSize;
+ return this.bytesRead;
+ }
+ }
+
+ /**
+ * Returns the percent read of the file
+ *
+ * @return the percentage complete of the process
+ */
+ public int getPercentComplete() {
+
+ long currentRead;
+ synchronized (this) {
+ if (this.bytesRead > this.fileSize)
+ this.bytesRead = this.fileSize;
+ currentRead = this.bytesRead;
+ }
+
+ double read = (100.0d / this.fileSize * currentRead);
+ return (int) read;
+ }
+
+ /**
+ * Returns true if {@link #close()} was called, false otherwise
+ *
+ * @return the closed
+ */
+ public boolean isClosed() {
+ return this.closed;
+ }
+}