    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements. See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache license, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License. You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the license for the specific language governing permissions and
    015     * limitations under the license.
    016     */
    017    package org.apache.logging.log4j.core.appender;
    018
    019    import java.io.IOException;
    020    import java.io.OutputStream;
    021
    022    import org.apache.logging.log4j.core.Layout;
    023
    024    /**
    025     * Manages an OutputStream so that it can be shared by multiple Appenders and will
    026     * allow appenders to reconfigure without requiring a new stream.
    027     */
    028    public class OutputStreamManager extends AbstractManager {
    029
    030        private volatile OutputStream os;
    031        protected final Layout<?> layout;
    032
    033        protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout) {
    034            super(streamName);
    035            this.os = os;
    036            this.layout = layout;
    037            if (layout != null) {
    038                final byte[] header = layout.getHeader();
    039                if (header != null) {
    040                    try {
    041                        this.os.write(header, 0, header.length);
    042                    } catch (final IOException ioe) {
    043                        LOGGER.error("Unable to write header", ioe);
    044                    }
    045                }
    046            }
    047        }
    048
    049        /**
    050         * Creates a Manager.
    051         *
    052         * @param name The name of the stream to manage.
    053         * @param data The data to pass to the Manager.
    054         * @param factory The factory to use to create the Manager.
    055         * @param <T> The type of the OutputStreamManager.
    056         * @return An OutputStreamManager.
    057         */
    058        public static <T> OutputStreamManager getManager(final String name, final T data,
    059                                                     final ManagerFactory<? extends OutputStreamManager, T> factory) {
    060            return AbstractManager.getManager(name, factory, data);
    061        }
    062
    063        /**
    064         * Default hook to write footer during close.
    065         */
    066        @Override
    067        public void releaseSub() {
    068            writeFooter();
    069            close();
    070        }
    071
    072        /**
    073         * Writes the footer.
    074         */
    075        protected void writeFooter() {
    076            if (layout == null) {
    077                return;
    078            }
    079            final byte[] footer = layout.getFooter();
    080            if (footer != null) {
    081                write(footer);
    082            }
    083        }
    084
    085        /**
    086         * Returns the status of the stream.
    087         * @return true if the stream is open, false if it is not.
    088         */
    089        public boolean isOpen() {
    090            return getCount() > 0;
    091        }
    092
    093        protected OutputStream getOutputStream() {
    094            return os;
    095        }
    096
    097        protected void setOutputStream(final OutputStream os) {
    098            final byte[] header = layout.getHeader();
    099            if (header != null) {
    100                try {
    101                    os.write(header, 0, header.length);
    102                    this.os = os; // only update field if os.write() succeeded
    103                } catch (final IOException ioe) {
    104                    LOGGER.error("Unable to write header", ioe);
    105                }
    106            } else {
    107                this.os = os;
    108            }
    109        }
    110
    111        /**
    112         * Some output streams synchronize writes while others do not. Synchronizing here insures that
    113         * log events won't be intertwined.
    114         * @param bytes The serialized Log event.
    115         * @param offset The offset into the byte array.
    116         * @param length The number of bytes to write.
    117         * @throws AppenderLoggingException if an error occurs.
    118         */
    119        protected synchronized void write(final byte[] bytes, final int offset, final int length)  {
    120            //System.out.println("write " + count);
    121            try {
    122                os.write(bytes, offset, length);
    123            } catch (final IOException ex) {
    124                final String msg = "Error writing to stream " + getName();
    125                throw new AppenderLoggingException(msg, ex);
    126            }
    127        }
    128
    129        /**
    130         * Some output streams synchronize writes while others do not.
    131         * @param bytes The serialized Log event.
    132         * @throws AppenderLoggingException if an error occurs.
    133         */
    134        protected void write(final byte[] bytes)  {
    135            write(bytes, 0, bytes.length);
    136        }
    137
    138        protected synchronized void close() {
    139            final OutputStream stream = os; // access volatile field only once per method
    140            if (stream == System.out || stream == System.err) {
    141                return;
    142            }
    143            try {
    144                stream.close();
    145            } catch (final IOException ex) {
    146                LOGGER.error("Unable to close stream " + getName() + ". " + ex);
    147            }
    148        }
    149
    150        /**
    151         * Flushes any buffers.
    152         */
    153        public synchronized void flush() {
    154            try {
    155                os.flush();
    156            } catch (final IOException ex) {
    157                final String msg = "Error flushing stream " + getName();
    158                throw new AppenderLoggingException(msg, ex);
    159            }
    160        }
    161    }