Aspose.words for thread-safe?

Hello:

I didn’t get the answer I was looking for in the same titled post earlier:

I have two issues; Please take a look at the attached test code:

I start 10 threads and each thread does two things:

  1. sets license
  2. loads a document

The test case can be run with two run-time parameters -l and -d

Issue # 1: I get following error:

java.util.concurrent.ExecutionException: com.aspose.words.FileCorruptedException: The document appears to be corrupted and cannot be loaded.
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.blox.workflow.util.TestConcurrentDocLoading.testAddElement(TestConcurrentDocLoading.java:116)
at com.blox.workflow.util.TestConcurrentDocLoading.main(TestConcurrentDocLoading.java:56)
Caused by: com.aspose.words.FileCorruptedException: The document appears to be corrupted and cannot be loaded.
at com.aspose.words.Document.a(Document.java:1376)
at com.aspose.words.Document.b(Document.java:1363)
at com.aspose.words.Document.a(Document.java:1251)
at com.aspose.words.Document.(Document.java:248)
at com.aspose.words.Document.(Document.java:232)
at com.aspose.words.Document.(Document.java:227)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:35)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED
at java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:951)
at java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:640)
at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:769)
at asposewobfuscated.mg.U(Encoding.java:460)
at com.aspose.words.gj.j(PBString.java:38)
at com.aspose.words.iw.m(FileSmartTags.java:142)
at com.aspose.words.iw.c(FileSmartTags.java:99)
at com.aspose.words.iw.(FileSmartTags.java:42)
at com.aspose.words.acl.a(DocReaderCore.java:240)
at com.aspose.words.acl.(DocReaderCore.java:28)
at com.aspose.words.ck.(DocReader.java:32)
at com.aspose.words.Document.b(Document.java:1279)
… 14 more

In an answer to the earlier thread, it was suggested that one can instantiate a Document object
“only if you have different ‘path’ value for each thread. You get one document instance per one thread in such a case.”

This is not possible in my case. I have a template document which all concurrent threads need to load and each will create a separate output by substituting different values in the mail-merge variables.

Issue # 2:
Is there a problem executing License.setLicense() in individual threads. I have occasionally seen 'ConcurrentModificationException’

java.util.concurrent.ExecutionException: java.util.ConcurrentModificationException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.blox.workflow.util.TestConcurrentDocLoading.testAddElement(TestConcurrentDocLoading.java:116)
at com.blox.workflow.util.TestConcurrentDocLoading.main(TestConcurrentDocLoading.java:56)
Caused by: java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.aspose.words.da.c(License.java:382)
at com.aspose.words.License.setLicense(License.java:293)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:31)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

Appreciate your help.
Parag

Hi

Thanks for your request. Regarding your second issue, it is not good and not recommended to set license few times per application domain. You should apply the license only once per application domain. For example, you can do that in static constructor of the class, which is used to process your documents.

https://docs.aspose.com/words/net/licensing/

You can read the template document into InputStream and lock this stream while Aspose.Words opens it.

Best regards.

Thanks for your quick response.
I agree with the FAQ that setting license multiple time is wasteful. But as shown by the test, it is certainly not harmless. So it would be good to declare that clearly in the FAQ.

Synchronizing on a shared resource is a good suggestion, but it may not work. In the test case I have attached:

If I synchronize on the document path, before creating a stream, everything works great

// Now load the input document
synchronized(_inputDoc) {
    FileInputStream inputDocStream = new FileInputStream(_inputDoc);
    Document doc = new Document(inputDocStream);
    inputDocStream.close();
}

but if I synchronize on the template document’s stream - it’s not a shared resource and has no effect on the error.

// Now load the input document

FileInputStream inputDocStream = new FileInputStream(_inputDoc);
synchronized(inputDocStream) {
    Document doc = new Document(inputDocStream);
    inputDocStream.close();
}

And in my case, I won’t be able to synchronize on the template name as it would make the entire functionality serialized. Any other ideas?

