Example usage for java.util.concurrent ScheduledExecutorService isTerminated

List of usage examples for java.util.concurrent ScheduledExecutorService isTerminated

Introduction

In this page you can find the example usage for java.util.concurrent ScheduledExecutorService isTerminated.

Prototype

boolean isTerminated();

Source Link

Document

Returns true if all tasks have completed following shut down.

Usage

From source file:org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizer.java

private void synchronizeInternal(boolean forceUpdate, boolean isFullSync, final boolean splitTxns) {
    if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {

        if (forceUpdate) {
            ChainingUserRegistrySynchronizer.logger.debug("Running a full sync.");
        } else {//w  w w .ja  va  2 s  . c o  m
            ChainingUserRegistrySynchronizer.logger.debug("Running a differential sync.");
        }
        if (allowDeletions) {
            ChainingUserRegistrySynchronizer.logger.debug("deletions are allowed");
        } else {
            ChainingUserRegistrySynchronizer.logger.debug("deletions are not allowed");
        }
        // Don't proceed with the sync if the repository is read only
        if (this.transactionService.isReadOnly()) {
            ChainingUserRegistrySynchronizer.logger
                    .warn("Unable to proceed with user registry synchronization. Repository is read only.");
            return;
        }
    }

    // Don't proceed with the sync if the repository is read only
    if (this.transactionService.isReadOnly()) {
        ChainingUserRegistrySynchronizer.logger
                .warn("Unable to proceed with user registry synchronization. Repository is read only.");
        return;
    }

    // Create a background executor that will refresh our lock. This means we can request a lock with a relatively
    // small persistence time and not worry about it lasting after server restarts. Note we use an independent
    // executor because this is a compound operation that spans accross multiple batch processors.
    String lockToken = null;
    TraceableThreadFactory threadFactory = new TraceableThreadFactory();
    threadFactory.setNamePrefix("ChainingUserRegistrySynchronizer lock refresh");
    threadFactory.setThreadDaemon(true);
    ScheduledExecutorService lockRefresher = new ScheduledThreadPoolExecutor(1, threadFactory);

    // Let's ensure all exceptions get logged
    try {
        // First, try to obtain a lock to ensure we are the only node trying to run this job
        try {
            if (splitTxns) {
                // If this is an automated sync on startup or scheduled sync, don't even wait around for the lock.
                // Assume the sync will be completed on another node.
                lockToken = this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<String>() {
                            public String execute() throws Throwable {
                                return ChainingUserRegistrySynchronizer.this.jobLockService.getLock(
                                        ChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        ChainingUserRegistrySynchronizer.LOCK_TTL, 0, 1);
                            }
                        }, false, splitTxns);
            } else {
                // If this is a login-triggered sync, give it a few retries before giving up
                lockToken = this.jobLockService.getLock(ChainingUserRegistrySynchronizer.LOCK_QNAME,
                        ChainingUserRegistrySynchronizer.LOCK_TTL, 3000, 10);
            }
        } catch (LockAcquisitionException e) {
            // Don't proceed with the sync if it is running on another node
            ChainingUserRegistrySynchronizer.logger.warn(
                    "User registry synchronization already running in another thread. Synchronize aborted");
            return;
        }

        // Schedule the lock refresh to run at regular intervals
        final String token = lockToken;
        lockRefresher.scheduleAtFixedRate(new Runnable() {
            public void run() {
                ChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<Object>() {
                            public Object execute() throws Throwable {
                                ChainingUserRegistrySynchronizer.this.jobLockService.refreshLock(token,
                                        ChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        ChainingUserRegistrySynchronizer.LOCK_TTL);
                                return null;
                            }
                        }, false, splitTxns);
            }
        }, ChainingUserRegistrySynchronizer.LOCK_TTL / 2, ChainingUserRegistrySynchronizer.LOCK_TTL / 2,
                TimeUnit.MILLISECONDS);

        Set<String> visitedZoneIds = new TreeSet<String>();
        Collection<String> instanceIds = this.applicationContextManager.getInstanceIds();

        // Work out the set of all zone IDs in the authentication chain so that we can decide which users / groups
        // need 're-zoning'
        Set<String> allZoneIds = new TreeSet<String>();
        for (String id : instanceIds) {
            allZoneIds.add(AuthorityService.ZONE_AUTH_EXT_PREFIX + id);
        }

        // Collect the plugins that we can sync : zoneId, plugin
        Map<String, UserRegistry> plugins = new HashMap<String, UserRegistry>();

        for (String id : instanceIds) {
            UserRegistry plugin;
            try {
                ApplicationContext context = this.applicationContextManager.getApplicationContext(id);
                plugin = (UserRegistry) context.getBean(this.sourceBeanName);
            } catch (RuntimeException e) {
                // The bean doesn't exist or this subsystem won't start. The reason would have been logged. Ignore and continue.
                continue;
            }

            if (!(plugin instanceof ActivateableBean) || ((ActivateableBean) plugin).isActive()) {
                // yes this plugin needs to be synced
                plugins.put(id, plugin);
            }
        }

        /**
         *  Sync starts here
         */
        notifySyncStart(plugins.keySet());

        for (String id : instanceIds) {
            UserRegistry plugin = plugins.get(id);

            if (plugin != null) {
                // If debug is enabled then dump out the contents of the authentication JMX bean
                if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                    mbeanServer = (MBeanServerConnection) getApplicationContext()
                            .getBean("alfrescoMBeanServer");
                    try {
                        StringBuilder nameBuff = new StringBuilder(200)
                                .append("Alfresco:Type=Configuration,Category=Authentication,id1=managed,id2=")
                                .append(URLDecoder.decode(id, "UTF-8"));
                        ObjectName name = new ObjectName(nameBuff.toString());
                        if (mbeanServer != null && mbeanServer.isRegistered(name)) {
                            MBeanInfo info = mbeanServer.getMBeanInfo(name);
                            MBeanAttributeInfo[] attributes = info.getAttributes();
                            ChainingUserRegistrySynchronizer.logger.debug(id + " attributes:");
                            for (MBeanAttributeInfo attribute : attributes) {
                                Object value = mbeanServer.getAttribute(name, attribute.getName());
                                ChainingUserRegistrySynchronizer.logger
                                        .debug(attribute.getName() + " = " + value);
                            }
                        }
                    } catch (UnsupportedEncodingException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (MalformedObjectNameException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (InstanceNotFoundException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (IntrospectionException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (AttributeNotFoundException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (ReflectionException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (MBeanException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (IOException e) {
                        if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            ChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    }
                } // end of debug dump of active JMX bean
                if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
                    ChainingUserRegistrySynchronizer.logger
                            .info("Synchronizing users and groups with user registry '" + id + "'");
                }
                if (isFullSync && ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                    ChainingUserRegistrySynchronizer.logger
                            .warn("Full synchronization with user registry '" + id + "'");
                    if (allowDeletions) {
                        ChainingUserRegistrySynchronizer.logger.warn(
                                "Some users and groups previously created by synchronization with this user registry may be removed.");
                    } else {
                        ChainingUserRegistrySynchronizer.logger.warn(
                                "Deletions are disabled. Users and groups removed from this registry will be logged only and will remain in the repository. Users previously found in a different registry will be moved in the repository rather than recreated.");
                    }
                }
                // Work out whether we should do the work in a separate transaction (it's most performant if we
                // bunch it into small transactions, but if we are doing a sync on login, it has to be the same
                // transaction)
                boolean requiresNew = splitTxns
                        || AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;

                try {
                    /**
                     * Do the sync with the specified plugin
                     */
                    syncWithPlugin(id, plugin, forceUpdate, isFullSync, requiresNew, visitedZoneIds,
                            allZoneIds);

                    this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryEndEvent(this, id));
                } catch (final RuntimeException e) {
                    notifySyncDirectoryEnd(id, e);
                    throw e;
                }
            } // if plugin exists
        } // for each instanceId

        //End of successful synchronization here
        notifySyncEnd();
    } catch (final RuntimeException e) {
        notifySyncEnd(e);
        ChainingUserRegistrySynchronizer.logger.error("Synchronization aborted due to error", e);
        throw e;
    } finally {
        // Release the lock if necessary
        if (lockToken != null) {
            // Cancel the lock refresher
            // Because we may hit a perfect storm when trying to interrupt workers in their unsynchronized getTask()
            // method we can't wait indefinitely and may have to retry the shutdown
            int trys = 0;
            do {
                lockRefresher.shutdown();
                try {
                    lockRefresher.awaitTermination(ChainingUserRegistrySynchronizer.LOCK_TTL,
                            TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                }
            } while (!lockRefresher.isTerminated() && trys++ < 3);
            if (!lockRefresher.isTerminated()) {
                lockRefresher.shutdownNow();
                ChainingUserRegistrySynchronizer.logger.error("Failed to shut down lock refresher");
            }

            final String token = lockToken;
            this.transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Object>() {
                        public Object execute() throws Throwable {
                            ChainingUserRegistrySynchronizer.this.jobLockService.releaseLock(token,
                                    ChainingUserRegistrySynchronizer.LOCK_QNAME);
                            return null;
                        }
                    }, false, splitTxns);
        }
    }
}

From source file:org.alfresco.repo.security.sync.TenantChainingUserRegistrySynchronizer.java

private void synchronizeInternal(boolean forceUpdate, boolean isFullSync, final boolean splitTxns) {
    TenantChainingUserRegistrySynchronizer.logger
            .debug("Running a sync for domain: " + SEIPTenantIntegration.getTenantId());
    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {

        if (forceUpdate) {
            TenantChainingUserRegistrySynchronizer.logger.debug("Running a full sync.");
        } else {/*from  w ww.j  a  v a 2s .  co  m*/
            TenantChainingUserRegistrySynchronizer.logger.debug("Running a differential sync.");
        }
        if (allowDeletions) {
            TenantChainingUserRegistrySynchronizer.logger.debug("deletions are allowed");
        } else {
            TenantChainingUserRegistrySynchronizer.logger.debug("deletions are not allowed");
        }
        // Don't proceed with the sync if the repository is read only
        if (this.transactionService.isReadOnly()) {
            TenantChainingUserRegistrySynchronizer.logger
                    .warn("Unable to proceed with user registry synchronization. Repository is read only.");
            return;
        }
    }

    // Don't proceed with the sync if the repository is read only
    if (this.transactionService.isReadOnly()) {
        TenantChainingUserRegistrySynchronizer.logger
                .warn("Unable to proceed with user registry synchronization. Repository is read only.");
        return;
    }

    // Create a background executor that will refresh our lock. This means
    // we can request a lock with a relatively
    // small persistence time and not worry about it lasting after server
    // restarts. Note we use an independent
    // executor because this is a compound operation that spans accross
    // multiple batch processors.
    String lockToken = null;
    TraceableThreadFactory threadFactory = new TraceableThreadFactory();
    threadFactory.setNamePrefix("TenantChainingUserRegistrySynchronizer lock refresh");
    threadFactory.setThreadDaemon(true);
    ScheduledExecutorService lockRefresher = new ScheduledThreadPoolExecutor(1, threadFactory);

    // Let's ensure all exceptions get logged
    try {
        // First, try to obtain a lock to ensure we are the only node trying
        // to run this job
        try {
            if (splitTxns) {
                // If this is an automated sync on startup or scheduled
                // sync, don't even wait around for the lock.
                // Assume the sync will be completed on another node.
                lockToken = this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<String>() {
                            public String execute() throws Throwable {
                                return TenantChainingUserRegistrySynchronizer.this.jobLockService.getLock(
                                        TenantChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        TenantChainingUserRegistrySynchronizer.LOCK_TTL, 0, 1);
                            }
                        }, false, splitTxns);
            } else {
                // If this is a login-triggered sync, give it a few retries
                // before giving up
                lockToken = this.jobLockService.getLock(TenantChainingUserRegistrySynchronizer.LOCK_QNAME,
                        TenantChainingUserRegistrySynchronizer.LOCK_TTL, 3000, 10);
            }
        } catch (LockAcquisitionException e) {
            // Don't proceed with the sync if it is running on another node
            TenantChainingUserRegistrySynchronizer.logger.warn(
                    "User registry synchronization already running in another thread. Synchronize aborted");
            return;
        }

        // Schedule the lock refresh to run at regular intervals
        final String token = lockToken;
        lockRefresher.scheduleAtFixedRate(new Runnable() {
            public void run() {
                TenantChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<Object>() {
                            public Object execute() throws Throwable {
                                TenantChainingUserRegistrySynchronizer.this.jobLockService.refreshLock(token,
                                        TenantChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        TenantChainingUserRegistrySynchronizer.LOCK_TTL);
                                return null;
                            }
                        }, false, splitTxns);
            }
        }, TenantChainingUserRegistrySynchronizer.LOCK_TTL / 2,
                TenantChainingUserRegistrySynchronizer.LOCK_TTL / 2, TimeUnit.MILLISECONDS);

        Set<String> visitedZoneIds = new TreeSet<String>();
        Collection<String> instanceIds = this.applicationContextManager.getInstanceIds();

        // Work out the set of all zone IDs in the authentication chain so
        // that we can decide which users / groups
        // need 're-zoning'
        Set<String> allZoneIds = new TreeSet<String>();
        for (String id : instanceIds) {
            allZoneIds.add(AuthorityService.ZONE_AUTH_EXT_PREFIX + id);
        }
        for (String id : instanceIds) {
            ApplicationContext context = this.applicationContextManager.getApplicationContext(id);
            try {
                UserRegistry plugin = (UserRegistry) context.getBean(this.sourceBeanName);
                if (!(plugin instanceof ActivateableBean) || ((ActivateableBean) plugin).isActive()) {
                    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                        mbeanServer = (MBeanServerConnection) getApplicationContext()
                                .getBean("alfrescoMBeanServer");
                        try {
                            StringBuilder nameBuff = new StringBuilder(200).append(
                                    "Alfresco:Type=Configuration,Category=Authentication,id1=managed,id2=")
                                    .append(URLDecoder.decode(id, "UTF-8"));
                            ObjectName name = new ObjectName(nameBuff.toString());
                            if (mbeanServer != null && mbeanServer.isRegistered(name)) {
                                MBeanInfo info = mbeanServer.getMBeanInfo(name);
                                MBeanAttributeInfo[] attributes = info.getAttributes();
                                TenantChainingUserRegistrySynchronizer.logger.debug(id + " attributes:");
                                for (MBeanAttributeInfo attribute : attributes) {
                                    Object value = mbeanServer.getAttribute(name, attribute.getName());
                                    TenantChainingUserRegistrySynchronizer.logger
                                            .debug(attribute.getName() + " = " + value);
                                }
                            }
                        } catch (UnsupportedEncodingException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (MalformedObjectNameException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (InstanceNotFoundException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (IntrospectionException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (AttributeNotFoundException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (ReflectionException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (MBeanException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        } catch (IOException e) {
                            if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger.warn("Exception during logging",
                                        e);
                            }
                        }

                    }
                    if (TenantChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger
                                .info("Synchronizing users and groups with user registry '" + id + "'");
                    }
                    if (isFullSync && TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger
                                .warn("Full synchronization with user registry '" + id + "'");
                        if (allowDeletions) {
                            TenantChainingUserRegistrySynchronizer.logger.warn(
                                    "Some users and groups previously created by synchronization with this user registry may be removed.");
                        } else {
                            TenantChainingUserRegistrySynchronizer.logger.warn(
                                    "Deletions are disabled. Users and groups removed from this registry will be logged only and will remain in the repository. Users previously found in a different registry will be moved in the repository rather than recreated.");
                        }
                    }
                    // Work out whether we should do the work in a separate
                    // transaction (it's most performant if we
                    // bunch it into small transactions, but if we are doing
                    // a sync on login, it has to be the same
                    // transaction)
                    boolean requiresNew = splitTxns || AlfrescoTransactionSupport
                            .getTransactionReadState() == TxnReadState.TXN_READ_ONLY;

                    syncWithPlugin(id, plugin, forceUpdate, isFullSync, requiresNew, visitedZoneIds,
                            allZoneIds);
                }
            } catch (NoSuchBeanDefinitionException e) {
                // Ignore and continue
            }

        }
    } catch (RuntimeException e) {
        TenantChainingUserRegistrySynchronizer.logger.error("Synchronization aborted due to error", e);
        throw e;
    }
    // Release the lock if necessary
    finally {
        if (lockToken != null) {
            // Cancel the lock refresher
            // Because we may hit a perfect storm when trying to interrupt
            // workers in their unsynchronized getTask()
            // method we can't wait indefinitely and may have to retry the
            // shutdown
            int trys = 0;
            do {
                lockRefresher.shutdown();
                try {
                    lockRefresher.awaitTermination(TenantChainingUserRegistrySynchronizer.LOCK_TTL,
                            TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                }
            } while (!lockRefresher.isTerminated() && trys++ < 3);
            if (!lockRefresher.isTerminated()) {
                lockRefresher.shutdownNow();
                TenantChainingUserRegistrySynchronizer.logger.error("Failed to shut down lock refresher");
            }

            final String token = lockToken;
            this.transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Object>() {
                        public Object execute() throws Throwable {
                            TenantChainingUserRegistrySynchronizer.this.jobLockService.releaseLock(token,
                                    TenantChainingUserRegistrySynchronizer.LOCK_QNAME);
                            return null;
                        }
                    }, false, splitTxns);
        }
    }
}

From source file:org.cggh.repo.security.sync.CustomChainingUserRegistrySynchronizer.java

private void synchronizeInternal(boolean forceUpdate, boolean isFullSync, final boolean splitTxns) {
    if (CustomChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {

        if (forceUpdate) {
            CustomChainingUserRegistrySynchronizer.logger.debug("Running a full sync.");
        } else {/*from w w w.ja v  a2 s  .  c o  m*/
            CustomChainingUserRegistrySynchronizer.logger.debug("Running a differential sync.");
        }
        if (allowDeletions) {
            CustomChainingUserRegistrySynchronizer.logger.debug("deletions are allowed");
        } else {
            CustomChainingUserRegistrySynchronizer.logger.debug("deletions are not allowed");
        }
        // Don't proceed with the sync if the repository is read only
        if (this.transactionService.isReadOnly()) {
            CustomChainingUserRegistrySynchronizer.logger
                    .warn("Unable to proceed with user registry synchronization. Repository is read only.");
            return;
        }
    }

    // Don't proceed with the sync if the repository is read only
    if (this.transactionService.isReadOnly()) {
        CustomChainingUserRegistrySynchronizer.logger
                .warn("Unable to proceed with user registry synchronization. Repository is read only.");
        return;
    }

    // Create a background executor that will refresh our lock. This means we can request a lock with a relatively
    // small persistence time and not worry about it lasting after server restarts. Note we use an independent
    // executor because this is a compound operation that spans accross multiple batch processors.
    String lockToken = null;
    TraceableThreadFactory threadFactory = new TraceableThreadFactory();
    threadFactory.setNamePrefix("ChainingUserRegistrySynchronizer lock refresh");
    threadFactory.setThreadDaemon(true);
    ScheduledExecutorService lockRefresher = new ScheduledThreadPoolExecutor(1, threadFactory);

    // Let's ensure all exceptions get logged
    try {
        // First, try to obtain a lock to ensure we are the only node trying to run this job
        try {
            if (splitTxns) {
                // If this is an automated sync on startup or scheduled sync, don't even wait around for the lock.
                // Assume the sync will be completed on another node.
                lockToken = this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<String>() {
                            public String execute() throws Throwable {
                                return CustomChainingUserRegistrySynchronizer.this.jobLockService.getLock(
                                        CustomChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        CustomChainingUserRegistrySynchronizer.LOCK_TTL, 0, 1);
                            }
                        }, false, splitTxns);
            } else {
                // If this is a login-triggered sync, give it a few retries before giving up
                lockToken = this.jobLockService.getLock(CustomChainingUserRegistrySynchronizer.LOCK_QNAME,
                        CustomChainingUserRegistrySynchronizer.LOCK_TTL, 3000, 10);
            }
        } catch (LockAcquisitionException e) {
            // Don't proceed with the sync if it is running on another node
            CustomChainingUserRegistrySynchronizer.logger.warn(
                    "User registry synchronization already running in another thread. Synchronize aborted");
            return;
        }

        // Schedule the lock refresh to run at regular intervals
        final String token = lockToken;
        lockRefresher.scheduleAtFixedRate(new Runnable() {
            public void run() {
                CustomChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper()
                        .doInTransaction(new RetryingTransactionCallback<Object>() {
                            public Object execute() throws Throwable {
                                CustomChainingUserRegistrySynchronizer.this.jobLockService.refreshLock(token,
                                        CustomChainingUserRegistrySynchronizer.LOCK_QNAME,
                                        CustomChainingUserRegistrySynchronizer.LOCK_TTL);
                                return null;
                            }
                        }, false, splitTxns);
            }
        }, CustomChainingUserRegistrySynchronizer.LOCK_TTL / 2,
                CustomChainingUserRegistrySynchronizer.LOCK_TTL / 2, TimeUnit.MILLISECONDS);

        Set<String> visitedZoneIds = new TreeSet<String>();
        Collection<String> instanceIds = this.applicationContextManager.getInstanceIds();

        // Work out the set of all zone IDs in the authentication chain so that we can decide which users / groups
        // need 're-zoning'
        Set<String> allZoneIds = new TreeSet<String>();
        for (String id : instanceIds) {
            allZoneIds.add(AuthorityService.ZONE_AUTH_EXT_PREFIX + id);
        }

        // Collect the plugins that we can sync : zoneId, plugin
        Map<String, UserRegistry> plugins = new HashMap<String, UserRegistry>();

        for (String id : instanceIds) {
            UserRegistry plugin;
            try {
                ApplicationContext context = this.applicationContextManager.getApplicationContext(id);
                plugin = (UserRegistry) context.getBean(this.sourceBeanName);
            } catch (RuntimeException e) {
                // The bean doesn't exist or this subsystem won't start. The reason would have been logged. Ignore and continue.
                continue;
            }

            if (!(plugin instanceof ActivateableBean) || ((ActivateableBean) plugin).isActive()) {
                // yes this plugin needs to be synced
                plugins.put(id, plugin);
            }
        }

        /**
         *  Sync starts here
         */
        notifySyncStart(plugins.keySet());

        for (String id : instanceIds) {
            UserRegistry plugin = plugins.get(id);

            if (plugin != null) {
                // If debug is enabled then dump out the contents of the authentication JMX bean
                if (CustomChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                    mbeanServer = (MBeanServerConnection) getApplicationContext()
                            .getBean("alfrescoMBeanServer");
                    try {
                        StringBuilder nameBuff = new StringBuilder(200)
                                .append("Alfresco:Type=Configuration,Category=Authentication,id1=managed,id2=")
                                .append(URLDecoder.decode(id, "UTF-8"));
                        ObjectName name = new ObjectName(nameBuff.toString());
                        if (mbeanServer != null && mbeanServer.isRegistered(name)) {
                            MBeanInfo info = mbeanServer.getMBeanInfo(name);
                            MBeanAttributeInfo[] attributes = info.getAttributes();
                            CustomChainingUserRegistrySynchronizer.logger.debug(id + " attributes:");
                            for (MBeanAttributeInfo attribute : attributes) {
                                Object value = mbeanServer.getAttribute(name, attribute.getName());
                                CustomChainingUserRegistrySynchronizer.logger
                                        .debug(attribute.getName() + " = " + value);
                            }
                        }
                    } catch (UnsupportedEncodingException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (MalformedObjectNameException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (InstanceNotFoundException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (IntrospectionException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (AttributeNotFoundException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (ReflectionException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (MBeanException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    } catch (IOException e) {
                        if (CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                            CustomChainingUserRegistrySynchronizer.logger.warn("Exception during logging", e);
                        }
                    }
                } // end of debug dump of active JMX bean
                if (CustomChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
                    CustomChainingUserRegistrySynchronizer.logger
                            .info("Synchronizing users and groups with user registry '" + id + "'");
                }
                if (isFullSync && CustomChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                    CustomChainingUserRegistrySynchronizer.logger
                            .warn("Full synchronization with user registry '" + id + "'");
                    if (allowDeletions) {
                        CustomChainingUserRegistrySynchronizer.logger.warn(
                                "Some users and groups previously created by synchronization with this user registry may be removed.");
                    } else {
                        CustomChainingUserRegistrySynchronizer.logger.warn(
                                "Deletions are disabled. Users and groups removed from this registry will be logged only and will remain in the repository. Users previously found in a different registry will be moved in the repository rather than recreated.");
                    }
                }
                // Work out whether we should do the work in a separate transaction (it's most performant if we
                // bunch it into small transactions, but if we are doing a sync on login, it has to be the same
                // transaction)
                boolean requiresNew = splitTxns
                        || AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;

                try {
                    /**
                     * Do the sync with the specified plugin
                     */
                    syncWithPlugin(id, plugin, forceUpdate, isFullSync, requiresNew, visitedZoneIds,
                            allZoneIds);

                    this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryEndEvent(this, id));
                } catch (final RuntimeException e) {
                    notifySyncDirectoryEnd(id, e);
                    throw e;
                }
            } // if plugin exists
        } // for each instanceId

        //End of successful synchronization here
        notifySyncEnd();
    } catch (final RuntimeException e) {
        notifySyncEnd(e);
        CustomChainingUserRegistrySynchronizer.logger.error("Synchronization aborted due to error", e);
        throw e;
    } finally {
        // Release the lock if necessary
        if (lockToken != null) {
            // Cancel the lock refresher
            // Because we may hit a perfect storm when trying to interrupt workers in their unsynchronized getTask()
            // method we can't wait indefinitely and may have to retry the shutdown
            int trys = 0;
            do {
                lockRefresher.shutdown();
                try {
                    lockRefresher.awaitTermination(CustomChainingUserRegistrySynchronizer.LOCK_TTL,
                            TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                }
            } while (!lockRefresher.isTerminated() && trys++ < 3);
            if (!lockRefresher.isTerminated()) {
                lockRefresher.shutdownNow();
                CustomChainingUserRegistrySynchronizer.logger.error("Failed to shut down lock refresher");
            }

            final String token = lockToken;
            this.transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Object>() {
                        public Object execute() throws Throwable {
                            CustomChainingUserRegistrySynchronizer.this.jobLockService.releaseLock(token,
                                    CustomChainingUserRegistrySynchronizer.LOCK_QNAME);
                            return null;
                        }
                    }, false, splitTxns);
        }
    }
}