org.roda.core.plugins.InternalPluginsTest.java Source code

Java tutorial

Introduction

Here is the source code for org.roda.core.plugins.InternalPluginsTest.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE file at the root of the source
 * tree and available online at
 *
 * https://github.com/keeps/roda
 */
package org.roda.core.plugins;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.xmlbeans.XmlException;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.hamcrest.core.Is;
import org.roda.core.RodaCoreFactory;
import org.roda.core.TestsHelper;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.monitor.TransferredResourcesScanner;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.common.RodaConstants.PreservationEventType;
import org.roda.core.data.exceptions.AlreadyExistsException;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.InvalidParameterException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RODAException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.IndexRunnable;
import org.roda.core.data.v2.index.filter.Filter;
import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
import org.roda.core.data.v2.index.select.SelectedItemsList;
import org.roda.core.data.v2.index.sublist.Sublist;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.AIPState;
import org.roda.core.data.v2.ip.File;
import org.roda.core.data.v2.ip.IndexedAIP;
import org.roda.core.data.v2.ip.IndexedFile;
import org.roda.core.data.v2.ip.Permissions;
import org.roda.core.data.v2.ip.TransferredResource;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent;
import org.roda.core.data.v2.ip.metadata.PreservationMetadata;
import org.roda.core.data.v2.ip.metadata.PreservationMetadata.PreservationMetadataType;
import org.roda.core.data.v2.jobs.Job;
import org.roda.core.data.v2.jobs.PluginType;
import org.roda.core.index.IndexService;
import org.roda.core.model.ModelService;
import org.roda.core.plugins.plugins.PluginHelper;
import org.roda.core.plugins.plugins.antivirus.AntivirusPlugin;
import org.roda.core.plugins.plugins.characterization.PremisSkeletonPlugin;
import org.roda.core.plugins.plugins.characterization.SiegfriedPlugin;
import org.roda.core.plugins.plugins.ingest.AutoAcceptSIPPlugin;
import org.roda.core.plugins.plugins.ingest.TransferredResourceToAIPPlugin;
import org.roda.core.storage.Binary;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.w3c.util.InvalidDateException;

import com.google.common.collect.Iterables;

import gov.loc.premis.v3.EventComplexType;
import gov.loc.premis.v3.FormatComplexType;
import gov.loc.premis.v3.FormatRegistryComplexType;
import gov.loc.premis.v3.LinkingAgentIdentifierComplexType;
import gov.loc.premis.v3.ObjectCharacteristicsComplexType;
import gov.loc.premis.v3.Representation;
import jersey.repackaged.com.google.common.collect.Lists;

@Test(groups = { RodaConstants.TEST_GROUP_ALL, RodaConstants.TEST_GROUP_TRAVIS })
public class InternalPluginsTest {
    private static final String FAKE_REPORTING_CLASS = "NONE";

    private static final Logger LOGGER = LoggerFactory.getLogger(InternalPluginsTest.class);

    private static final int CORPORA_FILES_COUNT = 13;
    private static final int CORPORA_FOLDERS_COUNT = 3;
    private static final String CORPORA_PDF = "test.docx";
    private static final String CORPORA_TEST1 = "test1";
    private static final String CORPORA_TEST1_TXT = "test1.txt";
    private static final int GENERATED_FILE_SIZE = 100;

    private static Path basePath;

    private static ModelService model;
    private static IndexService index;

    @BeforeClass
    public void setUp() throws Exception {
        basePath = TestsHelper.createBaseTempDir(getClass(), true,
                PosixFilePermissions.asFileAttribute(new HashSet<>(Arrays.asList(PosixFilePermission.OWNER_READ,
                        PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE,
                        PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE))));

        boolean deploySolr = true;
        boolean deployLdap = true;
        boolean deployFolderMonitor = true;
        boolean deployOrchestrator = true;
        boolean deployPluginManager = true;
        boolean deployDefaultResources = false;
        RodaCoreFactory.instantiateTest(deploySolr, deployLdap, deployFolderMonitor, deployOrchestrator,
                deployPluginManager, deployDefaultResources);
        model = RodaCoreFactory.getModelService();
        index = RodaCoreFactory.getIndexService();

        LOGGER.info("Running internal plugins tests under storage {}", basePath);
    }

