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