it.doqui.index.ecmengine.business.personalization.multirepository.bootstrap.MultiTAdminServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for it.doqui.index.ecmengine.business.personalization.multirepository.bootstrap.MultiTAdminServiceImpl.java

Source

/* Index ECM Engine - A system for managing the capture (when created
 * or received), classification (cataloguing), storage, retrieval,
 * revision, sharing, reuse and disposition of documents.
 *
 * Copyright (C) 2008 Regione Piemonte
 * Copyright (C) 2008 Provincia di Torino
 * Copyright (C) 2008 Comune di Torino
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

package it.doqui.index.ecmengine.business.personalization.multirepository.bootstrap;

import it.doqui.index.ecmengine.business.personalization.multirepository.Repository;
import it.doqui.index.ecmengine.business.personalization.multirepository.RepositoryManager;
import it.doqui.index.ecmengine.business.personalization.multirepository.ContentStoreDefinition;
import it.doqui.index.ecmengine.business.personalization.multirepository.Tenant;
import it.doqui.index.ecmengine.business.personalization.multirepository.util.EcmEngineMultirepositoryConstants;

import it.doqui.index.ecmengine.business.personalization.multirepository.index.RepositoryAwareIndexerAndSearcher;
import org.alfresco.service.cmr.security.AuthenticationService;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
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.regex.Pattern;

import javax.transaction.UserTransaction;

import net.sf.acegisecurity.providers.encoding.PasswordEncoder;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.admin.RepoModelDefinition;
import org.alfresco.repo.attributes.BooleanAttributeValue;
import org.alfresco.repo.attributes.MapAttribute;
import org.alfresco.repo.attributes.Attribute;
import org.alfresco.repo.attributes.MapAttributeValue;
import org.alfresco.repo.attributes.StringAttributeValue;
import org.alfresco.repo.content.TenantRoutingFileContentStore;
import org.alfresco.repo.dictionary.DictionaryComponent;
import org.alfresco.repo.node.db.DbNodeServiceImpl;
import org.alfresco.repo.node.index.NodeIndexer;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantDeployer;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.core.io.ClassPathResource;

import it.doqui.index.ecmengine.business.personalization.splitting.SplittingDbNodeServiceImpl;

public class MultiTAdminServiceImpl extends org.alfresco.repo.tenant.MultiTAdminServiceImpl
        implements MultiTTenantAdminService {
    // Logger
    private static Logger logger = Logger
            .getLogger(EcmEngineMultirepositoryConstants.MULTIREPOSITORY_BOOTSTRAP_LOG_CATEGORY);

    // Dependencies
    private DbNodeServiceImpl nodeService; // TODO - replace with NodeService, when deleteStore is exposed via public API
    private DictionaryComponent dictionaryComponent;
    private RepoAdminService repoAdminService;
    private AuthenticationComponent authenticationComponent;
    private TransactionService transactionService;
    private MultiTServiceImpl tenantService;
    private AttributeService attributeService;
    private PasswordEncoder passwordEncoder;
    private TenantRoutingFileContentStore tenantFileContentStore;
    private WorkflowService workflowService;

    protected final static String REGEX_VALID_TENANT_NAME = "^[a-zA-Z0-9]([a-zA-Z0-9]|.[a-zA-Z0-9])*$"; // note: must also be a valid filename

    public void setNodeService(DbNodeServiceImpl dbNodeService) {
        this.nodeService = dbNodeService;
    }

    public void setDictionaryComponent(DictionaryComponent dictionaryComponent) {
        this.dictionaryComponent = dictionaryComponent;
    }

    public void setRepoAdminService(RepoAdminService repoAdminService) {
        this.repoAdminService = repoAdminService;
    }

    public void setAuthenticationComponent(AuthenticationComponent authenticationComponent) {
        this.authenticationComponent = authenticationComponent;
    }

    public void setTransactionService(TransactionService transactionService) {
        this.transactionService = transactionService;
    }

    public void setTenantService(MultiTServiceImpl tenantService) {
        this.tenantService = tenantService;
    }

    public void setAttributeService(AttributeService attributeService) {
        this.attributeService = attributeService;
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    public void setTenantFileContentStore(TenantRoutingFileContentStore tenantFileContentStore) {
        this.tenantFileContentStore = tenantFileContentStore;
    }

    public void setWorkflowService(WorkflowService workflowService) {
        this.workflowService = workflowService;
    }

    private RepositoryManager repositoryManager;

    public void setRepositoryManager(RepositoryManager repositoryManager) {
        this.repositoryManager = repositoryManager;
    }

    private AuthenticationService authenticationService;

    public void setAuthenticationService(AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }

    public static final String PROTOCOL_STORE_USER = "user";
    public static final String PROTOCOL_STORE_WORKSPACE = "workspace";
    public static final String PROTOCOL_STORE_SYSTEM = "system";
    public static final String PROTOCOL_STORE_ARCHIVE = "archive";
    public static final String STORE_BASE_ID_USER = "alfrescoUserStore";
    public static final String STORE_BASE_ID_SYSTEM = "system";
    public static final String STORE_BASE_ID_VERSION = "lightWeightVersionStore";
    public static final String STORE_BASE_ID_SPACES = "SpacesStore";

    private static final String TENANTS_ATTRIBUTE_PATH = "alfresco-tenants";
    private static final String TENANT_ATTRIBUTE_ENABLED = "enabled";
    private static final String TENANT_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir";

    private static final String TENANT_CONTENT_STORE_TYPE = "contentStore-type";
    private static final String TENANT_CONTENT_STORE_PROTOCOL = "contentStore-protocol";
    private static final String TENANT_CONTENT_STORE_RESOURCE = "contentStore-resource";

    private static final String ADMIN_BASENAME = TenantService.ADMIN_BASENAME;

    private Map<String, List<TenantDeployer>> tenantDeployerMap = new HashMap<String, List<TenantDeployer>>();

    // Occorre riassegnare ns nel modo corretto .. forse basta aggiustare il nodeService
    // per il momento lo splitting lo usiamo solo per la tenant delete, in futuro verr utilizzato per tutte le operazioni
    SplittingDbNodeServiceImpl ns = null;

    @Override
    protected void onBootstrap(ApplicationEvent event) {
        logger.debug("[MultiTAdminServiceImpl::onBootstrap] BEGIN");

        ns = (SplittingDbNodeServiceImpl) getApplicationContext().getBean("splittingDbNodeServiceImpl");

        for (Repository repository : repositoryManager.getRepositories()) {
            RepositoryManager.setCurrentRepository(repository.getId());
            logger.info("[MultiTAdminServiceImpl::onBootstrap] Repository '"
                    + RepositoryManager.getCurrentRepository() + "' -- Executing multi-tenant admin bootstrap.");

            // initialise the tenant admin service and status of tenants (using attribute service)
            // note: this requires that the repository schema has already been initialised

            // register dictionary - to allow enable/disable tenant callbacks
            register(dictionaryComponent);

            // register file store - to allow enable/disable tenant callbacks
            register(tenantFileContentStore);

            UserTransaction userTransaction = transactionService.getUserTransaction();
            authenticationComponent.setSystemUserAsCurrentUser();

            try {
                userTransaction.begin();

                // bootstrap Tenant Service internal cache
                List<org.alfresco.repo.tenant.Tenant> tenants = getAllTenants();

                int enabledCount = 0;
                int disabledCount = 0;

                if (tenants != null) {
                    for (org.alfresco.repo.tenant.Tenant tenant : tenants) {
                        if (tenant.isEnabled()) {
                            // this will also call tenant deployers registered so far ...
                            enableTenant(tenant.getTenantDomain(), true);
                            enabledCount++;
                        } else {
                            // explicitly disable, without calling disableTenant callback
                            disableTenant(tenant.getTenantDomain(), false);
                            disabledCount++;
                        }
                    }

                    tenantService.register(this); // callback to refresh tenantStatus cache
                }

                userTransaction.commit();

                if (logger.isInfoEnabled()) {
                    logger.info(
                            String.format("Alfresco Multi-Tenant startup - %d enabled tenants, %d disabled tenants",
                                    enabledCount, disabledCount));
                }
            } catch (Throwable e) {
                // rollback the transaction
                try {
                    if (userTransaction != null) {
                        userTransaction.rollback();
                    }
                } catch (Exception ex) {
                }
                try {
                    authenticationComponent.clearCurrentSecurityContext();
                } catch (Exception ex) {
                }
                throw new AlfrescoRuntimeException("Failed to bootstrap tenants", e);
            }
        }
        logger.debug("[MultiTAdminServiceImpl::onBootstrap] END");
    }

    @Override
    protected void onShutdown(ApplicationEvent event) {
        //tenantDeployerMap.clear();
    }

    /**
     * @see TenantAdminService.createTenant()
     */
    public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword) {
        createTenant(tenantDomain, tenantAdminRawPassword, null);
    }

    /**
     * @see TenantAdminService.createTenant()
     */
    public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword,
            String rootContentStoreDir) {
        createTenant(tenantDomain, tenantAdminRawPassword, rootContentStoreDir, null);
    }

    /**
     * @see TenantAdminService.createTenant()
     */
    public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword,
            String rootContentStoreDir, List<ContentStoreDefinition> stores) {
        logger.debug("[MultiTAdminServiceImpl::createTenant] BEGIN");
        // Check that all the passed values are not null
        ParameterCheck.mandatory("tenantDomain", tenantDomain);
        ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword);

        if (!Pattern.matches(REGEX_VALID_TENANT_NAME, tenantDomain)) {
            throw new IllegalArgumentException(
                    tenantDomain + " is not a valid tenant name (must match " + REGEX_VALID_TENANT_NAME + ")");
        }

        if (existsTenant(tenantDomain)) {
            throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain);
        } else {
            authenticationComponent.setSystemUserAsCurrentUser();

            // Se rootContentStoreDir e' null o vuoto, lo riempo col path del repository padre
            if (rootContentStoreDir == null || rootContentStoreDir.length() == 0) {
                rootContentStoreDir = tenantFileContentStore.getDefaultRootDir();
            }

            logger.debug("[MultiTAdminServiceImpl::createTenant] rootContentStoreDir: " + rootContentStoreDir);

            // init - need to enable tenant (including tenant service) before stores bootstrap
            Tenant t = new Tenant(tenantDomain, true, rootContentStoreDir);
            // MB: Imposto i contentStore
            t.setContentStores(stores);

            putTenantAttributes(tenantDomain, t);

            AuthenticationUtil.runAs(new RunAsWork<Object>() {
                public Object doWork() {
                    logger.info("[MultiTAdminServiceImpl::createTenant] INIT doWork");
                    dictionaryComponent.init();
                    // DoQui 06/05/2008
                    // WORKAROUND:
                    //   Nella configurazione multirepository il metodo init() esegue
                    //   l'inizializzazione per tutti i repository configurati, ma in
                    //   questo caso e` necessario inizializzare solo il repository
                    //   corrente.
                    //   Il metodo onEnableTenant() esegue la stessa operazione, ma solo
                    //   sul repository corrente (vedere implementazione della classe
                    //   it.doqui.index.ecmengine.business.personalization.multirepository.TenantRoutingFileContentStore
                    //   per i dettagli).
                    //tenantFileContentStore.init();
                    tenantFileContentStore.onEnableTenant();

                    // create tenant-specific stores
                    bootstrapUserTenantStore(tenantDomain, tenantAdminRawPassword);
                    bootstrapSystemTenantStore(tenantDomain);
                    bootstrapVersionTenantStore(tenantDomain);
                    bootstrapSpacesArchiveTenantStore(tenantDomain);
                    bootstrapSpacesTenantStore(tenantDomain);

                    List<TenantDeployer> tenantDeployers = getTenantDeployers();
                    // notify listeners that tenant has been created & hence enabled
                    for (TenantDeployer tenantDeployer : tenantDeployers) {
                        tenantDeployer.onEnableTenant();
                    }

                    /*
                    // MB: Aggiorno la password dell'utente ADMIN, in modo da forzare la creazione
                    // di una nuova alf_transaction. Questa transazione e' replicata fra i nodi
                    // dei cluster, e anche sulla partizione batch, forzando la reindicizzazione
                    // dell'utente admin. Utente che, a volte, non e' indicizzato e non permette
                    // il login su partizioni batch, dopo la creazione di un tenant sulla partizione
                    // online
                    // Ci si accorge del problema, guardando la directory lucene degli user, che rimane
                    // senza document
                    */
                    /*
                    // Non risolve .. anzi .. potrebbe essere la causa di un errore di transaction already exist
                    // Nel momento in cui un nodo dlave indicizza il grappolo di transazioni
                    UserTransaction userTransaction = transactionService.getUserTransaction();
                    try
                    {
                        // Inizio la transazione
                      userTransaction.begin();
                        
                        // Cambio la PWD e creo una nuova transazione
                        authenticationService.updateAuthentication( getTenantAdminUser(tenantDomain)   ,
                                                                    tenantAdminRawPassword             ,
                                                                    tenantAdminRawPassword             );
                        
                        // Chiudo la transazione
                     userTransaction.commit();
                        
                    } catch(Throwable e) {
                       logger.error("[MultiTAdminServiceImpl::createTenant] update user failed " + e);
                       try {
                         if (userTransaction != null) {
                             userTransaction.rollback();
                         }
                       } catch (Throwable ex) {}
                    }
                    // ---------------------------------------------------------------------------
                    */

                    logger.info("[MultiTAdminServiceImpl::createTenant] END doWork");

                    return null;
                }
            }, getTenantAdminUser(tenantDomain));
        }

        logger.info("Tenant created: " + tenantDomain);
        logger.debug("[MultiTAdminServiceImpl::createTenant] END");
    }

    public boolean existsTenant(String tenantDomain) {
        // Check that all the passed values are not null
        ParameterCheck.mandatory("tenantDomain", tenantDomain);

        return (getTenantAttributes(tenantDomain) != null);
    }

    private void putTenantAttributes(String tenantDomain, Tenant tenant) {
        if (!attributeService.exists(TENANTS_ATTRIBUTE_PATH)) {
            // bootstrap
            attributeService.setAttribute("", TENANTS_ATTRIBUTE_PATH, new MapAttributeValue());
        }

        MapAttribute tenantProps = new MapAttributeValue();
        if (logger.isDebugEnabled()) {
            logger.debug("[MultiTAdminServiceImpl::putTenantAttributes] BooleanAttributeValue(tenant.isEnabled()): "
                    + new BooleanAttributeValue(tenant.isEnabled()));
            logger.debug(
                    "[MultiTAdminServiceImpl::putTenantAttributes] StringAttributeValue(tenant.getRootContentStoreDir()): "
                            + new StringAttributeValue(tenant.getRootContentStoreDir()));
        }
        tenantProps.put(TENANT_ATTRIBUTE_ENABLED, new BooleanAttributeValue(tenant.isEnabled()));
        tenantProps.put(TENANT_ROOT_CONTENT_STORE_DIR, new StringAttributeValue(tenant.getRootContentStoreDir()));

        // MB: aggiungo la lista dei bean di content store
        logger.debug("[MultiTAdminServiceImpl::putTenantAttributes] getContentStore");
        if (tenant.getContentStores() != null) {
            int nStore = 0;
            for (Object cs : tenant.getContentStores()) {
                ContentStoreDefinition tcs = (ContentStoreDefinition) cs;

                if (logger.isDebugEnabled()) {
                    logger.debug("[MultiTAdminServiceImpl::putTenantAttributes] contentStore (" + nStore + ") "
                            + tcs.getType() + ":" + tcs.getProtocol() + "->" + tcs.getResource());
                }

                tenantProps.put(TENANT_CONTENT_STORE_TYPE + nStore, new StringAttributeValue(tcs.getType()));
                tenantProps.put(TENANT_CONTENT_STORE_PROTOCOL + nStore,
                        new StringAttributeValue(tcs.getProtocol()));
                tenantProps.put(TENANT_CONTENT_STORE_RESOURCE + nStore,
                        new StringAttributeValue(tcs.getResource()));
                nStore++;
            }
        }

        attributeService.setAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain, tenantProps);
        //attributeService.setAttribute(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain, tenantDomain, tenantProps);
        // update tenant status cache
        ((MultiTServiceImpl) tenantService).putTenant(tenantDomain, tenant);
    }

    private Tenant getTenantAttributes(String tenantDomain) {

        if (logger.isDebugEnabled()) {
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] exist11 "
                    + attributeService.exists(TENANTS_ATTRIBUTE_PATH + "/" + tenantDomain));
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] exist12 "
                    + attributeService.exists(TENANTS_ATTRIBUTE_PATH));
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] exist13 "
                    + attributeService.exists(tenantDomain));
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] getAttribute11 "
                    + (MapAttribute) attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH + "/" + tenantDomain));
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] getAttribute12 "
                    + (MapAttribute) attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH));
            logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] getAttribute13 "
                    + (MapAttribute) attributeService.getAttribute(tenantDomain));
        }

        /*orig*/ if (attributeService.exists(TENANTS_ATTRIBUTE_PATH + "/" + tenantDomain))
        //if (attributeService.exists(TENANTS_ATTRIBUTE_PATH))
        {
            /*orig*/MapAttribute map = (MapAttribute) attributeService
                    .getAttribute(TENANTS_ATTRIBUTE_PATH + "/" + tenantDomain);
            //MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH);
            if (map != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] map " + map);
                    logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] tenantDomain: " + tenantDomain);
                    logger.debug("[MultiTAdminServiceImpl::getTenantAttributes] map.get TENANT_ATTRIBUTE_ENABLED: "
                            + map.get(TENANT_ATTRIBUTE_ENABLED));
                    logger.debug(
                            "[MultiTAdminServiceImpl::getTenantAttributes] map.get TENANT_ROOT_CONTENT_STORE_DIR: "
                                    + map.get(TENANT_ROOT_CONTENT_STORE_DIR));
                }
                Tenant t = new Tenant(tenantDomain, map.get(TENANT_ATTRIBUTE_ENABLED).getBooleanValue(),
                        map.get(TENANT_ROOT_CONTENT_STORE_DIR).getStringValue());

                // MB: Leggo i contentStore
                List<ContentStoreDefinition> tcs = new ArrayList<ContentStoreDefinition>();
                int nStore = 0;
                while (true) {
                    Attribute at = map.get(TENANT_CONTENT_STORE_TYPE + nStore);
                    if (at == null)
                        break;

                    Attribute ap = map.get(TENANT_CONTENT_STORE_PROTOCOL + nStore);
                    if (ap == null)
                        break;

                    Attribute ar = map.get(TENANT_CONTENT_STORE_RESOURCE + nStore);
                    if (ar == null)
                        break;

                    ContentStoreDefinition cs = new ContentStoreDefinition();
                    cs.setType(at.getStringValue());
                    cs.setProtocol(ap.getStringValue());
                    cs.setResource(ar.getStringValue());

                    tcs.add(cs);
                    nStore++;
                }
                t.setContentStores(tcs);

                return t;
            }
        }

        return null;
    }

    public void enableTenant(String tenantDomain) {
        if (isEnabledTenant(tenantDomain)) {
            logger.warn("Tenant already enabled: " + tenantDomain);
        }

        enableTenant(tenantDomain, true);
    }

    private void enableTenant(String tenantDomain, boolean notifyTenantDeployers) {
        logger.debug("[MultiTAdminServiceImpl::enableTenant] BEGIN");
        // Check that all the passed values are not null
        ParameterCheck.mandatory("tenantDomain", tenantDomain);

        Tenant tenantAtr = getTenantAttributes(tenantDomain);
        Tenant tenant = new Tenant(tenantDomain, true, tenantAtr.getRootContentStoreDir()); // enable
        tenant.setContentStores(tenantAtr.getContentStores());
        putTenantAttributes(tenantDomain, tenant);

        if (notifyTenantDeployers) {
            // notify listeners that tenant has been enabled
            AuthenticationUtil.runAs(new RunAsWork<Object>() {
                public Object doWork() {
                    List<TenantDeployer> tenantDeployers = getTenantDeployers();
                    for (TenantDeployer tenantDeployer : tenantDeployers) {
                        tenantDeployer.onEnableTenant();
                    }
                    return null;
                }
            }, getTenantAdminUser(tenantDomain));
        }

        logger.debug("[MultiTAdminServiceImpl::enableTenant] Tenant enabled: " + tenantDomain);
        logger.debug("[MultiTAdminServiceImpl::enableTenant] END");
    }

    public void disableTenant(String tenantDomain) {
        if (!isEnabledTenant(tenantDomain)) {
            logger.warn("Tenant already disabled: " + tenantDomain);
        }

        disableTenant(tenantDomain, true);
    }

    public void disableTenant(String tenantDomain, boolean notifyTenantDeployers) {
        if (notifyTenantDeployers) {
            // notify listeners that tenant has been disabled
            AuthenticationUtil.runAs(new RunAsWork<Object>() {
                public Object doWork() {
                    List<TenantDeployer> tenantDeployers = getTenantDeployers();
                    for (TenantDeployer tenantDeployer : tenantDeployers) {
                        tenantDeployer.onDisableTenant();
                    }
                    return null;
                }
            }, getTenantAdminUser(tenantDomain));
        }

        // update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable)
        Tenant tenantAtr = getTenantAttributes(tenantDomain);
        Tenant tenant = new Tenant(tenantDomain, false, tenantAtr.getRootContentStoreDir()); // disable
        tenant.setContentStores(tenantAtr.getContentStores());
        putTenantAttributes(tenantDomain, tenant);

        logger.debug("[MultiTAdminServiceImpl::disableTenant] Tenant disabled: " + tenantDomain);
    }

    public boolean isEnabledTenant(String tenantDomain) {
        // Check that all the passed values are not null
        ParameterCheck.mandatory("tenantDomain", tenantDomain);

        Tenant tenant = getTenantAttributes(tenantDomain);
        if (tenant != null) {
            return tenant.isEnabled();
        }

        return false;
    }

    protected String getRootContentStoreDir(String tenantDomain) {
        // Check that all the passed values are not null
        ParameterCheck.mandatory("tenantDomain", tenantDomain);

        Tenant tenant = getTenantAttributes(tenantDomain);
        if (tenant != null) {
            return tenant.getRootContentStoreDir();
        }

        return null;
    }

    protected void putRootContentStoreDir(String tenantDomain, String rootContentStoreDir) {
        Tenant tenantAtr = getTenantAttributes(tenantDomain);
        Tenant tenant = new Tenant(tenantDomain, tenantAtr.isEnabled(), rootContentStoreDir);
        tenant.setContentStores(tenantAtr.getContentStores());
        putTenantAttributes(tenantDomain, tenant);
    }

    public Tenant getTenant(String tenantDomain) {
        Tenant tenant = new Tenant(tenantDomain, isEnabledTenant(tenantDomain),
                getRootContentStoreDir(tenantDomain));

        Tenant tenantAtr = getTenantAttributes(tenantDomain);
        if (tenantAtr != null) {
            tenant.setContentStores(tenantAtr.getContentStores());
        }
        return tenant;
    }

    public void bootstrapWorkflows() {
        // use this to deploy standard workflow process defs to the JBPM engine
        WorkflowDeployer workflowBootstrap = (WorkflowDeployer) getApplicationContext()
                .getBean("workflowBootstrap");

        String resourceClasspath = null;

        // Workflow process definitions
        try {
            List<Properties> workflowDefs = workflowBootstrap.getWorkflowDefinitions();
            if (workflowDefs != null) {
                for (Properties workflowDefProps : workflowDefs) {
                    resourceClasspath = workflowDefProps.getProperty(WorkflowDeployer.LOCATION);
                    ClassPathResource resource = new ClassPathResource(resourceClasspath);
                    workflowService.deployDefinition(workflowDefProps.getProperty(WorkflowDeployer.ENGINE_ID),
                            resource.getInputStream(), workflowDefProps.getProperty(WorkflowDeployer.MIMETYPE));
                }
            }
        } catch (IOException ioe) {
            throw new AlfrescoRuntimeException("Failed to find workflow process def: " + resourceClasspath);
        }

        logger.info("Tenant workflows bootstrapped: " + tenantService.getCurrentUserDomain());
    }

    /**
     * @see TenantAdminService.deleteTenant()
     */
    public void deleteTenant(String tenantDomain) {
        if (!existsTenant(tenantDomain)) {
            throw new RuntimeException("Tenant does not exist: " + tenantDomain);
        } else {
            try {
                final String tenantAdminUser = getTenantAdminUser(tenantDomain);
                //final String tenantAdminUser = tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);

                AuthenticationUtil.runAs(new RunAsWork<Object>() {
                    public Object doWork() {
                        List<WorkflowDefinition> workflowDefs = workflowService.getDefinitions();
                        if (workflowDefs != null) {
                            for (WorkflowDefinition workflowDef : workflowDefs) {
                                workflowService.undeployDefinition(workflowDef.getId());
                            }
                        }

                        List<String> messageResourceBundles = repoAdminService.getMessageBundles();
                        if (messageResourceBundles != null) {
                            for (String messageResourceBundle : messageResourceBundles) {
                                repoAdminService.undeployMessageBundle(messageResourceBundle);
                            }
                        }

                        List<RepoModelDefinition> models = repoAdminService.getModels();
                        if (models != null) {
                            for (RepoModelDefinition model : models) {
                                repoAdminService.undeployModel(model.getRepoName());
                            }
                        }

                        return null;
                    }
                }, tenantAdminUser);

                //-------------------------------------
                UserTransaction userTransaction = transactionService.getUserTransaction();
                authenticationComponent.setSystemUserAsCurrentUser();
                try {
                    // TODO: occorre usare lo SplittingDbNodeServiceImpl
                    // che ha dentro un deleteStore che aggiorna gli indici
                    // ora e' usata l'imlementation di ALF che ha il metodo ma non aggiorna gli indici di lucene
                    userTransaction.begin();

                    ns.deleteStore(tenantService.getName(tenantAdminUser,
                            new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES)));
                    ns.deleteStore(tenantService.getName(tenantAdminUser,
                            new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES)));
                    ns.deleteStore(tenantService.getName(tenantAdminUser,
                            new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION)));
                    ns.deleteStore(tenantService.getName(tenantAdminUser,
                            new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM)));
                    ns.deleteStore(tenantService.getName(tenantAdminUser,
                            new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER)));

                    userTransaction.commit();

                } catch (Throwable e) {
                    // rollback the transaction
                    try {
                        if (userTransaction != null) {
                            userTransaction.rollback();
                        }
                    } catch (Exception ex) {
                    }
                    try {
                        authenticationComponent.clearCurrentSecurityContext();
                    } catch (Exception ex) {
                    }
                    throw new AlfrescoRuntimeException("Failed to delete tenant", e);
                }

                // notify listeners that tenant has been deleted & hence disabled
                AuthenticationUtil.runAs(new RunAsWork<Object>() {
                    public Object doWork() {
                        List<TenantDeployer> tenantDeployers = getTenantDeployers();
                        for (TenantDeployer tenantDeployer : tenantDeployers) {
                            tenantDeployer.onDisableTenant();
                        }
                        return null;
                    }
                }, tenantAdminUser);

                // remove tenant
                attributeService.removeAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain);
            } catch (Throwable t) {
                throw new AlfrescoRuntimeException("Failed to delete tenant: " + tenantDomain, t);
            }
        }
    }

    /**
     * @see TenantAdminService.getAllTenants()
     */
    public List<Tenant> getAllTenantsDoqui() {
        MapAttribute map = (MapAttribute) attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH);

        List<Tenant> tenants = new ArrayList<Tenant>();

        if (map != null) {
            // note: getAllTenants is called first, by TenantDeployer - hence need to initialise the TenantService status cache
            Set<String> tenantDomains = map.keySet();

            for (String tenantDomain : tenantDomains) {
                Tenant tenant = getTenantAttributes(tenantDomain);

                // Create Tenant
                Tenant tenant2Add = new Tenant(tenantDomain, tenant.isEnabled(), tenant.getRootContentStoreDir());
                tenant2Add.setContentStores(tenant.getContentStores());

                // Add it to tenants list
                tenants.add(tenant2Add);
            }
        }

        return tenants; // list of tenants or empty list
    }

    /**
     * @see TenantAdminService.getAllTenants()
     */
    public List<org.alfresco.repo.tenant.Tenant> getAllTenants() {
        List<org.alfresco.repo.tenant.Tenant> tenants = new ArrayList<org.alfresco.repo.tenant.Tenant>();

        List<Tenant> lt = getAllTenantsDoqui();
        for (Tenant t : lt) {
            // Add it to tenants list
            tenants.add(t);
        }

        return tenants; // list of tenants or empty list
    }

    private void bootstrapUserTenantStore(String tenantDomain, char[] tenantAdminRawPassword) {
        // Bootstrap Tenant-Specific User Store
        StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_USER,
                tenantService.getName(STORE_BASE_ID_USER, tenantDomain));

        ImporterBootstrap userImporterBootstrap = (ImporterBootstrap) getApplicationContext()
                .getBean("userBootstrap");
        userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());

        // override admin username property
        String salt = null; // GUID.generate();
        Properties props = userImporterBootstrap.getConfiguration();

        props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));
        props.put("alfresco_user_store.adminpassword",
                passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt));

        userImporterBootstrap.bootstrap();

        logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
    }

    private void bootstrapSystemTenantStore(String tenantDomain) {
        // Bootstrap Tenant-Specific System Store
        StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_SYSTEM,
                tenantService.getName(STORE_BASE_ID_SYSTEM, tenantDomain));

        ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap) getApplicationContext()
                .getBean("systemBootstrap");
        systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());

        // override default property (workspace://SpacesStore)
        List<String> mustNotExistStoreUrls = new ArrayList<String>();
        mustNotExistStoreUrls
                .add(new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_USER, tenantDomain))
                        .toString());
        systemImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);

        systemImporterBootstrap.bootstrap();

        logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
    }

    private void bootstrapVersionTenantStore(String tenantDomain) {
        // Bootstrap Tenant-Specific Version Store
        StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE,
                tenantService.getName(STORE_BASE_ID_VERSION, tenantDomain));

        ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap) getApplicationContext()
                .getBean("versionBootstrap");
        versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());

        versionImporterBootstrap.bootstrap();

        logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
    }

    private void bootstrapSpacesArchiveTenantStore(String tenantDomain) {
        // Bootstrap Tenant-Specific Spaces Store
        StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_ARCHIVE,
                tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));

        ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap) getApplicationContext()
                .getBean("spacesArchiveBootstrap");
        spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());

        // override default property (archive://SpacesStore)
        List<String> mustNotExistStoreUrls = new ArrayList<String>();
        mustNotExistStoreUrls
                .add(new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain))
                        .toString());
        spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);

        spacesArchiveImporterBootstrap.bootstrap();

        logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
    }

    private void bootstrapSpacesTenantStore(String tenantDomain) {
        // Bootstrap Tenant-Specific Spaces Store
        StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE,
                tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));

        final ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap) getApplicationContext()
                .getBean("spacesBootstrap");
        spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());

        // override admin username property
        Properties props = spacesImporterBootstrap.getConfiguration();
        props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));

        // override guest username property
        props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain));

        spacesImporterBootstrap.bootstrap();

        logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
    }

    public void deployTenants(final TenantDeployer deployer, Log logger) {
        if (deployer == null) {
            throw new AlfrescoRuntimeException("Deployer must be provided");
        }
        if (logger == null) {
            throw new AlfrescoRuntimeException("Logger must be provided");
        }

        if (tenantService.isEnabled()) {
            UserTransaction userTransaction = transactionService.getUserTransaction();
            authenticationComponent.setSystemUserAsCurrentUser();

            List<org.alfresco.repo.tenant.Tenant> tenants = null;
            try {
                userTransaction.begin();
                tenants = getAllTenants();
                userTransaction.commit();
            } catch (Throwable e) {
                // rollback the transaction
                try {
                    if (userTransaction != null) {
                        userTransaction.rollback();
                    }
                } catch (Exception ex) {
                }
                try {
                    authenticationComponent.clearCurrentSecurityContext();
                } catch (Exception ex) {
                }
                throw new AlfrescoRuntimeException("Failed to get tenants", e);
            }

            String currentUser = AuthenticationUtil.getCurrentUserName();

            if (tenants != null) {
                try {
                    for (org.alfresco.repo.tenant.Tenant tenant : tenants) {
                        if (tenant.isEnabled()) {
                            try {
                                // switch to admin in order to deploy within context of tenant domain
                                // assumes each tenant has default "admin" user
                                AuthenticationUtil.runAs(new RunAsWork<Object>() {
                                    public Object doWork() {
                                        // init the service within tenant context
                                        deployer.init();
                                        return null;
                                    }
                                }, getTenantAdminUser(tenant.getTenantDomain()));

                            } catch (Throwable e) {
                                logger.error("Deployment failed" + e);

                                StringWriter stringWriter = new StringWriter();
                                e.printStackTrace(new PrintWriter(stringWriter));
                                logger.error(stringWriter.toString());

                                // tenant deploy failure should not necessarily affect other tenants
                            }
                        }
                    }
                } finally {
                    if (currentUser != null) {
                        AuthenticationUtil.setCurrentUser(currentUser);
                    }
                }
            }
        }
    }

    public void undeployTenants(final TenantDeployer deployer, Log logger) {
        if (deployer == null) {
            throw new AlfrescoRuntimeException("Deployer must be provided");
        }
        if (logger == null) {
            throw new AlfrescoRuntimeException("Logger must be provided");
        }

        if (tenantService.isEnabled()) {
            UserTransaction userTransaction = transactionService.getUserTransaction();
            authenticationComponent.setSystemUserAsCurrentUser();

            List<org.alfresco.repo.tenant.Tenant> tenants = null;
            try {
                userTransaction.begin();
                tenants = getAllTenants();
                userTransaction.commit();
            } catch (Throwable e) {
                // rollback the transaction
                try {
                    if (userTransaction != null) {
                        userTransaction.rollback();
                    }
                } catch (Exception ex) {
                }
                try {
                    authenticationComponent.clearCurrentSecurityContext();
                } catch (Exception ex) {
                }
                throw new AlfrescoRuntimeException("Failed to get tenants", e);
            }

            String currentUser = AuthenticationUtil.getCurrentUserName();

            if (tenants != null) {
                try {
                    for (org.alfresco.repo.tenant.Tenant tenant : tenants) {
                        if (tenant.isEnabled()) {
                            try {
                                // switch to admin in order to deploy within context of tenant domain
                                // assumes each tenant has default "admin" user
                                AuthenticationUtil.runAs(new RunAsWork<Object>() {
                                    public Object doWork() {
                                        // destroy the service within tenant context
                                        deployer.destroy();
                                        return null;
                                    }
                                }, getTenantAdminUser(tenant.getTenantDomain()));

                            } catch (Throwable e) {
                                logger.error("Undeployment failed" + e);

                                StringWriter stringWriter = new StringWriter();
                                e.printStackTrace(new PrintWriter(stringWriter));
                                logger.error(stringWriter.toString());

                                // tenant undeploy failure should not necessarily affect other tenants
                            }
                        }
                    }
                } finally {
                    if (currentUser != null) {
                        AuthenticationUtil.setCurrentUser(currentUser);
                    }
                }
            }
        }
    }

    public void register(TenantDeployer deployer) {
        logger.debug("[MultiTAdminServiceImpl::register] BEGIN");
        if (deployer == null) {
            throw new AlfrescoRuntimeException("Deployer must be provided");
        }

        List<TenantDeployer> tenantDeployers = getTenantDeployers();
        if (!tenantDeployers.contains(deployer)) {
            tenantDeployers.add(deployer);
        }
        logger.debug("[MultiTAdminServiceImpl::register] END");
    }

    public void unregister(TenantDeployer deployer) {
        String currentRepository = RepositoryManager.getCurrentRepository();
        for (Repository repository : repositoryManager.getRepositories()) {
            RepositoryManager.setCurrentRepository(repository.getId());
            if (deployer == null) {
                throw new AlfrescoRuntimeException("Deployer must be provided");
            }

            List<TenantDeployer> tenantDeployers = getTenantDeployers();
            if (tenantDeployers != null) {
                tenantDeployers.remove(deployer);
            }
        }
        RepositoryManager.setCurrentRepository(currentRepository);
        tenantDeployerMap.clear();
    }

    public boolean isEnabled() {
        return tenantService.isEnabled();
    }

    public void resetCache(String tenantDomain) {
        if (existsTenant(tenantDomain)) {
            if (isEnabledTenant(tenantDomain)) {
                enableTenant(tenantDomain);
            } else {
                disableTenant(tenantDomain);
            }
        } else {
            throw new AlfrescoRuntimeException("No such tenant " + tenantDomain);
        }
    }

    // local helper
    private String getTenantAdminUser(String tenantDomain) {
        return tenantService.getDomainUser(ADMIN_BASENAME, tenantDomain);
    }

    // local helper
    private String getTenantGuestUser(String tenantDomain) {
        return tenantService.getDomainUser(authenticationComponent.getGuestUserName(), tenantDomain);
    }

    private List<TenantDeployer> getTenantDeployers() {
        String currentRepositoryId = RepositoryManager.getCurrentRepository();
        if (logger.isDebugEnabled()) {
            logger.debug("[MultiTAdminServiceImpl::getTenantDeployers] getting tenant deployers for repository '"
                    + currentRepositoryId + "'");
        }
        List<TenantDeployer> tenantDeployers = tenantDeployerMap.get(currentRepositoryId);
        if (tenantDeployers == null) {
            tenantDeployers = new ArrayList<TenantDeployer>();
            tenantDeployerMap.put(currentRepositoryId, tenantDeployers);
        }
        return tenantDeployers;
    }

}