richtercloud.document.scanner.it.ImageStorageIT.java Source code

Java tutorial

Introduction

Here is the source code for richtercloud.document.scanner.it.ImageStorageIT.java

Source

/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package richtercloud.document.scanner.it;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import javafx.embed.swing.JFXPanel;
import javax.cache.Caching;
import javax.swing.ImageIcon;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import richtercloud.document.scanner.gui.Constants;
import richtercloud.document.scanner.gui.DefaultOCRSelectPanel;
import richtercloud.document.scanner.gui.conf.DocumentScannerConf;
import richtercloud.document.scanner.ifaces.ImageWrapper;
import richtercloud.document.scanner.ifaces.ImageWrapperException;
import richtercloud.document.scanner.ifaces.OCRSelectPanel;
import richtercloud.document.scanner.it.entities.EntityByteArray;
import richtercloud.document.scanner.it.entities.EntityImageIcon;
import richtercloud.document.scanner.it.entities.EntityImageWrapper;
import richtercloud.document.scanner.model.imagewrapper.CachingImageWrapper;
import richtercloud.jhbuild.java.wrapper.ActionOnMissingBinary;
import richtercloud.jhbuild.java.wrapper.ArchitectureNotRecognizedException;
import richtercloud.jhbuild.java.wrapper.BuildFailureException;
import richtercloud.jhbuild.java.wrapper.ExtractionException;
import richtercloud.jhbuild.java.wrapper.JHBuildJavaWrapper;
import richtercloud.jhbuild.java.wrapper.MissingSystemBinary;
import richtercloud.jhbuild.java.wrapper.ModuleBuildFailureException;
import richtercloud.jhbuild.java.wrapper.OSNotRecognizedException;
import richtercloud.jhbuild.java.wrapper.download.AutoDownloader;
import richtercloud.message.handler.IssueHandler;
import richtercloud.message.handler.LoggerIssueHandler;
import richtercloud.reflection.form.builder.jpa.MemorySequentialIdGenerator;
import richtercloud.reflection.form.builder.jpa.retriever.JPAOrderedCachedFieldRetriever;
import richtercloud.reflection.form.builder.jpa.storage.DerbyEmbeddedPersistenceStorage;
import richtercloud.reflection.form.builder.jpa.storage.DerbyEmbeddedPersistenceStorageConf;
import richtercloud.reflection.form.builder.jpa.storage.PersistenceStorage;
import richtercloud.reflection.form.builder.jpa.storage.PostgresqlAutoPersistenceStorage;
import richtercloud.reflection.form.builder.jpa.storage.PostgresqlAutoPersistenceStorageConf;
import richtercloud.reflection.form.builder.retriever.FieldOrderValidationException;
import richtercloud.reflection.form.builder.storage.StorageConfValidationException;
import richtercloud.reflection.form.builder.storage.StorageCreationException;
import richtercloud.reflection.form.builder.storage.StorageException;
import richtercloud.validation.tools.FieldRetriever;

/**
 *
 * @author richter
 */
public class ImageStorageIT {
    private final static Logger LOGGER = LoggerFactory.getLogger(ImageStorageIT.class);

