org.alfresco.repo.admin.patch.impl.SiteLoadPatch.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.admin.patch.impl.SiteLoadPatch.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * 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.admin.patch.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.cmr.view.ImporterBinding.UUID_BINDING;
import org.alfresco.service.descriptor.Descriptor;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;

/**
 * A Patch based importer which creates and populates
 *  a site based on the supplied data
 * 
 * @author Nick Burch
 */
public class SiteLoadPatch extends AbstractPatch {
    public static final String PROPERTIES_USERS = "users";
    public static final String PROPERTIES_PEOPLE = "people";
    public static final String PROPERTIES_GROUPS = "groups";
    public static final String PROPERTIES_CONTENTS = "contents";

    private static final Map<String, String> DEFAULT_PATHS = new HashMap<String, String>();
    static {
        DEFAULT_PATHS.put(PROPERTIES_USERS,
                "/${alfresco_user_store.system_container.childname}/${alfresco_user_store.user_container.childname}");
        DEFAULT_PATHS.put(PROPERTIES_PEOPLE,
                "/${system.system_container.childname}/${system.people_container.childname}");
        DEFAULT_PATHS.put(PROPERTIES_GROUPS, null);
        DEFAULT_PATHS.put(PROPERTIES_CONTENTS, "/${spaces.company_home.childname}/${spaces.sites.childname}");
    }

    private static final String MSG_SITE_ALREADY_EXISTS = "patch.siteLoadPatch.exists";
    private static final String MSG_NO_BOOTSTRAP_VIEWS_GIVEN = "patch.siteLoadPatch.noBootstrapViews";
    private static final String MSG_SITE_CREATED = "patch.siteLoadPatch.result";
    private static final String MSG_SITE_NOT_CREATED = "patch.siteLoadPatch.siteNotCreated";
    private static final String MSG_SITE_LOAD_DISABLED = "patch.siteLoadPatch.siteLoadDisabled";

    // Logger
    private static final Log logger = LogFactory.getLog(SiteLoadPatch.class);

    private AuthorityService authorityService;
    private BehaviourFilter behaviourFilter;
    private SiteService siteService;
    private DescriptorService descriptorService;

    private String siteName;

    private ImporterBootstrap spacesBootstrap;
    private ImporterBootstrap usersBootstrap;

    private Map<String, Properties> bootstrapViews;

    private Boolean disabled = false;

    public SiteLoadPatch() {
        // We do need to run in our own transaction
        setRequiresTransaction(true);
    }

    /**
     * Sets the name of the site to be bootstrapped.
     * 
     * @param siteName The short name of the site
     */
    public void setSiteName(String siteName) {
        this.siteName = siteName;
    }

    public void setSpacesBootstrap(ImporterBootstrap spacesBootstrap) {
        this.spacesBootstrap = spacesBootstrap;
    }

    public void setUsersBootstrap(ImporterBootstrap usersBootstrap) {
        this.usersBootstrap = usersBootstrap;
    }

    /**
     * Sets the details of the bootstraps to perform
     */
    public void setBootstrapViews(Map<String, Properties> bootstrapViews) {
        this.bootstrapViews = bootstrapViews;
    }

    /**
     * Sets the Site Service to be used for importing into
     * 
     * @param siteService The Site Service
     */
    public void setSiteService(SiteService siteService) {
        this.siteService = siteService;
    }

    /**
     * Sets the Authority Service to be used for groups and people
     * 
     * @param authorityService The Authority Service
     */
    public void setAuthorityService(AuthorityService authorityService) {
        this.authorityService = authorityService;
    }

    /**
     * @param descriptorService the descriptorService to set
     */
    public void setDescriptorService(DescriptorService descriptorService) {
        this.descriptorService = descriptorService;
    }

    public void setBehaviourFilter(BehaviourFilter behaviourFilter) {
        this.behaviourFilter = behaviourFilter;
    }

    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    public boolean isDisabled() {
        return disabled;
    }

    @Override
    protected void checkProperties() {
        super.checkProperties();
        PropertyCheck.mandatory(this, "siteService", siteService);
        PropertyCheck.mandatory(this, "nodeService", nodeService);
        PropertyCheck.mandatory(this, "siteName", siteName);
    }

