content: /**
   * Copyright 2000-2006 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.server;
  
  // General Java Classes
  
  import android.util.Log;
  
  import org.apache.http.HttpResponse;
  
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.Reader;
  import java.net.Socket;
  
  import lib.sound.sampled.AudioSystem;
  import marytts.datatypes.MaryDataType;
  import marytts.server.http.MaryHttpServerUtils;
  import marytts.util.MaryRuntimeUtils;
  import marytts.util.MaryUtils;
  
  //import marytts.util.io.LoggingReader;
  
  
  /**
   * A lightweight process handling one Request in a thread of its own.
   * This is to be used when running as a socket server.
   *
   * @author Marc Schr&ouml;der
   */
  
  public class RequestHandler extends Thread {
      private Request request;
      private Socket infoSocket;
      private Socket dataSocket;
      // private Logger logger;
      //  private Logger clientLogger;
  
      /**
       * Constructor to be used for Socket processing (running as a standalone
       * socket server).  <code>inputReader</code> is a Reader reading from from
       * <code>dataSocket.inputStream()</code>. Passing this on is necessary
       * because the mary server does a buffered read on that input stream, and
       * without passing that buffered reader on, data gets lost.
       */
      public RequestHandler(
              Request request,
              Socket infoSocket,
              Socket dataSocket,
              Reader inputReader) {
          if (request == null)
              throw new NullPointerException("Cannot handle null request");
          this.request = request;
          if (infoSocket == null)
              throw new NullPointerException("Received null infoSocket");
          this.infoSocket = infoSocket;
          if (dataSocket == null)
              throw new NullPointerException("Received null dataSocket");
          this.dataSocket = dataSocket;
          this.setName("RH " + request.getId());
          //   logger = MaryUtils.getLogger(this.getName());
          //   this.inputReader = new LoggingReader(inputReader, logger);
          //  clientLogger = MaryUtils.getLogger(this.getName() + " client");
  //        try {
  //          //  clientLogger.addAppender(
  //                new WriterAppender(
  //                    new SimpleLayout(),
  //                    new PrintWriter(infoSocket.getOutputStream(), true)));
  //        //    clientLogger.setLevel(Level.WARN);
  //            // What goes to clientLogger does not go to the normal logfile:
  //        //    clientLogger.setAdditivity(false);
  //        } catch (IOException e) {
  //        //    logger.warn("Cannot write warnings to client", e);
  //        }
      }
  
  //    private void clientLogWarning(String message, Exception e) {
  //        // Only print to client if there is one;
  //        // do not print TransformerException and SAXParseException
  //        // (that has been done by the LoggingErrorHander already).
  //        if (!(clientLogger == null
  //            || e instanceof TransformerException
  //            || e instanceof SAXParseException))
  //            clientLogger.warn(message + "\n" + e.toString());
  //        // No stack trace on clientLogger
  //    }
  //
  //    private void clientLogError(String message, Throwable e) {
  //        // Only print to client if there is one;
  //        // do not print TransformerException and SAXParseException
  //        // (that has been done by the LoggingErrorHander already).
  //        if (!(clientLogger == null || e instanceof TransformerException || e instanceof SAXParseException))
  //            clientLogger.error(message + "\n" + MaryUtils.getThrowableAndCausesAsString(e));
  //        // No stack trace on clientLogger
  //    }
  
      /**
       * Perform the actual processing by calling the appropriate methods
       * of the associated <code>Request</code> object.
       * <p/>
       * Note that while different request handlers run as different threads,
       * they all use the same module objects. How a given module deals with
       * several requests simultaneously is its own problem, the simplest
       * solution being a synchronized <code>process()</code> method.
       *
       * @see Request
       * @see marytts.modules.MaryModule
       * @see marytts.modules.ExternalModule
       * @see marytts.modules.InternalModule
       */
      @Override
      public void run() {
          boolean ok = true;
          // tasks:
          // * read the input according to its type
          // * determine which modules are needed
          // * in turn, write to and read from each module according to data type
          // * write output according to its type
  
          try {
              // request.readInputData(inputReader);
          } catch (Exception e) {
              String message = "Problem reading input";
              Log.w(Mary.LOG, message, e);
  //            clientLogWarning(message, e);
              ok = false;
          }
  
          boolean streamingOutput = false;
          StreamingOutputWriter rw = null;
          // Process input data to output data
          if (ok)
              try {
                  if (request.getOutputType().equals(MaryDataType.get("AUDIO"))
                          && request.getStreamAudio()) {
                      streamingOutput = true;
                      rw = new StreamingOutputWriter(request, dataSocket.getOutputStream());
                      rw.start();
                  }
  
                  request.process();
              } catch (Throwable e) {
                  String message = "Processing failed.";
                  Log.e(Mary.LOG, e.toString());
                  //  clientLogError(message, e);
                  ok = false;
              }
  
          // For simple clients, we need to close the infoSocket before sending
          // the data on dataSocket. Otherwise there may be deadlock.
          try {
  //            if (clientLogger != null) {
  //                clientLogger.removeAllAppenders();
  //                clientLogger = null;
  //            }
              infoSocket.close();
          } catch (IOException e) {
              Log.w(Mary.LOG, "Couldn't close info socket properly.", e);
              ok = false;
          }
  
          // Write output:
          if (ok) {
              if (!streamingOutput) {
                  try {
                      request.writeOutputData(dataSocket.getOutputStream());
                  } catch (Exception e) {
                      String message = "Cannot write output, client seems to have disconnected.";
                      Log.w(message, e);
                      ok = false;
                  }
              } else { // streaming output
                  try {
                      rw.join();
                  } catch (InterruptedException ie) {
                      Log.w(Mary.LOG, ie);
                  }
              }
          }
          try {
              dataSocket.close();
          } catch (IOException e) {
              Log.w(Mary.LOG, "Couldn't close data socket properly.", e);
              ok = false;
          }
          if (ok)
              Log.i(Mary.LOG, "Request handled successfully.");
          else
              Log.i(Mary.LOG, "Request couldn't be handled successfully.");
          if (MaryRuntimeUtils.lowMemoryCondition()) {
              Log.i(Mary.LOG, "Low memory condition detected (only " + MaryUtils.availableMemory() + " bytes left). Triggering garbage collection.");
              Runtime.getRuntime().gc();
              Log.i(Mary.LOG, "After garbage collection: " + MaryUtils.availableMemory() + " bytes available.");
          }
  
      } // run()
  
      public static class StreamingOutputWriter extends Thread {
          private Request request;
          private OutputStream output;
          //  private Logger logger;
  
          public StreamingOutputWriter(Request request, OutputStream output) throws Exception {
              this.request = request;
              this.output = output;
              this.setName("RW " + request.getId());
              //    logger = MaryUtils.getLogger(this.getName());
          }
  
          @Override
          public void run() {
              try {
                  AudioSystem.write(request.getAudio(), request.getAudioFileFormat().getType(), output);
                  output.flush();
                  output.close();
                  Log.i(Mary.LOG, "Finished writing output");
              } catch (IOException ioe) {
                  Log.i(Mary.LOG, "Cannot write output, client seems to have disconnected. ", ioe);
                  request.abort();
              }
          }
      }
  
      //A reader class for debugging purposes for StreamingOutputWriter
      public static class StreamingOutputPiper extends Thread {
          private InputStream input;
          // private Logger logger = null;
          private BufferedWriter textWriter = null;
          private FileOutputStream binaryWriter = null;
          private HttpResponse response = null;
          private String contentType = null;
  
          //Reads from input and pipes the output to logger
          public StreamingOutputPiper(InputStream input) throws Exception {
              this.input = input;
  
              //logger = MaryUtils.getLogger(this.getName());
              textWriter = null;
              binaryWriter = null;
              response = null;
              contentType = null;
          }
  
          //Reads from input and pipes the output to text file
          public StreamingOutputPiper(InputStream input, String outTextFile) throws Exception {
              this.input = input;
  
              // logger = null;
              textWriter = new BufferedWriter(new FileWriter(outTextFile));
              binaryWriter = null;
              response = null;
              contentType = null;
          }
  
          //Reads from input and pipes the output to binary file
          public StreamingOutputPiper(InputStream input, File outFile) throws Exception {
              this.input = input;
  
              //  logger = null;
              textWriter = null;
              binaryWriter = new FileOutputStream(outFile);
              response = null;
              contentType = null;
          }
  
          //Reads from input and pipes the output to Http response
          public StreamingOutputPiper(InputStream input, HttpResponse response, String contentType) throws Exception {
              this.input = input;
  
              //logger = null;
              textWriter = null;
              binaryWriter = null;
              this.response = response;
              this.contentType = contentType;
          }
  
          @Override
          public void run() {
              String message;
  
              try {
                  int val;
  //                if (logger!=null)
  //                {
  //                    while ((val=input.read())!=-1)
  //                        logger.debug(val+System.getProperty("line.separator"));
  //                    
  //                    input.close(); 
  //                }
                  if (textWriter != null) {
                      while ((val = input.read()) != -1) {
                          textWriter.write(val);
                          textWriter.newLine();
                      }
  
                      input.close();
                  } else if (binaryWriter != null) {
                      while ((val = input.read()) != -1)
                          binaryWriter.write((byte) val);
  
                      input.close();
                  } else if (response != null) {
                      MaryHttpServerUtils.toHttpResponse(input, response, contentType);
  
                      //Important! input.close() is not needed here since toHttpResponse already closes the input
                  } else {
                      System.out.println("Error: No writers initialised!");
                      input.close();
                      return;
                  }
  
                  message = "Finished reading output";
  //                if (logger!=null)
  //                    logger.info(message);
  //                else
                  if (textWriter != null) {
                      textWriter.write(message);
                      textWriter.newLine();
                      textWriter.close();
                  } else if (binaryWriter != null)
                      binaryWriter.close();
              } catch (IOException ioe) {
                  message = "Cannot read output, client seems to have disconnected. ";
  //                if (logger!=null)
  //                    logger.info(message, ioe);
  //                else 
                  if (textWriter != null) {
                      try {
                          textWriter.write(message);
                          textWriter.newLine();
                      } catch (IOException e) {
                          System.out.println("Error: Cannot write to text writer!");
                      }
                  }
              }
          }
      }
  }
  
  