Package org.exoplatform.portal.mop.management.operations

Source Code of org.exoplatform.portal.mop.management.operations.TemplateImportResource$ZipTemplateException

/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.exoplatform.portal.mop.management.operations;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.exoplatform.commons.chromattic.ChromatticManager;
import org.exoplatform.commons.utils.ListAccess;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.model.NavigationFragment;
import org.exoplatform.portal.config.model.Page;
import org.exoplatform.portal.config.model.PageNavigation;
import org.exoplatform.portal.config.model.PageNode;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.SiteType;
import org.exoplatform.portal.mop.description.DescriptionService;
import org.exoplatform.portal.mop.importer.ImportMode;
import org.exoplatform.portal.mop.management.exportimport.NavigationExportTask;
import org.exoplatform.portal.mop.management.exportimport.NavigationImportTask;
import org.exoplatform.portal.mop.management.exportimport.PageExportTask;
import org.exoplatform.portal.mop.management.exportimport.PageImportTask;
import org.exoplatform.portal.mop.management.exportimport.SiteLayoutExportTask;
import org.exoplatform.portal.mop.management.exportimport.SiteLayoutImportTask;
import org.exoplatform.portal.mop.management.operations.navigation.NavigationUtils;
import org.exoplatform.portal.mop.management.operations.page.PageUtils;
import org.exoplatform.portal.mop.navigation.NavigationContext;
import org.exoplatform.portal.mop.navigation.NavigationService;
import org.exoplatform.portal.mop.page.PageService;
import org.exoplatform.portal.pom.config.POMSession;
import org.exoplatform.portal.pom.config.POMSessionManager;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.Query;
import org.exoplatform.services.organization.User;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.management.api.ContentType;
import org.gatein.management.api.binding.Marshaller;
import org.gatein.management.api.exceptions.OperationException;
import org.gatein.management.api.exceptions.ResourceNotFoundException;
import org.gatein.management.api.operation.OperationAttachment;
import org.gatein.management.api.operation.OperationContext;
import org.gatein.management.api.operation.OperationHandler;
import org.gatein.management.api.operation.ResultHandler;
import org.gatein.management.api.operation.model.NoResultModel;
import org.gatein.mop.api.workspace.Site;
import org.gatein.mop.api.workspace.Workspace;

/**
* @author <a href="mailto:lponce@redhat.com">Lucas Ponce</a>
* @version $Revision$
*/
public class TemplateImportResource extends SecureOperationHandler implements OperationHandler {

    private static final Logger log = LoggerFactory.getLogger(TemplateImportResource.class);

    private static final Set<String> FILES;
    private static final Set<String> DIR;
    static {
        HashSet<String> files = new HashSet<String>(5);
        files.add("portal.xml");
        files.add("group.xml");
        files.add("user.xml");
        files.add("pages.xml");
        files.add("navigation.xml");
        FILES = files;
        HashSet<String> dir = new HashSet<String>(3);
        dir.add("portal");
        dir.add("group");
        dir.add("user");
        DIR = dir;
    }

    private final String OWNER = "@owner@";
    private final String CREATE = "create";

    @Override
    public void doExecute(final OperationContext operationContext, ResultHandler resultHandler)
            throws ResourceNotFoundException, OperationException {

        final String PORTAL = "portal";
        final String GROUP = "group";
        final String USER = "user";

        OperationAttributes attr = new OperationAttributes();

        initAttributes(operationContext, attr);

        List<MopTemplate> templates = readZip(operationContext, attr);

        BackendServices svc = new BackendServices();

        initBackendServices(operationContext, attr, svc);

        // Expands template with proper sites/groups/users and populates importMap to perform import operation
        Map<SiteKey, MopImport> importMap = null;
        if (PORTAL.equals(attr.importType)) {
            importMap = expandPortalTemplate(attr, svc, templates);
        } else if (GROUP.equals(attr.importType)) {
            importMap = expandGroupTemplate(attr, svc, templates);
        } else if (USER.equals(attr.importType)) {
            importMap = expandUserTemplate(attr, svc, templates);
        }

        validationRules(attr, importMap);

        OperationException importError = performImport(attr, importMap);

        endRequest(attr, svc, importError);

        resultHandler.completed(NoResultModel.INSTANCE);
    }

    private void initAttributes(OperationContext operationContext, OperationAttributes attr)
            throws OperationException {

        // Expected operationName == "import-resource"
        attr.operationName = operationContext.getOperationName();

        // Expected importType == {template-type: portal|group|user}
        attr.importType = operationContext.getAddress().resolvePathTemplate("template-type");

        // Expected mode == {merge (default), overwrite, conserve, insert}
        attr.mode = operationContext.getAttributes().getValue("importMode");
        if (attr.mode == null || "".equals(attr.mode))
            attr.mode = "merge";

        try {
            attr.importMode = ImportMode.valueOf(attr.mode.trim().toUpperCase());
        } catch (Exception e) {
            throw new OperationException(attr.operationName,
                                         "Unknown importMode " + attr.mode + " for " + attr.importType + " template import.");
        }

        // Expression for users. This option is valid when importType == user
        attr.targetExpr = operationContext.getAttributes().getValue("targetExpr");
        // List of users. Option valid when importType == user
        attr.targetUser = operationContext.getAttributes().getValues("targetUser");
        // Flag to create dashboard if user has not initialized it. Option valid when importType == user
        // dashboardMode == create means dashboard will be created if it doesn't exist on import
        attr.dashboardMode = operationContext.getAttributes().getValue("dashboardMode");
        // List of groups. Option valid when importType == group
        attr.targetGroup = operationContext.getAttributes().getValues("targetGroup");
        // List of sites. Option valid when importType == portal
        attr.targetSite = operationContext.getAttributes().getValues("targetSite");
    }

    private void initBackendServices(OperationContext operationContext, OperationAttributes attr, BackendServices svc)
            throws OperationException {

        svc.mgr = operationContext.getRuntimeContext().getRuntimeComponent(POMSessionManager.class);
        POMSession session = svc.mgr.getSession();
        if (session == null)
            throw new OperationException(attr.operationName, "MOP session was null");

        svc.workspace = session.getWorkspace();
        if (svc.workspace == null)
            throw new OperationException(attr.operationName, "MOP workspace was null");

        svc.dataStorage = operationContext.getRuntimeContext().getRuntimeComponent(DataStorage.class);
        if (svc.dataStorage == null)
            throw new OperationException(attr.operationName, "DataStorage was null");

        svc.pageService = operationContext.getRuntimeContext().getRuntimeComponent(PageService.class);
        if (svc.pageService == null)
            throw new OperationException(attr.operationName, "PageService was null");

        svc.navigationService = operationContext.getRuntimeContext().getRuntimeComponent(NavigationService.class);
        if (svc.navigationService == null)
            throw new OperationException(attr.operationName, "Navigation service was null");

        svc.descriptionService = operationContext.getRuntimeContext().getRuntimeComponent(
                DescriptionService.class);
        if (svc.descriptionService == null)
            throw new OperationException(attr.operationName, "Description service was null");

        svc.organizationService = operationContext.getRuntimeContext().getRuntimeComponent(OrganizationService.class);
        if (svc.organizationService == null)
            throw new OperationException(attr.operationName, "Organization service was null");

        svc.chromatticManager = operationContext.getRuntimeContext().getRuntimeComponent(ChromatticManager.class);
        if (svc.chromatticManager == null)
            throw new OperationException(attr.operationName, "Chromattic manager was null");

    }

