com.enioka.jqm.tools.MultiplexPrintStream.java Source code

Java tutorial

Introduction

Here is the source code for com.enioka.jqm.tools.MultiplexPrintStream.java

Source

/**
 * Copyright  2013 enioka. All rights reserved
 *
 * 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 com.enioka.jqm.tools;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;

import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;

import com.enioka.jqm.jpamodel.GlobalParameter;

/**
 * The goal of this Stream is to provide a replacement for stdout/err in which every running job instance has its own personal flow. This is
 * basically flow multiplexing, with the multiplexing key being the caller Thread object. Used by default, can be disabled with a
 * {@link GlobalParameter}. <br>
 * Should a payload create a new thread, its stdout would go to the global log as the multiplexing key is the Thread. But is not a big deal
 * as creating threads inside an app server is not a good idea anyway.
 */
class MultiplexPrintStream extends PrintStream {
    private static Logger jqmlogger = Logger.getLogger(MultiplexPrintStream.class);
    private static Logger alljobslogger = Logger.getLogger("alljobslogger");
    private static String ls = System.getProperty("line.separator");

    private BufferedWriter original = null;
    private boolean useCommonLogFile = false;
    private ThreadLocal<BufferedWriter> logger = new ThreadLocal<BufferedWriter>() {
        protected BufferedWriter initialValue() {
            return original;
        }
    };
    String rootLogDir;

    MultiplexPrintStream(OutputStream out, String rootLogDir, boolean alsoWriteToCommonLog) {
        super(out);
        this.useCommonLogFile = alsoWriteToCommonLog;
        this.original = new BufferedWriter(new OutputStreamWriter(out));
        this.rootLogDir = rootLogDir;

        File d = new File(this.rootLogDir);
        if (!d.isDirectory() && !d.mkdir()) {
            throw new JqmInitError("could not create log dir " + this.rootLogDir);
        }
    }

    void registerThread(String fileName) {
        try {
            unregisterThread();
            Writer w = new FileWriter(FilenameUtils.concat(rootLogDir, fileName), true);
            logger.set(new BufferedWriter(w));
        } catch (IOException e) {
            // A PrintStream is supposed to never throw IOException
            jqmlogger.warn("could not register specific logger for a thread. Stdout will be used instead.", e);
        }
    }

    void unregisterThread() {
        try {
            BufferedWriter bf = logger.get();
            if (bf != original) {
                bf.close();
                logger.remove();
            }
        } catch (IOException e) {
            // A PrintStream is supposed to never throw IOException
            jqmlogger.warn("could not close log file", e);
        }
    }

    /** Check to make sure that the stream has not been closed */
    private void ensureOpen() throws IOException {
        if (out == null) {
            throw new IOException("Stream closed");
        }
    }

    private void write(String s) {
        write(s, false);
    }

    private void write(String s, boolean newLine) {
        BufferedWriter textOut = logger.get();
        try {
            ensureOpen();
            textOut.write(s);
            if (newLine) {
                textOut.newLine();
            }
            textOut.flush();

            if (useCommonLogFile && textOut != original) {
                alljobslogger.info(s + (newLine ? ls : ""));
            }
        } catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        } catch (IOException x) {
            // don't log exceptions, it could trigger a StackOverflow
        }
    }

    // TODO: write something that's not a performance hog...
    @Override
    public void write(byte[] buf, int off, int len) {
        write(new String(buf, off, len));
    }

    // ///////////////////////////////////////////////////////////////////
    @Override
    public void println() {
        write("", true);
    }

    @Override
    public void println(boolean b) {
        write(b ? "true" : "false", true);
    }

    @Override
    public void println(char x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(int x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(long x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(float x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(double x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(char[] x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(String x) {
        write(String.valueOf(x), true);
    }

    @Override
    public void println(Object x) {
        write(String.valueOf(x), true);
    }

    // ///////////////////////////////////////////////////////////////////
    @Override
    public void print(boolean b) {
        write(b ? "true" : "false");
    }

    @Override
    public void print(char x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(int x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(long x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(float x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(double x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(char[] x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(String x) {
        write(String.valueOf(x), false);
    }

    @Override
    public void print(Object x) {
        write(String.valueOf(x), false);
    }
}