org.pentaho.platform.plugin.services.importer.SolutionImportHandlerNamingIT.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.plugin.services.importer.SolutionImportHandlerNamingIT.java

Source

/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 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 Lesser General Public License for more details.
 *
 * Copyright (c) 2002-2016 Pentaho Corporation..  All rights reserved.
 */

package org.pentaho.platform.plugin.services.importer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.pentaho.metadata.repository.DomainAlreadyExistsException;
import org.pentaho.metadata.repository.DomainIdNullException;
import org.pentaho.metadata.repository.DomainStorageException;
import org.pentaho.platform.api.engine.ISolutionEngine;
import org.pentaho.platform.api.engine.PluginBeanException;
import org.pentaho.platform.api.mimetype.IMimeType;
import org.pentaho.platform.api.mimetype.IPlatformMimeResolver;
import org.pentaho.platform.api.repository.datasource.IDatasourceMgmtService;
import org.pentaho.platform.api.repository2.unified.Converter;
import org.pentaho.platform.api.repository2.unified.IPlatformImportBundle;
import org.pentaho.platform.api.repository2.unified.IRepositoryContentConverterHandler;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData;
import org.pentaho.platform.core.mimetype.MimeType;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.core.system.boot.PlatformInitializationException;
import org.pentaho.platform.engine.services.solution.SolutionEngine;
import org.pentaho.platform.plugin.services.importexport.Log4JRepositoryImportLogger;
import org.pentaho.platform.plugin.services.importexport.StreamConverter;
import org.pentaho.platform.repository2.unified.fs.FileSystemBackedUnifiedRepository;
import org.pentaho.test.platform.engine.core.MicroPlatform;
import org.pentaho.test.platform.utils.TestResourceLocation;

/**
 * 
 * Checks that SolutionImportHandler processes file names correctly
 *
 */
public class SolutionImportHandlerNamingIT extends Assert {

    private static final String SOLUTION_PATH = TestResourceLocation.TEST_RESOURCES
            + "/SolutionImportHandlerNamingTest";
    private static final String REPO_PATH = "/repo";
    private static final String IMPORT_REPO_DIR = "/home/testuser";

    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final String SRC_ROOT = "/exported";
    private static final String SRC_EXPORTMANIFEST = SRC_ROOT + "/exportManifest.xml";
    private static final String SRC_CONTENT_FILE = SRC_ROOT + "/content.prpt";
    private static final String ZIPENTRY_EXPORTMANIFEST = "exportManifest.xml";
    private static final String ZIPENTRY_CONTENT_FILE = "two+words%2525/eval+%28%2B%29%2525.prpt";
    private static final String ZIPENTRY_CONTENT_FILE_NOMANIFEST = "two words%25/eval (+)%25.prpt";

    private final IMimeType MIME_SOLUTION = new MimeType("application/vnd.pentaho.solution-repository", "zip");
    private final IMimeType MIME_PRPT = new MimeType("text/prptMimeType", "prpt");
    private final IMimeType MIME_XML = new MimeType("text/xml", "xml");

    private static File tempDir;
    private static File solutionDir;

    private SolutionImportHandler solutionImportHandler;

    private File repoRoot;
    private FileSystemBackedUnifiedRepository repo;
    private MicroPlatform microPlatform;
    private AnswerTest<RepositoryFile> answer;

    // private PentahoPlatformImporter importer;

    @BeforeClass
    public static void beforeClass() throws IOException {
        tempDir = File.createTempFile("test", null);
        tempDir.delete();
        tempDir.mkdir();
        FileUtils.forceDeleteOnExit(tempDir);

        solutionDir = new File(getSolutionPath());
        assertTrue(solutionDir.exists());
        assertTrue(solutionDir.isDirectory());
    }

    @AfterClass
    public static void afterClass() throws IOException {
        FileUtils.forceDelete(tempDir);
    }