    @AfterClass
    public void tearDown() throws Exception {
        RodaCoreFactory.shutdown();
        FSUtils.deletePath(basePath);
    }

    @AfterMethod
    public void cleanUp() throws RODAException {

        // delete all AIPs
        index.execute(IndexedAIP.class, Filter.ALL, new ArrayList<>(), new IndexRunnable<IndexedAIP>() {
            @Override
            public void run(IndexedAIP item)
                    throws GenericException, RequestNotValidException, AuthorizationDeniedException {
                try {
                    model.deleteAIP(item.getId());
                } catch (NotFoundException e) {
                    // do nothing
                }
            }
        });

        // delete all Transferred Resources
        index.execute(TransferredResource.class, Filter.ALL, new ArrayList<>(),
                new IndexRunnable<TransferredResource>() {

                    @Override
                    public void run(TransferredResource item)
                            throws GenericException, RequestNotValidException, AuthorizationDeniedException {
                        model.deleteTransferredResource(item);
                    }
                });
    }

    private ByteArrayInputStream generateContentData() {
        return new ByteArrayInputStream(RandomStringUtils.randomAscii(GENERATED_FILE_SIZE).getBytes());
    }

    private TransferredResource createCorpora() throws InterruptedException, IOException, NotFoundException,
            GenericException, RequestNotValidException, AlreadyExistsException {
        TransferredResourcesScanner f = RodaCoreFactory.getTransferredResourcesScanner();

        String parentUUID = f.createFolder(null, "test").getUUID();
        String test1UUID = f.createFolder(parentUUID, CORPORA_TEST1).getUUID();
        String test2UUID = f.createFolder(parentUUID, "test2").getUUID();
        String test3UUID = f.createFolder(parentUUID, "test3").getUUID();

        f.createFile(parentUUID, CORPORA_TEST1_TXT, generateContentData());
        f.createFile(parentUUID, "test2.txt", generateContentData());
        f.createFile(parentUUID, "test3.txt", generateContentData());
        f.createFile(test1UUID, CORPORA_TEST1_TXT, generateContentData());
        f.createFile(test1UUID, "test2.txt", generateContentData());
        f.createFile(test1UUID, "test3.txt", generateContentData());
        f.createFile(test2UUID, CORPORA_TEST1_TXT, generateContentData());
        f.createFile(test2UUID, "test2.txt", generateContentData());
        f.createFile(test2UUID, "test3.txt", generateContentData());
        f.createFile(test3UUID, CORPORA_TEST1_TXT, generateContentData());
        f.createFile(test3UUID, "test2.txt", generateContentData());
        f.createFile(test3UUID, "test3.txt", generateContentData());

        f.createFile(parentUUID, CORPORA_PDF, getClass().getResourceAsStream("/corpora/Media/" + CORPORA_PDF));

        index.commit(TransferredResource.class);

        TransferredResource transferredResource = index.retrieve(TransferredResource.class,
                IdUtils.createUUID("test"), new ArrayList<>());
        return transferredResource;
    }

    private AIP ingestCorpora() throws RequestNotValidException, NotFoundException, GenericException,
            AlreadyExistsException, AuthorizationDeniedException, InvalidParameterException, InterruptedException,
            IOException, SolrServerException {
        String parentId = null;
        String aipType = RodaConstants.AIP_TYPE_MIXED;
        AIP root = model.createAIP(parentId, aipType, new Permissions(), RodaConstants.ADMIN);

        Map<String, String> parameters = new HashMap<>();
        parameters.put(RodaConstants.PLUGIN_PARAMS_PARENT_ID, root.getId());
        TransferredResource transferredResource = createCorpora();
        AssertJUnit.assertNotNull(transferredResource);

        Job job = TestsHelper.executeJob(TransferredResourceToAIPPlugin.class, parameters, PluginType.SIP_TO_AIP,
                SelectedItemsList.create(TransferredResource.class, transferredResource.getUUID()));

        TestsHelper.getJobReports(index, job, true);

        index.commitAIPs();

        IndexResult<IndexedAIP> find = index.find(IndexedAIP.class,
                new Filter(new SimpleFilterParameter(RodaConstants.AIP_PARENT_ID, root.getId())), null,
                new Sublist(0, 10), new ArrayList<>());

        AssertJUnit.assertEquals(1L, find.getTotalCount());
        IndexedAIP indexedAIP = find.getResults().get(0);

        AIP aip = model.retrieveAIP(indexedAIP.getId());
        return aip;
    }

