org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package org.alfresco.repo.web.scripts.custommodel;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.dictionary.CustomModelServiceImpl;
import org.alfresco.repo.dictionary.M2Association;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.dictionary.M2Type;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.TempFileProvider;
import org.alfresco.util.XMLUtil;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
 * This class tests the custom model upload REST API.
 *
 * @author Jamal Kaabi-Mofrad
 */
public class CustomModelImportTest extends BaseWebScriptTest {
    private static final String NON_ADMIN_USER = "nonAdminUserName";
    private static final String CUSTOM_MODEL_ADMIN = "customModelAdmin";
    private static final String RESOURCE_PREFIX = "custommodel/";
    private static final String UPLOAD_URL = "/api/cmm/upload";
    private static final int BUFFER_SIZE = 20 * 1024;

    private MutableAuthenticationService authenticationService;
    private AuthorityService authorityService;
    private PersonService personService;
    private RetryingTransactionHelper transactionHelper;
    private CustomModelService customModelService;
    private List<String> importedModels = new ArrayList<>();
    private List<File> tempFiles = new ArrayList<>();

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        authenticationService = getServer().getApplicationContext().getBean("AuthenticationService",
                MutableAuthenticationService.class);
        authorityService = getServer().getApplicationContext().getBean("AuthorityService", AuthorityService.class);
        personService = getServer().getApplicationContext().getBean("PersonService", PersonService.class);
        transactionHelper = getServer().getApplicationContext().getBean("retryingTransactionHelper",
                RetryingTransactionHelper.class);
        customModelService = getServer().getApplicationContext().getBean("customModelService",
                CustomModelService.class);

        AuthenticationUtil.clearCurrentSecurityContext();