    @Before
    public void init() throws IOException, PlatformInitializationException, PlatformImportException,
            DomainIdNullException, DomainAlreadyExistsException, DomainStorageException {
        // repository
        File repoDir = new File(tempDir.getAbsolutePath() + REPO_PATH);
        FileUtils.forceMkdir(repoDir);
        FileUtils.cleanDirectory(repoDir);
        repoRoot = repoDir;
        repo = new FileSystemBackedUnifiedRepository();
        repo = Mockito.spy(repo);
        repo.setRootDir(repoRoot);

        // mimeResolver
        final Converter defaultConverter = new StreamConverter();

        final List<IMimeType> solutionMimeList = java.util.Collections.singletonList(MIME_SOLUTION);
        final List<IMimeType> contentMimeList = java.util.Arrays.asList(new IMimeType[] { MIME_PRPT, MIME_XML });
        final List<IMimeType> allMimeTypes = new ArrayList<IMimeType>(
                solutionMimeList.size() + contentMimeList.size());
        {
            allMimeTypes.addAll(solutionMimeList);
            allMimeTypes.addAll(contentMimeList);
            for (IMimeType mimeType : allMimeTypes) {
                mimeType.setConverter(defaultConverter);
            }
        }

        final IPlatformMimeResolver mimeResolver = new NameBaseMimeResolver();
        for (IMimeType mimeType : allMimeTypes) {
            mimeResolver.addMimeType(mimeType);
        }

        // platform, import handlers
        PentahoSystem.clearObjectFactory();
        microPlatform = new MicroPlatform(getSolutionPath());
        microPlatform.defineInstance(IUnifiedRepository.class, repo);
        microPlatform.defineInstance(IPlatformMimeResolver.class, mimeResolver);
        microPlatform.defineInstance(ISolutionEngine.class, Mockito.mock(SolutionEngine.class));
        microPlatform.defineInstance(IDatasourceMgmtService.class, Mockito.mock(IDatasourceMgmtService.class));

        IRepositoryContentConverterHandler converterHandler = new DefaultRepositoryContentConverterHandler(
                new HashMap<String, Converter>());

        RepositoryFileImportFileHandler contentImportFileHandler = new RepositoryFileImportFileHandler(
                contentMimeList);
        contentImportFileHandler.setRepository(repo);
        solutionImportHandler = new SolutionImportHandler(solutionMimeList);

        List<IPlatformImportHandler> handlers = new ArrayList<IPlatformImportHandler>();
        handlers.add(contentImportFileHandler);
        handlers.add(solutionImportHandler);

        PentahoPlatformImporter importer = new PentahoPlatformImporter(handlers, converterHandler);
        importer.setDefaultHandler(contentImportFileHandler);
        importer.setRepositoryImportLogger(new Log4JRepositoryImportLogger());

        microPlatform.defineInstance(IPlatformImporter.class, importer);

        microPlatform.start();
    }

    @After
    public void cleanup() throws PluginBeanException, PlatformInitializationException, IOException {
        microPlatform.stop();
        FileUtils.cleanDirectory(repoRoot);
    }

    private static String getSolutionPath() {
        return SOLUTION_PATH;
    }

    private void assertFileContentEquals(File expected, RepositoryFile actual) throws IOException {
        FileInputStream inExp = null;
        InputStream inAct = null;
        try {
            inExp = new FileInputStream(expected);
            try {
                inAct = repo.getDataForRead(actual.getId(), SimpleRepositoryFileData.class).getInputStream();
                final String msg = "src{ " + expected.getName() + " } == repo{ " + actual.getName() + " }";
                assertTrue(msg, IOUtils.contentEquals(inExp, inAct));
            } finally {
                if (inAct != null) {
                    inAct.close();
                }
            }
        } finally {
            if (inExp != null) {
                inExp.close();
            }
        }
    }

