All Downloads are FREE. Search and download functionalities are using the official Maven repository.

marytts.server.RequestHandler Maven / Gradle / Ivy

The newest version!
/**
 * 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!"); } } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy