package com.linkedin.databus.bootstrap.common; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import com.linkedin.databus.core.DatabusThreadBase; import com.linkedin.databus.core.util.NamedThreadFactory; import com.linkedin.databus2.core.DatabusException; public class BootstrapDBCleaner { public static final String MODULE = BootstrapDBCleaner.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); private final long TERMINATION_TIMEOUT_IN_MS = 10000; public BootstrapCleanerStaticConfig _cleanerConfig; public BootstrapReadOnlyConfig _bootstrapConfig; private Map<String, DatabusThreadBase> _appliers; private final Map<String, BootstrapDBSingleSourceCleaner> _cleaners; private final ExecutorService _cleanerThreadPoolService; private final Map<String, Future<?>> _cleanerFutures; public BootstrapDBCleaner(String name, BootstrapCleanerStaticConfig config, BootstrapReadOnlyConfig bootstrapConfig, Map<String, DatabusThreadBase> appliers, List<String> sources) throws SQLException { _cleanerConfig = config; _bootstrapConfig = bootstrapConfig; if (null != appliers) { _appliers = appliers; } else { _appliers = new HashMap<String, DatabusThreadBase>(); } _cleaners = new HashMap<String, BootstrapDBSingleSourceCleaner>(); if (null != sources) { for (String source: sources) { String perSourceName = name + "_" + source; DatabusThreadBase perSourceApplier = _appliers.get(source); BootstrapDBSingleSourceCleaner cleaner = new BootstrapDBSingleSourceCleaner(perSourceName, source, perSourceApplier, config, bootstrapConfig); _cleaners.put(source,cleaner); } } ThreadFactory tf = new NamedThreadFactory(name); _cleanerThreadPoolService = Executors.newCachedThreadPool(tf); _cleanerFutures = new HashMap<String, Future<?>>(); LOG.info("Cleaner Config is :" + _cleanerConfig); } public synchronized void doClean() { // Invoke doClean on each of the individual single source cleaners for (Map.Entry<String, BootstrapDBSingleSourceCleaner> entry : _cleaners.entrySet()) { String source = entry.getKey(); BootstrapDBSingleSourceCleaner singleSourceCleaner = entry.getValue(); Future<?> c = _cleanerFutures.get(source); if (c != null && !c.isDone()) { LOG.info("Skipping running cleaner as it is already running for source = " + source); } else { LOG.info("Submitting a cleaner task for source = " + source); Future<?> cleaner = _cleanerThreadPoolService.submit(singleSourceCleaner); _cleanerFutures.put(source, cleaner); } } } public synchronized boolean isAnyCleanerRunning() { for (Map.Entry<String, Future<?>> entry : _cleanerFutures.entrySet()) { Future<?> cleanerFuture = entry.getValue(); if (!cleanerFuture.isDone()) { LOG.debug("Cleaner process is running for source = " + entry.getKey()); return true; } } LOG.info("There are no cleaner processes running"); return false; } public synchronized void sleepTillNoCleanerIsRunning() throws DatabusException, InterruptedException { final long maxWaitTime = TERMINATION_TIMEOUT_IN_MS; long waitTime = 0; while (isAnyCleanerRunning()) { if (waitTime >= TERMINATION_TIMEOUT_IN_MS) { throw new DatabusException("The cleaners have not terminated within " + maxWaitTime + " ms"); } final long sleepIntervalInMs = 100; Thread.sleep(sleepIntervalInMs); waitTime += sleepIntervalInMs; } } public void close() { List<Runnable> incompleteCleaners = _cleanerThreadPoolService.shutdownNow(); if (incompleteCleaners.size() > 0) { // The cleaners that have not started as of initiating a shutdown, will not be started // Not an error, hence logging for informational purpose LOG.info("Number of cleaners that have not completed = " + incompleteCleaners.size()); LOG.info("Printing out sources for which cleaners what not completed "); for (Runnable r: incompleteCleaners) { BootstrapDBSingleSourceCleaner bsc = (BootstrapDBSingleSourceCleaner) r; LOG.error(bsc.getName()); } } try { boolean hasTerminated = _cleanerThreadPoolService.awaitTermination(TERMINATION_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); LOG.info("Result of terminating cleaner thread pool service: " + (hasTerminated ? "success" : "failure")); } catch (InterruptedException e) { LOG.error("Cleaner thread pool service termination has been interrupted", e); } finally { for (Map.Entry<String, BootstrapDBSingleSourceCleaner> entry : _cleaners.entrySet()) { String source = entry.getKey(); BootstrapDBSingleSourceCleaner singleSourceCleaner = entry.getValue(); LOG.info("Invoking close on cleaner for source = " + source); singleSourceCleaner.close(); } } } }