Search code examples
javaautocloseable

How to run parallel tasks in a for loop, for dealing with AutoCloseable files in Java


MOTIVATION
On a youtube video of Venkat Subramaniam, he tells about how not use AutoCloseable, but try to use a function like "use". Because, one may forget to implement try block on a class which implements AutoCloseable.

QUESTION
I have implemented this strategy on many cases, however there is one case that I could not able to.

I have many file writers according to their file types. All of them uses same interface. So main macro code can be parsed and file actions can be processed for every kind of file types. (on the blog, I have reduced the function names to only "addText" and file-types as 2: "FileWeb" and "FileTable".)

And, users decides which type of files they want on everytime.

The sequencial way of doing is as below. (There, in function "constructFileAccordingToMacroCode", "the parsing of macrocode line by line" is processed twice).

{//SEQUENTIAL
  boolean produceFileTable = true;
  if (produceFileTable) {
    FileTable.use(dir, fileLabel, fileTable -> {
      constructFileAccordingToMacroCode(macroCode, fileTable);
    });
  }

  boolean produceFileWeb = true;
  if (produceFileWeb) {
    FileWeb.use(dir, fileLabel, fileWeb -> {
      constructFileAccordingToMacroCode(macroCode, fileWeb);
    });
  }
}

Kind of parallel way of doing is as below. (There, the function "constructFileAccordingToMacroCode" runs once, hence, "the parsing of macrocode line by line" is processed once).

{//KIND OF PARALLEL
  boolean produceFileTable = true;//?
  boolean produceFileWeb = true;//?
  FileTable.use(dir, fileLabel, fileTable -> {
    FileWeb.use(dir, fileLabel, fileWeb -> {
      constructFileAccordingToMacroCode(macroCode, fileTable, fileWeb);
    });
  });
}

However, If I am gonna implement it, I will lose the option of not creating the unwanted file types.

Is there more smart way of doing things?

RELEVANT EXAMPLE CLASS FILES

Main

public class Main {
    public static void main(String... s) {
        var macroCode = """
                        LINE1: ADD_FILE_TYPE
                        LINE2: ADD_SYSTEM_MILLIS
                        """;
        var dir = Path.of("c:\\fileFirectory");
        var fileLabel = "fileLabel";

        {//SEQUENTIAL
            boolean produceFileTable = true;
            if (produceFileTable) {
                FileTable.use(dir, fileLabel, fileTable -> {
                    constructFileAccordingToMacroCode(macroCode, fileTable);
                });
            }

            boolean produceFileWeb = true;
            if (produceFileWeb) {
                FileWeb.use(dir, fileLabel, fileWeb -> {
                    constructFileAccordingToMacroCode(macroCode, fileWeb);
                });
            }
        }

        {//KIND OF PARALLEL
            boolean produceFileTable = true;//?
            boolean produceFileWeb = true;//?
            FileTable.use(dir, fileLabel, fileTable -> {
                FileWeb.use(dir, fileLabel, fileWeb -> {
                    constructFileAccordingToMacroCode(macroCode, fileTable, fileWeb);
                });
            });
        }
    }

    private static boolean constructFileAccordingToMacroCode(String macroCode, FileInterface... files) {
        boolean result;
        for (var file : files) {
            //parse macroCode line by line in a for loop {
                /*FOREXAMPLE LINE1: from macroCode*/
                result = file.addText("I am a %s type file.".formatted(file.getClass().getSimpleName()));
                if (!result) {
                    return false;
                }
                /*FOREXAMPLE LINE2: from macroCode*/
                file.addText(", and time is %L".formatted(System.currentTimeMillis()));
                if (!result) {
                    return false;
                }
            //}
        }
        return true;
    }
}

TGS_RunnableType1

public interface TGS_RunnableType1<A> {
    public void run(A result);
}

FileInterface

public interface FileInterface /*implements AutoCloseable*/{
    public boolean addText(CharSequence text);
}

FileTable

public class FileTable implements FileInterface {

    private FileTable(Path dir) {
        this.dir = dir;
    }
    final public Path dir;

    @Override
    public boolean addText(CharSequence text) {
        //TODO add text code
        return true;
    }

    public static void use(Path dir, String fileLabel, TGS_RunnableType1<FileTable> fileTable) {
        var instance = new FileTable(dir);
        try {
            instance.open();
            fileTable.run(instance);
            instance.close();
        } catch (Exception e) {//SILENTLY CLOSE
            try {
                instance.close();
            } catch (Exception e2) {
                if (e2 instanceof InterruptedException) {//let InterruptedException propagate
                    throw e2;
                }
            }
            if (e instanceof InterruptedException) {//let InterruptedException propagate
                throw e;
            }
        }
    }

    private void open() {
        //open according to dir & fileLabel & type
    }

    private void close() {
        //close according to dir & fileLabel & type
    }
}

FileWeb

public class FileWeb implements FileInterface {

    private FileWeb(Path dir) {
        this.dir = dir;
    }
    final public Path dir;

    @Override
    public boolean addText(CharSequence text) {
        //TODO add text code
        return true;
    }

    public static void use(Path dir, String fileLabel, TGS_RunnableType1<FileWeb> fileWeb) {
        var instance = new FileWeb(dir);
        try {
            instance.open();
            fileWeb.run(instance);
            instance.close();
        } catch (Exception e) {//SILENTLY CLOSE
            try {
                instance.close();
            } catch (Exception e2) {
                if (e2 instanceof InterruptedException) {//let InterruptedException propagate
                    throw e2;
                }
            }
            if (e instanceof InterruptedException) {//let InterruptedException propagate
                throw e;
            }
        }
    }

    private void open() {
        //open according to dir & fileLabel & type
    }

    private void close() {
        //close according to dir & fileLabel & type
    }




------------------------- UPDATE ----------------
PARALLEL VERSION OF constructFileAccordingToMacroCode
    //WILL POSSIBLY CREATE OUT OF MEMORY ERROR
    private static boolean constructFileAccordingToMacroCode(String macroCode, FileInterface... files) {
        //parse macroCode line by line in a for loop {
            var errorPresent = IntStream.range(0, files.length).parallel()
                    .mapToObj(i -> {
                        var file = files[i];
                        /*FOREXAMPLE LINE1: from macroCode*/
                        var result = file.addText("I am a %s type file.".formatted(file.getClass().getSimpleName()));
                        if (!result) {
                            return false;
                        }
                        /*FOREXAMPLE LINE2: from macroCode*/
                        file.addText(", and time is %L".formatted(System.currentTimeMillis()));
                        if (!result) {
                            return false;
                        }
                        return true;
                    })
                    .filter(result -> false)
                    .findAny().isPresent();
            if (errorPresent) {
                return false;
            }
        //}
        return true;
    }

Solution

  • This is what i have come up with, but readability of nested code is far from good.

    1. I have added "enabled" variable on abstract file class TS_FileCommonAbstract
    public abstract class TS_FileCommonAbstract {
    
        public TS_FileCommonAbstract(boolean enabled, Path localFile, TGS_Url remoteFile) {
            isEnabled = enabled;
            if (!enabled) {
                setClosed();
            }
            ...
        }
    
        public boolean isEnabled() {
            return isEnabled;
        }
        private boolean isEnabled = false;
    
        public boolean isClosed() {
            return isClosed;
        }
        private boolean isClosed = false;
    
        final protected void setClosed() {
            isClosed = true;
        }
        public abstract boolean createNewPage(int pageSizeAX, boolean landscape, Integer marginLeft, Integer marginRight, Integer marginTop, Integer marginBottom);
        public abstract boolean beginText(int allign_Left0_center1_right2_just3);
        public abstract boolean addText(String text);
        public abstract boolean endText();
        public abstract boolean saveFile(String errorSource);
        ....
    }
    
    1. I have sent "enabled" variable to super class. And use it via "isClosed()" function to skip every possible function available.TS_FileDocx
    public class TS_FileDocx extends TS_FileCommonAbstract {
    
        private TS_FileCommonConfig fileCommonConfig;
    
        private TS_FileDocx(boolean enabled, Path localFile, TGS_Url remoteFile) {
            super(enabled, localFile, remoteFile);
        }
    
        public static void use(boolean enabled, TS_FileCommonConfig fileCommonConfig, Path localFile, TGS_Url remoteFile, TGS_RunnableType1<TS_FileDocx> docx) {
            var instance = new TS_FileDocx(enabled, localFile, remoteFile);
            try {
                instance.use_init(fileCommonConfig);
                docx.run(instance);
            } catch (Exception e) {
                instance.saveFile(e.getMessage());
                throw e;
            } finally {
                instance.saveFile(null);
            }
        }
    
