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    /**
023     * Manage an OutputStream so that it can be shared by multiple Appenders and will
024     * allow appenders to reconfigure without requiring a new stream.
025     */
026    public class OutputStreamManager extends AbstractManager {
027    
028        private OutputStream os;
029    
030        private byte[] footer = null;
031    
032        protected OutputStreamManager(final OutputStream os, final String streamName) {
033            super(streamName);
034            this.os = os;
035        }
036    
037        /**
038         * Create a Manager.
039         *
040         * @param name The name of the stream to manage.
041         * @param data The data to pass to the Manager.
042         * @param factory The factory to use to create the Manager.
043         * @param <T> The type of the OutputStreamManager.
044         * @return An OutputStreamManager.
045         */
046        public static <T> OutputStreamManager getManager(final String name, final T data,
047                                                     final ManagerFactory<? extends OutputStreamManager, T> factory) {
048            return AbstractManager.getManager(name, factory, data);
049        }
050    
051        /**
052         * Set the header to write when the stream is opened.
053         * @param header The header.
054         */
055        public synchronized void setHeader(final byte[] header) {
056            if (header != null) {
057                try {
058                    this.os.write(header, 0, header.length);
059                } catch (final IOException ioe) {
060                    LOGGER.error("Unable to write header", ioe);
061                }
062            }
063        }
064    
065        /**
066         * Set the footer to write when the stream is closed.
067         * @param footer The footer.
068         */
069        public synchronized void setFooter(final byte[] footer) {
070            if (footer != null) {
071                this.footer = footer;
072            }
073        }
074    
075        /**
076         * Default hook to write footer during close.
077         */
078        @Override
079        public void releaseSub() {
080            if (footer != null) {
081                write(footer);
082            }
083            close();
084        }
085    
086        /**
087         * Returns the status of the stream.
088         * @return true if the stream is open, false if it is not.
089         */
090        public boolean isOpen() {
091            return getCount() > 0;
092        }
093    
094        protected OutputStream getOutputStream() {
095            return os;
096        }
097    
098        protected void setOutputStream(final OutputStream os) {
099            this.os = os;
100        }
101    
102        /**
103         * Some output streams synchronize writes while others do not. Synchronizing here insures that
104         * log events won't be intertwined.
105         * @param bytes The serialized Log event.
106         * @param offset The offset into the byte array.
107         * @param length The number of bytes to write.
108         * @throws AppenderRuntimeException if an error occurs.
109         */
110        protected synchronized void write(final byte[] bytes, final int offset, final int length)  {
111            //System.out.println("write " + count);
112            try {
113                os.write(bytes, offset, length);
114            } catch (final IOException ex) {
115                final String msg = "Error writing to stream " + getName();
116                throw new AppenderRuntimeException(msg, ex);
117            }
118        }
119    
120        /**
121         * Some output streams synchronize writes while others do not. Synchronizing here insures that
122         * log events won't be intertwined.
123         * @param bytes The serialized Log event.
124         * @throws AppenderRuntimeException if an error occurs.
125         */
126        protected void write(final byte[] bytes)  {
127            write(bytes, 0, bytes.length);
128        }
129    
130        protected void close() {
131            if (os == System.out || os == System.err) {
132                return;
133            }
134            try {
135                os.close();
136            } catch (final IOException ex) {
137                LOGGER.error("Unable to close stream " + getName() + ". " + ex);
138            }
139        }
140    
141        /**
142         * Flush any buffers.
143         */
144        public void flush() {
145            try {
146                os.flush();
147            } catch (final IOException ex) {
148                final String msg = "Error flushing stream " + getName();
149                throw new AppenderRuntimeException(msg, ex);
150            }
151        }
152    }