command.line/java/com/jetbrains/teamcity/command/RemoteRun.java (579 lines of code) (raw):
package com.jetbrains.teamcity.command;
import com.jetbrains.teamcity.*;
import com.jetbrains.teamcity.Util.IFileFilter;
import com.jetbrains.teamcity.resources.FileBasedMatcher;
import com.jetbrains.teamcity.resources.ITCResource;
import com.jetbrains.teamcity.resources.ITCResourceMatcher;
import com.jetbrains.teamcity.resources.TCWorkspace;
import java.io.*;
import java.util.*;
import javax.naming.directory.InvalidAttributesException;
import jetbrains.buildServer.*;
import jetbrains.buildServer.core.runtime.IProgressMonitor;
import jetbrains.buildServer.core.runtime.IProgressStatus;
import jetbrains.buildServer.core.runtime.ProgressStatus;
import jetbrains.buildServer.serverSide.userChanges.PersonalChangeCommitDecision;
import jetbrains.buildServer.util.Converter;
import jetbrains.buildServer.util.FileUtil;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.util.ThreadUtil;
import jetbrains.buildServer.util.filters.Filter;
import jetbrains.buildServer.vcs.patches.LowLevelPatchBuilder;
import jetbrains.buildServer.vcs.patches.LowLevelPatchBuilderImpl;
import jetbrains.buildServer.vcs.patches.PatchBuilderImpl;
import org.jetbrains.annotations.NotNull;
import static java.text.MessageFormat.format;
import static jetbrains.buildServer.util.CollectionsUtil.filterAndConvertCollection;
public class RemoteRun implements ICommand {
private static final IFileFilter TCC_FILTER = new IFileFilter() {
public Collection<File> accept(Collection<File> files) {
final HashSet<File> result = new HashSet<File>();
for (final File file : files) {
if (!file.getName().toLowerCase().equals(TCWorkspace.TCC_ADMIN_FILE)) {
result.add(file);
}
}
return result;
}
};
private static final int SLEEP_INTERVAL = 1000 * 5;
private static final int DEFAULT_TIMEOUT = 1000 * 60 * 60;
static final String ID = getMsg("RemoteRun.command.id");
static final String OVERRIDING_MAPPING_FILE_PARAM = getMsg("RemoteRun.overriding.config.file.argument");
private static final String CONFIGURATION_PARAM = getMsg("RemoteRun.config.runtime.param");
static final String CONFIGURATION_PARAM_LONG = getMsg("RemoteRun.config.runtime.param.long");
static final String PROJECT_PARAM = getMsg("RemoteRun.project.runtime.param");
static final String PROJECT_PARAM_LONG = getMsg("RemoteRun.project.runtime.param.long");
static final String MESSAGE_PARAM = getMsg("RemoteRun.message.runtime.param");
static final String MESSAGE_PARAM_LONG = getMsg("RemoteRun.message.runtime.param.long");
static final String TIMEOUT_PARAM = getMsg("RemoteRun.timeout.runtime.param");
private static final String TIMEOUT_PARAM_LONG = getMsg("RemoteRun.timeout.runtime.param.long");
private static final String NO_WAIT_SWITCH = getMsg("RemoteRun.nowait.runtime.param");
static final String NO_WAIT_SWITCH_LONG = getMsg("RemoteRun.nowait.runtime.param.long");
static final String CHECK_FOR_CHANGES_EARLY_SWITCH = getMsg("RemoteRun.checkforchangesearly.runtime.param.long");
private static final String FORCE_COMPATIBILITY_CHECK_SWITCH = getMsg("RemoteRun.force.compatibility.check.runtime.param.long");
private static final String FORCE_CLEAN_SWITCH = getMsg("RemoteRun.force.clean.param.long");
private static final String REBUILD_DEPS_SWITCH = getMsg("RemoteRun.rebuild.dependencies");
static final String BUILD_PARAM_SWITCH = getMsg("RemoteRun.build.param");
private Server myServer;
private String myComment;
private String myResultDescription;
private boolean isNoWait = false;
private long myTimeout;
private boolean myCleanoff;
private Map<String, String> myConfigExternal2InternalMap;
private volatile ECommunicationException myRecentSummaryError;
private int mySummaryFailureCount;
static {
Args.registerArgument(MESSAGE_PARAM, String.format(".*%s\\s+\\S.*", MESSAGE_PARAM));
Args.registerArgument(MESSAGE_PARAM_LONG, String.format(".*%s\\s+\\S.*", MESSAGE_PARAM_LONG));
Args.registerArgument(CONFIGURATION_PARAM, String.format(".*%s\\s+\\S.*", CONFIGURATION_PARAM));
Args.registerArgument(CONFIGURATION_PARAM_LONG, String.format(".*%s\\s+\\S.*", CONFIGURATION_PARAM_LONG));
Args.registerArgument(PROJECT_PARAM, String.format(".*%s\\s?\\S*", PROJECT_PARAM));
Args.registerArgument(PROJECT_PARAM_LONG, String.format(".*%s\\s?\\S*", PROJECT_PARAM_LONG));
Args.registerArgument(TIMEOUT_PARAM, String.format(".*%s\\s+\\d+.*", TIMEOUT_PARAM));
Args.registerArgument(TIMEOUT_PARAM_LONG, String.format(".*%s\\s+\\d+.*", TIMEOUT_PARAM_LONG));
Args.registerArgument(OVERRIDING_MAPPING_FILE_PARAM, String.format(".*%s\\s+\\S.*", OVERRIDING_MAPPING_FILE_PARAM));
}
public void execute(final Server server, Args args, final IProgressMonitor monitor) throws EAuthorizationException, ECommunicationException, ERemoteError, InvalidAttributesException {
myServer = server;
// comment
myComment = args.getArgument(MESSAGE_PARAM, MESSAGE_PARAM_LONG);
// wait/no wait for build result
if (args.hasArgument(NO_WAIT_SWITCH, NO_WAIT_SWITCH_LONG)) {
isNoWait = true;
}
// timeout
if (args.hasArgument(TIMEOUT_PARAM, TIMEOUT_PARAM_LONG)) {
myTimeout = Long.valueOf(args.getArgument(TIMEOUT_PARAM, TIMEOUT_PARAM_LONG));
} else {
myTimeout = DEFAULT_TIMEOUT;
}
// do not clean after run
myCleanoff = args.isCleanOff();
final TCWorkspace workspace = new TCWorkspace(getOverridingMatcher(args));
// collect files
final Collection<File> files = getFiles(args, monitor);
// collect TC files
final Collection<ITCResource> tcResources = getTCResources(workspace, files, monitor);
// prepare patch
final File patchFile = createPatch(tcResources, monitor);
// collect configurations for running
final Collection<String> requestedInternalIds = getRequestedConfigurations(args);
final Collection<String> internalIds = getApplicableConfigurations(requestedInternalIds, tcResources, monitor, args.hasArgument(FORCE_COMPATIBILITY_CHECK_SWITCH));
if (internalIds.isEmpty()) {
throw new IllegalArgumentException(String.format("No one of [%s] configurations affected by collected changes",
StringUtil.join(",", requestedInternalIds)));
}
// prepare changes list
final long chaneListId = myServer.createChangeList(patchFile, myComment, monitor);
Map<String, String> parameterMap = convertToMapAndUnescape(args.getArgValues(BUILD_PARAM_SWITCH));
// fire RR
scheduleRemoteRun(internalIds, chaneListId,
args.hasArgument(CHECK_FOR_CHANGES_EARLY_SWITCH),
args.hasArgument(FORCE_CLEAN_SWITCH),
args.hasArgument(REBUILD_DEPS_SWITCH),
parameterMap,
monitor);
// process result
if (isNoWait) {
myResultDescription = String.format("Build for Change %d scheduled successfully", chaneListId);
} else {
waitForSuccessResult(chaneListId, myTimeout, monitor);
myResultDescription = String.format("Build for Change %d run successfully", chaneListId);
}
}
/**
* @param paramValues list of name=value pairs
* @return
*/
static Map<String, String> convertToMapAndUnescape(@NotNull List<String> paramValues) {
final Map<String, String> result = new HashMap<String, String>();
for (String s : paramValues) {
final int idx = s.indexOf("=");
if (idx > 0) {
String name = s.substring(0, idx).trim();
if (StringUtil.isEmpty(name)) continue;
// Allow escaping of \n with |n
String value = s.substring(idx + 1)
.replaceAll("\\|\\|", "#@TCC@#")
.replaceAll("\\|n", "\n")
.replaceAll("#@TCC@#", "|");
result.put(name, value);
}
}
return result;
}
/**
* @return A string of joined build configuration internal IDs which should be used to start the build
*/
private Collection<String> getRequestedConfigurations(final Args args) throws ECommunicationException {
final String projectId = args.getArgument(PROJECT_PARAM, PROJECT_PARAM_LONG);
if (projectId != null) {
return getBuildTypeInternalIds(projectId);
}
final String buildTypeIds = args.getArgument(CONFIGURATION_PARAM, CONFIGURATION_PARAM_LONG);
return convertExternalId2InternalId(buildTypeIds);
}
/**
* @param projectId Could be internal or external ID
*/
private List<String> getBuildTypeInternalIds(final String projectId) throws ECommunicationException {
final List<String> result = filterAndConvertCollection(myServer.getConfigurations(), new Converter<String, BuildTypeData>() {
public String createFrom(@NotNull final BuildTypeData source) {
return source.getId();
}
}, new Filter<BuildTypeData>() {
public boolean accept(@NotNull final BuildTypeData data) {
return projectId.equals(data.getProjectId()) || projectId.equals(data.getProjectExternalId());
}
}
);
if (result.size() == 0 && StringUtil.isNotEmpty(projectId)) {
throw new IllegalArgumentException(String.format("Cannot find any relevant configurations for project with id [%s]", projectId));
}
return result;
}
private List<String> convertExternalId2InternalId(final String buildTypeIds) throws ECommunicationException {
final Collection<String> ids = parseConfigurations(buildTypeIds);
final ArrayList<String> result = new ArrayList<String>();
for (String id : ids) {
final String internalId = getExternal2InternalMap().get(id);
if (internalId != null) {
result.add(internalId);
}
else if (id.matches("bt\\d+")) {
result.add(id);
}
}
if (ids.size() > 0 && result.size() == 0) {
throw new IllegalArgumentException(String.format("Cannot find any relevant configuration ids for [%s]", buildTypeIds));
}
return result;
}
private Map<String, String> getExternal2InternalMap() throws ECommunicationException {
if (myConfigExternal2InternalMap == null) {
myConfigExternal2InternalMap = new HashMap<String, String>();
for (BuildTypeData configuration : myServer.getConfigurations()) {
myConfigExternal2InternalMap.put(configuration.getExternalId(), configuration.getId());
}
}
return myConfigExternal2InternalMap;
}
Collection<ITCResource> getTCResources(final TCWorkspace workspace, final Collection<File> files, final IProgressMonitor monitor) throws IllegalArgumentException {
monitor.beginTask(getMsg("RemoteRun.mapping.step.name"));
final HashSet<ITCResource> out = new HashSet<ITCResource>(files.size());
for (final File file : files) {
final ITCResource resource = workspace.getTCResource(file);
if (resource != null && resource.getRepositoryPath() != null) {
out.add(resource);
} else {
debug("? \"%s\" has not associated ITCResource(%s) or empty RepositoryPath(%s)", file, resource, resource != null ? resource.getRepositoryPath() : null);
}
}
// fire exception if nothing found
if (out.isEmpty()) {
throw new IllegalArgumentException(String.format(getMsg("RemoteRun.no.one.mappings.found.error.message"), files.size()));
}
monitor.status(new ProgressStatus(IProgressStatus.INFO, String.format(getMsg("RemoteRun.mapping.step.done.message"), out.size(), files.size())));
monitor.done();
return out;
}
File createPatch(Collection<ITCResource> resources, IProgressMonitor monitor) throws ECommunicationException {
try {
final File emptyPatchFile = createPatchFile();
final File patch = fillPatch(emptyPatchFile, resources, monitor);
if (!myCleanoff) {
patch.deleteOnExit();
}
debug("Patch %s filled with %d bytes", patch.getAbsolutePath(), patch.length());
return patch;
} catch (IOException e) {
throw new ECommunicationException(e);
}
}
ITCResourceMatcher getOverridingMatcher(final Args args) {
if (args.hasArgument(OVERRIDING_MAPPING_FILE_PARAM)) {
return new FileBasedMatcher(new File(args.getArgument(OVERRIDING_MAPPING_FILE_PARAM)));
}
return null;
}
private Collection<String> getApplicableConfigurations(Collection<String> requestedIDs,
final Collection<ITCResource> files,
final IProgressMonitor monitor,
final boolean forceCompatibilityCheck) throws ECommunicationException {
monitor.beginTask("Collecting configurations for running");
try {
if (!requestedIDs.isEmpty()) {
debug("Requested configurations for running: %s", requestedIDs);
List<String> intersection = new ArrayList<String>(requestedIDs);
if (forceCompatibilityCheck) {
// make intersection of passed and applicable
final Collection<String> applicable = collectApplicableConfigurations(monitor, files);
debug("Comparing with applicable configurations: %s", applicable);
intersection.retainAll(applicable);
debug("Use configurations for running: %s", intersection);
}
return intersection;
}
else {
// if specific configurations are not specified, run on all applicable configurations
final Collection<String> applicable = collectApplicableConfigurations(monitor, files);
debug("Using all applicable configurations for running: %s", applicable);
return applicable;
}
}
finally {
monitor.done();
}
}
@NotNull
private Collection<String> collectApplicableConfigurations(final IProgressMonitor monitor, final Collection<ITCResource> files) throws ECommunicationException {
final HashSet<String> urls = new HashSet<String>();
for (ITCResource file : files) {
urls.add(file.getRepositoryPath());
}
final Set<String> buffer = new TreeSet<String>(myServer.getApplicableConfigurations(urls));
monitor.status(new ProgressStatus(IProgressStatus.INFO, format(getMsg("RemoteRun.collected.configuration.done.pattern"), buffer.size(), buffer)));
return buffer;
}
static Collection<String> parseConfigurations(final String cfgId) {
if (cfgId == null) {
return Collections.emptyList();
}
final String[] configs = cfgId.trim().split(",");
final HashSet<String> out = new HashSet<String>(configs.length);
for (final String config : configs) {
final String trimed = config.trim();
if (trimed.length() > 0) {
out.add(trimed);
}
}
return Collections.unmodifiableSet(out);
}
private void waitForSuccessResult(final long changeListId, final long timeOut, IProgressMonitor monitor) throws ERemoteError {
sleep(3000);
monitor.beginTask("Waiting for Remote Run to finish");
final long startTime = System.currentTimeMillis();
UserChangeStatus prevCurrentStatus = null;
while ((System.currentTimeMillis() - startTime) < timeOut) {
final List<UserChangeInfoData> personalChanges = getPersonalChanges();
if (personalChanges == null) {
if (processFailureAndContinue()) {
continue;
}
else {
throw new RuntimeException("Error obtaining remote run status after multiple attempts", myRecentSummaryError);
}
}
mySummaryFailureCount = 0;
for (final UserChangeInfoData data : personalChanges) {
if (data.getPersonalDesc() != null && data.getPersonalDesc().getId() == changeListId) {
// check builds status
final UserChangeStatus currentStatus = data.getChangeStatus();
if (!currentStatus.equals(prevCurrentStatus)) {
prevCurrentStatus = currentStatus;
System.out.print(getBuildStatusDescription(currentStatus));
}
if (UserChangeStatus.FAILED_WITH_RESPONSIBLE == currentStatus || UserChangeStatus.FAILED == currentStatus || UserChangeStatus.CANCELED == currentStatus) {
System.out.println();
throw new ERemoteError("Remote Run failed: build status=" + getBuildStatusDescription(currentStatus));
}
if (UserChangeStatus.RUNNING_FAILED == currentStatus) {
System.out.println("Remote Run failed: build status=" + getBuildStatusDescription(currentStatus));
}
if (UserChangeStatus.CHECKED == currentStatus) {
// Successful finish
System.out.println();
// OK
monitor.done();
return;
}
}
}
System.out.print(".");
ThreadUtil.sleep(SLEEP_INTERVAL);
}
// so, timeout exceed
throw new RuntimeException(String.format("Stopped waiting for Remote Run %s, timeout exceeded: %dms", myTimeout, changeListId));
}
private boolean processFailureAndContinue() {
mySummaryFailureCount ++;
if (mySummaryFailureCount <= 7) {
final int seconds = 2 << mySummaryFailureCount;
System.out.println(" Failure accessing server [" + myRecentSummaryError.getMessage() + "]; next attempt in " + seconds + " seconds");
ThreadUtil.sleep(1000 * seconds);
return true;
}
return false;
}
private List<UserChangeInfoData> getPersonalChanges() {
try {
return myServer.getSummary().getPersonalChanges();
} catch (ECommunicationException e) {
Debug.getInstance().error(getClass(), "Unable to get data summary from server", e);
myRecentSummaryError = e;
return null;
}
}
@NotNull
private static String getMsg(final String key) {
return Messages.getString(key);
}
private void sleep(int millis) {
debug("Falling asleep for [%s] millis...", millis);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
debug(e.getMessage());
}
}
private void debug(final String format, Object ... data) {
Debug.getInstance().debug(getClass(), String.format(format, data));
}
private Object getCommitStatusDescription(final PersonalChangeCommitDecision commitStatus) {
return commitStatus;
}
private Object getBuildStatusDescription(final UserChangeStatus currentStatus) {
if (UserChangeStatus.CANCELED == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.CANCELED");
} else if (UserChangeStatus.CHECKED == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.CHECKED");
} else if (UserChangeStatus.FAILED == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.FAILED");
} else if (UserChangeStatus.FAILED_WITH_RESPONSIBLE == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.FAILED_WITH_RESPONSIBLE");
} else if (UserChangeStatus.PENDING == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.PENDING");
} else if (UserChangeStatus.RUNNING_FAILED == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.RUNNING_FAILED");
} else if (UserChangeStatus.RUNNING_SUCCESSFULY == currentStatus) {
return getMsg("RemoteRun.UserChangeStatus.RUNNING_SUCCESSFULLY");
}
return currentStatus;
}
private void scheduleRemoteRun(final Collection<String> internalBtIds,
final long changeId,
boolean checkForChangesEarly,
final boolean forceCleanCheckout,
boolean rebuildDependencies,
@NotNull Map<String, String> parameterMap,
@NotNull IProgressMonitor monitor) throws ECommunicationException, ERemoteError {
final ArrayList<AddToQueueRequest> batch = new ArrayList<AddToQueueRequest>();
for (final String internalBtId : internalBtIds) {
final AddToQueueRequest request = new AddToQueueRequest(internalBtId, changeId);
request.setCheckForChangesEarly(checkForChangesEarly);
request.setCleanSources(forceCleanCheckout);
request.setRebuildDependencies(rebuildDependencies);
request.setBuildParameters(parameterMap);
batch.add(request);
final String debugMessage = String.format("Created build request for \"%s\" configuration of changeId=%s, checkForChangesEarly=%s, forceCleanCheckout=%s", internalBtId, changeId, checkForChangesEarly, forceCleanCheckout);
debug(debugMessage);
}
monitor.beginTask("Scheduling personal build");
final AddToQueueResult result = myServer.addRemoteRunToQueue(batch);
processSchedulingResult(internalBtIds, result);
monitor.done();
}
private void processSchedulingResult(final Collection<String> internalBtIds, final AddToQueueResult result) throws ERemoteError {
int addedSuccessfully = 0;
final StringBuilder errors = new StringBuilder();
for (final String cfgId : internalBtIds) {
if (result.isSuccessful(cfgId)) {
addedSuccessfully++;
}
else {
errors.append(cfgId).append(": ").append(result.getFailureReason(cfgId)).append("\n");
}
}
if (errors.length() > 0) {
debug("Remote Run scheduling failed: %s", errors.toString());
}
if (addedSuccessfully == 0) {
throw new ERemoteError(errors.toString());
}
if (addedSuccessfully > 0) {
debug("Remote Run has successfully scheduled " + addedSuccessfully + " builds.");
}
}
private File fillPatch(final File patchFile, final Collection<ITCResource> resources, final IProgressMonitor monitor) throws IOException {
DataOutputStream os = null;
LowLevelPatchBuilderImpl patcher = null;
final HashSet<String> modifiedResources = new HashSet<String>();
final HashSet<String> deletedResources = new HashSet<String>();
try {
monitor.beginTask("Preparing patch");
os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(patchFile)));
patcher = new LowLevelPatchBuilderImpl(os);
for (final ITCResource resource : resources) {
// threat file which is not exist as deleted
if (resource.getLocal().exists()) {
debug("+ %s", resource.getRepositoryPath());
final LowLevelPatchBuilder.WriteFileContent content = new PatchBuilderImpl.StreamWriteFileContent(new BufferedInputStream(new FileInputStream(resource.getLocal())), resource.getLocal().length());
patcher.changeBinary(resource.getRepositoryPath(), (int) resource.getLocal().length(), content, false);
modifiedResources.add(resource.getLocal().getPath());
} else {
debug("- %s", resource.getRepositoryPath());
patcher.delete(resource.getRepositoryPath(), true, false);
deletedResources.add(resource.getLocal().getPath());
}
}
} finally {// finalize patching
if (patcher != null) {
patcher.exit("");
patcher.close();
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
//
}
}
final StringBuilder patchingResult = new StringBuilder();
if (!modifiedResources.isEmpty()) {
patchingResult.append(String.format("%d new/modified file(s)", modifiedResources.size()));
if (!deletedResources.isEmpty()) {
patchingResult.append(", ");
}
}
if (!deletedResources.isEmpty()) {
patchingResult.append(String.format("%d deleted file(s): %s", deletedResources.size(), deletedResources));
}
monitor.status(new ProgressStatus(IProgressStatus.INFO, patchingResult.toString()));
monitor.done();
}
return patchFile;
}
private static File createPatchFile() throws IOException {
return FileUtil.createTempFile("tcc.jar-", ".patch");
}
Collection<File> getFiles(Args args, IProgressMonitor monitor) throws IllegalArgumentException {
monitor.beginTask(getMsg("RemoteRun.collect.changes.step.name"));
final String[] elements = args.getArguments();
int i = 0;// skip command
while (i < elements.length) {
final String currentToken = elements[i].toLowerCase();
if (elements[i].startsWith("-")) {
if (elements[i].toLowerCase().equals(CONFIGURATION_PARAM) || elements[i].toLowerCase().equals(CONFIGURATION_PARAM_LONG)) {
i++; // arg
if (elements[i].toLowerCase().equals(PROJECT_PARAM) || elements[i].toLowerCase().equals(PROJECT_PARAM_LONG)) {
i++; // arg
}
i++; // arg value
}
else if (elements[i].toLowerCase().equals(NO_WAIT_SWITCH) || currentToken.equals(NO_WAIT_SWITCH_LONG) || currentToken.equals(CHECK_FOR_CHANGES_EARLY_SWITCH)) {
i++; // single token
}
else {
i++; // arg
i++; // args value
}
} else {
// reach files
break;
}
}
Collection<File> result;
if (elements.length > i) {// file's part existing
final String[] buffer = new String[elements.length - i];
System.arraycopy(elements, i, buffer, 0, buffer.length);
debug("Read from arguments: %s", Arrays.toString(buffer));
result = collectFiles(buffer);
} else {
// try read from stdin
debug("Trying stdin...");
final String input = readFromStream(System.in);
if (input != null && input.trim().length() > 0) {
final String[] buffer = input.split("[\n\r]");
debug("Read from stdin: %s", Arrays.toString(buffer));
result = collectFiles(buffer);
} else { // let's use current directory as root if nothing passed
debug("Stdin is empty. Will use current (%s) folder as root", new File("."));
result = TCC_FILTER.accept(Util.SVN_FILES_FILTER.accept(Util.CVS_FILES_FILTER.accept(Util.getFiles("."))));
}
}
if (result.size() == 0) {
throw new IllegalArgumentException(getMsg("RemoteRun.no.files.collected.for.remoterun.error.message"));
}
result = normalizePaths(result);
monitor.status(new ProgressStatus(IProgressStatus.INFO, format(getMsg("RemoteRun.collect.changes.step.result.pattern"), result.size())));
monitor.done();
for (final File collected : result) {
debug("%s", collected);
}
return result;
}
private Collection<File> normalizePaths(final Collection<File> files) {
TreeSet<File> out = new TreeSet<File>(new Comparator<File>() {
public int compare(File o1, File o2) {
return o1.compareTo(o2);
}
});
for (final File file : files) {
final String trimPath = Util.trim(file.getAbsolutePath(), "\"", "\\", "/");
out.add(new File(trimPath));
}
return out;
}
private Collection<File> collectFiles(final String[] elements) {
final HashSet<File> out = new HashSet<File>();
for (final String path : elements) {
final Collection<File> files;
if (!path.startsWith("@")) {
files = Util.getFiles(path);
} else {
files = Util.getFiles(new File(path.substring(1)));
}
// filter out system files
out.addAll(TCC_FILTER.accept(Util.SVN_FILES_FILTER.accept(Util.CVS_FILES_FILTER.accept(files))));
}
return out;
}
String readFromStream(final InputStream stream) {
final StringBuilder buffer = new StringBuilder();
final InputStreamReader in = new InputStreamReader(new BufferedInputStream(stream));
try {
if (stream.available() != 0) {
int n;
while ((n = in.read()) != -1) {
buffer.append((char) n);
}
}
} catch (IOException e) {
Debug.getInstance().error(RemoteRun.class, e.getMessage(), e);
}
return buffer.toString();
}
public String getId() {
return ID;
}
public boolean isConnectionRequired(final Args args) {
return true;
}
public String getUsageDescription() {
return String.format(
getMsg("RemoteRun.help.usage.pattern"),
getCommandDescription(), getId(), CONFIGURATION_PARAM, CONFIGURATION_PARAM_LONG, CONFIGURATION_PARAM, CONFIGURATION_PARAM_LONG,
PROJECT_PARAM, PROJECT_PARAM_LONG, MESSAGE_PARAM, MESSAGE_PARAM_LONG, TIMEOUT_PARAM, TIMEOUT_PARAM_LONG, OVERRIDING_MAPPING_FILE_PARAM,
NO_WAIT_SWITCH, NO_WAIT_SWITCH_LONG, CHECK_FOR_CHANGES_EARLY_SWITCH, FORCE_COMPATIBILITY_CHECK_SWITCH, FORCE_CLEAN_SWITCH, REBUILD_DEPS_SWITCH, BUILD_PARAM_SWITCH
);
}
public String getCommandDescription() {
return getMsg("RemoteRun.help.description");
}
public String getResultDescription() {
return myResultDescription;
}
public void validate(Args args) throws IllegalArgumentException {
if (args == null || !args.hasArgument(MESSAGE_PARAM, MESSAGE_PARAM_LONG)) {
throw new IllegalArgumentException(format(getMsg("RemoteRun.missing.message.para.error.pattern"), MESSAGE_PARAM, MESSAGE_PARAM_LONG));
}
}
}