        AuthenticationUtil.runAsSystem(new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception {
                createUser(NON_ADMIN_USER);
                createUser(CUSTOM_MODEL_ADMIN);

                if (!authorityService.getContainingAuthorities(AuthorityType.GROUP, CUSTOM_MODEL_ADMIN, true)
                        .contains(CustomModelServiceImpl.GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY)) {
                    authorityService.addAuthority(
                            CustomModelServiceImpl.GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY,
                            CUSTOM_MODEL_ADMIN);
                }
                return null;
            }
        });
        AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
    }

    @Override
    public void tearDown() throws Exception {
        for (File file : tempFiles) {
            file.delete();
        }

        transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() {
            public Void execute() throws Throwable {
                for (String modelName : importedModels) {
                    customModelService.deleteCustomModel(modelName);
                }
                return null;
            }
        });

        AuthenticationUtil.runAsSystem(new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception {
                transactionHelper
                        .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() {
                            public Void execute() throws Throwable {
                                deleteUser(NON_ADMIN_USER);
                                deleteUser(CUSTOM_MODEL_ADMIN);
                                return null;
                            }
                        });
                return null;
            }
        });

        AuthenticationUtil.clearCurrentSecurityContext();

        super.tearDown();
    }

    public void testValidUpload_ModelAndExtModule() throws Exception {
        File zipFile = getResourceFile("validModelAndExtModule.zip");
        PostRequest postRequest = buildMultipartPostRequest(zipFile);

        AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
        Response response = sendRequest(postRequest, 403);

        AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
        response = sendRequest(postRequest, 200);

        JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));

        String importedModelName = json.getString("modelName");
        importedModels.add(importedModelName);

        String extModule = json.getString("shareExtModule");
        Document document = XMLUtil.parse(extModule);
        NodeList nodes = document.getElementsByTagName("id");
        assertEquals(1, nodes.getLength());
        assertNotNull(nodes.item(0).getTextContent());
    }

    public void testValidUpload_ModelOnly() throws Exception {
        File zipFile = getResourceFile("validModel.zip");
        PostRequest postRequest = buildMultipartPostRequest(zipFile);

        AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
        Response response = sendRequest(postRequest, 403);

        AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
        response = sendRequest(postRequest, 200);

        JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));
        String importedModelName = json.getString("modelName");
        importedModels.add(importedModelName);

        assertFalse(json.has("shareExtModule"));

        // Import the same model again
        sendRequest(postRequest, 409); // name conflict
    }

    public void testValidUpload_ExtModuleOnly() throws Exception {
        File zipFile = getResourceFile("validExtModule.zip");
        PostRequest postRequest = buildMultipartPostRequest(zipFile);

        AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
        Response response = sendRequest(postRequest, 403);

        AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
        response = sendRequest(postRequest, 200);

        JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));
        assertFalse(json.has("modelName"));

        String extModule = json.getString("shareExtModule");
        Document document = XMLUtil.parse(extModule);
        NodeList nodes = document.getElementsByTagName("id");
        assertEquals(1, nodes.getLength());
        assertNotNull(nodes.item(0).getTextContent());
    }

    public void testNotZipFileUpload() throws Exception {
        File file = getResourceFile("validModel.zip");
        ZipFile zipFile = new ZipFile(file);
        ZipEntry zipEntry = zipFile.entries().nextElement();

        File unzippedModelFile = TempFileProvider.createTempFile(zipFile.getInputStream(zipEntry), "validModel",
                ".xml");
        tempFiles.add(unzippedModelFile);
        zipFile.close();

        PostRequest postRequest = buildMultipartPostRequest(unzippedModelFile);
        sendRequest(postRequest, 400); // CMM upload supports only zip file.
    }

    public void testInvalidZipUpload() throws Exception {
        String content = "<note>" + "<from>Jane</from>" + "<to>John</to>" + "<heading>Upload test</heading>"
                + "<body>This is an invalid model or a Share extension module</body>" + "</note>";

        ZipEntryContext context = new ZipEntryContext("invalidFormat.xml", content.getBytes());
        File zipFile = createZip(context);

        PostRequest postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 400); // Invalid. Neither a model nor a Share extension module file
    }

    public void testUploadModel_Invalid() throws Exception {
        long timestamp = System.currentTimeMillis();
        final String modelName = getClass().getSimpleName() + timestamp;
        final String prefix = "prefix" + timestamp;
        final String uri = "uriNamespace" + timestamp;

        M2Model model = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
        model.setAuthor("Admin");
        model.setDescription("Desc");

        ByteArrayOutputStream xml = new ByteArrayOutputStream();
        model.toXML(xml);
        ZipEntryContext context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
        File zipFile = createZip(context);

        PostRequest postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 409); // no namespace has been defined

        // Create two namespaces
        model.createNamespace(uri, prefix);
        model.createNamespace(uri + "anotherUri", prefix + "anotherPrefix");
        xml = new ByteArrayOutputStream();
        model.toXML(xml);
        context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
        zipFile = createZip(context);

        postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 409); // custom model can only have one namespace
    }

    public void testUploadModel_UnsupportedModelElements() throws Exception {
        // Note: here we only test a couple of not-supported model elements to check for the correct status code.
        // This test should be removed when we implement the required support

        long timestamp = System.currentTimeMillis();
        final String modelName = getClass().getSimpleName() + timestamp;
        final String prefix = "prefix" + timestamp;
        final String uri = "uriNamespace" + timestamp;
        final String aspectName = prefix + QName.NAMESPACE_PREFIX + "testAspec";
        final String typeName = prefix + QName.NAMESPACE_PREFIX + "testType";
        final String associationName = prefix + QName.NAMESPACE_PREFIX + "testAssociation";

        M2Model model = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
        model.createNamespace(uri, prefix);
        model.setAuthor("John Doe");
        model.createAspect(aspectName);
        model.createImport(NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.CONTENT_MODEL_PREFIX);

        M2Type type = model.createType(typeName);
        // Add 'association' not supported yet.
        M2Association association = type.createAssociation(associationName);
        association.setSourceMandatory(false);
        association.setSourceMany(false);
        association.setTargetMandatory(false);
        association.setTargetClassName("cm:content");

        ByteArrayOutputStream xml = new ByteArrayOutputStream();
        model.toXML(xml);
        ZipEntryContext context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
        File zipFile = createZip(context);

        PostRequest postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 409); // <associations> element is not supported yet

        type.removeAssociation(associationName);
        // Add 'mandatory-aspect' not supported yet.
        type.addMandatoryAspect(aspectName);
        xml = new ByteArrayOutputStream();
        model.toXML(xml);
        context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
        zipFile = createZip(context);

        postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 409); // <mandatory-aspects> element is not supported yet
    }

    public void testInvalidNumberOfZipEntries() throws Exception {
        long timestamp = System.currentTimeMillis();
        String modelName = getClass().getSimpleName() + timestamp;
        String prefix = "prefix" + timestamp;
        String uri = "uriNamespace" + timestamp;

        // Model one
        M2Model modelOne = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
        modelOne.createNamespace(uri, prefix);
        modelOne.setDescription("Model 1");
        ByteArrayOutputStream xml = new ByteArrayOutputStream();
        modelOne.toXML(xml);
        ZipEntryContext contextOne = new ZipEntryContext(modelName + ".xml", xml.toByteArray());

        // Model two
        modelName += "two";
        prefix += "two";
        uri += "two";
        M2Model modelTwo = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
        modelTwo.createNamespace(uri, prefix);
        modelTwo.setDescription("Model 2");
        xml = new ByteArrayOutputStream();
        modelTwo.toXML(xml);
        ZipEntryContext contextTwo = new ZipEntryContext(modelName + ".xml", xml.toByteArray());

        // Model three
        modelName += "three";
        prefix += "three";
        uri += "three";
        M2Model modelThree = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
        modelThree.createNamespace(uri, prefix);
        modelThree.setDescription("Model 3");
        xml = new ByteArrayOutputStream();
        modelThree.toXML(xml);
        ZipEntryContext contextThree = new ZipEntryContext(modelName + ".xml", xml.toByteArray());

        File zipFile = createZip(contextOne, contextTwo, contextThree);

        PostRequest postRequest = buildMultipartPostRequest(zipFile);
        sendRequest(postRequest, 400); // more than two zip entries
    }

    public PostRequest buildMultipartPostRequest(File file) throws IOException {
        Part[] parts = { new FilePart("filedata", file.getName(), file, "application/zip", null) };

        MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(parts, new HttpMethodParams());

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        multipartRequestEntity.writeRequest(os);

        PostRequest postReq = new PostRequest(UPLOAD_URL, os.toByteArray(),
                multipartRequestEntity.getContentType());
        return postReq;
    }

    private void createUser(String userName) {
        if (!authenticationService.authenticationExists(userName)) {
            authenticationService.createAuthentication(userName, "PWD".toCharArray());
        }

        if (!personService.personExists(userName)) {
            PropertyMap ppOne = new PropertyMap(4);
            ppOne.put(ContentModel.PROP_USERNAME, userName);
            ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName");
            ppOne.put(ContentModel.PROP_LASTNAME, "lastName");
            ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
            ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");

            personService.createPerson(ppOne);
        }
    }

    private void deleteUser(String userName) {
        if (personService.personExists(userName)) {
            personService.deletePerson(userName);
        }
    }

    private File getResourceFile(String xmlFileName) throws FileNotFoundException {
        URL url = CustomModelImportTest.class.getClassLoader().getResource(RESOURCE_PREFIX + xmlFileName);
        if (url == null) {
            fail("Cannot get the resource: " + xmlFileName);
        }
        return ResourceUtils.getFile(url);
    }

    private File createZip(ZipEntryContext... zipEntryContexts) {
        File zipFile = TempFileProvider.createTempFile(getClass().getSimpleName(), ".zip");
        tempFiles.add(zipFile);

        byte[] buffer = new byte[BUFFER_SIZE];
        try {
            OutputStream out = new BufferedOutputStream(new FileOutputStream(zipFile), BUFFER_SIZE);
            ZipOutputStream zos = new ZipOutputStream(out);

            for (ZipEntryContext context : zipEntryContexts) {
                ZipEntry zipEntry = new ZipEntry(context.getZipEntryName());
                zos.putNextEntry(zipEntry);

                InputStream input = context.getEntryContent();
                int len;
                while ((len = input.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                input.close();
            }
            zos.closeEntry();
            zos.close();
        } catch (IOException ex) {
            fail("couldn't create zip file.");
        }

        return zipFile;
    }

    private static class ZipEntryContext {
        private final String zipEntryName;
        private final InputStream entryContent;

        public ZipEntryContext(String zipEntryName, byte[] zipEntryContent) {
            this.zipEntryName = zipEntryName;
            this.entryContent = new ByteArrayInputStream(zipEntryContent);
        }

        public String getZipEntryName() {
            return this.zipEntryName;
        }

        public InputStream getEntryContent() {
            return this.entryContent;
        }
    }
}