Commits

Sebastian Sdorra committed 0df092a Merge

merge with branch issue-138

Comments (0)

Files changed (9)

scm-core/src/main/java/sonia/scm/i18n/Bundle.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. 2. Redistributions in
+ * binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. 3. Neither the name of SCM-Manager;
+ * nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.i18n;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import sonia.scm.util.Util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.text.MessageFormat;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * This class is a wrapper for {@link ResourceBundle}, it applies some missing
+ * format options missing in {@link ResourceBundle}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.15
+ */
+public class Bundle
+{
+
+  /** Field description */
+  private static final String SEPARATOR = System.getProperty("line.separator");
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param bundle
+   */
+  private Bundle(ResourceBundle bundle)
+  {
+    this.bundle = bundle;
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Creates a new bundle instance
+   *
+   *
+   * @param path path to the properties file
+   *
+   * @return new bundle instance
+   */
+  public static Bundle getBundle(String path)
+  {
+    return new Bundle(ResourceBundle.getBundle(path));
+  }
+
+  /**
+   * Creates a new bundle instance
+   *
+   *
+   * @param path path to the properties file
+   * @param locale locale for the properties file
+   *
+   * @return new bundle instance
+   */
+  public static Bundle getBundle(String path, Locale locale)
+  {
+    return new Bundle(ResourceBundle.getBundle(path, locale));
+  }
+
+  /**
+   * This method returns the same value as
+   * {@link #getString(java.lang.String, java.lang.Object[])}
+   * with a line separator at the end.
+   *
+   * @param key key in the properties file
+   * @param args format arguments
+   *
+   * @return formated message
+   */
+  public String getLine(String key, Object... args)
+  {
+    return getString(key, args).concat(SEPARATOR);
+  }
+
+  /**
+   * Returns the value of the key, formatted with {@link MessageFormat}
+   *
+   *
+   * @param key key in the properties file
+   * @param args format arguments
+   *
+   * @return formated message
+   */
+  public String getString(String key, Object... args)
+  {
+    String msg = bundle.getString(key);
+
+    if (Util.isNotEmpty(args))
+    {
+      msg = MessageFormat.format(msg, args);
+    }
+
+    return msg;
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private ResourceBundle bundle;
+}

scm-core/src/main/java/sonia/scm/web/cgi/AbstractCGIExecutor.java

   }
 
   /**
+   * {@inheritDoc}
+   *
+   *
+   * @return
+   * @since 1.15
+   */
+  @Override
+  public CGIExceptionHandler getExceptionHandler()
+  {
+    return exceptionHandler;
+  }
+
+  /**
    * Method description
    *
    *
   }
 
   /**
+   * {@inheritDoc}
+   *
+   *
+   * @return
+   * @since 1.15
+   */
+  @Override
+  public CGIStatusCodeHandler getStatusCodeHandler()
+  {
+    return statusCodeHandler;
+  }
+
+  /**
    * Method description
    *
    *
   }
 
   /**
+   * {@inheritDoc}
+   *
+   *
+   * @param exceptionHandler
+   * @since 1.15
+   */
+  @Override
+  public void setExceptionHandler(CGIExceptionHandler exceptionHandler)
+  {
+    this.exceptionHandler = exceptionHandler;
+  }
+
+  /**
    * Method description
    *
    *
   }
 
   /**
+   * {@inheritDoc}
+   *
+   *
+   * @param statusCodeHandler
+   * @since 1.15
+   */
+  @Override
+  public void setStatusCodeHandler(CGIStatusCodeHandler statusCodeHandler)
+  {
+    this.statusCodeHandler = statusCodeHandler;
+  }
+
+  /**
    * Method description
    *
    *
   protected EnvList environment;
 
   /** Field description */
+  protected CGIExceptionHandler exceptionHandler;
+
+  /** Field description */
   protected boolean ignoreExitCode = false;
 
   /** Field description */
   protected boolean passShellEnvironment = false;
 
   /** Field description */
+  protected CGIStatusCodeHandler statusCodeHandler;
+
+  /** Field description */
   protected File workDirectory;
 }