    /**
     * It will have three possible layouts:
     *  a) portal templates:
     *      .zip/portal/template/
     *          <templateName1>/{portal,pages,navigation}.xml
     *          <templateName2>/{portal,pages,navigation}.xml
     *          ...
     *          <templateNameN>/{portal,pages,navigation}.xml
     *
     *      This layout allows to have 1..N templates
     *
     * b) group templates:
     *      .zip/group/template/{portal,pages,navigation}.xml
     *
     *      This layout only accepts 1 group template
     *
     * c) user templates:
     *      .zip/user/template/{portal,pages,navigation}.xml
     *
     *      This layout only accepts 1 group template
     *
     * Each template is mapped in a MopTemplate object containing unmarshalled representation of template.
     *
     * @param operationContext context used in doExecute()
     * @param attr attributes used for this operation
     * @return list of templates read from .zip
     * @throws OperationException
     */
    private List<MopTemplate> readZip(OperationContext operationContext, OperationAttributes attr)
            throws OperationException {

        ArrayList<MopTemplate> templates = new ArrayList<MopTemplate>();
        // Gets .zip attachment
        OperationAttachment attachment = operationContext.getAttachment(true);
        if (attachment == null)
            throw new OperationException(attr.operationName,
                                         "No attachment available for " + attr.importType + " template import.");

        InputStream inputStream = attachment.getStream();
        if (inputStream == null)
            throw new OperationException(attr.operationName,
                                         "No data stream available for " + attr.importType + " template import.");

        // Reads .zip file
        final NonCloseableZipInputStream zis = new NonCloseableZipInputStream(inputStream);
        ZipEntry entry;

        boolean portal = false, group = false, user = false, template = false;
        try {
            log.info("Preparing data for import.");
            while ((entry = zis.getNextEntry()) != null) {
                // Skip directories
                if (entry.isDirectory()) {
                    // Directory entries finish with "/"
                    String[] folders = entry.getName().split("/");
                    String name = folders[folders.length - 1];
                    // 1st directory entry
                    if (!(portal || group || user)) {
                        if (DIR.contains(name)) {
                            if ("portal".equals(name)) {
                                portal = true;
                                if (!"portal".equalsIgnoreCase(attr.importType))
                                    throw new ZipTemplateException(".zip contains a portal folder but not " +
                                                                   "/template/portal operation found, " +
                                                                   "instead /template/" + attr.importType + " .");
                            } else if ("group".equals(name)) {
                                group = true;
                                if (!"group".equalsIgnoreCase(attr.importType))
                                    throw new ZipTemplateException(".zip contains a group folder but not " +
                                                                   "/template/group operation found, " +
                                                                   "instead /template/" + attr.importType + " .");
                            } else if ("user".equals(name)) {
                                user = true;
                                if (!"user".equalsIgnoreCase(attr.importType))
                                    throw new ZipTemplateException(".zip contains a user folder but not " +
                                                                   "/template/user operation found, " +
                                                                   "instead /template/" + attr.importType + " .");
                            }
                        } else {
                            throw new ZipTemplateException(".zip contains a not valid folder: " + name + ". " +
                                                           "Expecting one of " + DIR);
                        }
                    } else {
                        // 2nd directory entry
                        if (!template) {
                            if ("template".equals(name)) {
                                template = true;
                                if (group) {
                                    MopTemplate groupTemplate = new MopTemplate();
                                    groupTemplate.templateType = SiteType.GROUP;
                                    templates.add(groupTemplate);
                                } else if (user) {
                                    MopTemplate userTemplate = new MopTemplate();
                                    userTemplate.templateType = SiteType.USER;
                                    templates.add(userTemplate);
                                }
                            } else
                                throw new ZipTemplateException(".zip does not contains a template folder under " +
                                                               "portal/group/user folder, instead " + name + " .");
                        } else {
                            // 3rd directory
                            if (portal) {
                                MopTemplate portalTemplate = new MopTemplate();
                                portalTemplate.templateName = name;
                                portalTemplate.templateType = SiteType.PORTAL;
                                templates.add(portalTemplate);
                            } else {
                                throw new ZipTemplateException(".zip contains a folder under {portal,group,user}/template/ " +
                                                               "layout. It is expected one of " + FILES);
                            }
                        }
                    }
                    continue;
                }

                // Parses zip entry
                String[] parts = parseEntry(entry, portal);
                // Expected filesystem as
                //      portal/template/<templateName>/{portal,pages,navigation}.xml
                //      group/template/{group,pages,navigation}.xml
                //      user/template/{user,pages.navigation}.xml
                String file;
                if (portal)
                    file = parts[3];
                else
                    file = parts[2];

                // Validates file name
                if (!FILES.contains(file)) {
                    log.warn(".zip contains a not valid template file: " + file + ". Skipping...");
                    continue;
                }

                // Validates that a portal.xml is used with /template/portal operation
                if ("portal.xml".equals(file) && !portal) {
                    log.warn("portal.xml in .zip but not /template/portal operation found, " +
                            "instead /template/" + attr.importType + " . Skipping... ");
                    continue;
                }

                // Validates that a group.xml is used with /template/group operation
                if ("group.xml".equals(file) && !group) {
                    log.warn("group.xml in .zip but not /template/group operation found, " +
                            "instead /template/" + attr.importType + " . Skipping...");
                    continue;
                }

                // Validates that a user.xml is used with /template/user operation
                if ("user.xml".equals(file) && !user) {
                    log.warn("user.xml in .zip but not /template/user operation found, " +
                            "instead /template/" + attr.importType + " . Skipping... ");
                    continue;
                }

                // Templates are unmarshalled in generic objects, pattern substitution will be performed later
                if (SiteLayoutExportTask.FILES.contains(file)) {
                    Marshaller<PortalConfig> marshaller = operationContext.getBindingProvider()
                            .getMarshaller(PortalConfig.class, ContentType.XML);
                    templates.get(templates.size() - 1).portalConfig = marshaller.unmarshal(zis);
                } else if (file.equals(PageExportTask.FILE)) {
                    Marshaller<Page.PageSet> marshaller = operationContext.getBindingProvider()
                            .getMarshaller(Page.PageSet.class, ContentType.XML);
                    templates.get(templates.size() - 1).pageSet = marshaller.unmarshal(zis);
                } else if (file.equals(NavigationExportTask.FILE)) {
                    Marshaller<PageNavigation> marshaller = operationContext.getBindingProvider()
                            .getMarshaller(PageNavigation.class, ContentType.XML);
                    templates.get(templates.size() - 1).pageNavigation = marshaller.unmarshal(zis);
                }
            }

        } catch (Throwable t) {
            throw new OperationException(operationContext.getOperationName(), "Exception reading data for import.", t);
        } finally {
            try {
                zis.reallyClose();
            } catch (IOException e) {
                log.warn("Exception closing underlying data stream from import.");
            }
        }

        return templates;
    }

