@Override
  public void shutdown() {
    if (_luceneIndexThreadPoolExecutor != null) {
      _luceneIndexThreadPoolExecutor.shutdownNow();

      try {
        _luceneIndexThreadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS);
      } catch (InterruptedException ie) {
        _log.error("Lucene indexer shutdown interrupted", ie);
      }
    }

    if (isLoadIndexFromClusterEnabled()) {
      ClusterExecutorUtil.removeClusterEventListener(_loadIndexClusterEventListener);
    }

    MessageBus messageBus = MessageBusUtil.getMessageBus();

    for (String searchEngineId : SearchEngineUtil.getSearchEngineIds()) {
      String searchWriterDestinationName =
          SearchEngineUtil.getSearchWriterDestinationName(searchEngineId);

      Destination searchWriteDestination = messageBus.getDestination(searchWriterDestinationName);

      if (searchWriteDestination != null) {
        ThreadPoolExecutor threadPoolExecutor =
            PortalExecutorManagerUtil.getPortalExecutor(searchWriterDestinationName);

        int maxPoolSize = threadPoolExecutor.getMaxPoolSize();

        CountDownLatch countDownLatch = new CountDownLatch(maxPoolSize);

        ShutdownSyncJob shutdownSyncJob = new ShutdownSyncJob(countDownLatch);

        for (int i = 0; i < maxPoolSize; i++) {
          threadPoolExecutor.submit(shutdownSyncJob);
        }

        try {
          countDownLatch.await();
        } catch (InterruptedException ie) {
          _log.error("Shutdown waiting interrupted", ie);
        }

        List<Runnable> runnables = threadPoolExecutor.shutdownNow();

        if (_log.isDebugEnabled()) {
          _log.debug("Cancelled appending indexing jobs: " + runnables);
        }

        searchWriteDestination.close(true);
      }
    }

    for (IndexAccessor indexAccessor : _indexAccessors.values()) {
      indexAccessor.close();
    }
  }
  protected void submitClusterIndexLoadingSyncJob(
      Set<BaseAsyncDestination> baseAsyncDestinations, long[] companyIds) throws Exception {

    if (_log.isInfoEnabled()) {
      StringBundler sb = new StringBundler(baseAsyncDestinations.size() + 1);

      sb.append("[");

      for (BaseAsyncDestination baseAsyncDestination : baseAsyncDestinations) {

        sb.append(baseAsyncDestination.getName());
        sb.append(", ");
      }

      sb.setStringAt("]", sb.index() - 1);

      _log.info("Synchronizecluster index loading for destinations " + sb.toString());
    }

    int totalWorkersMaxSize = 0;

    for (BaseAsyncDestination baseAsyncDestination : baseAsyncDestinations) {

      totalWorkersMaxSize += baseAsyncDestination.getWorkersMaxSize();
    }

    if (_log.isInfoEnabled()) {
      _log.info("There are " + totalWorkersMaxSize + " synchronization threads");
    }

    CountDownLatch countDownLatch = new CountDownLatch(totalWorkersMaxSize + 1);

    ClusterLoadingSyncJob slaveClusterLoadingSyncJob =
        new ClusterLoadingSyncJob(companyIds, countDownLatch, false);

    for (BaseAsyncDestination baseAsyncDestination : baseAsyncDestinations) {

      ThreadPoolExecutor threadPoolExecutor =
          PortalExecutorManagerUtil.getPortalExecutor(baseAsyncDestination.getName());

      for (int i = 0; i < baseAsyncDestination.getWorkersMaxSize(); i++) {
        threadPoolExecutor.execute(slaveClusterLoadingSyncJob);
      }
    }

    ClusterLoadingSyncJob masterClusterLoadingSyncJob =
        new ClusterLoadingSyncJob(companyIds, countDownLatch, true);

    ThreadPoolExecutor threadPoolExecutor =
        PortalExecutorManagerUtil.getPortalExecutor(EditServerAction.class.getName());

    threadPoolExecutor.execute(masterClusterLoadingSyncJob);
  }
  @Test
  public void testAbortPolicy() {
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(
            1,
            2,
            TestUtil.KEEPALIVE_TIME,
            TimeUnit.MILLISECONDS,
            true,
            3,
            new AbortPolicy(),
            Executors.defaultThreadFactory(),
            new ThreadPoolHandlerAdapter());

    threadPoolExecutor.shutdown();

    try {
      threadPoolExecutor.execute(new MarkerBlockingJob());

      Assert.fail();
    } catch (RejectedExecutionException ree) {
    }
  }
  @Override
  protected void dispatch(final Set<MessageListener> messageListeners, final Message message) {

    if (!message.contains("companyId")) {
      message.put("companyId", CompanyThreadLocal.getCompanyId());
    }

    if (!message.contains("permissionChecker")) {
      message.put("permissionChecker", PermissionThreadLocal.getPermissionChecker());
    }

    if (!message.contains("principalName")) {
      message.put("principalName", PrincipalThreadLocal.getName());
    }

    if (!message.contains("principalPassword")) {
      message.put("principalPassword", PrincipalThreadLocal.getPassword());
    }

    ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

    Runnable runnable =
        new MessageRunnable(message) {

          public void run() {
            try {
              long messageCompanyId = message.getLong("companyId");

              if (messageCompanyId > 0) {
                CompanyThreadLocal.setCompanyId(messageCompanyId);
              }

              PermissionChecker permissionChecker =
                  (PermissionChecker) message.get("permissionChecker");

              if (permissionChecker != null) {
                PermissionThreadLocal.setPermissionChecker(permissionChecker);
              }

              String messagePrincipalName = message.getString("principalName");

              if (Validator.isNotNull(messagePrincipalName)) {
                PrincipalThreadLocal.setName(messagePrincipalName);
              }

              String messagePrincipalPassword = message.getString("principalPassword");

              if (Validator.isNotNull(messagePrincipalPassword)) {
                PrincipalThreadLocal.setPassword(messagePrincipalPassword);
              }

              Boolean clusterForwardMessage =
                  (Boolean) message.get(ClusterLinkUtil.CLUSTER_FORWARD_MESSAGE);

              if (clusterForwardMessage != null) {
                MessageValuesThreadLocal.setValue(
                    ClusterLinkUtil.CLUSTER_FORWARD_MESSAGE, clusterForwardMessage);
              }

              for (MessageListener messageListener : messageListeners) {
                try {
                  messageListener.receive(message);
                } catch (MessageListenerException mle) {
                  _log.error("Unable to process message " + message, mle);
                }
              }
            } finally {
              ThreadLocalCacheManager.clearAll(Lifecycle.REQUEST);

              CentralizedThreadLocal.clearShortLivedThreadLocals();
            }
          }
        };

    threadPoolExecutor.execute(runnable);
  }
  @Override
  protected void dispatch(Set<MessageListener> messageListeners, final Message message) {

    if (!message.contains("companyId")) {
      message.put("companyId", CompanyThreadLocal.getCompanyId());
    }

    if (!message.contains("principalName")) {
      message.put("principalName", PrincipalThreadLocal.getName());
    }

    if (!message.contains("principalPassword")) {
      message.put("principalPassword", PrincipalThreadLocal.getPassword());
    }

    ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

    for (final MessageListener messageListener : messageListeners) {
      Runnable runnable =
          new MessageRunnable(message) {

            public void run() {
              long companyId = CompanyThreadLocal.getCompanyId();
              String principalName = PrincipalThreadLocal.getName();
              String principalPassword = PrincipalThreadLocal.getPassword();

              try {
                long messageCompanyId = message.getLong("companyId");

                if (messageCompanyId > 0) {
                  CompanyThreadLocal.setCompanyId(messageCompanyId);
                }

                String messagePrincipalName = message.getString("principalName");

                if (Validator.isNotNull(messagePrincipalName)) {
                  PrincipalThreadLocal.setName(messagePrincipalName);
                }

                String messagePrincipalPassword = message.getString("principalPassword");

                if (Validator.isNotNull(messagePrincipalPassword)) {
                  PrincipalThreadLocal.setPassword(messagePrincipalPassword);
                }

                messageListener.receive(message);
              } catch (MessageListenerException mle) {
                _log.error("Unable to process message " + message, mle);
              } finally {
                CompanyThreadLocal.setCompanyId(companyId);
                PrincipalThreadLocal.setName(principalName);
                PrincipalThreadLocal.setPassword(principalPassword);

                CentralizedThreadLocal.clearShortLivedThreadLocals();
              }
            }
          };

      threadPoolExecutor.execute(runnable);
    }
  }