List of usage examples for java.util.concurrent ScheduledExecutorService shutdownNow
List<Runnable> shutdownNow();
From source file:com.linkedin.d2.balancer.simple.SimpleLoadBalancerTest.java
@Test(groups = { "small", "back-end" }) public void testLoadBalancerWithPartitionsSmoke() throws URISyntaxException, ServiceUnavailableException, InterruptedException, ExecutionException { for (int tryAgain = 0; tryAgain < 12; ++tryAgain) { Map<String, LoadBalancerStrategyFactory<? extends LoadBalancerStrategy>> loadBalancerStrategyFactories = new HashMap<String, LoadBalancerStrategyFactory<? extends LoadBalancerStrategy>>(); Map<String, TransportClientFactory> clientFactories = new HashMap<String, TransportClientFactory>(); List<String> prioritizedSchemes = new ArrayList<String>(); MockStore<ServiceProperties> serviceRegistry = new MockStore<ServiceProperties>(); MockStore<ClusterProperties> clusterRegistry = new MockStore<ClusterProperties>(); MockStore<UriProperties> uriRegistry = new MockStore<UriProperties>(); ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); loadBalancerStrategyFactories.put("degrader", new DegraderLoadBalancerStrategyFactoryV3()); clientFactories.put("http", new DoNothingClientFactory()); SimpleLoadBalancerState state = new SimpleLoadBalancerState(executorService, uriRegistry, clusterRegistry, serviceRegistry, clientFactories, loadBalancerStrategyFactories); SimpleLoadBalancer loadBalancer = new SimpleLoadBalancer(state, 5, TimeUnit.SECONDS); FutureCallback<None> balancerCallback = new FutureCallback<None>(); loadBalancer.start(balancerCallback); balancerCallback.get();/*ww w . j av a 2s. c o m*/ URI uri1 = URI.create("http://test.qa1.com:1234"); URI uri2 = URI.create("http://test.qa2.com:2345"); URI uri3 = URI.create("http://test.qa3.com:6789"); Map<URI, Double> uris = new HashMap<URI, Double>(); uris.put(uri1, 1d); uris.put(uri2, 1d); uris.put(uri3, 1d); Map<URI, Map<Integer, PartitionData>> partitionDesc = new HashMap<URI, Map<Integer, PartitionData>>(); Map<Integer, PartitionData> server1 = new HashMap<Integer, PartitionData>(); server1.put(0, new PartitionData(1d)); server1.put(1, new PartitionData(1d)); Map<Integer, PartitionData> server2 = new HashMap<Integer, PartitionData>(); server2.put(0, new PartitionData(1d)); Map<Integer, PartitionData> server3 = new HashMap<Integer, PartitionData>(); server3.put(1, new PartitionData(1d)); partitionDesc.put(uri1, server1); partitionDesc.put(uri2, server2); partitionDesc.put(uri3, server3); prioritizedSchemes.add("http"); int partitionMethod = tryAgain % 4; switch (partitionMethod) { case 0: clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1", null, new HashMap<String, String>(), new HashSet<URI>(), new RangeBasedPartitionProperties("id=(\\d+)", 0, 50, 2))); break; case 1: clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1", null, new HashMap<String, String>(), new HashSet<URI>(), new HashBasedPartitionProperties("id=(\\d+)", 2, HashBasedPartitionProperties.HashAlgorithm.valueOf("MODULO")))); break; case 2: clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1", null, new HashMap<String, String>(), new HashSet<URI>(), new HashBasedPartitionProperties("id=(\\d+)", 2, HashBasedPartitionProperties.HashAlgorithm.valueOf("MD5")))); break; case 3: // test getRings with gap. here, no server serves partition 2 clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1", null, new HashMap<String, String>(), new HashSet<URI>(), new RangeBasedPartitionProperties("id=(\\d+)", 0, 50, 4))); server3.put(3, new PartitionData(1d)); partitionDesc.put(uri3, server3); break; default: break; } serviceRegistry.put("foo", new ServiceProperties("foo", "cluster-1", "/foo", Arrays.asList("degrader"), Collections.<String, Object>emptyMap(), null, null, prioritizedSchemes, null)); uriRegistry.put("cluster-1", new UriProperties("cluster-1", partitionDesc)); if (partitionMethod == 3) { Map<Integer, Ring<URI>> ringMap = loadBalancer.getRings(URI.create("d2://foo")); assertEquals(ringMap.size(), 4); // the ring for partition 2 should be empty assertEquals(ringMap.get(2).toString(), new ConsistentHashRing<URI>(Collections.emptyList()).toString()); continue; } URI expectedUri1 = URI.create("http://test.qa1.com:1234/foo"); URI expectedUri2 = URI.create("http://test.qa2.com:2345/foo"); URI expectedUri3 = URI.create("http://test.qa3.com:6789/foo"); Set<URI> expectedUris = new HashSet<URI>(); expectedUris.add(expectedUri1); expectedUris.add(expectedUri2); expectedUris.add(expectedUri3); for (int i = 0; i < 1000; ++i) { int ii = i % 100; RewriteClient client = (RewriteClient) loadBalancer.getClient(new URIRequest("d2://foo/id=" + ii), new RequestContext()); String clientUri = client.getUri().toString(); HashFunction<String[]> hashFunction = null; String[] str = new String[1]; // test KeyMapper target host hint: request is always to target host regardless of what's in d2 URI and whether it's hash-based or range-based partitions RequestContext requestContextWithHint = new RequestContext(); KeyMapper.TargetHostHints.setRequestContextTargetHost(requestContextWithHint, uri1); RewriteClient hintedClient1 = (RewriteClient) loadBalancer .getClient(new URIRequest("d2://foo/id=" + ii), requestContextWithHint); String hintedUri1 = hintedClient1.getUri().toString(); Assert.assertEquals(hintedUri1, uri1.toString() + "/foo"); RewriteClient hintedClient2 = (RewriteClient) loadBalancer .getClient(new URIRequest("d2://foo/action=purge-all"), requestContextWithHint); String hintedUri2 = hintedClient2.getUri().toString(); Assert.assertEquals(hintedUri2, uri1.toString() + "/foo"); // end test KeyMapper target host hint if (partitionMethod == 2) { hashFunction = new MD5Hash(); } for (URI uri : expectedUris) { if (clientUri.contains(uri.toString())) { // check if only key belonging to partition 0 gets uri2 if (uri.equals(uri2)) { if (partitionMethod == 0) { assertTrue(ii < 50); } else if (partitionMethod == 1) { assertTrue(ii % 2 == 0); } else { str[0] = ii + ""; assertTrue(hashFunction.hash(str) % 2 == 0); } } // check if only key belonging to partition 1 gets uri3 if (uri.equals(uri3)) { if (partitionMethod == 0) { assertTrue(ii >= 50); } else if (partitionMethod == 1) { assertTrue(ii % 2 == 1); } else { str[0] = ii + ""; assertTrue(hashFunction.hash(str) % 2 == 1); } } } } } // two rings for two partitions Map<Integer, Ring<URI>> ringMap = loadBalancer.getRings(URI.create("d2://foo")); assertEquals(ringMap.size(), 2); if (partitionMethod != 2) { Set<String> keys = new HashSet<String>(); for (int j = 0; j < 50; j++) { if (partitionMethod == 0) { keys.add(j + ""); } else { keys.add(j * 2 + ""); } } // if it is range based partition, all keys from 0 ~ 49 belong to partition 0 according to the range definition // if it is modulo based partition, all even keys belong to partition 0 because the partition count is 2 // only from partition 0 MapKeyResult<Ring<URI>, String> mapKeyResult = loadBalancer.getRings(URI.create("d2://foo"), keys); Map<Ring<URI>, Collection<String>> keyToPartition = mapKeyResult.getMapResult(); assertEquals(keyToPartition.size(), 1); for (Ring<URI> ring : keyToPartition.keySet()) { assertEquals(ring, ringMap.get(0)); } // now also from partition 1 keys.add("51"); mapKeyResult = loadBalancer.getRings(URI.create("d2://foo"), keys); assertEquals(mapKeyResult.getMapResult().size(), 2); assertEquals(mapKeyResult.getUnmappedKeys().size(), 0); // now only from partition 1 keys.clear(); keys.add("99"); mapKeyResult = loadBalancer.getRings(URI.create("d2://foo"), keys); keyToPartition = mapKeyResult.getMapResult(); assertEquals(keyToPartition.size(), 1); assertEquals(mapKeyResult.getUnmappedKeys().size(), 0); for (Ring<URI> ring : keyToPartition.keySet()) { assertEquals(ring, ringMap.get(1)); } keys.add("100"); mapKeyResult = loadBalancer.getRings(URI.create("d2://foo"), keys); if (partitionMethod == 0) { // key out of range Collection<MapKeyResult.UnmappedKey<String>> unmappedKeys = mapKeyResult.getUnmappedKeys(); assertEquals(unmappedKeys.size(), 1); } try { loadBalancer.getClient(new URIRequest("d2://foo/id=100"), new RequestContext()); if (partitionMethod == 0) { // key out of range fail("Should throw ServiceUnavailableException caused by PartitionAccessException"); } } catch (ServiceUnavailableException e) { } } final CountDownLatch latch = new CountDownLatch(1); PropertyEventShutdownCallback callback = new PropertyEventShutdownCallback() { @Override public void done() { latch.countDown(); } }; state.shutdown(callback); if (!latch.await(60, TimeUnit.SECONDS)) { fail("unable to shutdown state"); } executorService.shutdownNow(); assertTrue(executorService.isShutdown(), "ExecutorService should have shut down!"); } }
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 {// ww w . j av a 2s . c om 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.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 .j av a2s.c om 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.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 ww w .ja va 2s . co 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); } } }