/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.jenkins.results.parser; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author Peter Yoo */ public class FilePropagator { public FilePropagator( String[] fileNames, String sourceDirName, String targetDirName, List<String> targetSlaves) { for (String fileName : fileNames) { _filePropagatorTasks.add( new FilePropagatorTask( sourceDirName + "/" + fileName, targetDirName + "/" + fileName)); } _targetSlaves.addAll(targetSlaves); } public long getAverageThreadDuration() { if (_threadsCompletedCount == 0) { return 0; } return _threadsDurationTotal / _threadsCompletedCount; } public void setCleanUpCommand(String cleanUpCommand) { _cleanUpCommand = cleanUpCommand; } public void start(int threadCount) { _copyFromSource(); ExecutorService executorService = Executors.newFixedThreadPool( threadCount); System.out.println( "File propagation starting with " + threadCount + " threads."); try { long start = System.currentTimeMillis(); while (!_targetSlaves.isEmpty() || !_busySlaves.isEmpty()) { synchronized (this) { for (String mirrorSlave : _mirrorSlaves) { if (_targetSlaves.isEmpty()) { break; } String targetSlave = _targetSlaves.remove(0); executorService.execute( new FilePropagatorThread( this, mirrorSlave, targetSlave)); _busySlaves.add(mirrorSlave); _busySlaves.add(targetSlave); } _mirrorSlaves.removeAll(_busySlaves); } StringBuffer sb = new StringBuffer(); sb.append("Average thread duration: "); sb.append(getAverageThreadDuration()); sb.append("ms\nBusy slaves:"); sb.append(_busySlaves.size()); sb.append("\nMirror slaves:"); sb.append(_mirrorSlaves.size()); sb.append("\nTarget slaves:"); sb.append(_targetSlaves.size()); sb.append("\nTotal duration: "); sb.append(System.currentTimeMillis() - start); sb.append("\n"); System.out.println(sb.toString()); JenkinsResultsParserUtil.sleep(5000); } System.out.println( "File propagation completed in " + (System.currentTimeMillis() - start) + "ms."); if (!_errorSlaves.isEmpty()) { System.out.println( _errorSlaves.size() + " slaves failed to respond:\n" + _errorSlaves); } } finally { executorService.shutdown(); } } private void _copyFromSource() { List<String> commands = new ArrayList<>(); String targetSlave = null; for (FilePropagatorTask filePropagatorTask : _filePropagatorTasks) { targetSlave = _targetSlaves.get(0); String sourceFileName = filePropagatorTask._sourceFileName; System.out.println("Copying from source " + sourceFileName); String targetFileName = filePropagatorTask._targetFileName; commands.add(_getMkdirCommand(targetFileName)); if (sourceFileName.startsWith("http")) { commands.add( "curl -o " + targetFileName + " " + sourceFileName); } else { commands.add( "rsync -svI " + sourceFileName + " " + targetFileName); } String targetDirName = targetFileName.substring( 0, targetFileName.lastIndexOf("/")); commands.add("ls -al " + targetDirName); } try { if (_executeBashCommands(commands, targetSlave) != 0) { _errorSlaves.add(targetSlave); _targetSlaves.remove(targetSlave); _copyFromSource(); targetSlave = null; } } catch (Exception e) { throw new RuntimeException( "Unable to copy from source. Executed: " + commands, e); } if (targetSlave != null) { _mirrorSlaves.add(targetSlave); _targetSlaves.remove(targetSlave); } System.out.println("Finished copying from source."); } private int _executeBashCommands(List<String> commands, String targetSlave) throws InterruptedException, IOException { StringBuffer sb = new StringBuffer("ssh -o PasswordAuthentication=no "); sb.append(targetSlave); sb.append(" '"); if ((_cleanUpCommand != null) && !_cleanUpCommand.isEmpty()) { sb.append(_cleanUpCommand); sb.append("; "); } for (int i = 0; i < commands.size(); i++) { sb.append(commands.get(i)); if (i < (commands.size() - 1)) { sb.append(" && "); } } sb.append("'"); Process process = JenkinsResultsParserUtil.executeBashCommands( sb.toString()); return process.exitValue(); } private String _getMkdirCommand(String fileName) { String dirName = fileName.substring(0, fileName.lastIndexOf("/") + 1); return "mkdir -p " + dirName; } private final List<String> _busySlaves = new ArrayList<>(); private String _cleanUpCommand; private final List<String> _errorSlaves = new ArrayList<>(); private final List<FilePropagatorTask> _filePropagatorTasks = new ArrayList<>(); private final List<String> _mirrorSlaves = new ArrayList<>(); private final List<String> _targetSlaves = new ArrayList<>(); private int _threadsCompletedCount; private long _threadsDurationTotal; private static class FilePropagatorTask { private FilePropagatorTask( String sourceFileName, String targetFileName) { _sourceFileName = _escapeParentheses(sourceFileName); _targetFileName = _escapeParentheses(targetFileName); } private String _escapeParentheses(String fileName) { fileName = fileName.replace(")", "\\)"); fileName = fileName.replace("(", "\\("); return fileName; } private final String _sourceFileName; private final String _targetFileName; } private static class FilePropagatorThread implements Runnable { @Override public void run() { long start = System.currentTimeMillis(); List<FilePropagatorTask> filePropagatorTasks = _filePropagator._filePropagatorTasks; List<String> commands = new ArrayList<>(filePropagatorTasks.size()); for (FilePropagatorTask filePropagatorTask : filePropagatorTasks) { commands.add( _filePropagator._getMkdirCommand( filePropagatorTask._targetFileName)); commands.add( "rsync -svI " + _mirrorSlave + ":" + filePropagatorTask._targetFileName + " " + filePropagatorTask._targetFileName); } try { _successful = _filePropagator._executeBashCommands( commands, _targetSlave) == 0; } catch (Exception e) { _successful = false; } _duration = System.currentTimeMillis() - start; synchronized (_filePropagator) { _filePropagator._busySlaves.remove(_mirrorSlave); _filePropagator._busySlaves.remove(_targetSlave); _filePropagator._mirrorSlaves.add(_mirrorSlave); _filePropagator._threadsCompletedCount++; _filePropagator._threadsDurationTotal += _duration; if (!_successful) { _filePropagator._errorSlaves.add(_targetSlave); return; } _filePropagator._mirrorSlaves.add(_targetSlave); } } private FilePropagatorThread( FilePropagator filePropagator, String mirrorSlave, String targetSlave) { _filePropagator = filePropagator; _mirrorSlave = mirrorSlave; _targetSlave = targetSlave; } private long _duration; private final FilePropagator _filePropagator; private final String _mirrorSlave; private boolean _successful; private final String _targetSlave; } }