package org.apache.blur; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 static org.apache.blur.utils.BlurConstants.BLUR_CONTROLLER_BIND_PORT; import static org.apache.blur.utils.BlurConstants.BLUR_GUI_CONTROLLER_PORT; import static org.apache.blur.utils.BlurConstants.BLUR_GUI_SHARD_PORT; import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_BIND_PORT; import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_BLOCKCACHE_DIRECT_MEMORY_ALLOCATION; import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_BLOCKCACHE_SLAB_COUNT; import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_HOSTNAME; import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_SAFEMODEDELAY; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_CONNECTION; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.lang.reflect.Field; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import org.apache.blur.log.Log; import org.apache.blur.log.LogFactory; import org.apache.blur.store.buffer.BufferStore; import org.apache.blur.thirdparty.thrift_0_9_0.TException; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TTransportException; import org.apache.blur.thrift.BlurClient; import org.apache.blur.thrift.ThriftBlurControllerServer; import org.apache.blur.thrift.ThriftBlurShardServer; import org.apache.blur.thrift.ThriftServer; import org.apache.blur.thrift.generated.Blur.Iface; import org.apache.blur.thrift.generated.BlurException; import org.apache.blur.thrift.generated.BlurQuery; import org.apache.blur.thrift.generated.BlurResults; import org.apache.blur.thrift.generated.Column; import org.apache.blur.thrift.generated.Record; import org.apache.blur.thrift.generated.Row; import org.apache.blur.thrift.generated.RowMutation; import org.apache.blur.thrift.generated.TableDescriptor; import org.apache.blur.thrift.util.BlurThriftHelper; import org.apache.blur.utils.BlurUtil; import org.apache.blur.utils.MemoryReporter; import org.apache.blur.zookeeper.ZkMiniCluster; import org.apache.blur.zookeeper.ZooKeeperClient; import org.apache.blur.zookeeper.ZookeeperPathConstants; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.util.VersionInfo; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class MiniCluster { private static Log LOG = LogFactory.getLog(MiniCluster.class); private MiniDFSCluster cluster; private final String id = UUID.randomUUID().toString(); private ZkMiniCluster zkMiniCluster = new ZkMiniCluster(); private List<MiniClusterServer> controllers = new ArrayList<MiniClusterServer>(); private List<MiniClusterServer> shards = new ArrayList<MiniClusterServer>(); private ThreadGroup group = new ThreadGroup(id); private Configuration _conf; private Object mrMiniCluster; private Configuration _mrConf; public static void main(String[] args) throws IOException, InterruptedException, KeeperException, BlurException, TException { MiniCluster miniCluster = new MiniCluster(); miniCluster.startDfs("./tmp/hdfs"); miniCluster.startZooKeeper("./tmp/zk"); miniCluster.startControllers(1, false, false); miniCluster.startShards(1, false, false); try { Iface client = BlurClient.getClient(miniCluster.getControllerConnectionStr()); miniCluster.createTable("test", client); long start = System.nanoTime(); for (int i = 0; i < 1000; i++) { long now = System.nanoTime(); if (start + 5000000000L < now) { System.out.println("Total [" + i + "]"); start = now; } miniCluster.addRow("test", i, client); } // This waits for all the data to become visible. Thread.sleep(2000); for (int i = 0; i < 1000; i++) { miniCluster.searchRow("test", i, client); } } finally { miniCluster.stopShards(); miniCluster.stopControllers(); miniCluster.shutdownZooKeeper(); miniCluster.shutdownDfs(); } } public void startBlurCluster(String path, int controllerCount, int shardCount) { startBlurCluster(path, controllerCount, shardCount, false, false); } public void startBlurCluster(String path, int controllerCount, int shardCount, boolean randomPort) { startBlurCluster(path, controllerCount, shardCount, randomPort, false); } public void startBlurCluster(final String path, final int controllerCount, final int shardCount, final boolean randomPort, final boolean externalProcesses) { Thread thread = new Thread(group, new Runnable() { @Override public void run() { MemoryReporter.enable(); startDfs(path + "/hdfs"); startZooKeeper(path + "/zk", randomPort); setupBuffers(); startControllers(controllerCount, randomPort, externalProcesses); startShards(shardCount, randomPort, externalProcesses); try { waitForSafeModeToExit(); } catch (BlurException e) { throw new RuntimeException(e); } catch (TException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void stopMrMiniCluster() throws IOException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("js"); if (useYarn()) { engine.put("mrMiniCluster", mrMiniCluster); try { engine.eval("mrMiniCluster.stop();"); } catch (ScriptException e) { throw new IOException(e); } } else { engine.put("mrMiniCluster", mrMiniCluster); try { engine.eval("mrMiniCluster.shutdown();"); } catch (ScriptException e) { throw new IOException(e); } } } public void startMrMiniCluster() throws IOException { _mrConf = startMrMiniClusterInternal(getFileSystemUri().toString()); } public void startMrMiniCluster(String fileSystemUri) throws IOException { _mrConf = startMrMiniClusterInternal(fileSystemUri); } public Configuration getMRConfiguration() { return _mrConf; } private Configuration startMrMiniClusterInternal(String fileSystemUri) throws IOException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("js"); if (useYarn()) { int nodeManagers = 4; Class<?> c = getClass(); engine.put("c", c); engine.put("nodeManagers", nodeManagers); engine.put("fileSystemUri", fileSystemUri); try { engine.eval("conf = new org.apache.hadoop.yarn.conf.YarnConfiguration()"); engine.eval("org.apache.hadoop.fs.FileSystem.setDefaultUri(conf, fileSystemUri);"); engine .eval("mrMiniCluster = org.apache.hadoop.mapred.MiniMRClientClusterFactory.create(c, nodeManagers, conf);"); engine.eval("mrMiniCluster.start();"); engine.eval("configuration = mrMiniCluster.getConfig();"); } catch (ScriptException e) { throw new IOException(e); } Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); mrMiniCluster = bindings.get("mrMiniCluster"); return (Configuration) bindings.get("configuration"); } else { int numTaskTrackers = 2; int numDir = 1; engine.put("fileSystemUri", fileSystemUri); engine.put("numTaskTrackers", numTaskTrackers); engine.put("numDir", numDir); try { engine .eval("mrMiniCluster = new org.apache.hadoop.mapred.MiniMRCluster(numTaskTrackers, fileSystemUri, numDir);"); engine.eval("configuration = mrMiniCluster.createJobConf();"); } catch (ScriptException e) { throw new IOException(e); } Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); mrMiniCluster = bindings.get("mrMiniCluster"); return (Configuration) bindings.get("configuration"); } } public boolean useYarn() { String version = VersionInfo.getVersion(); if (version.startsWith("0.20.") || version.startsWith("1.")) { return false; } // Check for mr1 hadoop2 if (isMr1Hadoop2()) { return false; } return true; } public boolean isMr1Hadoop2() { try { Enumeration<URL> e = ClassLoader.getSystemClassLoader().getResources( "META-INF/maven/org.apache.hadoop/hadoop-client/pom.properties"); while (e.hasMoreElements()) { URL url = e.nextElement(); InputStream stream = url.openStream(); Properties properties = new Properties(); properties.load(stream); Object object = properties.get("version"); if (object.toString().contains("mr1")) { return true; } } } catch (Exception e) { e.printStackTrace(); } return false; } private void waitForSafeModeToExit() throws BlurException, TException, IOException { String controllerConnectionStr = getControllerConnectionStr(); Iface client = BlurClient.getClient(controllerConnectionStr); String clusterName = "default"; boolean inSafeMode; boolean isNoLonger = false; do { inSafeMode = client.isInSafeMode(clusterName); if (!inSafeMode) { if (isNoLonger) { System.out.println("Cluster " + cluster + " is no longer in safemode."); } else { System.out.println("Cluster " + cluster + " is not in safemode."); } return; } isNoLonger = true; try { Thread.sleep(TimeUnit.SECONDS.toMillis(1)); } catch (InterruptedException e) { throw new RuntimeException(e); } } while (inSafeMode); } private void setupBuffers() { BufferStore.initNewBuffer(128, 128 * 128); } public void shutdownBlurCluster() { stopShards(); stopControllers(); shutdownZooKeeper(); shutdownDfs(); } private void createTable(String test, Iface client) throws BlurException, TException, IOException { final TableDescriptor descriptor = new TableDescriptor(); descriptor.setName(test); descriptor.setShardCount(7); descriptor.setTableUri(getFileSystemUri() + "/blur/" + test); client.createTable(descriptor); } public String getControllerConnectionStr() { String zkConnectionString = zkMiniCluster.getZkConnectionString(); ZooKeeper zk; try { zk = new ZooKeeperClient(zkConnectionString, 30000, new Watcher() { @Override public void process(WatchedEvent event) { } }); } catch (IOException e) { throw new RuntimeException(e); } String onlineControllersPath = ZookeeperPathConstants.getOnlineControllersPath(); try { List<String> children = zk.getChildren(onlineControllersPath, false); StringBuilder builder = new StringBuilder(); for (String s : children) { if (builder.length() != 0) { builder.append(','); } builder.append(s); } return builder.toString(); } catch (KeeperException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { try { zk.close(); } catch (InterruptedException e) { LOG.error("Unkown error while trying to close ZooKeeper client.", e); } } } private void addRow(String table, int i, Iface client) throws BlurException, TException { Row row = new Row(); row.setId(Integer.toString(i)); Record record = new Record(); record.setRecordId(Integer.toString(i)); record.setFamily("test"); record.addToColumns(new Column("test", Integer.toString(i))); row.addToRecords(record); RowMutation rowMutation = BlurUtil.toRowMutation(table, row); client.mutate(rowMutation); } private void searchRow(String table, int i, Iface client) throws BlurException, TException { BlurQuery blurQuery = BlurThriftHelper.newSimpleQuery("test.test:" + i); System.out.println("Running [" + blurQuery + "]"); BlurResults results = client.query(table, blurQuery); if (results.getTotalResults() != 1L) { throw new RuntimeException("we got a problem here."); } } public void stopControllers() { IOUtils.cleanup(LOG, controllers.toArray(new Closeable[] {})); } public void stopShards() { IOUtils.cleanup(LOG, shards.toArray(new Closeable[] {})); } public void startControllers(int num, boolean randomPort, boolean externalProcesses) { BlurConfiguration configuration = getBlurConfiguration(); startControllers(configuration, num, randomPort, externalProcesses); } private BlurConfiguration getBlurConfiguration(BlurConfiguration overrides) { BlurConfiguration conf = getBlurConfiguration(); for (Map.Entry<String, String> over : overrides.getProperties().entrySet()) { conf.set(over.getKey().toString(), over.getValue().toString()); } return conf; } private BlurConfiguration getBlurConfiguration() { BlurConfiguration configuration; try { configuration = new BlurConfiguration(); } catch (IOException e) { LOG.error(e); throw new RuntimeException(e); } configuration.set(BLUR_ZOOKEEPER_CONNECTION, getZkConnectionString()); configuration.set(BLUR_SHARD_BLOCKCACHE_DIRECT_MEMORY_ALLOCATION, "false"); configuration.set(BLUR_SHARD_BLOCKCACHE_SLAB_COUNT, "0"); configuration.setLong(BLUR_SHARD_SAFEMODEDELAY, 5000); configuration.setInt(BLUR_GUI_CONTROLLER_PORT, -1); configuration.setInt(BLUR_GUI_SHARD_PORT, -1); return configuration; } public void startControllers(BlurConfiguration configuration, int num, boolean randomPort, boolean externalProcesses) { BlurConfiguration localConf = getBlurConfiguration(configuration); if (randomPort) { localConf.setInt(BLUR_CONTROLLER_BIND_PORT, 0); } for (int i = 0; i < num; i++) { try { MiniClusterServer miniClusterServer = toMiniClusterServer(i, TYPE.controller, localConf, externalProcesses); controllers.add(miniClusterServer); startServer(miniClusterServer); } catch (Exception e) { LOG.error(e); throw new RuntimeException(e); } } } enum TYPE { shard, controller } private MiniClusterServer toMiniClusterServer(int serverIndex, TYPE type, final BlurConfiguration configuration, boolean externalProcesses) { if (externalProcesses) { return toMiniClusterServerExternal(serverIndex, type, configuration); } else { return toMiniClusterServerInternal(serverIndex, type, configuration); } } private MiniClusterServer toMiniClusterServerExternal(final int serverIndex, final TYPE type, BlurConfiguration configuration) { // write out configuration to tmp file. // build args // build class path try { File dir = new File("./target/blurtesttemp"); dir.mkdirs(); final File file = new File(dir, "blurtestconf." + UUID.randomUUID().toString() + ".properties").getAbsoluteFile(); OutputStream outputStream = new FileOutputStream(file); configuration.write(outputStream); outputStream.close(); System.out.println("File [" + file.getAbsolutePath() + "] exists [" + file.exists() + "]"); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { file.delete(); } })); String javaHome = System.getProperty("java.home"); String classPath = System.getProperty("java.class.path"); List<String> command = new ArrayList<String>(); command.add(javaHome + "/bin/java"); command.add("-Xmx512m"); command.add("-Xms512m"); command.add("-cp"); command.add(classPath); command.add(ExternalThriftServer.class.getName()); command.add(type.name()); command.add(Integer.toString(serverIndex)); command.add(file.getAbsolutePath()); final ProcessBuilder builder = new ProcessBuilder(command); final MiniClusterServer miniClusterServer = new MiniClusterServer() { private Process _process; private AtomicBoolean _online = new AtomicBoolean(); @Override public void close() throws IOException { kill(); } @Override public void waitUntilOnline() { while (!_online.get()) { try { Thread.sleep(100); } catch (InterruptedException e) { LOG.error("Unknown error while trying to wait for process to come online.", e); } } } @Override public void start() { try { _process = builder.start(); } catch (IOException e) { throw new RuntimeException(e); } pipeOutputToStdOut(_process.getInputStream()); pipeOutputToStdOut(_process.getErrorStream()); } private void pipeOutputToStdOut(final InputStream inputStream) { new Thread(new Runnable() { @Override public void run() { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; try { while ((line = reader.readLine()) != null) { LOG.info("Process Output Type [" + type + "] Index [" + serverIndex + "] Line [" + line + "]"); if (line.trim().equals("ONLINE")) { _online.set(true); } } } catch (IOException e) { LOG.error("Unknown error while trying to follow input stream.", e); } } }).start(); } @Override public void kill() { if (_process != null) { _process.destroy(); } } }; Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { miniClusterServer.kill(); } })); return miniClusterServer; } catch (Exception e) { throw new RuntimeException(e); } } private MiniClusterServer toMiniClusterServerInternal(int serverIndex, TYPE type, final BlurConfiguration configuration) { final ThriftServer thriftServer; try { thriftServer = getThriftServer(serverIndex, type, configuration); } catch (Exception e) { throw new RuntimeException(e); } return new MiniClusterServer() { @Override public void close() throws IOException { thriftServer.close(); } @Override public void kill() { ZooKeeper zk = null; try { int shardPort = ThriftServer.getBindingPort(thriftServer.getServerTransport()); String nodeNameHostname = ThriftServer.getNodeName(configuration, BLUR_SHARD_HOSTNAME); String nodeName = nodeNameHostname + ":" + shardPort; zk = new ZooKeeperClient(getZkConnectionString(), 30000, new Watcher() { @Override public void process(WatchedEvent event) { } }); String onlineShardsPath = ZookeeperPathConstants .getOnlineShardsPath(org.apache.blur.utils.BlurConstants.BLUR_CLUSTER); String path = onlineShardsPath + "/" + nodeName; zk.delete(path, -1); zk.close(); } catch (Exception e) { throw new RuntimeException(e); } finally { if (zk != null) { try { zk.close(); } catch (InterruptedException e) { LOG.error("Unknown error while trying to close ZooKeeper client.", e); } } } } @Override public void start() { try { thriftServer.start(); } catch (TTransportException e) { LOG.error(e); throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void waitUntilOnline() { while (true) { try { Thread.sleep(50); } catch (InterruptedException e) { return; } int localPort = ThriftServer.getBindingPort(thriftServer.getServerTransport()); if (localPort == 0) { continue; } else { try { Thread.sleep(500); } catch (InterruptedException e) { LOG.error("Unknown error", e); } return; } } } }; } private ThriftServer getThriftServer(int serverIndex, TYPE type, BlurConfiguration configuration) throws Exception { switch (type) { case controller: return ThriftBlurControllerServer.createServer(serverIndex, configuration); case shard: return ThriftBlurShardServer.createServer(serverIndex, configuration); default: throw new RuntimeException("Type not supported [" + type + "]"); } } public void startShards(int num, boolean randomPort, boolean externalProcesses) { BlurConfiguration configuration = getBlurConfiguration(); startShards(configuration, num, randomPort, externalProcesses); } public void startShards(final BlurConfiguration configuration, int num, final boolean randomPort, final boolean externalProcesses) { final BlurConfiguration localConf = getBlurConfiguration(configuration); if (randomPort) { localConf.setInt(BLUR_SHARD_BIND_PORT, 0); } ExecutorService executorService = Executors.newFixedThreadPool(num); List<Future<MiniClusterServer>> futures = new ArrayList<Future<MiniClusterServer>>(); for (int i = 0; i < num; i++) { final int index = i; futures.add(executorService.submit(new Callable<MiniClusterServer>() { @Override public MiniClusterServer call() throws Exception { return toMiniClusterServer(index, TYPE.shard, localConf, externalProcesses); } })); } for (int i = 0; i < num; i++) { try { MiniClusterServer server = futures.get(i).get(); shards.add(server); startServer(server); } catch (Exception e) { LOG.error(e); throw new RuntimeException(e); } } } public void killShardServer(int shardServer) throws IOException, InterruptedException, KeeperException { killShardServer(getBlurConfiguration(), shardServer); } public void killShardServer(final BlurConfiguration configuration, int shardServer) throws IOException, InterruptedException, KeeperException { MiniClusterServer miniClusterServer = shards.get(shardServer); miniClusterServer.kill(); } private static void startServer(final MiniClusterServer server) { new Thread(new Runnable() { @Override public void run() { server.start(); } }).start(); server.waitUntilOnline(); } public Configuration getConfiguration() { return _conf; } public String getZkConnectionString() { return zkMiniCluster.getZkConnectionString(); } public void startZooKeeper(String path) { zkMiniCluster.startZooKeeper(path); } public void startZooKeeper(String path, boolean randomPort) { zkMiniCluster.startZooKeeper(path, randomPort); } public void startZooKeeper(boolean format, String path) { zkMiniCluster.startZooKeeper(format, path); } public void startZooKeeper(boolean format, String path, boolean randomPort) { zkMiniCluster.startZooKeeper(format, path, randomPort); } public void startZooKeeper(Properties properties, String path) { zkMiniCluster.startZooKeeper(properties, path); } public void startZooKeeper(Properties properties, String path, boolean randomPort) { zkMiniCluster.startZooKeeper(properties, path, randomPort); } public void startZooKeeper(final Properties properties, boolean format, String path, final boolean randomPort) { zkMiniCluster.startZooKeeper(properties, format, path, randomPort); } public FileSystem getFileSystem() throws IOException { return cluster.getFileSystem(); } public URI getFileSystemUri() throws IOException { return getFileSystem().getUri(); } public void startDfs(String path) { startDfs(true, path); } public void startDfs(boolean format, String path) { startDfs(new Configuration(), format, path); } public void startDfs(Configuration conf, String path) { startDfs(conf, true, path); } public void startDfs(final Configuration conf, final boolean format, final String path) { startDfs(conf, format, path, null); } public void startDfs(final Configuration conf, final boolean format, final String path, final String[] racks) { Thread thread = new Thread(group, new Runnable() { @SuppressWarnings("deprecation") @Override public void run() { _conf = conf; String perm; Path p = new Path(new File(path).getAbsolutePath()); try { FileSystem fileSystem = p.getFileSystem(conf); if (!fileSystem.exists(p)) { if (!fileSystem.mkdirs(p)) { throw new RuntimeException("Could not create path [" + path + "]"); } } FileStatus fileStatus = fileSystem.getFileStatus(p); FsPermission permission = fileStatus.getPermission(); perm = permission.getUserAction().ordinal() + "" + permission.getGroupAction().ordinal() + "" + permission.getOtherAction().ordinal(); } catch (IOException e) { throw new RuntimeException(e); } LOG.info("dfs.datanode.data.dir.perm=" + perm); conf.set("dfs.datanode.data.dir.perm", perm); System.setProperty("test.build.data", path); try { if (racks == null) { cluster = new MiniDFSCluster(conf, 1, format, racks); } else { cluster = new MiniDFSCluster(conf, racks.length, format, racks); } } catch (Exception e) { LOG.error("error opening file system", e); throw new RuntimeException(e); } } }); thread.start(); try { thread.join(); cluster.waitActive(); } catch (IOException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void shutdownZooKeeper() { zkMiniCluster.shutdownZooKeeper(); } public void shutdownDfs() { if (cluster != null) { LOG.info("Shutting down Mini DFS "); try { cluster.shutdown(); } catch (Exception e) { // / Can get a java.lang.reflect.UndeclaredThrowableException thrown // here because of an InterruptedException. Don't let exceptions in // here be cause of test failure. } try { FileSystem fs = cluster.getFileSystem(); if (fs != null) { LOG.info("Shutting down FileSystem"); fs.close(); } FileSystem.closeAll(); } catch (IOException e) { LOG.error("error closing file system", e); } // This has got to be one of the worst hacks I have ever had to do. // This is needed to shutdown 2 thread pools that are not shutdown by // themselves. ThreadGroup threadGroup = group; Thread[] threads = new Thread[100]; int enumerate = threadGroup.enumerate(threads); for (int i = 0; i < enumerate; i++) { Thread thread = threads[i]; if (thread.getName().startsWith("pool")) { if (thread.isAlive()) { thread.interrupt(); LOG.info("Stopping ThreadPoolExecutor [" + thread.getName() + "]"); Object target = getField(Thread.class, thread, "target"); if (target != null) { ThreadPoolExecutor e = (ThreadPoolExecutor) getField(ThreadPoolExecutor.class, target, "this$0"); if (e != null) { e.shutdownNow(); } } try { LOG.info("Waiting for thread pool to exit [" + thread.getName() + "]"); thread.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } } private static Object getField(Class<?> c, Object o, String fieldName) { try { Field field = c.getDeclaredField(fieldName); field.setAccessible(true); return field.get(o); } catch (NoSuchFieldException e) { try { Field field = o.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(o); } catch (Exception ex) { throw new RuntimeException(ex); } } catch (Exception e) { throw new RuntimeException(e); } } }