marytts.server.RequestHandler Maven / Gradle / Ivy
/**
* Copyright 2000-2006 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* This file is part of MARY TTS.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
package marytts.server;
// General Java Classes
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.PrintWriter;
import java.io.Reader;
import java.net.Socket;
import javax.sound.sampled.AudioSystem;
import javax.xml.transform.TransformerException;
import marytts.datatypes.MaryDataType;
import marytts.server.http.MaryHttpServerUtils;
import marytts.util.MaryRuntimeUtils;
import marytts.util.MaryUtils;
import marytts.util.io.LoggingReader;
import org.apache.http.HttpResponse;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.WriterAppender;
import org.xml.sax.SAXParseException;
/**
* 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öder
*/
public class RequestHandler extends Thread {
private Request request;
private Socket infoSocket;
private Socket dataSocket;
private LoggingReader inputReader;
private Logger logger;
private Logger clientLogger;
/**
* Constructor to be used for Socket processing (running as a standalone socket server). inputReader
is a Reader
* reading from from dataSocket.inputStream()
. 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.
*
* @param request
* request
* @param infoSocket
* infoSocket
* @param dataSocket
* dataSocket
* @param inputReader
* inputReader
*/
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 Request
object.
*
* 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
* process()
method.
*
* @see Request
* @see marytts.modules.MaryModule
* @see marytts.modules.ExternalModule
* @see marytts.modules.InternalModule
*/
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";
logger.warn(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.";
logger.error(message, e);
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) {
logger.warn("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.";
logger.warn(message, e);
ok = false;
}
} else { // streaming output
try {
rw.join();
} catch (InterruptedException ie) {
logger.warn(ie);
}
}
}
try {
dataSocket.close();
} catch (IOException e) {
logger.warn("Couldn't close data socket properly.", e);
ok = false;
}
if (ok)
logger.info("Request handled successfully.");
else
logger.info("Request couldn't be handled successfully.");
if (MaryRuntimeUtils.lowMemoryCondition()) {
logger.info("Low memory condition detected (only " + MaryUtils.availableMemory()
+ " bytes left). Triggering garbage collection.");
Runtime.getRuntime().gc();
logger.info("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());
}
public void run() {
try {
AudioSystem.write(request.getAudio(), request.getAudioFileFormat().getType(), output);
output.flush();
output.close();
logger.info("Finished writing output");
} catch (IOException ioe) {
logger.info("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;
}
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();
} else 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!");
}
}
}
}
}
}