Creating and processing multiple PST files concurrently

I am trying to populate several pst files, each in a separate thread. I have not seen any examples of this. I have seen similar questions, which suggest it is possible. I understand that you cant do multiple threads on a single pst instance. But when I try creating separate pst instances it just seems to throw issues. When I run it with a single thread, it works. Here is the code (in Groovy/java)…

def executorService = Executors.newFixedThreadPool(numThreads)
def futures = (1..numThreads).collect { threadNum ->
    executorService.submit { t ->

    //This starts a new pst instance - so in theory can support multiple pst in parallel.
    pst = new PersonalStorage(exceptionsCallback) 
    if (pstFile.exists()){
      pst.load(pstFile.absolutePath);   //seems to load in read-only mode.
       //  pst = pst.fromFile(x) is static method - not thread safe?
    } else {
      pst = pst.create(pstFile.absolutePath, 0)
      pst.store.changeDisplayName('Test-' + pstFile.name)
      pst.createPredefinedFolder('Inbox', StandardIpmFolder.Inbox)
    }
    inbox = pst.getPredefinedFolder(StandardIpmFolder.Inbox)

    <code to write mail into inbox>

Hello @mbeedell,

You can use pst.fromFile, it is thread safe.
The pst.load method opens PST in read only mode.
We tested your code with 10 threads on 1000 new MSG
For two cases:

  1. create new PST
    pst.create
  2. overwrite existing PST
    //pst.load !!! not working, PST in read only mode
    pst.fromFile

Thank you for that. I must have missed something. It raises the following exception, but only when I use 2 or more threads. The first thread continues and writes to the pst. Running on a Windows server.

      inbox = pst.createPredefinedFolder('Inbox', StandardIpmFolder.Inbox)
>>>>>>>>>>>>>>>>>>>>>>> AsposeException: Page data is not valid.
[Ljava.lang.StackTraceElement;@7a7996f6
AsposeException: Page data is not valid.
[Ljava.lang.StackTraceElement;@60eb1ba4
        at com.aspose.email.NdbDoer$BTree.b(SourceFile:3587)
        at com.aspose.email.NdbDoer$BTree.b(SourceFile:3636)
        at com.aspose.email.NdbDoer$BTree.a(SourceFile:2979)
        at com.aspose.email.NdbDoer.f(SourceFile:506)
        at com.aspose.email.NdbDoer.c(SourceFile:394)
        at com.aspose.email.zaag.<init>(SourceFile:97)
        at com.aspose.email.zaxl.<init>(SourceFile:68)
        at com.aspose.email.zaiv.a(SourceFile:383)
        at com.aspose.email.zaxt.b(SourceFile:626)
        at com.aspose.email.FolderInfo.a(SourceFile:173)
        at com.aspose.email.FolderInfo.<init>(SourceFile:152)
        at com.aspose.email.FolderInfo.<init>(SourceFile:198)
        at com.aspose.email.zaxt.b(SourceFile:1627)
        at com.aspose.email.zaxt.a(SourceFile:647)
        at com.aspose.email.PersonalStorage.getPredefinedFolder(SourceFile:1303)
        at com.aspose.email.PersonalStorage.a(SourceFile:1432)
        at com.aspose.email.PersonalStorage.createPredefinedFolder(SourceFile:1378)
        at com.aspose.email.PersonalStorage$createPredefinedFolder$2.call(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148)
        at PstWriter.startPstFile(convEml2Pst.groovy:194)
        at PstWriter$startPstFile$0.call(Unknown Source)

Hello @mbeedell,

We cannot reproduce the issue on our side using the code you provided.
Could you provide more detailed information?
Below is the code that we used for the test:

public final void test() throws InterruptedException {
    int threadsCount = 10;
    ExecutorService es = Executors.newFixedThreadPool(threadsCount);
    for (int i = 0; i < threadsCount; i++) {
        final int threadIdx = i;
        es.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    java.io.File pstFile = new java.io.File("c:\\testFolder\\" + threadIdx + ".pst");

                    //This starts a new pst instance - so in theory can support multiple pst in parallel.
                    // >>> used only with the load method, we are working on adding an exception callback to all initialization methods
                    /*PersonalStorage pst = new PersonalStorage(new TraversalExceptionsCallback() {
                        @Override
                        public void invoke(TraversalAsposeException exception, String itemId) {
                            System.err.println(threadIdx + " STOR-ERROR " + exception.getMessage());
                        }
                    });*/

                    PersonalStorage pst;
                    if (pstFile.exists()) {
                        System.out.println(threadIdx + " EXISTING PST");
                        //pst.load(pstFile.getAbsolutePath());   //seems to load in read-only mode.
                        // >>> static initialization
                        pst = PersonalStorage.fromFile(pstFile.getAbsolutePath()); //is static method - not thread safe?
                    } else {
                        System.out.println(threadIdx + " NEW PST");
                        // >>> static initialization
                        pst = PersonalStorage.create(pstFile.getAbsolutePath(), 0);
                        pst.getStore().changeDisplayName("Test-" + pstFile.getName());
                        pst.createPredefinedFolder("Inbox", StandardIpmFolder.Inbox);
                    }
                    FolderInfo inbox = pst.getPredefinedFolder(StandardIpmFolder.Inbox);

                    //<code to write mail into inbox>
                    for (int j = 0; j < 10; j++) {
                        MapiMessage m = new MapiMessage("from@host.com", "to@host.com", "subj " + j, "body", 0);
                        inbox.addMessage(m);
                        if (j % 10 == 0) {
                            System.out.println(threadIdx + " COMPLETED " + j);
                        }
                        /*try {
                            Thread.sleep(getRandomNumberUsingNextInt(50, 200));
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }*/
                    }
                    System.out.println(threadIdx + " COMPLETED ALL");
                } catch (Exception e) {
                    System.err.println(threadIdx + " ERROR " + e.getMessage());
                }
            }
        });
    }
    es.shutdown();
    es.awaitTermination(10, TimeUnit.MINUTES);
}

Thank you very much for your assistance and sample code - which, of course, worked straight away.
I now have the multi-pst code running nicely. The issue, it seems, was placing the PST code in its own class outside of the thread’s Runnable class:

ExecutorService es = Executors.newFixedThreadPool(threadsCount);
for (int i = 0; i < threadsCount; i++) {
    final int threadIdx = i;
    es.submit(new Runnable() {
        @Override
        public void run() {
            myPSTHandlingClass aPST = new myPSTHandlingClass(...init...);
            aPST.startPST (fName);  //this method did the create or fromFile stuff
                            // the changeDisplayName worked..
                            // - but would error on the  createPredefinedFolder(...) 

I moved the PST Handling method code into the Runnable (like your sample) - and then it worked like a charm.
I clearly have more to learn about Java style threading.

@mbeedell ,

That’s great! If you have questions, feel free to ask. We’ll be glad to assist you further.