    @Test
    public void testIngestTransferredResource()
            throws IOException, InterruptedException, RODAException, SolrServerException {
        AIP aip = ingestCorpora();
        AssertJUnit.assertEquals(1, aip.getRepresentations().size());

        CloseableIterable<OptionalWithCause<File>> allFiles = model.listFilesUnder(aip.getId(),
                aip.getRepresentations().get(0).getId(), true);
        List<File> reusableAllFiles = new ArrayList<>();
        Iterables.addAll(reusableAllFiles, Lists.newArrayList(allFiles).stream().filter(f -> f.isPresent())
                .map(f -> f.get()).collect(Collectors.toList()));

        // All folders and files
        AssertJUnit.assertEquals(CORPORA_FOLDERS_COUNT + CORPORA_FILES_COUNT, reusableAllFiles.size());
    }

    @Test
    public void testVirusCheck()
            throws RODAException, InterruptedException, IOException, SolrServerException, XmlException {
        AIP aip = ingestCorpora();

        Job job = TestsHelper.executeJob(AntivirusPlugin.class, PluginType.AIP_TO_AIP,
                SelectedItemsList.create(AIP.class, aip.getId()));

        TestsHelper.getJobReports(index, job, true);

        aip = model.retrieveAIP(aip.getId());

        Plugin<? extends IsRODAObject> plugin = RodaCoreFactory.getPluginManager()
                .getPlugin(AntivirusPlugin.class.getName());
        String agentID = PluginHelper.getPluginAgentId(plugin);
        boolean found = false;
        CloseableIterable<OptionalWithCause<PreservationMetadata>> preservationMetadataList = model
                .listPreservationMetadata(aip.getId(), true);
        for (OptionalWithCause<PreservationMetadata> opm : preservationMetadataList) {
            if (opm.isPresent()) {
                PreservationMetadata pm = opm.get();
                if (pm.getType().equals(PreservationMetadataType.EVENT)) {
                    EventComplexType event = PremisV3Utils.binaryToEvent(model
                            .retrievePreservationEvent(pm.getAipId(), pm.getRepresentationId(),
                                    pm.getFileDirectoryPath(), pm.getFileId(), pm.getId())
                            .getContent().createInputStream());
                    if (event.getLinkingAgentIdentifierArray() != null
                            && event.getLinkingAgentIdentifierArray().length > 0) {
                        for (LinkingAgentIdentifierComplexType laict : event.getLinkingAgentIdentifierArray()) {
                            if (laict.getLinkingAgentIdentifierValue() != null
                                    && laict.getLinkingAgentIdentifierValue().equalsIgnoreCase(agentID)) {
                                found = true;
                                break;
                            }
                        }
                        if (found) {
                            break;
                        }
                    }
                }
            }
        }
        IOUtils.closeQuietly(preservationMetadataList);
        AssertJUnit.assertTrue(found);

        index.commitAIPs();

        Filter filter = new Filter();
        filter.add(new SimpleFilterParameter(RodaConstants.PRESERVATION_EVENT_TYPE,
                PreservationEventType.VIRUS_CHECK.toString()));
        filter.add(new SimpleFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, aip.getId()));
        IndexResult<IndexedPreservationEvent> events = index.find(IndexedPreservationEvent.class, filter, null,
                new Sublist(0, 10), new ArrayList<>());
        AssertJUnit.assertEquals(1, events.getTotalCount());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPremisSkeleton() throws RODAException, InterruptedException, IOException, SolrServerException {
        AIP aip = ingestCorpora();

        TestsHelper.executeJob(PremisSkeletonPlugin.class, PluginType.AIP_TO_AIP,
                SelectedItemsList.create(AIP.class, aip.getId()));

        aip = model.retrieveAIP(aip.getId());

        CloseableIterable<OptionalWithCause<PreservationMetadata>> preservationMetadata = model
                .listPreservationMetadata(aip.getId(), true);

        // Files plus one representation + 2 SIP To AIP Event + 1 Premis Skeleton
        // Event
        AssertJUnit.assertEquals(CORPORA_FILES_COUNT + 4, Iterables.size(preservationMetadata));
        preservationMetadata.close();

        Binary rpo_bin = model.retrievePreservationRepresentation(aip.getId(),
                aip.getRepresentations().get(0).getId());
        Representation rpo = PremisV3Utils.binaryToRepresentation(rpo_bin.getContent(), true);

        // Relates to files
        AssertJUnit.assertEquals(CORPORA_FILES_COUNT, rpo.getRelationshipArray().length);

        Binary fpo_bin = model.retrievePreservationFile(aip.getId(), aip.getRepresentations().get(0).getId(),
                Arrays.asList(CORPORA_TEST1), CORPORA_TEST1_TXT);

        gov.loc.premis.v3.File fpo = PremisV3Utils.binaryToFile(fpo_bin.getContent(), true);

        ObjectCharacteristicsComplexType fileCharacteristics = fpo.getObjectCharacteristicsArray(0);

        // check a fixity was generated
        AssertJUnit.assertTrue("No fixity checks", fileCharacteristics.getFixityArray().length > 0);

        // check file size
        long size = fileCharacteristics.getSize();
        AssertJUnit.assertTrue("File size is zero", size > 0);

        // check file original name
        String originalName = fpo.getOriginalName().getStringValue();
        AssertJUnit.assertEquals(CORPORA_TEST1_TXT, originalName);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testSiegfried()
            throws RODAException, InterruptedException, IOException, SolrServerException, XmlException {
        AIP aip = ingestCorpora();

        // ensure PREMIS objects are created
        TestsHelper.executeJob(PremisSkeletonPlugin.class, PluginType.AIP_TO_AIP,
                SelectedItemsList.create(AIP.class, aip.getId()));

        // run siegfried
        Map<String, String> parameters = new HashMap<>();
        parameters.put(RodaConstants.PLUGIN_PARAMS_REPORTING_CLASS, FAKE_REPORTING_CLASS);
        Job job = TestsHelper.executeJob(SiegfriedPlugin.class, parameters, PluginType.AIP_TO_AIP,
                SelectedItemsList.create(AIP.class, aip.getId()));
        TestsHelper.getJobReports(index, job, true);

        aip = model.retrieveAIP(aip.getId());

        // Files with Siegfried output
        AssertJUnit.assertEquals(CORPORA_FILES_COUNT, Iterables
                .size(model.listOtherMetadata(aip.getId(), RodaConstants.OTHER_METADATA_TYPE_SIEGFRIED, true)));

        Binary om = model.retrieveOtherMetadataBinary(aip.getId(), aip.getRepresentations().get(0).getId(),
                Arrays.asList(CORPORA_TEST1), CORPORA_TEST1_TXT, SiegfriedPlugin.FILE_SUFFIX,
                RodaConstants.OTHER_METADATA_TYPE_SIEGFRIED);

        AssertJUnit.assertNotNull(om);

        Binary fpo_bin = model.retrievePreservationFile(aip.getId(), aip.getRepresentations().get(0).getId(),
                Arrays.asList(CORPORA_TEST1), CORPORA_TEST1_TXT);

        gov.loc.premis.v3.File fpo = PremisV3Utils.binaryToFile(fpo_bin.getContent(), true);

        FormatComplexType format = fpo.getObjectCharacteristicsArray(0).getFormatArray(0);
        AssertJUnit.assertEquals("Plain Text File", format.getFormatDesignation().getFormatName().getStringValue());
        FormatRegistryComplexType pronomRegistry = PremisV3Utils.getFormatRegistry(fpo,
                RodaConstants.PRESERVATION_REGISTRY_PRONOM);
        AssertJUnit.assertEquals(RodaConstants.PRESERVATION_REGISTRY_PRONOM,
                pronomRegistry.getFormatRegistryName().getStringValue());
        AssertJUnit.assertEquals("x-fmt/111", pronomRegistry.getFormatRegistryKey().getStringValue());

        FormatRegistryComplexType mimeRegistry = PremisV3Utils.getFormatRegistry(fpo,
                RodaConstants.PRESERVATION_REGISTRY_MIME);
        String mimetype = "text/plain";
        AssertJUnit.assertEquals(mimetype, mimeRegistry.getFormatRegistryKey().getStringValue());

        index.commitAIPs();

        IndexedFile indFile = index.retrieve(IndexedFile.class, IdUtils.getFileId(aip.getId(),
                aip.getRepresentations().get(0).getId(), Arrays.asList(CORPORA_TEST1), CORPORA_TEST1_TXT),
                new ArrayList<>());

        AssertJUnit.assertEquals(mimetype, indFile.getFileFormat().getMimeType());
        AssertJUnit.assertEquals("x-fmt/111", indFile.getFileFormat().getPronom());
        AssertJUnit.assertEquals("Plain Text File", indFile.getFileFormat().getFormatDesignationName());

        List<String> suggest = index.suggest(IndexedFile.class, RodaConstants.FILE_FORMAT_MIMETYPE,
                mimetype.substring(0, 1), null, false, false);
        MatcherAssert.assertThat(suggest, Matchers.contains(mimetype));

        Plugin<? extends IsRODAObject> plugin = RodaCoreFactory.getPluginManager()
                .getPlugin(SiegfriedPlugin.class.getName());
        String agentID = PluginHelper.getPluginAgentId(plugin);

        boolean found = false;
        CloseableIterable<OptionalWithCause<PreservationMetadata>> preservationMetadataList = model
                .listPreservationMetadata(aip.getId(), true);
        for (OptionalWithCause<PreservationMetadata> opm : preservationMetadataList) {
            if (opm.isPresent()) {
                PreservationMetadata pm = opm.get();
                if (pm.getType().equals(PreservationMetadataType.EVENT)) {
                    EventComplexType event = PremisV3Utils.binaryToEvent(model
                            .retrievePreservationEvent(pm.getAipId(), pm.getRepresentationId(),
                                    pm.getFileDirectoryPath(), pm.getFileId(), pm.getId())
                            .getContent().createInputStream());
                    if (event.getLinkingAgentIdentifierArray() != null
                            && event.getLinkingAgentIdentifierArray().length > 0) {
                        for (LinkingAgentIdentifierComplexType laict : event.getLinkingAgentIdentifierArray()) {
                            if (laict.getLinkingAgentIdentifierValue() != null
                                    && laict.getLinkingAgentIdentifierValue().equalsIgnoreCase(agentID)) {
                                found = true;
                                break;
                            }
                        }
                        if (found) {
                            break;
                        }
                    }
                }
            }
        }
        IOUtils.closeQuietly(preservationMetadataList);
        AssertJUnit.assertTrue(found);

        Filter filter = new Filter();
        filter.add(new SimpleFilterParameter(RodaConstants.PRESERVATION_EVENT_TYPE,
                PreservationEventType.FORMAT_IDENTIFICATION.toString()));
        filter.add(new SimpleFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, aip.getId()));
        IndexResult<IndexedPreservationEvent> events = index.find(IndexedPreservationEvent.class, filter, null,
                new Sublist(0, 10), new ArrayList<>());
        AssertJUnit.assertEquals(1, events.getTotalCount());
    }

    @Test
    public void testAutoAccept()
            throws RODAException, InterruptedException, IOException, InvalidDateException, SolrServerException {
        AIP aip = ingestCorpora();

        TestsHelper.executeJob(AutoAcceptSIPPlugin.class, PluginType.AIP_TO_AIP,
                SelectedItemsList.create(AIP.class, aip.getId()));

        aip = model.retrieveAIP(aip.getId());
        MatcherAssert.assertThat(aip.getState(), Is.is(AIPState.ACTIVE));
    }

}