scm-core/src/main/java/sonia/scm/web/cgi/CGIExecutor.java

   public String getInterpreter();
 
   /**
+   * Returns the status code handler.
+   *
+   *
+   * @return status code handler
+   * @since 1.15
+   */
+  public CGIStatusCodeHandler getStatusCodeHandler();
+
+  /**
    * Method description
    *
    *
   public void setPassShellEnvironment(boolean passShellEnvironment);
 
   /**
+   * Sets the status code handler.
+   *
+   *
+   * @param statusCodeHandler the handler to set
+   * @since 1.15
+   */
+  public void setStatusCodeHandler(CGIStatusCodeHandler statusCodeHandler);
+
+  /**
    * Method description
    *
    *

scm-core/src/main/java/sonia/scm/web/cgi/CGIStatusCodeHandler.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. 2. Redistributions in
+ * binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. 3. Neither the name of SCM-Manager;
+ * nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web.cgi;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Interface for handling return codes of processes
+ * executed by the {@link CGIExecutor}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.15
+ */
+public interface CGIStatusCodeHandler
+{
+
+  /**
+   * Handles the return code of the process executed by {@link CGIExecutor}.
+   * <b>Note:</b> This method is called when the process has
+   * already written to the {@link OutputStream}.
+   *
+   *
+   * @param request the http request
+   * @param statusCode process return code
+   */
+  public void handleStatusCode(HttpServletRequest request, int statusCode);
+
+  /**
+   * Handles the return code of the process executed by {@link CGIExecutor}.
+   * <b>Note:</b> This method is only called when the process has
+   * not written to the {@link OutputStream}. Do not call
+   * {@link HttpServletResponse#getWriter()}, because there was already a call
+   * to {@link HttpServletResponse#getOutputStream()}.
+   *
+   *
+   * @param request the http request
+   * @param response the http response
+   * @param ouputStream the servlet output stream
+   * @param statusCode process return code
+   *
+   * @throws IOException
+   */
+  public void handleStatusCode(HttpServletRequest request,
+                               HttpServletResponse response,
+                               OutputStream ouputStream, int statusCode)
+          throws IOException;
+}

scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIExceptionHandler.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. 2. Redistributions in
+ * binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. 3. Neither the name of SCM-Manager;
+ * nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Charsets;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import sonia.scm.i18n.Bundle;
+import sonia.scm.util.IOUtil;
+import sonia.scm.util.Util;
+import sonia.scm.web.cgi.CGIExceptionHandler;
+import sonia.scm.web.cgi.CGIStatusCodeHandler;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class HgCGIExceptionHandler
+        implements CGIExceptionHandler, CGIStatusCodeHandler
+{
+
+  /** Field description */
+  public static final String BUNDLE_PATH = "sonia.scm.web.cgimessages";
+
+  /** Field description */
+  public static final String CONTENT_TYPE_ERROR = "application/hg-error";
+
+  /** TODO create a bundle for error messages */
+  public static final String ERROR_NOT_CONFIGURED = "error.notConfigured";
+
+  /** Field description */
+  public static final String ERROR_STATUSCODE = "error.statusCode";
+
+  /** Field description */
+  public static final String ERROR_UNEXPECTED = "error.unexpected";
+
+  /**
+   * the logger for HgCGIExceptionHandler
+   */
+  private static final Logger logger =
+    LoggerFactory.getLogger(HgCGIExceptionHandler.class);
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   */
+  public HgCGIExceptionHandler()
+  {
+    this.bundle = Bundle.getBundle(BUNDLE_PATH);
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   * @param ex
+   */
+  @Override
+  public void handleException(HttpServletRequest request,
+                              HttpServletResponse response, Throwable ex)
+  {
+    if (logger.isErrorEnabled())
+    {
+      logger.error("not able to handle mercurial request", ex);
+    }
+
+    sendError(response,
+              bundle.getString(ERROR_UNEXPECTED, Util.nonNull(ex.getMessage())));
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   * @param output
+   * @param statusCode
+   *
+   * @throws IOException
+   */
+  @Override
+  public void handleStatusCode(HttpServletRequest request,
+                               HttpServletResponse response,
+                               OutputStream output, int statusCode)
+          throws IOException
+  {
+    if (statusCode != 0)
+    {
+      response.setContentType(CONTENT_TYPE_ERROR);
+
+      String msg = bundle.getLine(ERROR_STATUSCODE, statusCode);
+
+      if (logger.isWarnEnabled())
+      {
+        logger.warn(msg);
+      }
+
+      output.write(msg.getBytes(Charsets.US_ASCII));
+    }
+    else if (logger.isDebugEnabled())
+    {
+      logger.debug("mercurial process ends successfully");
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param statusCode
+   */
+  @Override
+  public void handleStatusCode(HttpServletRequest request, int statusCode)
+  {
+    if (statusCode != 0)
+    {
+      if (logger.isWarnEnabled())
+      {
+        logger.error("mercurial process ends with {}", statusCode);
+      }
+    }
+    else if (logger.isDebugEnabled())
+    {
+      logger.debug("mercurial process ends successfully");
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param response
+   * @param message
+   *
+   */
+  public void sendError(HttpServletResponse response, String message)
+  {
+    response.setContentType(CONTENT_TYPE_ERROR);
+
+    PrintWriter writer = null;
+
+    try
+    {
+      writer = response.getWriter();
+      writer.println(message);
+    }
+    catch (IOException ex)
+    {
+      logger.error("could not write error message to client", ex);
+    }
+    finally
+    {
+      IOUtil.close(writer);
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param response
+   * @param key
+   */
+  public void sendFormattedError(HttpServletResponse response, String key)
+  {
+    sendError(response, bundle.getString(key));
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private Bundle bundle;
+}

scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java

     this.handler = handler;
     this.hookManager = hookManager;
     this.requestListenerUtil = requestListenerUtil;
+    this.exceptionHandler = new HgCGIExceptionHandler();
   }
 
   //~--- methods --------------------------------------------------------------
 
     if (repository == null)
     {
-      throw new ServletException("repository not found");
+      if (logger.isDebugEnabled())
+      {
+        logger.debug("no hg repository found at {}", request.getRequestURI());
+      }
+
+      response.setStatus(HttpServletResponse.SC_NOT_FOUND);
     }
+    else if (!handler.isConfigured())
+    {
+      exceptionHandler.sendFormattedError(response,
+              HgCGIExceptionHandler.ERROR_NOT_CONFIGURED);
+    }
+    else
+    {
+      try
+      {
+        handleRequest(request, response, repository);
+      }
+      catch (Exception ex)
+      {
+        exceptionHandler.handleException(request, response, ex);
+      }
+    }
+  }
 
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   * @param repository
+   *
+   * @throws IOException
+   * @throws ServletException
+   */
+  private void handleRequest(HttpServletRequest request,
+                             HttpServletResponse response,
+                             Repository repository)
+          throws ServletException, IOException
+  {
     if (requestListenerUtil.callListeners(request, response, repository))
     {
       process(request, response, repository);
     String name = repository.getName();
     File directory = handler.getDirectory(repository);
     String pythonPath = HgUtil.getPythonPath(handler.getConfig());
-    
     CGIExecutor executor = cgiExecutorFactory.createExecutor(configuration,
                              getServletContext(), request, response);
 
+    executor.setExceptionHandler(exceptionHandler);
+    executor.setStatusCodeHandler(exceptionHandler);
     executor.setContentLengthWorkaround(true);
     executor.getEnvironment().set(ENV_REPOSITORY_NAME, name);
     executor.getEnvironment().set(ENV_REPOSITORY_PATH,
   private ScmConfiguration configuration;
 
   /** Field description */
+  private HgCGIExceptionHandler exceptionHandler;
+
+  /** Field description */
   private HgRepositoryHandler handler;
 
   /** Field description */

scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/web/cgimessages.properties

+# Copyright (c) 2010, Sebastian Sdorra
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+# 3. Neither the name of SCM-Manager; nor the names of its
+#    contributors may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# http://bitbucket.org/sdorra/scm-manager
+# 
+
+error.notConfigured = The mercurial installation on the scm-manager server seems to be not configured correctly. Please check the settings.
+error.statusCode = Mercurial/Python process ends with return code {0}
+error.unexpected = There is an unexpected error occurred: {0}

scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java

 
 //~--- non-JDK imports --------------------------------------------------------
 
+import com.google.common.io.ByteStreams;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
    * @return
    */
   @Override
+  public CGIStatusCodeHandler getStatusCodeHandler()
+  {
+    if (statusCodeHandler == null)
+    {
+      statusCodeHandler = new DefaultCGIStatusCodeHandler();
+    }
+
+    return statusCodeHandler;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @Override
   public boolean isContentLengthWorkaround()
   {
     return contentLengthWorkaround;
     this.contentLengthWorkaround = contentLengthWorkaround;
   }
 
-  /**
-   * Method description
-   *
-   *
-   * @param exceptionHandler
-   */
-  @Override
-  public void setExceptionHandler(CGIExceptionHandler exceptionHandler)
-  {
-    this.exceptionHandler = exceptionHandler;
-  }
-
   //~--- methods --------------------------------------------------------------
 
   /**
   private void execute(Process process) throws IOException
   {
     InputStream processIS = null;
-    InputStream processES = null;
+    ServletOutputStream servletOS = null;
 
     try
     {
-      processES = process.getErrorStream();
-      processErrorStreamAsync(processES);
+      processErrorStreamAsync(process);
       processServletInput(process);
       processIS = process.getInputStream();
-      processProcessInputStream(processIS);
-      waitForFinish(process);
+      servletOS = response.getOutputStream();
+      parseHeaders(processIS);
+
+      long content = ByteStreams.copy(processIS, servletOS);
+
+      waitForFinish(process, servletOS, content);
     }
     finally
     {
       IOUtil.close(processIS);
-      IOUtil.close(processES);
+      IOUtil.close(servletOS);
     }
   }
 
    * Method description
    *
    *
-   * @param errorStream
+   *
+   * @param process
    */
-  private void processErrorStreamAsync(final InputStream errorStream)
+  private void processErrorStreamAsync(final Process process)
   {
     new Thread(new Runnable()
     {
       @Override
       public void run()
       {
+        InputStream errorStream = null;
+
         try
         {
+          errorStream = process.getErrorStream();
           processErrorStream(errorStream);
         }
         catch (IOException ex)
         {
           logger.error("could not read errorstream", ex);
         }
+        finally
+        {
+          IOUtil.close(errorStream);
+        }
       }
     }).start();
   }
    * Method description
    *
    *
-   * @param is
-   *
-   * @throws IOException
-   */
-  private void processProcessInputStream(InputStream is) throws IOException
-  {
-    parseHeaders(is);
-
-    ServletOutputStream servletOS = null;
-
-    try
-    {
-      servletOS = response.getOutputStream();
-      IOUtil.copy(is, servletOS, bufferSize);
-    }
-    finally
-    {
-      IOUtil.close(servletOS);
-    }
-  }
-
-  /**
-   * Method description
-   *
-   *
    * @param process
    */
   private void processServletInput(Process process)
    *
    *
    * @param process
+   * @param output
+   * @param content
    *
+   *
+   * @throws IOException
    */
-  private void waitForFinish(Process process)
+  private void waitForFinish(Process process, ServletOutputStream output,
+                             long content)
+          throws IOException
   {
     try
     {
       int exitCode = process.waitFor();
 
-      if ((exitCode != 0) &&!ignoreExitCode)
+      if (!ignoreExitCode)
       {
-        logger.warn("process ends with exit code {}", exitCode);
+        if (logger.isTraceEnabled())
+        {
+          logger.trace(
+              "handle status code {} with statusCodeHandler, there are {} bytes written to outputstream",
+              exitCode, content);
+        }
+
+        if (content == 0)
+        {
+          getStatusCodeHandler().handleStatusCode(request, response, output,
+                  exitCode);
+        }
+        else
+        {
+          getStatusCodeHandler().handleStatusCode(request, exitCode);
+        }
+      }
+      else if (logger.isDebugEnabled())
+      {
+        logger.debug("ignore status code {}", exitCode);
       }
     }
     catch (InterruptedException ex)
     {
-      logger.error("process interrupted", ex);
+      getExceptionHandler().handleException(request, response, ex);
     }
   }
 
   private ServletContext context;
 
   /** Field description */
-  private CGIExceptionHandler exceptionHandler;
-
-  /** Field description */
   private HttpServletRequest request;
 
   /** Field description */

scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIStatusCodeHandler.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. 2. Redistributions in
+ * binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. 3. Neither the name of SCM-Manager;
+ * nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web.cgi;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class DefaultCGIStatusCodeHandler implements CGIStatusCodeHandler
+{
+
+  /**
+   * the logger for DefaultCGIStatusCodeHandler
+   */
+  private static final Logger logger =
+    LoggerFactory.getLogger(DefaultCGIStatusCodeHandler.class);
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param statusCode
+   */
+  @Override
+  public void handleStatusCode(HttpServletRequest request, int statusCode)
+  {
+    if (statusCode != 0)
+    {
+      logger.warn("process ends with exit code {}", statusCode);
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   * @param ouputStream
+   * @param statusCode
+   *
+   * @throws IOException
+   */
+  @Override
+  public void handleStatusCode(HttpServletRequest request,
+                               HttpServletResponse response,
+                               OutputStream ouputStream, int statusCode)
+          throws IOException
+  {
+    handleStatusCode(request, statusCode);
+  }
+}