diff --git a/platform/platform-impl/src/com/intellij/execution/process/ProcessWrapper.java b/platform/platform-impl/src/com/intellij/execution/process/ProcessWrapper.java new file mode 100644 index 000000000000..76c1a13e52b8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/execution/process/ProcessWrapper.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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 com.intellij.execution.process; + +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author Konstantin Kolosovsky. + */ +public class ProcessWrapper extends Process { + + @NotNull private final Process myOriginalProcess; + + public ProcessWrapper(@NotNull Process originalProcess) { + myOriginalProcess = originalProcess; + } + + @Override + public OutputStream getOutputStream() { + return myOriginalProcess.getOutputStream(); + } + + @Override + public InputStream getInputStream() { + return myOriginalProcess.getInputStream(); + } + + @Override + public InputStream getErrorStream() { + return myOriginalProcess.getErrorStream(); + } + + @Override + public int waitFor() throws InterruptedException { + return myOriginalProcess.waitFor(); + } + + @Override + public int exitValue() { + return myOriginalProcess.exitValue(); + } + + @Override + public void destroy() { + myOriginalProcess.destroy(); + } + + @NotNull + public Process getOriginalProcess() { + return myOriginalProcess; + } +} diff --git a/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java index e2dd827b5018..8e142c45c1a4 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java +++ b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java @@ -20,49 +20,10 @@ import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.util.SystemInfo; import org.jetbrains.annotations.NotNull; -import java.io.InputStream; -import java.io.OutputStream; - -public class RunnerWinProcess extends Process { - - private final Process myOriginalProcess; +public class RunnerWinProcess extends ProcessWrapper { private RunnerWinProcess(@NotNull Process originalProcess) { - myOriginalProcess = originalProcess; - } - - @Override - public OutputStream getOutputStream() { - return myOriginalProcess.getOutputStream(); - } - - @Override - public InputStream getInputStream() { - return myOriginalProcess.getInputStream(); - } - - @Override - public InputStream getErrorStream() { - return myOriginalProcess.getErrorStream(); - } - - @Override - public int waitFor() throws InterruptedException { - return myOriginalProcess.waitFor(); - } - - @Override - public int exitValue() { - return myOriginalProcess.exitValue(); - } - - @Override - public void destroy() { - myOriginalProcess.destroy(); - } - - public Process getOriginalProcess() { - return myOriginalProcess; + super(originalProcess); } /** diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java index cc94b50963d4..06005731a860 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java @@ -73,6 +73,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im public static final String HTTP_PROXY_PASSWORD = "http-proxy-password"; private Project myProject; private File myConfigDirectory; + private ISVNAuthenticationProvider myRuntimeCacheProvider; private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy; private SvnConfiguration myConfig; private static final ThreadLocal ourJustEntered = new ThreadLocal(); @@ -119,6 +120,19 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im } }); + // This is not the same instance as DefaultSVNAuthenticationManager.myProviders[1], but currently + // DefaultSVNAuthenticationManager.CacheAuthenticationProvider uses only its outer class state - so we utilize necessary logic with + // this new instance. + myRuntimeCacheProvider = createRuntimeAuthenticationProvider(); + } + + public SVNAuthentication requestFromCache(String kind, + SVNURL url, + String realm, + SVNErrorMessage errorMessage, + SVNAuthentication previousAuth, + boolean authMayBeStored) { + return myRuntimeCacheProvider.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored); } public String getDefaultUsername(String kind, SVNURL url) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java index 824bd1b7c7a4..6c0be5f0a3dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java @@ -350,7 +350,7 @@ public class SvnConfiguration implements PersistentStateComponent { return getInfo(new File(path)); } + // TODO: Most likely make getInfo(File) correspond to getInfo(File, SVNRevision.UNDEFINED) + // TODO: And create special method that tries also to resolve HEAD revision - to be used only in special code paths @Nullable public SVNInfo getInfo(@NotNull File ioFile) { SVNInfo result = null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/InfoCommandRepositoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/InfoCommandRepositoryProvider.java index 9c119223aa9e..f58dfb5b72f7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/InfoCommandRepositoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/InfoCommandRepositoryProvider.java @@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; /** @@ -41,7 +42,7 @@ public class InfoCommandRepositoryProvider extends BaseRepositoryProvider { result = new Repository(myTarget.getURL()); } else { - SVNInfo info = myVcs.getInfo(myTarget.getFile()); + SVNInfo info = myVcs.getInfo(myTarget.getFile(), SVNRevision.UNDEFINED); result = info != null ? new Repository(info.getRepositoryRootURL()) : null; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java index 29d5068073f5..4fd546987bfb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.checkin; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -169,7 +170,9 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall } }; - WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command); + // Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in + // the thread that started progress + WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any()); return new Pair(answer.get(), !save.isNull() && save.get()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/Command.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/Command.java index 4facdd367034..b6979140a466 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/Command.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/Command.java @@ -28,7 +28,7 @@ public class Command { private File workingDirectory; @Nullable private File myConfigDir; @Nullable private LineCommandListener myResultBuilder; - @Nullable private SVNURL myRepositoryUrl; + @Nullable private volatile SVNURL myRepositoryUrl; @NotNull private SvnTarget myTarget; @Nullable private ISVNCanceller myCanceller; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java index 5d909a5e186e..982eb44ce725 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java @@ -26,16 +26,16 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.EventDispatcher; -import com.intellij.util.io.BaseDataReader; -import com.intellij.util.io.BinaryOutputReader; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNCancelException; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.List; -import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -57,7 +57,7 @@ public class CommandExecutor { private volatile boolean myWasCancelled; protected final GeneralCommandLine myCommandLine; protected Process myProcess; - protected OSProcessHandler myHandler; + protected SvnProcessHandler myHandler; private OutputStreamWriter myProcessWriter; // TODO: Try to implement commands in a way that they manually indicate if they need full output - to prevent situations // TODO: when large amount of data needs to be stored instead of just sequential processing. @@ -157,41 +157,62 @@ public class CommandExecutor { private void ensureMessageFile() throws SvnBindException { if (myMessage != null) { + myMessageFile = createTempFile("commit-message", ".txt"); try { - File vcsFolder = new File(PathManager.getSystemPath(), "vcs"); - myMessageFile = FileUtil.createTempFile(new File(vcsFolder, "svn"), "commit-message", ".txt"); FileUtil.writeToFile(myMessageFile, myMessage); - - myCommandLine.addParameters("-F", myMessageFile.getAbsolutePath()); - myCommandLine.addParameters("--config-option", "config:miscellany:log-encoding=" + CharsetToolkit.UTF8); } catch (IOException e) { throw new SvnBindException(e); } + myCommandLine.addParameters("-F", myMessageFile.getAbsolutePath()); + myCommandLine.addParameters("--config-option", "config:miscellany:log-encoding=" + CharsetToolkit.UTF8); } } private void cleanupMessageFile() { - if (myMessageFile != null) { - boolean wasDeleted = FileUtil.delete(myMessageFile); + deleteTempFile(myMessageFile); + } + + @NotNull + protected static File getSvnFolder() { + File vcsFolder = new File(PathManager.getSystemPath(), "vcs"); + + return new File(vcsFolder, "svn"); + } + + @NotNull + protected static File createTempFile(@NotNull String prefix, @NotNull String extension) throws SvnBindException { + try { + return FileUtil.createTempFile(getSvnFolder(), prefix, extension); + } + catch (IOException e) { + throw new SvnBindException(e); + } + } + + protected static void deleteTempFile(@Nullable File file) { + if (file != null) { + boolean wasDeleted = FileUtil.delete(file); if (!wasDeleted) { - LOG.info("Failed to delete temp commit message file " + myMessageFile.getAbsolutePath()); + LOG.info("Failed to delete temp file " + file.getAbsolutePath()); } } } @NotNull - protected OSProcessHandler createProcessHandler() { - return needsBinaryOutput() - ? new BinaryOSProcessHandler(myProcess, myCommandLine.getCommandLineString()) - : new MyOSProcessHandler(myProcess, myCommandLine.getCommandLineString()); + protected SvnProcessHandler createProcessHandler() { + return new SvnProcessHandler(myProcess, myCommandLine.getCommandLineString(), needsUtf8Output(), needsBinaryOutput()); } - private boolean needsBinaryOutput() { + protected boolean needsBinaryOutput() { return SvnCommandName.cat.equals(myCommand.getName()); } + protected boolean needsUtf8Output() { + return myCommand.getParameters().contains("--xml"); + } + @NotNull protected Process createProcess() throws ExecutionException { return myCommandLine.createProcess(); @@ -218,9 +239,9 @@ public class CommandExecutor { return outputAdapter.getOutput(); } - @Nullable + @NotNull public ByteArrayOutputStream getBinaryOutput() { - return myHandler instanceof BinaryOSProcessHandler ? ((BinaryOSProcessHandler)myHandler).myBinaryOutput : null; + return myHandler.getBinaryOutput(); } // TODO: Carefully here - do not modify command from threads other than the one started command execution @@ -444,53 +465,4 @@ public class CommandExecutor { } } } - - private class MyOSProcessHandler extends OSProcessHandler { - - public MyOSProcessHandler(@NotNull Process process, @Nullable String commandLine) { - super(process, commandLine); - } - - @Override - protected Reader createProcessOutReader() { - if (myCommand.getParameters().contains("--xml")) { - return new InputStreamReader(myProcess.getInputStream(), CharsetToolkit.UTF8_CHARSET); - } - return super.createProcessOutReader(); - } - } - - private static class BinaryOSProcessHandler extends OSProcessHandler { - - @NotNull private final ByteArrayOutputStream myBinaryOutput; - - public BinaryOSProcessHandler(@NotNull final Process process, @Nullable final String commandLine) { - super(process, commandLine); - myBinaryOutput = new ByteArrayOutputStream(); - } - - @NotNull - @Override - protected BaseDataReader createOutputDataReader(BaseDataReader.SleepingPolicy sleepingPolicy) { - return new SimpleBinaryOutputReader(myProcess.getInputStream(), sleepingPolicy); - } - - private class SimpleBinaryOutputReader extends BinaryOutputReader { - - public SimpleBinaryOutputReader(@NotNull InputStream stream, SleepingPolicy sleepingPolicy) { - super(stream, sleepingPolicy); - start(); - } - - @Override - protected void onBinaryAvailable(@NotNull byte[] data, int size) { - myBinaryOutput.write(data, 0, size); - } - - @Override - protected Future executeOnPooledThread(Runnable runnable) { - return BinaryOSProcessHandler.this.executeOnPooledThread(runnable); - } - } - } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandParametersResolutionModule.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandParametersResolutionModule.java index 2e9e42097495..e4883549b0aa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandParametersResolutionModule.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandParametersResolutionModule.java @@ -53,7 +53,7 @@ public class CommandParametersResolutionModule extends BaseCommandRuntimeModule InfoCommandRepositoryProvider infoCommandProvider = new InfoCommandRepositoryProvider(myVcs, command.getTarget()); Repository repository = urlMappingProvider.get(); - if (repository == null && !SvnCommandName.info.equals(command.getName())) { + if (repository == null && !command.isLocalInfo()) { repository = infoCommandProvider.get(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java index bd6c69c5e8d9..a93665f280ac 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; @@ -206,14 +207,20 @@ public class CommandRuntime { executor = new CommandExecutor(exePath, command); } else { - command.put("--force-interactive"); - executor = new TerminalExecutor(exePath, command); + // do not explicitly specify "--force-interactive" as it is not supported in svn 1.7 - commands will be interactive by default as + // running under terminal + executor = newTerminalExecutor(command); ((TerminalExecutor)executor).addInteractiveListener(new TerminalSshModule(this, executor)); } return executor; } + @NotNull + private TerminalExecutor newTerminalExecutor(@NotNull Command command) { + return SystemInfo.isWindows ? new WinTerminalExecutor(exePath, command) : new TerminalExecutor(exePath, command); + } + private static boolean isLocal(@NotNull Command command) { return SvnCommandName.version.equals(command.getName()) || SvnCommandName.cleanup.equals(command.getName()) || diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java index b38d1c914bdf..0b529a954415 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java @@ -167,7 +167,7 @@ public class CommandUtil { JAXBContext context = JAXBContext.newInstance(type); Unmarshaller unmarshaller = context.createUnmarshaller(); - return (T) unmarshaller.unmarshal(new StringReader(data)); + return (T) unmarshaller.unmarshal(new StringReader(data.trim())); } /** diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java index 8bbb58242e05..8f4cae8f313c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java @@ -156,7 +156,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { } private static void parseResult(@NotNull final ISVNInfoHandler handler, @Nullable File base, @Nullable String result) throws SVNException { - if (StringUtil.isEmpty(result)) { + if (StringUtil.isEmptyOrSpaces(result)) { return; } @@ -176,7 +176,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { try { SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); + parser.parse(new ByteArrayInputStream(result.trim().getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); } catch (SvnExceptionWrapper e) { LOG.info("info output " + result); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java index 860e858c7a8e..236aeadc80dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java @@ -136,7 +136,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]); + parser.parse(new ByteArrayInputStream(result.trim().getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]); if (!svnHandl[0].isAnythingReported()) { if (!SvnUtil.isSvnVersioned(myVcs, path)) { throw new SVNException( diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnProcessHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnProcessHandler.java new file mode 100644 index 000000000000..8bed772d70f9 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnProcessHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.process.OSProcessHandler; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.util.io.BaseDataReader; +import com.intellij.util.io.BinaryOutputReader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.concurrent.Future; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnProcessHandler extends OSProcessHandler { + + private final boolean myForceUtf8; + private final boolean myForceBinary; + @NotNull private final ByteArrayOutputStream myBinaryOutput; + + public SvnProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) { + this(process, null, forceUtf8, forceBinary); + } + + public SvnProcessHandler(@NotNull Process process, @Nullable String commandLine, boolean forceUtf8, boolean forceBinary) { + super(process, commandLine); + + myForceUtf8 = forceUtf8; + myForceBinary = forceBinary; + myBinaryOutput = new ByteArrayOutputStream(); + } + + @NotNull + public ByteArrayOutputStream getBinaryOutput() { + return myBinaryOutput; + } + + @Override + protected Reader createProcessOutReader() { + if (myForceUtf8) { + return new InputStreamReader(myProcess.getInputStream(), CharsetToolkit.UTF8_CHARSET); + } + return super.createProcessOutReader(); + } + + @NotNull + @Override + protected BaseDataReader createOutputDataReader(BaseDataReader.SleepingPolicy sleepingPolicy) { + if (myForceBinary) { + return new SimpleBinaryOutputReader(myProcess.getInputStream(), sleepingPolicy); + } + return super.createOutputDataReader(sleepingPolicy); + } + + private class SimpleBinaryOutputReader extends BinaryOutputReader { + + public SimpleBinaryOutputReader(@NotNull InputStream stream, SleepingPolicy sleepingPolicy) { + super(stream, sleepingPolicy); + start(); + } + + @Override + protected void onBinaryAvailable(@NotNull byte[] data, int size) { + myBinaryOutput.write(data, 0, size); + } + + @Override + protected Future executeOnPooledThread(Runnable runnable) { + return SvnProcessHandler.this.executeOnPooledThread(runnable); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalExecutor.java index 203b25cecf34..742cbd2584bc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalExecutor.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalExecutor.java @@ -17,10 +17,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.CommandLineUtil; import com.intellij.execution.ExecutionException; -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.pty4j.PtyProcess; import org.jetbrains.annotations.NonNls; @@ -34,17 +31,6 @@ import java.util.List; */ public class TerminalExecutor extends CommandExecutor { - // max available value is 480 - // if greater value is provided than the default value of 80 will be assumed - // this could provide unnecessary line breaks and thus could break parsing logic - private static final int TERMINAL_WINDOW_MAX_COLUMNS = 480; - - static { - if (SystemInfo.isWindows) { - System.setProperty("win.pty.cols", String.valueOf(TERMINAL_WINDOW_MAX_COLUMNS)); - } - } - private final List myInteractiveListeners = ContainerUtil.createLockFreeCopyOnWriteList(); public TerminalExecutor(@NotNull @NonNls String exePath, @NotNull Command command) { @@ -66,19 +52,28 @@ public class TerminalExecutor extends CommandExecutor { @NotNull @Override - protected OSProcessHandler createProcessHandler() { - return new TerminalProcessHandler(myProcess); + protected SvnProcessHandler createProcessHandler() { + return new TerminalProcessHandler(myProcess, needsUtf8Output(), needsBinaryOutput()); } @NotNull @Override protected Process createProcess() throws ExecutionException { - List parameters = - escapeArguments(CommandLineUtil.toCommandLine(myCommandLine.getExePath(), myCommandLine.getParametersList().getList())); + List parameters = escapeArguments(buildParameters()); + return createProcess(parameters); + } + + @NotNull + protected List buildParameters() { + return CommandLineUtil.toCommandLine(myCommandLine.getExePath(), myCommandLine.getParametersList().getList()); + } + + @NotNull + protected Process createProcess(@NotNull List parameters) throws ExecutionException { try { - return PtyProcess - .exec(ArrayUtil.toStringArray(parameters), myCommandLine.getEnvironment(), myCommandLine.getWorkDirectory().getAbsolutePath()); + return PtyProcess.exec(ArrayUtil.toStringArray(parameters), myCommandLine.getEnvironment(), + myCommandLine.getWorkDirectory().getAbsolutePath()); } catch (IOException e) { throw new ExecutionException(e); @@ -89,16 +84,11 @@ public class TerminalExecutor extends CommandExecutor { public void logCommand() { super.logCommand(); - LOG.info("Terminal output " + ((TerminalProcessHandler) myHandler).getTerminalOutput()); + LOG.info("Terminal output " + ((TerminalProcessHandler)myHandler).getTerminalOutput()); } - private static List escapeArguments(List collection) { - // TODO: Add additional checks like in java.lang.ProcessImpl constructor - return ContainerUtil.map(collection, new Function() { - @Override - public String fun(String s) { - return s.contains(" ") ? "\"" + s + "\"" : s; - } - }); + @NotNull + protected List escapeArguments(@NotNull List arguments) { + return arguments; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalProcessHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalProcessHandler.java index 96d6543c74e2..c6f8dd7cc081 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalProcessHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalProcessHandler.java @@ -16,11 +16,9 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.process.CapturingProcessAdapter; -import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -32,11 +30,7 @@ import java.util.regex.Matcher; /** * @author Konstantin Kolosovsky. */ -public class TerminalProcessHandler extends OSProcessHandler { - - // see http://en.wikipedia.org/wiki/ANSI_escape_code - private static final String NON_CSI_ESCAPE_CODE = "\u001B.[@-_]"; - private static final String CSI_ESCAPE_CODE = "\u001B\\[(.*?)[@-~]"; +public class TerminalProcessHandler extends SvnProcessHandler { private final List myInteractiveListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final CapturingProcessAdapter terminalOutputCapturer = new CapturingProcessAdapter(); @@ -44,8 +38,8 @@ public class TerminalProcessHandler extends OSProcessHandler { private final StringBuilder outputLine = new StringBuilder(); private final StringBuilder errorLine = new StringBuilder(); - public TerminalProcessHandler(@NotNull Process process) { - super(process); + public TerminalProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) { + super(process, forceUtf8, forceBinary); } public void addInteractiveListener(@NotNull InteractiveCommandListener listener) { @@ -88,24 +82,13 @@ public class TerminalProcessHandler extends OSProcessHandler { } } - private static String filterCombinedText(@NotNull String currentLine) { - // for windows platform output is assumed in format suitable for terminal emulator - // for instance, same text could be returned twice with '\r' symbol in between (so in emulator output we'll still see correct - // text without duplication) - // because of this we manually process '\r' occurrences to get correct output - if (SystemInfo.isWindows) { - currentLine = removeAllBeforeCaretReturn(currentLine); - } + @NotNull + protected String filterCombinedText(@NotNull String currentLine) { return currentLine; } - private static String filterText(@NotNull String text) { - if (SystemInfo.isWindows) { - // filter terminal escape codes - they are presented in the output for windows platform - text = text.replaceAll(CSI_ESCAPE_CODE, "").replaceAll(NON_CSI_ESCAPE_CODE, ""); - // trim leading '\r' symbols - as they break xml parsing logic - text = StringUtil.trimLeading(text, '\r'); - } + @NotNull + protected String filterText(@NotNull String text) { return text; } @@ -121,21 +104,6 @@ public class TerminalProcessHandler extends OSProcessHandler { } } - private static String removeAllBeforeCaretReturn(@NotNull String line) { - int caretReturn = line.lastIndexOf("\r"); - - while (caretReturn >= 0) { - if (caretReturn + 1 < line.length() && line.charAt(caretReturn + 1) != '\n') { - // next symbol is not '\n' - we should not treat text before found caret return symbol - line = line.substring(caretReturn + 1); - break; - } - caretReturn = line.lastIndexOf("\r", caretReturn - 1); - } - - return line; - } - private static Key resolveOutputType(@NotNull String line, @NotNull Key outputType) { Key result = outputType; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalSshModule.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalSshModule.java index a2ea3c7cecc6..5a624e9d3414 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalSshModule.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/TerminalSshModule.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; @@ -111,7 +112,9 @@ public class TerminalSshModule extends LineCommandAdapter implements CommandRunt } }; - WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command); + // Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in + // the thread that started progress + WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any()); unknownHost = null; fingerprintAlgorithm = null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalExecutor.java new file mode 100644 index 000000000000..e08594d33c28 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalExecutor.java @@ -0,0 +1,162 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.process.ProcessWrapper; +import com.intellij.execution.util.ExecUtil; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class WinTerminalExecutor extends TerminalExecutor { + + // max available value is 480 + // if greater value is provided than the default value of 80 will be assumed + // this could provide unnecessary line breaks and thus could break parsing logic + private static final int TERMINAL_WINDOW_MAX_COLUMNS = 480; + + static { + // still use isWindows check here not to initialize corresponding property on non-Windows environments + if (SystemInfo.isWindows) { + System.setProperty("win.pty.cols", String.valueOf(TERMINAL_WINDOW_MAX_COLUMNS)); + } + } + + @Nullable private File myRedirectFile; + @Nullable private FileInputStream myRedirectStream; + + public WinTerminalExecutor(@NotNull @NonNls String exePath, @NotNull Command command) { + super(exePath, command); + } + + @NotNull + @Override + protected SvnProcessHandler createProcessHandler() { + return new WinTerminalProcessHandler(myProcess, needsUtf8Output(), needsBinaryOutput()); + } + + @Override + protected void beforeCreateProcess() throws SvnBindException { + super.beforeCreateProcess(); + + createRedirectFile(); + } + + private void createRedirectFile() throws SvnBindException { + myRedirectFile = createTempFile("terminal-output", ""); + + try { + myRedirectStream = new FileInputStream(myRedirectFile); + } + catch (FileNotFoundException e) { + throw new SvnBindException(e); + } + } + + @Override + protected void cleanup() { + super.cleanup(); + + deleteRedirectFile(); + } + + private void deleteRedirectFile() { + if (myRedirectStream != null) { + try { + myRedirectStream.close(); + } + catch (IOException e) { + LOG.info(e); + } + } + + deleteTempFile(myRedirectFile); + } + + @NotNull + @Override + protected Process createProcess() throws ExecutionException { + checkRedirectFile(); + + List parameters = escapeArguments(buildParameters()); + parameters.add(0, ExecUtil.getWindowsShellName()); + parameters.add(1, "/c"); + parameters.add(">>"); + //noinspection ConstantConditions + parameters.add(quote(myRedirectFile.getAbsolutePath())); + + Process process = createProcess(parameters); + + return new ProcessWrapper(process) { + @Override + public InputStream getInputStream() { + return myRedirectStream; + } + + @Override + public InputStream getErrorStream() { + return getOriginalProcess().getInputStream(); + } + }; + } + + private void checkRedirectFile() { + if (myRedirectFile == null) { + throw new IllegalStateException("No redirect file found"); + } + if (myRedirectStream == null) { + throw new IllegalStateException("No redirect stream found"); + } + } + + /** + * TODO: Identify pty4j quoting requirements for Windows and implement accordingly + */ + @NotNull + @Override + protected List escapeArguments(@NotNull List arguments) { + return ContainerUtil.map(arguments, new Function() { + @Override + public String fun(String argument) { + return needQuote(argument) && !isQuoted(argument) ? quote(argument) : argument; + } + }); + } + + @NotNull + private static String quote(@NotNull String argument) { + return StringUtil.wrapWithDoubleQuote(argument); + } + + private static boolean needQuote(@NotNull String argument) { + return argument.contains(" "); + } + + private static boolean isQuoted(@NotNull String argument) { + return StringUtil.startsWithChar(argument, '\"') && StringUtil.endsWithChar(argument, '\"'); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalProcessHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalProcessHandler.java new file mode 100644 index 000000000000..3856b336d07f --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/WinTerminalProcessHandler.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Konstantin Kolosovsky. + */ +public class WinTerminalProcessHandler extends TerminalProcessHandler { + + // see http://en.wikipedia.org/wiki/ANSI_escape_code + private static final String NON_CSI_ESCAPE_CODE = "\u001B.[@-_]"; + private static final String CSI_ESCAPE_CODE = "\u001B\\[(.*?)[@-~]"; + + public WinTerminalProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) { + super(process, forceUtf8, forceBinary); + } + + @Override + protected boolean processHasSeparateErrorStream() { + return true; + } + + @NotNull + @Override + protected String filterCombinedText(@NotNull String currentLine) { + // for windows platform output is assumed in format suitable for terminal emulator + // for instance, same text could be returned twice with '\r' symbol in between (so in emulator output we'll still see correct + // text without duplication) + // because of this we manually process '\r' occurrences to get correct output + return removeAllBeforeCaretReturn(currentLine); + } + + @NotNull + @Override + protected String filterText(@NotNull String text) { + // filter terminal escape codes - they are presented in the output for windows platform + text = text.replaceAll(CSI_ESCAPE_CODE, "").replaceAll(NON_CSI_ESCAPE_CODE, ""); + // trim leading '\r' symbols - as they break xml parsing logic + text = StringUtil.trimLeading(text, '\r'); + + return text; + } + + @NotNull + private static String removeAllBeforeCaretReturn(@NotNull String line) { + int caretReturn = line.lastIndexOf("\r"); + + while (caretReturn >= 0) { + if (caretReturn + 1 < line.length() && line.charAt(caretReturn + 1) != '\n') { + // next symbol is not '\n' - we should not treat text before found caret return symbol + line = line.substring(caretReturn + 1); + break; + } + caretReturn = line.lastIndexOf("\r", caretReturn - 1); + } + + return line; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java index d654253bf426..17b195eb927e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java @@ -2,15 +2,15 @@ package org.jetbrains.idea.svn.content; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.impl.ContentRevisionCache; -import com.intellij.openapi.vfs.CharsetToolkit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; -import org.jetbrains.idea.svn.commandLine.*; +import org.jetbrains.idea.svn.commandLine.CommandExecutor; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; -import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; @@ -31,9 +31,7 @@ public class CmdContentClient extends BaseSvnClient implements ContentClient { CommandUtil.put(parameters, revision); CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.cat, parameters, null); - // TODO: currently binary output will be null for terminal mode - use text output in this case - ByteArrayOutputStream output = command.getBinaryOutput(); - byte[] bytes = output != null ? output.toByteArray() : CharsetToolkit.getUtf8Bytes(command.getOutput()); + byte[] bytes = command.getBinaryOutput().toByteArray(); ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java index 6638f5ce75d1..04868ae990b8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java @@ -16,9 +16,9 @@ package org.jetbrains.idea.svn.dialogs; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.util.SystemProperties; +import org.jetbrains.idea.svn.SvnAuthenticationManager; import org.jetbrains.idea.svn.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNErrorMessage; @@ -27,7 +27,6 @@ import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; import org.tmatesoft.svn.core.auth.SVNAuthentication; import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication; -import org.tmatesoft.svn.core.internal.wc.ISVNAuthenticationStorage; import java.util.HashSet; import java.util.Set; @@ -36,25 +35,21 @@ import java.util.Set; * @author alex */ public class SvnAuthenticationProvider implements ISVNAuthenticationProvider { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider"); + private final Project myProject; private final SvnAuthenticationNotifier myAuthenticationNotifier; private final ISVNAuthenticationProvider mySvnInteractiveAuthenticationProvider; - private final ISVNAuthenticationStorage myAuthenticationStorage; + private final SvnAuthenticationManager myAuthenticationManager; private static final Set ourForceInteractive = new HashSet(); public SvnAuthenticationProvider(final SvnVcs svnVcs, final ISVNAuthenticationProvider provider, - final ISVNAuthenticationStorage authenticationStorage) { - myAuthenticationStorage = authenticationStorage; + final SvnAuthenticationManager authenticationManager) { + myAuthenticationManager = authenticationManager; myProject = svnVcs.getProject(); myAuthenticationNotifier = svnVcs.getAuthNotifier(); mySvnInteractiveAuthenticationProvider = provider; } - private void log(final String s) { - LOG.debug(s); - } - public SVNAuthentication requestClientAuthentication(final String kind, final SVNURL url, final String realm, @@ -73,7 +68,7 @@ public class SvnAuthenticationProvider implements ISVNAuthenticationProvider { return mySvnInteractiveAuthenticationProvider.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored); } else { if (myAuthenticationNotifier.ensureNotify(obj)) { - return (SVNAuthentication) myAuthenticationStorage.getData(kind, realm); + return myAuthenticationManager.requestFromCache(kind, url, realm, errorMessage, previousAuth, authMayBeStored); } } return null; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java index 3700343842e3..f3552e99c392 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java @@ -80,7 +80,7 @@ public class SvnAuthenticationTest extends PlatformTestCase { myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile); myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager); - myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, SvnConfiguration.RUNTIME_AUTH_CACHE)); + myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, myAuthenticationManager)); myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); myTestInteraction = new TestInteraction(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java index 624cfbb25635..9652a1f25c8a 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java @@ -84,7 +84,7 @@ public class SvnAuthenticationTest extends PlatformTestCase { myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile); myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager); - myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, SvnConfiguration.RUNTIME_AUTH_CACHE)); + myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, myAuthenticationManager)); myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); myTestInteraction = new TestInteraction();