FWIW: iText works absolutely great when instantiated with a shared resource stream in exactly similar condition (when the generated document is a PDF, of course).

Thanks
Parag

Both loading a document from a file on multiple threads at the same time and setting a license on multiple threads should work fine. I am sure this works in Aspose.Words for .NET because we have tests for this. Maybe we have overlooked or not tested enough for this in Aspose.Words for Java. Alexey/Andrey please try to reproduce and log accordingly. We will fix asap if these are reproducible.

Hi

Thank you for additional information. I managed to reproduce the problem on my side. Your request has been linked to the appropriate issue. You will be notified as soon as the problems are resolved.

Best regards.

Hi Parag,

Thanks for your test units – we have reproduced and localized both bugs. The fix will be published within the next release of Aspose.Words for Java. But I just e-mailed to you the fixed jar so you can check it on your side. Please, post here how it works for you.

Best Regards,

Thanks for the Jar. I don’t think the issue is fixed. I ran the same test case multiple times and got the same error at least once. Here’s the stack trace.

java.util.concurrent.ExecutionException: com.aspose.words.FileCorruptedException: The document appears to be corrupted and cannot be loaded.
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.blox.workflow.util.TestConcurrentDocLoading.testAddElement(TestConcurrentDocLoading.java:117)
at com.blox.workflow.util.TestConcurrentDocLoading.main(TestConcurrentDocLoading.java:57)
Caused by: com.aspose.words.FileCorruptedException: The document appears to be corrupted and cannot be loaded.
at com.aspose.words.Document.a(Document.java:1376)
at com.aspose.words.Document.b(Document.java:1363)
at com.aspose.words.Document.a(Document.java:1251)
at com.aspose.words.Document.(Document.java:248)
at com.aspose.words.Document.(Document.java:232)
at com.aspose.words.Document.(Document.java:227)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:36)
at com.blox.workflow.util.TestConcurrentDocLoading$LoadDocumentCallable.call(TestConcurrentDocLoading.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED
at java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:951)
at java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:640)
at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:769)
at asposewobfuscated.mg.m(Encoding.java:441)
at com.aspose.words.ze.k(DocUtil.java:153)
at com.aspose.words.om.x(FontInfoFiler.java:203)
at com.aspose.words.om.a(FontInfoFiler.java:59)
at com.aspose.words.om.a(FontInfoFiler.java:41)
at com.aspose.words.ck.read(DocReader.java:42)
at com.aspose.words.Document.b(Document.java:1280)
… 14 more

Hi Parag,

I have sent you the Aspose.words jar for jdk1.6 as you asked. We are trying to include a very big new module into the next release so I can’t say certain date for it at the moment.

Please, post here (or by mail, if it is classified) the template document you use for your tests. I honestly run your test (with my template) about an hour without any crash. May be something wrong with jar I sent you – yesterday eventually was Friday, 13J. Please, try the new one.

Regards,

Thanks Konstantin. The new jar works flawlessly. However enticing the Friday the 13th theory is (;)), I believe it must be the fixes in jdk1.6 in the concurrency package.

Regards,
Parag

Hi Parag,

Thanks for the info. I believe that “post-13” jdk 1.5 will work as well. I think I just forgot to press one extra button during jar generation on Friday, 13 (probably I was nervous about all these F13:)))). And all works fine just after clean jar regeneration on Saturday, 14. I can send you new clean jdk 1.5 jar for checking, if you want. Anyway, sorry for inconvenience.

Regards,

Hello,

It seems we have the same issue : Template loading throws “com.aspose.words.FileCorruptedException” in some threads.

Can you send me jdk1.5 version for checking?

Regards

/CLBR

Hi,

I have sent the jar. Please check your mailbox. And please inform me, how it works on your side.

BTW, I believe that loading the template file from a disk for every thread is not very efficient operation. As an alternative you can load the template only once and then deep clone it for every thread:

private synchronized Document getTemplate() throws Exception
{
    if (_template == null)
        _template = new Document(_inputDoc);
    return _template.deepClone();
}

Regards,