    /**
     * Import has to be launched in a separate thread because importSession is thread-local.
     * 
     * @param solutionImportHandler
     * @param bundle
     * @throws InterruptedException
     */
    private void runImport(final SolutionImportHandler solutionImportHandler, final IPlatformImportBundle bundle)
            throws InterruptedException {

        Thread thr = new Thread() {
            @Override
            public void run() {
                try {
                    solutionImportHandler.importFile(bundle);
                } catch (Error | RuntimeException e) {
                    e.printStackTrace();
                    throw e;
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        };
        thr.start();
        thr.join(0L);
    }

    @Test
    public void testImportFileWithoutManifest()
            throws PlatformImportException, DomainIdNullException, DomainAlreadyExistsException,
            DomainStorageException, IOException, PlatformInitializationException, InterruptedException {

        ByteArrayInputStream solutionInputStream = null;

        final File srcContentFile = new File(solutionDir + SRC_CONTENT_FILE);

        ByteArrayOutputStream tmpOS = null;
        try {
            tmpOS = new ByteArrayOutputStream();

            ZipOutputStream zipOS = new ZipOutputStream(tmpOS);

            zipOS.putNextEntry(new ZipEntry(ZIPENTRY_CONTENT_FILE_NOMANIFEST));
            FileUtils.copyFile(srcContentFile, zipOS);
            zipOS.closeEntry();

            zipOS.close();
            solutionInputStream = new ByteArrayInputStream(tmpOS.toByteArray());
        } finally {
            if (tmpOS != null) {
                tmpOS.close();
            }
        }

        final IPlatformImportBundle bundle = buildBundle(solutionInputStream);

        runImport(solutionImportHandler, bundle);

        { // files with correct names do exist
            RepositoryFile dir = repo.getFile(IMPORT_REPO_DIR + "/two words%25");
            assertNotNull(dir);
            assertTrue(dir.isFolder());
            RepositoryFile file = repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval (+)%25.prpt");
            assertNotNull(file);
            assertFalse(file.isFolder());
            assertFileContentEquals(srcContentFile, file);
        }
        { // files with incorrect names do not exist
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%2525"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%25"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%2525"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval+%28%2B%29%2525.prpt"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval (+)%.prpt"));
        }
    }

    @Test
    public void testImportFileWithManifest()
            throws PlatformImportException, DomainIdNullException, DomainAlreadyExistsException,
            DomainStorageException, IOException, PlatformInitializationException, InterruptedException {

        ZipBuilder zipBuilder = new ZipBuilder();
        zipBuilder.addFile(solutionDir + SRC_EXPORTMANIFEST, ZIPENTRY_EXPORTMANIFEST);
        zipBuilder.addFile(solutionDir + SRC_CONTENT_FILE, ZIPENTRY_CONTENT_FILE);
        ByteArrayOutputStream out = zipBuilder.build();
        ByteArrayInputStream solutionInputStream = new ByteArrayInputStream(out.toByteArray());

        final IPlatformImportBundle bundle = buildBundle(solutionInputStream);

        runImport(solutionImportHandler, bundle);

        { // files with correct names do exist
            RepositoryFile dir = repo.getFile(IMPORT_REPO_DIR + "/two words%25");
            assertNotNull(dir);
            assertTrue(dir.isFolder());
            RepositoryFile file = repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval (+)%25.prpt");
            assertNotNull(file);
            assertFalse(file.isFolder());
            assertFileContentEquals(new File(solutionDir + SRC_CONTENT_FILE), file);
        }
        { // exportManifest.xml does not exist
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/exportManifest.xml"));
        }
        { // files with incorrect names do not exist
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%2525"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%25"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two+words%"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%2525"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval+%28%2B%29%2525.prpt"));
            assertNull(repo.getFile(IMPORT_REPO_DIR + "/two words%25/eval (+)%.prpt"));
        }
    }

    private static class ZipBuilder {

        private ByteArrayOutputStream out = new ByteArrayOutputStream();
        private ZipOutputStream zip = new ZipOutputStream(out);

        public void addFile(String filePath, String fileName) throws IOException {
            final File file = new File(filePath);
            assertTrue(file.getAbsolutePath() + " didn't found.", file.exists());
            zip.putNextEntry(new ZipEntry(fileName));
            FileUtils.copyFile(file, zip);
            zip.closeEntry();
        }

        public ByteArrayOutputStream build() throws IOException {
            zip.close();
            out.close();
            return out;
        }
    }

    private IPlatformImportBundle buildBundle(InputStream solutionInputStream) {
        final RepositoryFileImportBundle.Builder bundleBuilder = new RepositoryFileImportBundle.Builder();
        bundleBuilder.input(solutionInputStream);
        bundleBuilder.charSet(DEFAULT_ENCODING);
        bundleBuilder.hidden(RepositoryFile.HIDDEN_BY_DEFAULT);
        bundleBuilder.schedulable(RepositoryFile.SCHEDULABLE_BY_DEFAULT);
        bundleBuilder.path(IMPORT_REPO_DIR);
        bundleBuilder.overwriteFile(false);
        bundleBuilder.applyAclSettings(false);
        bundleBuilder.overwriteAclSettings(false);
        bundleBuilder.retainOwnership(true);
        bundleBuilder.name("testSrc.zip");
        bundleBuilder.mime("application/vnd.pentaho.solution-repository");
        return bundleBuilder.build();
    }

    @Test
    public void testImportHiddenFileByManifest() throws IOException, InterruptedException {
        repo.deleteFile(SRC_CONTENT_FILE, "");

        answer = new AnswerTest<RepositoryFile>() {
            @Override
            public RepositoryFile answer(InvocationOnMock invocation) throws Throwable {
                obj = invocation.getArgumentAt(1, RepositoryFile.class);
                return null;
            }
        };

        Mockito.doAnswer(answer).when(repo).createFile(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());

        // isHidden="true" isSchedulable="true"
        RepositoryFile file = importFile("/manifestFileHidden.xml");
        assertTrue(file.isHidden());
        assertTrue(file.isSchedulable());

        // isHidden="false" isSchedulable="false"
        file = importFile("/manifestFileNotHidden.xml");
        assertFalse(file.isHidden());
        assertFalse(file.isSchedulable());

        // nothing
        file = importFile("/manifestHiddenByDefault.xml");
        assertEquals(file.isHidden(), RepositoryFile.HIDDEN_BY_DEFAULT);
        assertEquals(file.isSchedulable(), RepositoryFile.SCHEDULABLE_BY_DEFAULT);
    }

    private RepositoryFile importFile(String manifest) throws IOException, InterruptedException {
        ZipBuilder zipBuilder = new ZipBuilder();
        zipBuilder.addFile(solutionDir + SRC_ROOT + manifest, "exportManifest.xml");
        zipBuilder.addFile(solutionDir + SRC_CONTENT_FILE, SRC_CONTENT_FILE);
        ByteArrayOutputStream out = zipBuilder.build();
        ByteArrayInputStream solutionInputStream = new ByteArrayInputStream(out.toByteArray());

        IPlatformImportBundle bundle = buildBundle(solutionInputStream);

        runImport(solutionImportHandler, bundle);

        return answer != null ? answer.getObj() : null;
    }

    private abstract static class AnswerTest<T> implements Answer<T> {

        protected T obj;

        public T getObj() {
            return obj;
        }
    }
}