/* * Copyright 2000-2010 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.community.intellij.plugins.communitycase.commands; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import org.community.intellij.plugins.communitycase.Util; import org.community.intellij.plugins.communitycase.Vcs; import org.jetbrains.annotations.NotNull; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; /** * The handler that allows consuming binary data as byte array */ public class BinaryHandler extends Handler { /** * The logger */ private static final Logger LOG = Logger.getInstance("#"+BinaryHandler.class.getName()); /** * Stdout stream */ private final ByteArrayOutputStream myStdout = new ByteArrayOutputStream(); /** * Stderr stream */ private final ByteArrayOutputStream myStderr = new ByteArrayOutputStream(); /** * The semaphore that waits for stream processing */ private final Semaphore mySteamSemaphore = new Semaphore(0); /** * The size of buffer to use */ private static final int BUFFER_SIZE = 8 * 1024; /** * The exception to use */ private AtomicReference<VcsException> myException = new AtomicReference<VcsException>(); /** * A constructor * * @param project a project * @param directory a process directory * @param command a command to execute (if empty string, the parameter is ignored) */ protected BinaryHandler(@NotNull Project project, @NotNull File directory, @NotNull Command command) { super(project, directory, command); } /** * A constructor * * @param project a project * @param vcsRoot a vcs root * @param command a command to execute (if empty string, the parameter is ignored) */ public BinaryHandler(final Project project, final VirtualFile vcsRoot, final Command command) { super(project, vcsRoot, command); } /** * {@inheritDoc} */ @Override protected void startHandlingStreams() { handleStream(myProcess.getErrorStream(), myStderr); handleStream(myProcess.getInputStream(), myStdout); } /** * Handle the single stream * * @param in the standard input * @param out the standard output */ private void handleStream(final InputStream in, final ByteArrayOutputStream out) { Thread t = new Thread(new Runnable() { @Override public void run() { try { byte[] buffer = new byte[BUFFER_SIZE]; while (true) { int rc = 0; rc = in.read(buffer); if (rc == -1) { break; } out.write(buffer, 0, rc); } } catch (IOException e) { //noinspection ThrowableInstanceNeverThrown if (!myException.compareAndSet(null, new VcsException("Stream IO problem", e))) { LOG.error("Problem reading stream", e); } } finally { mySteamSemaphore.release(1); } } }, "Stream copy thread"); t.setDaemon(true); t.start(); } /** * {@inheritDoc} */ @Override protected void destroyProcess() { myProcess.destroy(); } /** * {@inheritDoc} */ @Override protected void waitForProcess() { try { mySteamSemaphore.acquire(2); myProcess.waitFor(); int exitCode = myProcess.exitValue(); setExitCode(exitCode); } catch (InterruptedException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring process exception: ", e); } setExitCode(255); } listeners().processTerminated(getExitCode()); } /** * Run in the current thread and return the data as array * * @return the binary data * @throws VcsException in case of the problem with running git */ public byte[] run() throws VcsException { addListener(new HandlerListener() { @Override public void processTerminated(int exitCode) { if (exitCode != 0 && !isIgnoredErrorCode(exitCode)) { Charset cs = getCharset(); cs = cs == null ? Util.UTF8_CHARSET : cs; String message = new String(myStderr.toByteArray(), cs); //if(message != null && message.indexOf("cleartool: Error: con: Bad file descriptor") == -1) { if (message.length() == 0) { //noinspection ThrowableResultOfMethodCallIgnored if (myException.get() != null) { message = "Process finished with exit code " + exitCode; } else { message = null; } } else { if (!isStderrSuppressed()) { Vcs.getInstance(myProject).showErrorMessages(message); } } if (message != null) { //noinspection ThrowableInstanceNeverThrown VcsException e = myException.getAndSet(new VcsException(message)); if (e != null) { LOG.warn("Dropping previous exception: ", e); } } //} //else, do nothing. } } @Override public void startFailed(Throwable exception) { //noinspection ThrowableInstanceNeverThrown VcsException e = myException.getAndSet(new VcsException("Start failed: " + exception.getMessage(), exception)); if (e != null) { LOG.warn("Dropping previous exception: ", e); } } }); HandlerUtil.runInCurrentThread(this, null); //noinspection ThrowableResultOfMethodCallIgnored if (myException.get() != null) { throw myException.get(); } return myStdout.toByteArray(); } }