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(OutputStream os, 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         * @return An OutputStreamManager.
044         */
045        public static <T> OutputStreamManager getManager(String name, T data,
046                                                     ManagerFactory<? extends OutputStreamManager, T> factory) {
047            return AbstractManager.getManager(name, factory, data);
048        }
049    
050        /**
051         * Set the header to write when the stream is opened.
052         * @param header The header.
053         */
054        public synchronized void setHeader(byte[] header) {
055            if (header != null) {
056                try {
057                    this.os.write(header, 0, header.length);
058                } catch (IOException ioe) {
059                    LOGGER.error("Unable to write header", ioe);
060                }
061            }
062        }
063    
064        /**
065         * Set the footer to write when the stream is closed.
066         * @param footer The footer.
067         */
068        public synchronized void setFooter(byte[] footer) {
069            if (footer != null) {
070                this.footer = footer;
071            }
072        }
073    
074        /**
075         * Default hook to write footer during close.
076         */
077        @Override
078        public void releaseSub() {
079            if (footer != null) {
080                write(footer);
081            }
082            close();
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(OutputStream os) {
098            this.os = os;
099        }
100    
101        /**
102         * Some output streams synchronize writes while others do not. Synchronizing here insures that
103         * log events won't be intertwined.
104         * @param bytes The serialized Log event.
105         * @param offset The offset into the byte array.
106         * @param length The number of bytes to write.
107         * @throws AppenderRuntimeException if an error occurs.
108         */
109        protected synchronized void write(byte[] bytes, int offset, int length)  {
110            //System.out.println("write " + count);
111            try {
112                os.write(bytes, offset, length);
113            } catch (IOException ex) {
114                String msg = "Error writing to stream " + getName();
115                throw new AppenderRuntimeException(msg, ex);
116            }
117        }
118    
119        /**
120         * Some output streams synchronize writes while others do not. Synchronizing here insures that
121         * log events won't be intertwined.
122         * @param bytes The serialized Log event.
123         * @throws AppenderRuntimeException if an error occurs.
124         */
125        protected void write(byte[] bytes)  {
126            write(bytes, 0, bytes.length);
127        }
128    
129        protected void close() {
130            if (os == System.out || os == System.err) {
131                return;
132            }
133            try {
134                os.close();
135            } catch (IOException ex) {
136                LOGGER.error("Unable to close stream " + getName() + ". " + ex);
137            }
138        }
139    
140        /**
141         * Flush any buffers.
142         */
143        public void flush() {
144            try {
145                os.flush();
146            } catch (IOException ex) {
147                String msg = "Error flushing stream " + getName();
148                throw new AppenderRuntimeException(msg, ex);
149            }
150        }
151    }