I am getting the following StringIndexOutOfBoundsException:
Caused by: java.lang.StringIndexOutOfBoundsException at java.lang.AbstractStringBuilder.delete(AbstractStringBuilder.java:702) at java.lang.StringBuilder.delete(StringBuilder.java:255) at org.supercsv.encoder.DefaultCsvEncoder.encode(DefaultCsvEncoder.java:42) at org.supercsv.io.AbstractCsvWriter.escapeString(AbstractCsvWriter.java:102) at org.supercsv.io.AbstractCsvWriter.writeRow(AbstractCsvWriter.java:196) at org.supercsv.io.AbstractCsvWriter.writeRow(AbstractCsvWriter.java:162) at org.supercsv.io.CsvListWriter.write(CsvListWriter.java:79)
This is occurring when I am running multiple instances of the CsvListWriter concurrently. Here's what I think is happening:
CsvPreference is constructed with:
CsvPreference pref = new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE) .useQuoteMode(new ColumnQuoteMode(1,2,3,6)).build();
Based on CsvPreference, the CsvEncoder is coming from CsvPreference.STANDARD_PREFERENCE. But CsvPreference.STANDARD_PREFERENCE is static, which means all instances of CsvPreference are sharing the same CsvEncoder instance.
DefaultCsvEncoder re-uses its instance of StringBuilder. But when there are multiple threads, there might be concurrent calls to DefaultCsvEncoder.encode(). While probably resulting in gobly-gook, it can also lead to a StringIndexOutofBoundsException for the following line:
currentColumn.delete(0, currentColumn.length()); // reusing builder object
Well spotted. Super CSV is not really intended to be used multithreaded, so I hadn't even thought about that! The only workaround is to create a new preferences object for each thread - you could build each one from an existing preference, and just pass in a new instance of
DefaultCsvEncoder
via theuseEncoder()
method.I'll have to have a think about this one...
I had a very similar problem in a multi-threaded scenario running with CsvMapWriter. The symptom this time is an ArrayIndexOutOfBoundsException.
ArrayIndexOutOfBoundsException: -4
at java.lang.StringBuilder.append(StringBuilder.java:203)
at org.supercsv.encoder.DefaultCsvEncoder.encode(DefaultCsvEncoder.java:81)
I'll see if I can code the workaround you (James) suggested and let you know if that works.
Last edit: Stuart Rossiter 2013-06-06
I can confirm that I could bypass the problems by creating a CsvPreference instance per thread, each with its own DefaultCsvEncoder.
Stuart,
Even am facing the same issue , could you please share your code sample?
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter outputwriter = new OutputStreamWriter(baos);
ICsvBeanWriter beanWriter = null;
try {
beanWriter = new CsvBeanWriter(outputwriter,CsvPreference.STANDARD_PREFERENCE);
Last edit: FAZ 2014-02-14
Hi James, am trying something below but i think there is some obvious mistake, could you pls help me here.
CsvPreference cp = CsvPreference.STANDARD_PREFERENCE;
System.out.println("cp : "+cp);
System.out.println("cp encoder : "+cp.getEncoder());
Builder d = new Builder(cp);
d.useEncoder(new DefaultCsvEncoder());
beanWriter = new CsvBeanWriter(outputwriter,cp);
System.out.println("Builder : "+d);
System.out.println("beanWriter : "+beanWriter);
System.out.println("cp after: "+cp);
System.out.println("cp encoder after: "+cp.getEncoder());
thanks
Hi Faz,
You aren't actually using the new preferences you've created.
You'll need to change the following line
to
Remember you can create your new preferences in one go:
Thanks a lot James, the second option that you mentioned was indeed what I
tried and it worked,
On Sat, Feb 15, 2014 at 2:31 AM, James Bassett jamesbassett@users.sf.netwrote:
Related
Bugs:
#43I also fell for that problem. The useEncoder() workaround works for me.
As a bugfix, why not simply create a new StringBuilder for every method call? The performance penalty must be quite minimal, no?
Hi David,
Yeah Kasper is keen on optimizing, and I've tried to keep that idea in mind. But in this case I think you're right and we should take a small hit to make it more thread-safe. It was probably a bad idea to have an enum (preference) have an instance of a non-thread-safe encoder....things you don't think of until people start using the library in ways you didn't think of!
Fixed in [r303]
Related
Commit: [r303]
Just wondering if this could potentially also be an issue in org.supercsv.io.Tokenizer with multiple instances of CsvListReader reusing the same tokenizer. I see that in Tokenizer.java, we are reusing a StringBuilder called currentColumn. If this was executed concurrently,
currentColumn.setLength(0);
in readColumns() might screw things up?
Last edit: Karthik Kumar 2014-08-27