    private Map<SiteKey, MopImport> expandPortalTemplate(OperationAttributes attr,
                                                         BackendServices svc,
                                                         List<MopTemplate> templates)
            throws OperationException {

        Map<SiteKey, MopImport> expanded = new HashMap<SiteKey, MopImport>();
        try {
            // Pairs of (portalName, MopTemplate) filtered by targetSite
            Map<String, MopTemplate> portalNamesTemplates = filterPortalNames(attr, svc, templates);

            for (Map.Entry<String, MopTemplate> portalNameTemplate : portalNamesTemplates.entrySet()) {
                String portalName = portalNameTemplate.getKey();
                MopTemplate mopTemplate = portalNameTemplate.getValue();
                SiteKey newSiteKey = new SiteKey(SiteType.PORTAL, portalName);
                MopImport newMopImport = new MopImport();

                resolveTemplate(svc, newSiteKey, newMopImport, mopTemplate, portalName);

                expanded.put(newSiteKey, newMopImport);
            }

        } catch (Throwable t) {
            throw new OperationException("Import portal template", t.getMessage(), t);
        }

        return expanded;
    }

    private Map<String, MopTemplate> filterPortalNames(OperationAttributes attr,
                                                       BackendServices svc,
                                                       List<MopTemplate> templates)
            throws Exception {

        Map<String, MopTemplate> portalTemplates = new HashMap<String, MopTemplate>();
        List<String> portalNames = svc.dataStorage.getAllPortalNames();

        // Filters portals defined in targetSite attributes
        if (attr.targetSite.size() > 0) {
            Iterator<String> iterPortalNames = portalNames.iterator();
            while (iterPortalNames.hasNext()) {
                String name = iterPortalNames.next();
                boolean found = false;
                for (String targetSite : attr.targetSite) {
                    if (targetSite.equals(name)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    iterPortalNames.remove();
                }
            }
        }

        // Attaches portal templates
        for (String portalName : portalNames) {
            PortalConfig pConfig = svc.dataStorage.getPortalConfig(portalName);
            if (pConfig != null) {
                String portalTemplate = pConfig.getProperty("template");
                if (portalTemplate == null) {
                    log.warn("Portal " + portalName + " has not template property. " +
                            "It will not used in template import operation");
                } else {
                    for (MopTemplate mopTemplate : templates) {
                        if (mopTemplate.templateName.equals(portalTemplate)) {
                            portalTemplates.put(portalName, mopTemplate);
                            break;
                        }
                    }
                }
            }
        }

        return portalTemplates;
    }

    private void resolveTemplate(final BackendServices svc,
                                 SiteKey siteKey,
                                 MopImport mopImport,
                                 MopTemplate mopTemplate,
                                 String owner)
            throws Throwable {

        if (mopTemplate.portalConfig != null) {
            PortalConfig portalConfig = applyPattern(mopTemplate.portalConfig, owner);
            portalConfig.setType(siteKey.getTypeName());
            mopImport.siteTask = new SiteLayoutImportTask(portalConfig, siteKey, svc.dataStorage);
        }

        if (mopTemplate.pageSet != null) {
            Page.PageSet pageSet = applyPattern(mopTemplate.pageSet, owner);
            for (Page page : pageSet.getPages()) {
                page.setOwnerType(siteKey.getTypeName());
                page.setOwnerId(siteKey.getName());
            }
            // Obtains the site from the session when it's needed.
            MOPSiteProvider siteProvider = new MOPSiteProvider() {
                @Override
                public Site getSite(SiteKey siteKey) {
                    return svc.mgr.getSession().getWorkspace()
                            .getSite(Utils.getObjectType(siteKey.getType()), siteKey.getName());
                }
            };
            mopImport.pageTask = new PageImportTask(pageSet, siteKey, svc.dataStorage, svc.pageService, siteProvider);
        }

        if (mopTemplate.pageNavigation != null) {
            PageNavigation pageNavigation = applyPattern(mopTemplate.pageNavigation, owner);
            pageNavigation.setOwnerType(siteKey.getTypeName());
            pageNavigation.setOwnerId(siteKey.getName());
            mopImport.navigationTask = new NavigationImportTask(pageNavigation, siteKey, svc.navigationService,
                    svc.descriptionService, svc.dataStorage);
        }

    }

    /**
     * Applies "@owner@" pattern on following PortalConfig fields:
     * - name
     * - accessPermissions
     * - editPermissions
     *
     * "@owner@" pattern is not expected outside these fields.
     *
     * Note:
     * If pattern can appear in more fields may be it is better to store template as xml representation
     * and unmarshall after pattern replace
     *
     * @param template PortalConfig created from a xml template
     * @param owner final user who replace "@owner@" string in xml template
     * @return new PortalConfig object with @owner@ replaced
     */
    private PortalConfig applyPattern(PortalConfig template, String owner) {

        PortalConfig portalConfig = PageUtils.copy(template);

        if (portalConfig.getName() != null) {
            portalConfig.setName( portalConfig.getName().replaceAll(OWNER, owner) );
        }

        if (portalConfig.getAccessPermissions() != null) {
            for (int i=0; i<portalConfig.getAccessPermissions().length; i++) {
                if (portalConfig.getAccessPermissions()[i] != null) {
                    portalConfig.getAccessPermissions()[i] = portalConfig.getAccessPermissions()[i].replaceAll(OWNER, owner);
                }
            }
        }

        if (portalConfig.getEditPermission() != null) {
            portalConfig.setEditPermission( portalConfig.getEditPermission().replaceAll(OWNER, owner) );
        }

        return portalConfig;
    }

    /**
     * Applies "@owner@" pattern on following PageSet fields:
     * - name
     * - title
     * - accessPermissions
     * - editPermissions
     *
     * "@owner@" pattern is not expected outside these fields.
     *
     * Note:
     * If pattern can appear in more fields may be it is better to store template as xml representation
     * and unmarshall after pattern replace
     *
     * @param template PageSet created from a xml template
     * @param owner final user who replace "@owner@" string in xml template
     * @return new PageSet object with @owner@ replaced
     */
    private Page.PageSet applyPattern(Page.PageSet template, String owner) {

        Page.PageSet pageSet = PageUtils.copy(template);

        for (Page page : pageSet.getPages()) {
            if (page.getName() != null) {
                page.setName( page.getName().replaceAll(OWNER, owner) );
            }

            if (page.getTitle() != null) {
                page.setTitle( page.getTitle().replaceAll(OWNER, owner) );
            }

            if (page.getAccessPermissions() != null) {
                for (int i=0; i<page.getAccessPermissions().length; i++) {
                    if (page.getAccessPermissions()[i] != null) {
                        page.getAccessPermissions()[i] = page.getAccessPermissions()[i].replaceAll(OWNER, owner);
                    }
                }
            }

            if (page.getEditPermission() != null) {
                page.setEditPermission( page.getEditPermission().replaceAll(OWNER, owner) );
            }
        }

        return pageSet;
    }

    /**
     * Applies "@owner@" pattern on following PageNavigation fields:
     * - pageReference of PageNode objects
     *
     * "@owner@" pattern is not expected outside these fields.
     *
     * Note:
     * If pattern can appear in more fields may be it is better to store template as xml representation
     * and unmarshall after pattern replace
     *
     * @param template PageNavigation created from a xml template
     * @param owner final user who replace "@owner@" string in xml template
     * @return new PageNavigation object with @owner@ replaced
     */
    private PageNavigation applyPattern(PageNavigation template, String owner) {

        PageNavigation pageNavigation = NavigationUtils.copy(template);

        for (NavigationFragment fragment : pageNavigation.getFragments()) {
            for (PageNode pageNode : fragment.getNodes()) {
                applyPattern(pageNode, owner);
            }
        }

        return pageNavigation;
    }

    private void applyPattern(PageNode pageNode, String owner) {

        if (pageNode.getPageReference() != null) {
            pageNode.setPageReference( pageNode.getPageReference().replace(OWNER, owner) );
        }

        if (pageNode.getChildren() != null) {
            for (PageNode child : pageNode.getChildren()) {
                applyPattern(child, owner);
            }
        }

    }

    private Map<SiteKey, MopImport> expandGroupTemplate(OperationAttributes attr,
                                                        BackendServices svc,
                                                        List<MopTemplate> templates) {

        Map<SiteKey, MopImport> expanded = new HashMap<SiteKey, MopImport>();
        try {
            // Pairs of (groupName, MopTemplate) filtered by targetGroup
            Map<String, MopTemplate> groupNamesTemplates = filterGroupNames(attr, svc, templates);

            for (Map.Entry<String, MopTemplate> groupNameTemplate : groupNamesTemplates.entrySet()) {
                String groupName = groupNameTemplate.getKey();
                MopTemplate mopTemplate = groupNameTemplate.getValue();
                SiteKey newSiteKey = new SiteKey(SiteType.GROUP, groupName);
                MopImport newMopImport = new MopImport();

                resolveTemplate(svc, newSiteKey, newMopImport, mopTemplate, groupName);

                expanded.put(newSiteKey, newMopImport);
            }

        } catch (Throwable t) {
            throw new OperationException("Import group template", t.getMessage(), t);
        }

        return expanded;
    }

    private Map<String, MopTemplate> filterGroupNames(OperationAttributes attr,
                                                      BackendServices svc,
                                                      List<MopTemplate> templates)
            throws Exception {

        Map<String, MopTemplate> groupTemplates = new HashMap<String, MopTemplate>();
        List<String> groupNames = svc.dataStorage.getAllGroupNames();

        // Filters groups defined in targetGroup attributes
        if (attr.targetGroup.size() > 0) {
            Iterator<String> iterGroupNames = groupNames.iterator();
            while (iterGroupNames.hasNext()) {
                String name = iterGroupNames.next();
                if (!attr.targetGroup.contains(name)) {
                    iterGroupNames.remove();
                }
            }
        }

        // Filters groups with Navigation activated
        Iterator<String> iterGroupNames = groupNames.iterator();
        while (iterGroupNames.hasNext()) {
            String name = iterGroupNames.next();
            NavigationContext navigation = svc.navigationService.loadNavigation(SiteKey.group(name));
            if (navigation == null || navigation.getState() == null) {
                iterGroupNames.remove();
            }
        }

        for (String groupName : groupNames) {
            for (MopTemplate mopTemplate : templates) {
                if (mopTemplate.templateType.equals(SiteType.GROUP)) {
                    groupTemplates.put(groupName, mopTemplate);
                }
            }
        }

        return groupTemplates;
    }

    private Map<SiteKey, MopImport> expandUserTemplate(OperationAttributes attr,
                                                       BackendServices svc,
                                                       List<MopTemplate> templates) {

        Map<SiteKey, MopImport> expanded = new HashMap<SiteKey, MopImport>();
        try {
            // Pairs of (userName, MopTemplate) filtered by groups defined in targetUser attribute
            Map<String, MopTemplate> userNamesTemplates = filterUserNames(attr, svc, templates);

            for (Map.Entry<String, MopTemplate> userNameTemplate : userNamesTemplates.entrySet()) {
                String userName = userNameTemplate.getKey();
                MopTemplate mopTemplate = userNameTemplate.getValue();
                SiteKey newSiteKey = new SiteKey(SiteType.USER, userName);
                MopImport newMopImport = new MopImport();

                resolveTemplate(svc, newSiteKey, newMopImport, mopTemplate, userName);

                expanded.put(newSiteKey, newMopImport);
            }

        } catch (Throwable t) {
            throw new OperationException("Import user template", t.getMessage(), t);
        }

        return expanded;
    }

    private Map<String, MopTemplate> filterUserNames(OperationAttributes attr,
                                                     BackendServices svc,
                                                     List<MopTemplate> templates)
            throws Throwable {

        Map<String, MopTemplate> userTemplates = new HashMap<String, MopTemplate>();
        List<String> userNames = new ArrayList<String>();

        ListAccess<User> lUsers;

        // targetExpr attribute has preference over targetUser attribute
        if (attr.targetExpr != null && !"".equals(attr.targetExpr)) {
            Query qUsers = new Query();
            qUsers.setUserName(attr.targetExpr);
            lUsers = svc.organizationService.getUserHandler().findUsersByQuery(qUsers);
            for (User u : lUsers.load(0, lUsers.getSize())) {
                userNames.add(u.getUserName());
            }

        } else if (attr.targetUser != null && attr.targetUser.size() > 0) {
            // Validates that a user exists
            for (String u : attr.targetUser) {
                User user = svc.organizationService.getUserHandler().findUserByName(u);
                if (user != null) {
                    userNames.add(user.getUserName());
                }
            }
        } else {
            lUsers = svc.organizationService.getUserHandler().findAllUsers();
            for (User u : lUsers.load(0, lUsers.getSize())) {
                userNames.add(u.getUserName());
            }
        }

        if (attr.dashboardMode == null || !CREATE.equalsIgnoreCase(attr.dashboardMode)) {
            // Filters users with dashboard created
            Iterator<String> iterNames = userNames.iterator();
            while (iterNames.hasNext()) {
                String userName = iterNames.next();
                PortalConfig portalConfig = svc.dataStorage.getPortalConfig("user", userName);
                if (portalConfig == null) {
                    iterNames.remove();
                }
            }
        }

        for (String userName : userNames) {
            for (MopTemplate mopTemplate : templates) {
                if (mopTemplate.templateType.equals(SiteType.USER)) {
                    userTemplates.put(userName, mopTemplate);
                }
            }
        }

        return userTemplates;
    }

    private void validationRules(OperationAttributes attr, Map<SiteKey, MopImport> importMap) {

        for (Map.Entry<SiteKey, MopImport> mopImportEntry : importMap.entrySet()) {
            SiteKey siteKey = mopImportEntry.getKey();
            MopImport mopImport = mopImportEntry.getValue();

            // Rule #1: Send a warning if navigation.xml alone
            if (mopImport.siteTask == null &&
                mopImport.pageTask == null &&
                mopImport.navigationTask != null) {
                log.warn("Importing a template with only navigation.xml file. " +
                        "You should validate <page-reference> points to valid pages, " +
                        "if not this can create unstable references.");
            }

            // Rule #2: Send a warning if navigation.xml <page-reference> doesn't match with siteKey type
            List<String> refPages = new ArrayList<String>();
            if (mopImport.navigationTask != null) {
                PageNavigation pageNavigation = mopImport.navigationTask.getData();
                SiteType siteType = siteKey.getType();
                for (NavigationFragment fragment : pageNavigation.getFragments()) {
                    validateNodeType(siteType, fragment.getNodes(), refPages);
                }
            }

            // Rule #3: Send a warning if navigation.xml has <page-reference> without pointing to pages.xml
            if (mopImport.pageTask != null &&
                mopImport.navigationTask != null) {
                Page.PageSet pageSet = mopImport.pageTask.getData();
                if (pageSet != null) {
                    for (Page page : pageSet.getPages()) {
                        String name = page.getName();
                        if (!refPages.contains(name)) {
                            log.warn("pages.xml contains <page> not referenced on navigation.xml");
                        }
                    }
                }
            }

            // Rule #4: Send a warning if dashboardMode == create and missing some {user,pages,navigation}.xml file
            if (siteKey.getType() == SiteType.USER &&
                CREATE.equalsIgnoreCase(attr.dashboardMode) &&
                ( mopImport.siteTask == null ||
                  mopImport.pageTask == null ||
                  mopImport.navigationTask == null )) {
                log.warn("dashboardMode == " + attr.dashboardMode +
                         " and missing some {user,pages,navigation}.xml in .zip file.");
            }
        }

    }

    private void validateNodeType(SiteType siteType, List<PageNode> nodes, List<String> refPages) {

        if (nodes != null) {
            for (PageNode node : nodes) {
                String pageReference = node.getPageReference();
                if (pageReference != null) {
                    if (pageReference.startsWith("portal") && siteType != SiteType.PORTAL) {
                        log.warn("Detected navigation.xml with <page-reference> pointing to a portal pages for a site type "
                                + siteType + ".");
                    } else if (pageReference.startsWith("group") && siteType != SiteType.GROUP) {
                        log.warn("Detected navigation.xml with <page-reference> pointing to a group pages for a site type "
                                + siteType + ".");
                    } else if (pageReference.startsWith("user") && siteType != SiteType.USER) {
                        log.warn("Detected navigation.xml with <page-reference> pointing to a user pages for a site type "
                                + siteType + ".");
                    }
                    // Save referenced page for other validations
                    String[] chunks = pageReference.split("::");
                    if (chunks.length == 3) {
                        refPages.add(chunks[2]);
                    }
                }
                if (node.getChildren() != null) {
                    validateNodeType(siteType, node.getChildren(), refPages);
                }
            }
        }

    }

    private OperationException performImport(OperationAttributes attr, Map<SiteKey, MopImport> importMap)
            throws OperationException {

        // Performs import
        Map<SiteKey, MopImport> importsRan = new HashMap<SiteKey, MopImport>();
        OperationException importError = null;
        try {
            log.info("Performing import using importMode '" + attr.importMode + "'");
            for (Map.Entry<SiteKey, MopImport> mopImportEntry : importMap.entrySet()) {
                SiteKey siteKey = mopImportEntry.getKey();
                MopImport mopImport = mopImportEntry.getValue();
                MopImport ran = new MopImport();

                if (importsRan.containsKey(siteKey)) {
                    throw new IllegalStateException("Multiple site imports for same operation.");
                }
                importsRan.put(siteKey, ran);

                log.debug("Importing data for site " + siteKey);

                // Site layout import
                if (mopImport.siteTask != null) {
                    log.debug("Importing site layout data.");
                    ran.siteTask = mopImport.siteTask;
                    mopImport.siteTask.importData(attr.importMode);
                }

                // Pages import
                if (mopImport.pageTask != null) {
                    log.debug("Importing page data.");
                    ran.pageTask = mopImport.pageTask;
                    mopImport.pageTask.importData(attr.importMode);
                }

                // Navigation import
                if (mopImport.navigationTask != null) {
                    log.debug("Importing navigation data.");
                    ran.navigationTask = mopImport.navigationTask;
                    mopImport.navigationTask.importData(attr.importMode);
                }
            }
            log.info("Import successful !");
        } catch (Throwable t) {
            boolean rollbackSuccess = true;
            log.error("Exception importing data.", t);
            log.info("Attempting to rollback data modified by import.");
            for (Map.Entry<SiteKey, MopImport> mopImportEntry : importsRan.entrySet()) {
                SiteKey siteKey = mopImportEntry.getKey();
                MopImport mopImport = mopImportEntry.getValue();

                log.debug("Rolling back imported data for site " + siteKey);
                if (mopImport.navigationTask != null) {
                    log.debug("Rolling back navigation modified during import...");
                    try {
                        mopImport.navigationTask.rollback();
                    } catch (Throwable t1) // Continue rolling back even though there are exceptions.
                    {
                        rollbackSuccess = false;
                        log.error("Error rolling back navigation data for site " + siteKey, t1);
                    }
                }
                if (mopImport.pageTask != null) {
                    log.debug("Rolling back pages modified during import...");
                    try {
                        mopImport.pageTask.rollback();
                    } catch (Throwable t1) // Continue rolling back even though there are exceptions.
                    {
                        rollbackSuccess = false;
                        log.error("Error rolling back page data for site " + siteKey, t1);
                    }
                }
                if (mopImport.siteTask != null) {
                    log.debug("Rolling back site layout modified during import...");
                    try {
                        mopImport.siteTask.rollback();
                    } catch (Throwable t1) // Continue rolling back even though there are exceptions.
                    {
                        rollbackSuccess = false;
                        log.error("Error rolling back site layout for site " + siteKey, t1);
                    }
                }
            }

            String message = (rollbackSuccess) ?
                    "Error during import. Tasks successfully rolled back. Portal should be back to consistent state."
                    : "Error during import. Errors in rollback as well. Portal may be in an inconsistent state.";

            importError = new OperationException(attr.operationName, message, t);
        } finally {
            importMap.clear();
            importsRan.clear();
        }

        return importError;
    }

    // See GTNPORTAL-3257
    private static void endRequest(OperationAttributes attr, BackendServices svc, OperationException importError) {

        OperationException error = importError;
        try {
            // End the request to flush out anything that might go wrong when finalizing the request.
            svc.chromatticManager.endRequest(true);
        } catch (Throwable t) {
            // This allows us to properly respond with an error (500 in REST scenario) if ChromatticManager.endRequest fails
            if (importError == null) {
                log.error("Exception occurred ending the request of ChromatticManager after a successful import.", t);
                error = new OperationException(attr.operationName,
                                               "An exception occurred after a successful import. " +
                                               "See server logs for more details");
            } else {
                log.error("Exception occurred ending the request of ChromatticManager after a failed import.", t);
            }
        } finally {
            // Start it again, as the calling container ends all ComponentRequestLifecycle's,
            // and we don't want the end to be called w/out it beginning again.
            svc.chromatticManager.beginRequest();
        }

        if (error != null) {
            throw error;
        }

    }

    private static String[] parseEntry(ZipEntry entry, boolean portal)
            throws IOException {

        String name = entry.getName();
        if (isSiteLayoutEntry(name) || name.endsWith(PageExportTask.FILE) || name.endsWith(NavigationExportTask.FILE)) {
            String[] parts;
            if (portal) {
                parts = new String[4];
                parts[0] = name.substring(0, name.indexOf("/"));
                parts[1] = name.substring(parts[0].length() + 1, name.indexOf("/", parts[0].length() + 1));
                parts[2] = name.substring(parts[0].length() + parts[1].length() + 2, name.indexOf("/", parts[0].length()
                        + parts[1].length() + 3));
                parts[3] = name.substring(name.lastIndexOf("/") + 1);
            } else {
                parts = new String[3];
                parts[0] = name.substring(0, name.indexOf("/"));
                parts[1] = name.substring(parts[0].length() + 1, name.lastIndexOf("/"));
                parts[2] = name.substring(name.lastIndexOf("/") + 1);
            }
            return parts;
        } else {
            throw new IOException("Unknown entry " + name + " in zip file.");
        }

    }

    private static boolean isSiteLayoutEntry(String zipEntryName) {

        for (String file : SiteLayoutExportTask.FILES) {
            if (zipEntryName.endsWith(file))
                return true;
        }

        return false;
    }

    // Bug in SUN's JDK XMLStreamReader implementation closes the underlying stream when
    // it finishes reading an XML document. This is no good when we are using a ZipInputStream.
    // See http://bugs.sun.com/view_bug.do?bug_id=6539065 for more information.
    private static class NonCloseableZipInputStream extends ZipInputStream {

        private NonCloseableZipInputStream(InputStream inputStream) {
            super(inputStream);
        }

        @Override
        public void close() throws IOException {
        }

        private void reallyClose() throws IOException {
            super.close();
        }

    }

    private static class MopImport {

        private SiteLayoutImportTask siteTask;
        private PageImportTask pageTask;
        private NavigationImportTask navigationTask;

    }

    private static class OperationAttributes {

        private String operationName;
        private String importType;
        private String mode;
        private ImportMode importMode;
        private String targetExpr;
        private List<String> targetUser;
        private String dashboardMode;
        private List<String> targetGroup;
        private List<String> targetSite;

    }

    private static class BackendServices {

        private Workspace workspace = null;
        private DataStorage dataStorage = null;
        private PageService pageService = null;
        private NavigationService navigationService = null;
        private DescriptionService descriptionService = null;
        private POMSessionManager mgr = null;
        private OrganizationService organizationService = null;
        private ChromatticManager chromatticManager = null;

    }

    private static class MopTemplate {

        private String templateName;
        private SiteType templateType;
        private PortalConfig portalConfig;
        private Page.PageSet pageSet;
        private PageNavigation pageNavigation;

    }

    private static class ZipTemplateException extends Exception {

        public ZipTemplateException(String message) {
            super(message);
        }
    }
}
TOP

Related Classes of org.exoplatform.portal.mop.management.operations.TemplateImportResource$ZipTemplateException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.