Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting an InputStream to read more than once, regardless of markSupported()

I need to be able to re-use a java.io.InputStream multiple times, and I figured the following code would work, but it only works the first time.

Code


public class Clazz
{
  private java.io.InputStream dbInputStream, firstDBInputStream;
  private ArrayTable db;

  public Clazz(java.io.InputStream defDB)
  {
    this.firstDBInputStream = defDB;
    this.dbInputStream = defDB;
    if (db == null)
      throw new java.io.FileNotFoundException("Could not find the database at " + db);
    if (dbInputStream.markSupported())
      dbInputStream.mark(Integer.MAX_VALUE);
    loadDatabaseToArrayTable();
  }

  public final void loadDatabaseToArrayTable() throws java.io.IOException
  {
    this.dbInputStream = firstDBInputStream;
    if (dbInputStream.markSupported())
      dbInputStream.reset();

    java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
    String CSV = "";
    for (int i = 0; fileScanner.hasNextLine(); i++)
      CSV += fileScanner.nextLine() + "\n";
    db = ArrayTable.createArrayTableFromCSV(CSV);
  }

  public void reloadDatabase()//A method called by the UI
  {
    try
    {
      loadDatabaseToArrayTable();
    }
    catch (Throwable t)
    {
      //Alert the user that an error has occurred
    }
  }
}

Note that ArrayTable is a class of mine, which uses arrays to give an interface for working with tables.

Question


In this program, the database is shown directly to the user immediately after the reloadDatabase() method is called, and so any solution involving saving the initial read to an object in memory is useless, as that will NOT refresh the data (think of it like a browser; when you press "Refresh", you want it to fetch the information again, not just display the information it fetched the first time). How can I read a java.io.InputStream more than once?

like image 331
Ky. Avatar asked Oct 11 '11 21:10

Ky.


People also ask

Can you read an InputStream twice?

Don't read it twice. Save the result in a data structure.

How does InputStream read () method work?

read() reads next byte of data from the Input Stream. The value byte is returned in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned.

What happens if you don't close an InputStream?

resource-leak is probably more of a concern here. Handling inputstream requires OS to use its resources and if you don't free it up once you use it, you will eventually run out of resources.

What is the difference between InputStream and FileInputStream?

There is no real difference. FileInputStream extends InputStream , and so you can assign an InputStream object to be a FileInputStream object. In the end, it's the same object, so the same operations will happen. This behavior is called Polymorphism and is very important in Object-Oriented Programming.


2 Answers

You can't necessarily read an InputStream more than once. Some implementations support it, some don't. What you are doing is checking the markSupported method, which is indeed an indicator if you can read the same stream twice, but then you are ignoring the result. You have to call that method to see if you can read the stream twice, and if you can't, make other arrangements.

Edit (in response to comment): When I wrote my answer, my "other arrangements" was to get a fresh InputStream. However, when I read in your comments to your question about what you want to do, I'm not sure it is possible. For the basics of the operation, you probably want RandomAccessFile (at least that would be my first guess, and if it worked, that would be the easiest) - however you will have file access issues. You have an application actively writing to a file, and another reading that file, you will have problems - exactly which problems will depend on the OS, so whatever solution would require more testing. I suggest a separate question on SO that hits on that point, and someone who has tried that out can perhaps give you more insight.

like image 176
Yishai Avatar answered Oct 08 '22 10:10

Yishai


you never mark the stream to be reset

public Clazz(java.io.InputStream defDB)
  {
    firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
      //BufferedInputStream supports marking
    firstDBInputStream.mark(500000);//avoid IOException on first reset
  }

public final void loadDatabaseToArrayTable() throws java.io.IOException
  {
    this.dbInputStream = firstDBInputStream;

    dbInputStream.reset();
    dbInputStream.mark(500000);//or however long the data is

    java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
    StringBuilder CSV = "";//StringBuilder is more efficient in a loop
    while(fileScanner.hasNextLine())
      CSV.append(fileScanner.nextLine()).append("\n");
    db = ArrayTable.createArrayTableFromCSV(CSV.toString());
  }

however you could instead keep a copy of the original ArrayTable and copy that when you need to (or even the created string to rebuild it)

this code creates the string and caches it so you can safely discard the inputstreams and just use readCSV to build the ArrayTable

  private String readCSV=null;
  public final void loadDatabaseToArrayTable() throws java.io.IOException
  {
    if(readCSV==null){

        this.dbInputStream = firstDBInputStream;


        java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
        StringBuilder CSV = "";//StringBuilder is more efficient in a loop
        while(fileScanner.hasNextLine())
          CSV.append(fileScanner.nextLine()).append("\n");

        readCSV=CSV.toString();
        fileScanner.close();

    }
    db = ArrayTable.createArrayTableFromCSV(readCSV);
  }

however if you want new information you'll need to create a new stream to read from again

like image 27
ratchet freak Avatar answered Oct 08 '22 09:10

ratchet freak