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)); } } }