    @Override
    protected String applyInternal() throws Exception {
        //skip sites that we don't want imported automatically
        if (isDisabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Load of site \"" + siteName + "\" is disabled.");
            }
            return I18NUtil.getMessage(MSG_SITE_LOAD_DISABLED, siteName);
        }
        AuthenticationUtil.pushAuthentication();
        try {
            // The site service is funny about permissions,
            //  so even though we're running as the system we
            //  still need to identify us as the admin user
            AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

            return applyInternalImpl();
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Load the site in.<br/>
     * Site will be loaded as admin user.
     */
    private String applyInternalImpl() throws Exception {
        if (descriptorService != null) {
            // if the descriptor service is wired up only load the site at install time (and not on upgrade)
            Descriptor installed = descriptorService.getInstalledRepositoryDescriptor();
            Descriptor live = descriptorService.getServerDescriptor();

            if (!installed.getVersion().equals(live.getVersion())) {
                return I18NUtil.getMessage(MSG_SITE_NOT_CREATED, siteName);
            }
        }
        if (bootstrapViews == null || bootstrapViews.size() == 0) {
            if (logger.isDebugEnabled()) {
                logger.debug("No Bootstraps given to import from - bootstrap import ignored");
            }
            return I18NUtil.getMessage(MSG_NO_BOOTSTRAP_VIEWS_GIVEN, siteName);
        }

        // Is the site already there?
        // (Run now as we need DB + Security Context)
        if (siteService.getSite(siteName) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Site " + siteName + " already exists - bootstrap import ignored");
            }
            return I18NUtil.getMessage(MSG_SITE_ALREADY_EXISTS, siteName);
        }

        // If we get here, we're good to go!
        if (logger.isDebugEnabled()) {
            logger.debug("Performing bootstrap of site " + siteName);
        }

        // Create the site as the admin user
        SiteInfo site = siteService.createSite(siteName, siteName, siteName, null, SiteVisibility.PUBLIC);

        // At this point we can go back to being the system
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());

        // Setup the Importer Bootstrap Beans
        for (ImporterBootstrap bootstrap : new ImporterBootstrap[] { spacesBootstrap, usersBootstrap }) {
            bootstrap.setAllowWrite(true);
            bootstrap.setUseExistingStore(true);
            bootstrap.setUuidBinding(UUID_BINDING.REPLACE_EXISTING);
        }

        // Normally paths aren't given for the views, so supply
        //  the defaults where they weren't
        for (String type : DEFAULT_PATHS.keySet()) {
            Properties props = bootstrapViews.get(type);
            if (props != null && DEFAULT_PATHS.get(type) != null) {
                if (!props.containsKey("path")) {
                    props.setProperty("path", DEFAULT_PATHS.get(type));
                }
            }
        }

        // Load our various bootstraps, in the required order
        //  for things to come in correctly

        // Load any users requested
        if (bootstrapViews.containsKey(PROPERTIES_USERS)) {
            List<Properties> views = new ArrayList<Properties>(1);
            views.add(bootstrapViews.get(PROPERTIES_USERS));
            usersBootstrap.setBootstrapViews(views);
            usersBootstrap.bootstrap();
        }

        // Load any people requested
        if (bootstrapViews.containsKey(PROPERTIES_PEOPLE)) {
            List<Properties> views = new ArrayList<Properties>(1);
            views.add(bootstrapViews.get(PROPERTIES_PEOPLE));
            spacesBootstrap.setBootstrapViews(views);
            spacesBootstrap.bootstrap();
        }

        // Put people into groups
        if (bootstrapViews.containsKey(PROPERTIES_GROUPS)) {
            try {
                doGroupImport(bootstrapViews.get(PROPERTIES_GROUPS).getProperty("location"));
            } catch (Throwable t) {
                throw new AlfrescoRuntimeException("Bootstrap failed", t);
            }
        }

        // Load the Main (ACP) Contents
        if (bootstrapViews.containsKey(PROPERTIES_CONTENTS)) {
            // Disable the behaviour which prevents site deletion.
            behaviourFilter.disableBehaviour(site.getNodeRef(), ContentModel.ASPECT_UNDELETABLE);
            try {
                // Clear up the stub content that createSite gave us, first
                // apply the temporary aspect though to prevent the node from
                // being archived
                nodeService.addAspect(site.getNodeRef(), ContentModel.ASPECT_TEMPORARY, null);
                nodeService.deleteNode(site.getNodeRef());
            } finally {
                behaviourFilter.enableBehaviour(site.getNodeRef(), ContentModel.ASPECT_UNDELETABLE);
            }

            // Now load in the real content from the ACP
            List<Properties> views = new ArrayList<Properties>(1);
            views.add(bootstrapViews.get(PROPERTIES_CONTENTS));
            spacesBootstrap.setBootstrapViews(views);
            spacesBootstrap.bootstrap();
        }

        return I18NUtil.getMessage(MSG_SITE_CREATED, siteName);
    }

    /**
     * Note - This Could potentially be split out into another Bootstrap class,
     *  but for now is inline to keep things simple
     */
    private void doGroupImport(String location) throws Throwable {
        File groupFile = ImporterBootstrap.getFile(location);
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(groupFile), "UTF-8"));

        String line;
        while ((line = reader.readLine()) != null) {
            int splitAt = line.indexOf('=');
            if (splitAt == -1) {
                logger.warn("Invalid group line " + line);
                continue;
            }

            String user = line.substring(0, splitAt);
            Set<String> currentGroups = authorityService.getAuthoritiesForUser(user);

            StringTokenizer groups = new StringTokenizer(line.substring(splitAt + 1), ",");
            while (groups.hasMoreTokens()) {
                String group = groups.nextToken();
                if (!currentGroups.contains(group)) {
                    authorityService.addAuthority(group, user);

                    if (logger.isDebugEnabled()) {
                        logger.debug("Added user " + user + " to group " + group);
                    }
                }
            }
        }
        reader.close();
    }
}