Java tutorial
/* * Copyright 2009 LinkedIn, Inc. * * 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. */ package voldemort.utils.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import voldemort.utils.RemoteOperationException; /** * CommandLineRemoteOperation represents a command-line based remote operation. * That is, it invokes the remote system by means of invoking a command line * executable. Often (but not always) this is an SSH-based approach, using the * ssh client on the host operating system to communicate with the remote system * to invoke a command line script/program. * * <p/> * * What CommandLineRemoteOperation solves for its subclasses is converting * per-host String commands to invokable UnixCommand objects that are then * executed in parallel against the remote hosts. The results of those command * line invocations are then placed (in arbitrary order) into a list that is * returned to the caller. * * * @param <T> Return type for the operation, specific to the subclass * implementation */ abstract class CommandLineRemoteOperation { protected final Log logger = LogFactory.getLog(getClass()); /** * Executes the given commands in parallel on the remote hosts and * aggregates the results for the caller. * * @param hostNameCommandLineMap Map with a key is the external host name * and the value is the command line to execute remotely on that host * * @return List of result types as dictated by the subclass * * @throws RemoteOperationException Thrown on error invoking the command on * one or more clients. */ protected void execute(Map<String, String> hostNameCommandLineMap) throws RemoteOperationException { CommandLineParser commandLineParser = new CommandLineParser(); ExecutorService threadPool = Executors.newFixedThreadPool(hostNameCommandLineMap.size()); List<Future<?>> futures = new ArrayList<Future<?>>(); for (Map.Entry<String, String> entry : hostNameCommandLineMap.entrySet()) { String hostName = entry.getKey(); String commandLine = entry.getValue(); if (logger.isDebugEnabled()) logger.debug("Command to execute: " + commandLine); List<String> commandArgs = commandLineParser.parse(commandLine); UnixCommand command = new UnixCommand(hostName, commandArgs); Callable<?> callable = getCallable(command); Future<?> future = threadPool.submit(callable); futures.add(future); } // Build up a list of all the results and/or capture the errors as they // occur. try { StringBuilder errors = new StringBuilder(); for (Future<?> future : futures) { Throwable t = null; try { future.get(); } catch (ExecutionException ex) { t = ex.getCause(); } catch (Exception e) { t = e; } if (t != null) { if (logger.isWarnEnabled()) logger.warn(t, t); if (errors.length() > 0) errors.append("; "); errors.append(t.getMessage()); } } if (errors.length() > 0) throw new RemoteOperationException(errors.toString()); } finally { threadPool.shutdown(); try { threadPool.awaitTermination(60, TimeUnit.SECONDS); } catch (InterruptedException e) { if (logger.isWarnEnabled()) logger.warn(e, e); } } } /** * By default we'll add a logging listener to output the output from the * remote invocation and return the result of the ExitCodeCallable. * * <p/> * * This method can be overridden by subclasses to provide a more useful * interaction with the output from the remote host. * * @param command UnixCommand to execute * * @return Callable that is used in the thread pool in the execute method */ protected Callable<?> getCallable(UnixCommand command) { CommandOutputListener commandOutputListener = new LoggingCommandOutputListener(null, logger, true); return new ExitCodeCallable(command, commandOutputListener); } }