    @Test
    public void testImageStorage() throws IOException, StorageException, SQLException, InterruptedException,
            StorageConfValidationException, StorageCreationException, OSNotRecognizedException,
            ArchitectureNotRecognizedException, ExtractionException, MissingSystemBinary, BuildFailureException,
            ModuleBuildFailureException, FieldOrderValidationException, ImageWrapperException {
        LOGGER.info("testImageStorage");
        try {
            new JFXPanel();
            //- necessary in order to avoid
            //`java.lang.IllegalStateException: Toolkit not initialized`
            //- should be run here in order to be catchable at a useful
            //location; figure out how to do this if there's more than this
            //one test
            File databaseDir = Files.createTempDirectory(ImageStorageIT.class.getSimpleName()).toFile();
            FileUtils.forceDelete(databaseDir);
            //databaseDir mustn't exist for Apache Derby
            LOGGER.debug(String.format("database directory is %s", databaseDir.getAbsolutePath()));
            File schemeChecksumFile = File.createTempFile(ImageStorageIT.class.getSimpleName(), "scheme-checksum");
            LOGGER.debug(String.format("scheme checksum file is %s", schemeChecksumFile.getAbsolutePath()));
            File imageStorageDir = Files.createTempDirectory(ImageStorageIT.class.getSimpleName()).toFile();
            LOGGER.debug(String.format("image storage directory is %s", imageStorageDir.getAbsolutePath()));
            Connection connection = DriverManager
                    .getConnection(String.format("jdbc:derby:%s;create=true", databaseDir.getAbsolutePath()));
            connection.close();
            Set<Class<?>> entityClasses = new HashSet<Class<?>>(
                    Arrays.asList(EntityByteArray.class, EntityImageIcon.class));
            //        DerbyNetworkPersistenceStorageConf storageConf = new DerbyNetworkPersistenceStorageConf(entityClasses,
            //                "localhost",
            //                schemeChecksumFile);
            //        storageConf.setDatabaseDir(databaseDir);
            //        storageConf.setPassword("sa");
            //        PersistenceStorage storage = new DerbyNetworkPersistenceStorage(storageConf,
            //                "richtercloud_document-scanner-it_jar_1.0-SNAPSHOTPU");
            DerbyEmbeddedPersistenceStorageConf storageConf = new DerbyEmbeddedPersistenceStorageConf(entityClasses,
                    databaseDir.getAbsolutePath(), schemeChecksumFile);
            String persistenceUnitName = "document-scanner-it";
            FieldRetriever fieldRetriever = new JPAOrderedCachedFieldRetriever(
                    Constants.QUERYABLE_AND_EMBEDDABLE_CLASSES);
            final PersistenceStorage<Long> derbyEmbeddedStorage1 = new DerbyEmbeddedPersistenceStorage(storageConf,
                    persistenceUnitName, 1, //parallelQueryCount
                    fieldRetriever);
            derbyEmbeddedStorage1.start();
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                LOGGER.info("shutting down storage");
                derbyEmbeddedStorage1.shutdown();
                LOGGER.info("storage shut down");
                try {
                    FileUtils.deleteDirectory(databaseDir);
                    LOGGER.info(String.format("database directory '%s' deleted", databaseDir.getAbsolutePath()));
                } catch (IOException ex) {
                    LOGGER.info(String.format(
                            "deletion of database directory '%s' failed, see nested exception for details",
                            databaseDir.getAbsolutePath()), ex);
                }
                try {
                    FileUtils.deleteDirectory(imageStorageDir);
                } catch (IOException ex) {
                    java.util.logging.Logger.getLogger(ImageStorageIT.class.getName()).log(Level.SEVERE, null, ex);
                }
            }, String.format("%s shutdown hook", ImageStorageIT.class.getSimpleName())));
            List<ImageIcon> imageIcons = new LinkedList<>();
            InputStream pdfInputStream = ImageStorageIT.class.getResourceAsStream("/image_data.pdf");
            assert pdfInputStream != null;
            PDDocument document = PDDocument.load(pdfInputStream);
            @SuppressWarnings("unchecked")
            List<OCRSelectPanel> oCRSelectPanels = new LinkedList<>();
            List<ImageWrapper> imageWrappers = new LinkedList<>();
            byte[] data;
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            IssueHandler issueHandler = new LoggerIssueHandler(LOGGER);
            for (int page = 0; page < document.getNumberOfPages(); page++) {
                BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
                ImageWrapper imageWrapper = new CachingImageWrapper(databaseDir, image, issueHandler);
                OCRSelectPanel oCRSelectPanel = new DefaultOCRSelectPanel(imageWrapper,
                        DocumentScannerConf.PREFERRED_SCAN_RESULT_PANEL_WIDTH_DEFAULT, issueHandler);
                oCRSelectPanels.add(oCRSelectPanel);
                ImageIcon imageIcon = new ImageIcon(image);
                objectOutputStream.writeObject(imageIcon);
                imageIcons.add(imageIcon);
                imageWrappers.add(new CachingImageWrapper(imageStorageDir, image, issueHandler));
            }
            document.close();
            data = outputStream.toByteArray();

            EntityByteArray entityA = new EntityByteArray(1L, data);
            EntityByteArray entityA2 = new EntityByteArray(3L, data);
            EntityImageIcon entityB = new EntityImageIcon(2L, imageIcons);
            EntityImageIcon entityB2 = new EntityImageIcon(4L, imageIcons);
            EntityImageWrapper entityC1 = new EntityImageWrapper(imageWrappers);
            EntityImageWrapper entityC2 = new EntityImageWrapper(imageWrappers);
            long time0 = System.currentTimeMillis();
            entityA.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityA));
            derbyEmbeddedStorage1.store(entityA);
            long time1 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA: %d ms", time1 - time0));
            entityB.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityB));
            derbyEmbeddedStorage1.store(entityB);
            long time2 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityB: %d ms", time2 - time1));
            //store another time in order to figure out caching effects
            entityA2.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityA2));
            derbyEmbeddedStorage1.store(entityA2);
            long time3 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA2: %d ms", time3 - time2));
            entityB2.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityB2));
            derbyEmbeddedStorage1.store(entityB2);
            long time4 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityB2: %d ms", time4 - time3));
            entityC1.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityC1));
            derbyEmbeddedStorage1.store(entityC1);
            long time5 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityC1: %d ms", time5 - time4));
            entityC2.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityC2));
            derbyEmbeddedStorage1.store(entityC2);
            long time6 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityC2: %d ms", time6 - time5));
            LOGGER.info(String.format("size of entityA's data: %d KiB", entityA.getData().length / 1024));

            long randomSeed = System.currentTimeMillis();
            LOGGER.info(String.format("random seed is: %d", randomSeed));
            Random random = new Random(randomSeed);
            byte[] referenceBytes = new byte[data.length];
            random.nextBytes(referenceBytes);
            EntityByteArray entityA3 = new EntityByteArray(5L, referenceBytes);
            EntityByteArray entityA4 = new EntityByteArray(6L, referenceBytes);
            long time7 = System.currentTimeMillis();
            entityA3.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityA3));
            derbyEmbeddedStorage1.store(entityA3);
            long time8 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA3: %d ms", time8 - time7));
            entityA4.setId(MemorySequentialIdGenerator.getInstance().getNextId(entityA4));
            derbyEmbeddedStorage1.store(entityA4);
            long time9 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA4: %d ms", time9 - time8));
            derbyEmbeddedStorage1.shutdown();

            //test whether EntityImagerWrapper is deserializable
            PersistenceStorage<Long> derbyEmbeddedStorage2 = new DerbyEmbeddedPersistenceStorage(storageConf,
                    persistenceUnitName, 1, //parallelQueryCount
                    fieldRetriever);
            derbyEmbeddedStorage2.start();
            List<EntityImageWrapper> queryResults = derbyEmbeddedStorage2.runQueryAll(EntityImageWrapper.class);
            assert queryResults.size() == 2;
            EntityImageWrapper queryResult0 = queryResults.get(0);
            List<ImageWrapper> queryResult0Data = queryResult0.getData();
            for (ImageWrapper queryResult0Datum : queryResult0Data) {
                LOGGER.info(String.format("inspect image wrapper file %s ms", queryResult0Datum.getStorageFile()));
            }
            derbyEmbeddedStorage2.shutdown();

            //test PostgreSQL
            File databaseDirPostgresql = Files.createTempDirectory(ImageStorageIT.class.getSimpleName()).toFile();
            FileUtils.forceDelete(databaseDirPostgresql);
            //an inexisting database directory triggers creation of database
            //with initdb
            LOGGER.debug(
                    String.format("PostgreSQL database directory is %s", databaseDirPostgresql.getAbsolutePath()));
            //build PostgreSQL
            File postgresqlInstallationPrefixDir = Files.createTempDirectory(ImageStorageIT.class.getSimpleName())
                    .toFile();
            LOGGER.debug(String.format("using '%s' as PostgreSQL installation prefix",
                    postgresqlInstallationPrefixDir.getAbsolutePath()));
            File downloadDir = Files.createTempDirectory(ImageStorageIT.class.getSimpleName()).toFile();
            //SystemUtils.getUserHome() causes trouble
            //($HOME/jhbuild/checkout might be jhbuilds default extraction
            //directory)
            LOGGER.debug(String.format("using '%s' as JHBuild Java wrapper download directory", downloadDir));
            JHBuildJavaWrapper jHBuildJavaWrapper = new JHBuildJavaWrapper(postgresqlInstallationPrefixDir, //installationPrefixDir
                    downloadDir, //downloadDir
                    ActionOnMissingBinary.DOWNLOAD, ActionOnMissingBinary.DOWNLOAD, new AutoDownloader(), //downloader
                    false, true, //silenceStdout
                    true, //silenceStderr
                    issueHandler);
            String moduleName = "postgresql-9.6.3";
            LOGGER.info(
                    String.format("building module %s from JHBuild Java wrapper's default moduleset", moduleName));
            jHBuildJavaWrapper.installModuleset(moduleName);
            //moduleset shipped with jhbuild-java-wrapper
            String initdb = new File(postgresqlInstallationPrefixDir, String.join(File.separator, "bin", "initdb"))
                    .getAbsolutePath();
            String postgres = new File(postgresqlInstallationPrefixDir,
                    String.join(File.separator, "bin", "postgres")).getAbsolutePath();
            String createdb = new File(postgresqlInstallationPrefixDir,
                    String.join(File.separator, "bin", "createdb")).getAbsolutePath();
            String pgCtl = new File(postgresqlInstallationPrefixDir, String.join(File.separator, "bin", "pg_ctl"))
                    .getAbsolutePath();
            String databaseName = "image-storage-it";
            String username = "docu";
            String password = "docu";
            PostgresqlAutoPersistenceStorageConf postgresqlPersistenceStorageConf = new PostgresqlAutoPersistenceStorageConf(
                    entityClasses, "localhost", //hostname
                    username, //username
                    password, databaseName, schemeChecksumFile, databaseDirPostgresql.getAbsolutePath(), //databaseDir
                    initdb, postgres, createdb, pgCtl);
            PersistenceStorage<Long> postgresqlStorage = new PostgresqlAutoPersistenceStorage(
                    postgresqlPersistenceStorageConf, persistenceUnitName, 1, //parallelQueryCount
                    fieldRetriever, issueHandler);
            postgresqlStorage.start();
            time0 = System.currentTimeMillis();
            postgresqlStorage.store(entityA);
            time1 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA: %d ms", time1 - time0));
            postgresqlStorage.store(entityB);
            time2 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityB: %d ms", time2 - time1));
            //store another time in order to figure out caching effects
            postgresqlStorage.store(entityA2);
            time3 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA2: %d ms", time3 - time2));
            postgresqlStorage.store(entityB2);
            time4 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityB2: %d ms", time4 - time3));
            LOGGER.info(String.format("size of entityA's data: %d KiB", entityA.getData().length / 1024));

            time9 = System.currentTimeMillis();
            postgresqlStorage.store(entityA3);
            time8 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA3: %d ms", time8 - time9));
            postgresqlStorage.store(entityA4);
            time9 = System.currentTimeMillis();
            LOGGER.info(String.format("time for storing entityA4: %d ms", time9 - time8));
            postgresqlStorage.shutdown();
            Caching.getCachingProvider().close();
        } catch (UnsupportedOperationException ex) {
            //`new JFXPanel()` for JavaFX toolkit initialization causes
            //`java.lang.UnsupportedOperationException: Unable to open DISPLAY`
            //instead of HeadlessException (which is a subclass of
            //UnsupportedOperationException
            LOGGER.warn(
                    "UnsupportedOperationException indicates that the test is run on a headless machine, e.g. a CI service",
                    ex);
        }
    }
}