        private void use_init(TS_FileCommonConfig fileCommonConfig) {
            this.fileCommonConfig = fileCommonConfig;
            if (isClosed()) {
                return;
            }
            docx = new TS_FileDocxUtils(localFile);
        }
        ...
    }
    
    1. In the helper class, I nest them all; and supply their own "enabled" variables. TS_FileTmcrFileHandler
    public class TS_FileTmcrFileHandler {
         private TS_FileTmcrFileHandler(TS_FileCommonConfig fileCommonConfig, Path localfileZIP, TGS_Url remotefileZIP, TS_FileCommonAbstract... files) {
            this.fileCommonConfig = fileCommonConfig;
            this.localfileZIP = localfileZIP;
            this.remotefileZIP = remotefileZIP;
            this.files = TGS_StreamUtils.toLst(Arrays.stream(files));
        }
        public Path localfileZIP;
        public TGS_Url remotefileZIP;
        public static boolean use(TS_FileCommonConfig fileCommonConfig, TS_SQLConnAnchor anchor,
                TGS_RunnableType2<String, Integer> progressUpdate_with_userDotTable_and_percentage,
                TGS_RunnableType1<TS_FileTmcrFileHandler> fileHandler
        ) {
            TS_FileTmcrFileHandler.use_do(fileCommonConfig, fileHandler);
            ...
        }
        private static void use_do(TS_FileCommonConfig fileCommonConfig, TGS_RunnableType1<TS_FileTmcrFileHandler> fileHandler) {
            ...
            TS_FileTmcrFileTMCR.use(enableTMCR, fileCommonConfig, localfileTMCR, remotefileTMCR, tmcr -> {
                TS_FileHtml.use(enableHTML, fileCommonConfig, localfileHTML, remotefileHTML, webHTMLBase64, webWidthScalePercent, webFontHightPercent, (webHTM, imageLoc) -> TS_FileTmcrFileSetName.urlFromPath(fileCommonConfig, imageLoc), webHTML -> {
                    TS_FileHtml.use(enableHTM, fileCommonConfig, localfileHTM, remotefileHTM, webHTMBase64, webWidthScalePercent, webFontHightPercent, (webHTM, imageLoc) -> TS_FileTmcrFileSetName.urlFromPath(fileCommonConfig, imageLoc), webHTM -> {
                        TS_FilePdf.use(enablePDF, fileCommonConfig, localfilePDF, remotefilePDF, pdf -> {
                            TS_FileXlsx.use(enableXLSX, fileCommonConfig, localfileXLSX, remotefileXLSX, xlsx -> {
                                TS_FileDocx.use(enableDOCX, fileCommonConfig, localfileDOCX, remotefileDOCX, docx -> {
                                    var instance = new TS_FileTmcrFileHandler(fileCommonConfig, localfileZIP, remotefileZIP,
                                            tmcr, webHTML, webHTM, pdf, xlsx, docx
                                    );
                                    fileHandler.run(instance);
                                });
                            });
                        });
                    }
                    );
                });
            }
            );
        }
    }
    
    1. Here is the usage exmaple of helper class Main
    public class Main {
        public static void main(String... s) {
            List<String> macroLines = TGS_ListUtils.of(
                    TS_FileTmcrCodePageWriter.INSERT_PAGE(4, true),
                    TS_FileTmcrCodeTableWriter.BEGIN_TABLE(1),
                    TS_FileTmcrCodeTableWriter.BEGIN_TABLECELL(1, 1, null),
                    TS_FileTmcrCodeTextWriter.BEGIN_TEXT_LEFT(),
                    TS_FileTmcrCodeTextWriter.ADD_TEXT("Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ"),
                    TS_FileTmcrCodeTextWriter.END_TEXT(),
                    TS_FileTmcrCodeTableWriter.END_TABLECELL(),
                    TS_FileTmcrCodeTableWriter.END_TABLE()
            );
            var result = TS_FileTmcrFileHandler.use(toConfig(macroLines), createDbAnchor("test"), progressUpdate);
            d.cr("main", "result", result);
        }
        ...
    }